fix: resolve all 40 Svelte dev warnings for clean startup

- Add $state() to 4 reactive variables (guestMode, emailInput, passwordInput, searchInputElement)
- Replace 3 deprecated <svelte:component> with direct component references
- Fix 8 a11y issues: add ARIA roles, tabindex, keyboard handlers to click-handler divs
- Remove 22 unused CSS selectors across 8 shared-ui components

Zero warnings on dev startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-03 12:01:17 +02:00
parent 2bd8f0babf
commit c21793baaf
20 changed files with 55 additions and 133 deletions

View file

@ -146,7 +146,7 @@
<!-- Email (readonly) -->
<div>
<label class="block text-sm font-medium mb-2 text-muted-foreground">E-Mail</label>
<p class="block text-sm font-medium mb-2 text-muted-foreground">E-Mail</p>
<div class="px-4 py-3 border rounded-xl bg-muted text-muted-foreground">
{authStore.user?.email || 'Nicht verfügbar'}
</div>

View file

@ -229,7 +229,7 @@
}
// ── Guest Mode ──────────────────────────────────────────
let guestMode: GuestMode | null = null;
let guestMode = $state<GuestMode | null>(null);
// ── Onboarding ──────────────────────────────────────────
function handleOnboardingComplete() {

View file

@ -203,16 +203,21 @@
<svelte:window onkeydown={handleKeydown} />
{#if visible}
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
<div
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
onclick={handleBackdropClick}
onkeydown={(e) => {
if (e.key === 'Escape') handleBackdropClick();
}}
role="presentation"
tabindex="-1"
>
<div
class="bg-card border-border relative mx-4 w-full max-w-md rounded-xl border p-6 shadow-2xl"
role="dialog"
aria-modal="true"
aria-labelledby="auth-gate-title"
tabindex="-1"
onclick={(e) => e.stopPropagation()}
use:trapFocus
>
@ -230,7 +235,8 @@
<div
class="bg-primary/10 mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full"
>
<svelte:component this={ActionIcon()} size={32} class="text-primary" />
{@const Icon = ActionIcon()}
<Icon size={32} class="text-primary" />
</div>
<!-- Title -->

View file

@ -238,10 +238,12 @@
{#if visible && appInfo}
<!-- Modal Backdrop -->
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<div
class="modal-backdrop"
onclick={handleBackdropClick}
onkeydown={(e) => {
if (e.key === 'Escape') handleBackdropClick();
}}
role="dialog"
aria-modal="true"
aria-labelledby="welcome-title"

View file

@ -172,8 +172,8 @@
let rememberMe = $state(false);
let showSuccess = $state(false);
let shakeError = $state(false);
let emailInput: HTMLInputElement;
let passwordInput: HTMLInputElement;
let emailInput = $state<HTMLInputElement | undefined>(undefined);
let passwordInput = $state<HTMLInputElement | undefined>(undefined);
let successAnnouncement = $state('');
let showVerifiedBanner = $state(verified);
let showEmailNotVerified = $state(false);

View file

@ -32,7 +32,8 @@
{#if active.action}
<button class="notification-action" onclick={active.action.onClick}>
{#if active.action.icon}
<svelte:component this={active.action.icon} size={14} weight="bold" />
{@const ActionIcon = active.action.icon}
<ActionIcon size={14} weight="bold" />
{/if}
{active.action.label}
<ArrowRight size={12} />

View file

@ -439,13 +439,6 @@
border-bottom: 1px solid hsl(var(--color-border));
}
.command-icon {
width: 1.25rem;
height: 1.25rem;
color: hsl(var(--color-muted-foreground));
flex-shrink: 0;
}
/* Input with syntax highlighting overlay */
.input-highlight-container {
position: relative;
@ -668,13 +661,6 @@
text-overflow: ellipsis;
}
.result-favorite {
width: 1rem;
height: 1rem;
color: hsl(var(--color-error));
flex-shrink: 0;
}
.quick-actions-list {
padding: 0.5rem;
}
@ -699,12 +685,6 @@
background: hsl(var(--color-surface-hover));
}
.quick-action-icon {
width: 1.25rem;
height: 1.25rem;
color: hsl(var(--color-muted-foreground));
}
.quick-action span {
flex: 1;
font-size: 0.9375rem;

View file

@ -86,7 +86,6 @@
{#if visible}
<!-- Backdrop to block clicks on elements behind -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="context-menu-backdrop"
onpointerdown={(e) => {
@ -104,6 +103,11 @@
e.stopPropagation();
onClose();
}}
onkeydown={(e) => {
if (e.key === 'Escape') onClose();
}}
role="presentation"
tabindex="-1"
></div>
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->

View file

@ -60,7 +60,8 @@
role="button"
tabindex="-1"
>
<svelte:component this={iconComponent} size={20} weight="bold" />
{@const Icon = iconComponent}
<Icon size={20} weight="bold" />
{#if label}
<span class="action-label">{label}</span>
{/if}

View file

@ -217,8 +217,16 @@
<svelte:window onkeydown={handleKeydown} onclick={handleClickOutside} />
<!-- Trigger wrapper -->
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
<div class="confirmation-popover-trigger" bind:this={triggerRef} onclick={handleTriggerClick}>
<div
class="confirmation-popover-trigger"
bind:this={triggerRef}
onclick={handleTriggerClick}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') handleTriggerClick();
}}
role="button"
tabindex="0"
>
{@render children()}
</div>

View file

@ -291,10 +291,19 @@
</script>
{#if open}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="spotlight-overlay" onclick={onClose} onkeydown={handleKeydown}>
<div
class="spotlight-overlay"
onclick={onClose}
onkeydown={handleKeydown}
role="presentation"
tabindex="-1"
>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="spotlight-modal" onclick={(e) => e.stopPropagation()}>
<div
class="spotlight-modal"
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
>
<!-- Search input -->
<div class="spotlight-input-wrapper">
<svg class="spotlight-search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -591,11 +591,6 @@
border-color: var(--color-primary-800, rgba(59, 130, 246, 0.4));
}
.split-icon {
width: 0.875rem;
height: 0.875rem;
}
/* Footer for custom content (e.g., a11y toggles) */
.dropdown-footer {
animation: fanIn 0.15s ease-out forwards;

View file

@ -208,12 +208,6 @@
background: rgba(255, 255, 255, 0.15);
}
.tab-icon {
width: 1.125rem;
height: 1.125rem;
flex-shrink: 0;
}
.tab-label {
font-size: 0.8125rem;
font-weight: 500;

View file

@ -273,12 +273,6 @@
0 4px 6px -2px hsl(var(--color-foreground) / 0.05);
}
.pill-icon {
width: 1rem;
height: 1rem;
flex-shrink: 0;
}
.pill-label {
font-size: 0.8125rem;
}
@ -316,17 +310,6 @@
color: hsl(var(--color-primary));
}
.chevron-icon {
width: 0.75rem;
height: 0.75rem;
transition: transform 0.2s;
margin-left: 0.125rem;
}
.chevron-icon.rotated {
transform: rotate(180deg);
}
.backdrop {
position: fixed;
inset: 0;

View file

@ -174,12 +174,6 @@
cursor: not-allowed;
}
.switcher-icon {
width: 1rem;
height: 1rem;
flex-shrink: 0;
}
.switcher-label {
line-height: 1;
}

View file

@ -381,22 +381,6 @@
transform: scale(0.95) !important;
}
/* DnD: Item hovering over tag pill */
.tag-drop-highlight {
transform: scale(1.15) !important;
background: var(--tag-color) !important;
border-color: var(--tag-color) !important;
box-shadow: 0 0 16px color-mix(in srgb, var(--tag-color) 40%, transparent) !important;
}
.tag-drop-highlight .tag-dot {
background-color: white !important;
}
.tag-drop-highlight .tag-name {
color: white !important;
}
/* DnD: Success flash after drop */
:global(.tag-pill.mana-passive-zone-success) {
animation: tag-drop-success 400ms ease-out;

View file

@ -181,21 +181,6 @@
background: hsl(var(--color-muted));
}
.toolbar-fab.active .fab-icon {
color: hsl(var(--color-primary));
}
.fab-icon {
width: 1.5rem;
height: 1.5rem;
color: hsl(var(--color-muted-foreground));
transition: color 0.2s ease;
}
.toolbar-fab:hover .fab-icon {
color: hsl(var(--color-foreground));
}
.toolbar-divider {
width: 1px;
height: 1.5rem;

View file

@ -65,7 +65,7 @@
let showFilters = $state(false);
let showKeyboardHelp = $state(false);
let strengthValue = $state(minStrength);
let searchInputElement: HTMLInputElement;
let searchInputElement = $state<HTMLInputElement | undefined>(undefined);
// Sync searchInput with external searchQuery
$effect(() => {

View file

@ -688,11 +688,6 @@
flex-shrink: 0;
}
.app-icon svg {
width: 100%;
height: 100%;
}
.input-wrapper {
position: relative;
flex: 1;
@ -799,11 +794,6 @@
transform: none;
}
.submit-btn svg {
width: 1rem;
height: 1rem;
}
/* Results Panel */
.results-panel {
position: absolute;
@ -878,11 +868,6 @@
background: hsl(var(--color-success));
}
.result-avatar.create-avatar svg {
width: 1.25rem;
height: 1.25rem;
}
.result-item.search-option:hover,
.result-item.search-option.selected {
background: hsl(var(--color-primary) / 0.1);
@ -892,11 +877,6 @@
background: hsl(var(--color-muted-foreground) / 0.3);
}
.result-avatar.search-avatar svg {
width: 1.125rem;
height: 1.125rem;
}
.result-info {
flex: 1;
min-width: 0;
@ -918,13 +898,6 @@
text-overflow: ellipsis;
}
.favorite-icon {
width: 1rem;
height: 1rem;
color: hsl(var(--color-error, 0 84% 60%));
flex-shrink: 0;
}
.create-shortcut {
padding: 0.25rem 0.5rem;
font-size: 0.6875rem;

View file

@ -101,18 +101,21 @@
<svelte:window onkeydown={handleKeydown} />
{#if visible}
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<div
class="fixed inset-0 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
style="z-index: 9990;"
onclick={handleBackdropClick}
role="dialog"
aria-modal="true"
onkeydown={(e) => {
if (e.key === 'Escape') handleBackdropClick();
}}
role="presentation"
tabindex="-1"
>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="w-full max-w-md rounded-2xl border border-white/10 bg-gray-900 shadow-2xl"
role="dialog"
aria-modal="true"
tabindex="-1"
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
>
@ -143,7 +146,7 @@
{#if title}
<div>
<label class="block text-xs text-gray-400 mb-1">Titel</label>
<p class="block text-xs text-gray-400 mb-1">Titel</p>
<p class="text-sm text-white">{title}</p>
</div>
{/if}