fix: resolve all svelte-check warnings (a11y + state_referenced_locally)

- Add svelte-ignore for intentional state_referenced_locally patterns
- Wrap clickable images in button elements for keyboard accessibility
- Add proper ARIA roles (dialog, textbox) with tabindex and focus management
- Fix lightbox Escape key handling to not close parent editor overlay

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-03-09 02:37:07 +01:00
co-authored by Claude Opus 4.6
parent 1f0bbd570e
commit e09c2a9c02
9 changed files with 44 additions and 17 deletions
+1
View File
@@ -19,6 +19,7 @@
let { content, onChange }: Props = $props();
// svelte-ignore state_referenced_locally
let items = $state<ChecklistItem[]>(parseChecklist(content));
let doneExpanded = $state(true);
const flipDurationMs = 150;
+12 -3
View File
@@ -9,6 +9,8 @@
const { src, alt, onClose }: Props = $props();
let dialogEl: HTMLDivElement | undefined = $state();
function handleKeydown(e: KeyboardEvent) {
if (e.key === 'Escape') {
e.stopPropagation();
@@ -21,14 +23,21 @@
onClose();
}
}
$effect(() => {
dialogEl?.focus();
});
</script>
<svelte:window onkeydown={handleKeydown} />
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
bind:this={dialogEl}
class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 animate-[fade-in_150ms_ease-out]"
onclick={handleBackgroundClick}
onkeydown={handleKeydown}
role="dialog"
aria-modal="true"
aria-label="Image lightbox"
tabindex="-1"
data-testid="image-lightbox"
>
<button
+10 -6
View File
@@ -119,14 +119,18 @@
{@const isPending = 'pending' in attachment && attachment.pending}
{@const imgSrc = isPending ? attachment.path : `/api/notes/${noteId}/attachments?attachmentId=${attachment.id}`}
<div class="group relative">
<!-- svelte-ignore a11y_no_noninteractive_element_interactions a11y_click_events_have_key_events -->
<img
src={imgSrc}
alt={attachment.filename}
class="h-20 w-20 cursor-pointer rounded-sm object-cover {isPending ? 'opacity-70' : ''}"
<button
type="button"
class="h-20 w-20 cursor-pointer rounded-sm p-0 border-0 bg-transparent overflow-hidden"
onclick={() => { lightboxSrc = imgSrc; lightboxAlt = attachment.filename; }}
data-testid="attachment-thumbnail"
/>
>
<img
src={imgSrc}
alt={attachment.filename}
class="h-20 w-20 object-cover {isPending ? 'opacity-70' : ''}"
/>
</button>
{#if isPending}
<div class="absolute bottom-0.5 left-0.5 rounded-sm bg-black/50 p-0.5" title="Pending sync">
<RefreshCw class="h-3 w-3 text-white" />
+12 -7
View File
@@ -73,6 +73,7 @@
}
</script>
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
<article
class="group relative cursor-pointer rounded-sm border border-[var(--border-subtle)] p-4 transition-all hover:border-[var(--primary)] shadow-[var(--card-shadow)] hover:shadow-[var(--card-shadow-hover)] {fullHeight ? 'h-full' : ''}"
style={cardStyle}
@@ -88,15 +89,19 @@
<div class="-mx-4 -mt-4 mb-3 flex overflow-hidden rounded-t-sm" data-testid="card-thumbnails">
{#each featuredAttachments.slice(0, 3) as attachment}
<div class="relative min-w-0 flex-1">
<!-- svelte-ignore a11y_no_noninteractive_element_interactions a11y_click_events_have_key_events -->
<img
src="/api/notes/{note.id}/attachments?attachmentId={attachment.id}&thumb=1"
alt={attachment.filename}
class="h-24 w-full cursor-pointer object-cover"
loading="lazy"
<button
type="button"
class="h-24 w-full cursor-pointer p-0 border-0 bg-transparent"
onclick={(e) => { e.stopPropagation(); lightboxSrc = `/api/notes/${note.id}/attachments?attachmentId=${attachment.id}`; lightboxAlt = attachment.filename; }}
data-testid="card-thumbnail"
/>
>
<img
src="/api/notes/{note.id}/attachments?attachmentId={attachment.id}&thumb=1"
alt={attachment.filename}
class="h-24 w-full object-cover"
loading="lazy"
/>
</button>
</div>
{/each}
{#if featuredAttachments.length > 3}
+1
View File
@@ -33,6 +33,7 @@
let checklistMode = $state(note?.checklistMode ?? false);
let showColorPicker = $state(false);
let showImageUpload = $state(false);
// svelte-ignore state_referenced_locally
let rawMarkdownMode = $state(note?.checklistMode ?? false);
let textareaEl: HTMLTextAreaElement | undefined = $state();
let tiptapEditor: Editor | undefined = $state();
+4 -1
View File
@@ -57,11 +57,14 @@
});
</script>
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
bind:this={element}
class="tiptap-wrapper prose prose-sm flex min-h-[300px] max-w-none flex-col px-4 py-2 text-[var(--text)]"
data-testid="tiptap-editor"
role="textbox"
aria-multiline="true"
tabindex="0"
onclick={() => editor?.commands.focus()}
></div>
+2
View File
@@ -14,7 +14,9 @@
let { data } = $props();
// Profile state
// svelte-ignore state_referenced_locally
let displayName = $state(data.user?.displayName || '');
// svelte-ignore state_referenced_locally
let email = $state(data.user?.email || '');
let profileMsg = $state('');
let profileError = $state(false);
@@ -3,6 +3,7 @@
import PasswordStrengthMeter from '$lib/components/PasswordStrengthMeter.svelte';
let { data } = $props();
// svelte-ignore state_referenced_locally
let users = $state<User[]>(data.users);
// Create user form
+1
View File
@@ -42,6 +42,7 @@
}
}
// svelte-ignore state_referenced_locally
const providers = data.providers ?? [];
</script>