feat(a11y): respect prefers-reduced-motion for scroll-sync
Uses scrollIntoView behavior 'instant' instead of 'smooth', skips CSS animations (static highlight instead), and extends timeout to 2s for reduced-motion users. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -194,4 +194,11 @@ const containerStyle = $derived(
|
|||||||
.annotation-flash {
|
.annotation-flash {
|
||||||
animation: annotation-flash-anim 1.5s ease-out;
|
animation: annotation-flash-anim 1.5s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.annotation-flash {
|
||||||
|
animation: none;
|
||||||
|
outline: 3px solid rgba(0, 199, 177, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -48,4 +48,11 @@ let sorted = $derived([...blocks].sort((a, b) => a.sortOrder - b.sortOrder));
|
|||||||
.flash-highlight {
|
.flash-highlight {
|
||||||
animation: flash 1.2s ease-out;
|
animation: flash 1.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.flash-highlight {
|
||||||
|
animation: none;
|
||||||
|
background-color: rgba(0, 199, 177, 0.18);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ let activeAnnotationId = $state<string | null>(null);
|
|||||||
let highlightBlockId = $state<string | null>(null);
|
let highlightBlockId = $state<string | null>(null);
|
||||||
let flashAnnotationId = $state<string | null>(null);
|
let flashAnnotationId = $state<string | null>(null);
|
||||||
|
|
||||||
|
const prefersReducedMotion = $derived(
|
||||||
|
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||||
|
);
|
||||||
|
|
||||||
// ── Transcription blocks ─────────────────────────────────────────────────────
|
// ── Transcription blocks ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
let transcriptionBlocks = $state<TranscriptionBlockData[]>([]);
|
let transcriptionBlocks = $state<TranscriptionBlockData[]>([]);
|
||||||
@@ -162,16 +166,20 @@ async function handleAnnotationClick(annotationId: string) {
|
|||||||
const block = transcriptionBlocks.find((b) => b.annotationId === annotationId);
|
const block = transcriptionBlocks.find((b) => b.annotationId === annotationId);
|
||||||
if (block) {
|
if (block) {
|
||||||
highlightBlockId = block.id;
|
highlightBlockId = block.id;
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
highlightBlockId = null;
|
() => {
|
||||||
}, 1500);
|
highlightBlockId = null;
|
||||||
|
},
|
||||||
|
prefersReducedMotion ? 2000 : 1500
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for DOM to render, then scroll to the matching block
|
// Wait for DOM to render, then scroll to the matching block
|
||||||
|
const scrollBehavior = prefersReducedMotion ? 'instant' : 'smooth';
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (block) {
|
if (block) {
|
||||||
const el = document.querySelector(`[data-block-id="${block.id}"]`);
|
const el = document.querySelector(`[data-block-id="${block.id}"]`);
|
||||||
el?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
el?.scrollIntoView({ behavior: scrollBehavior, block: 'nearest' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -179,9 +187,12 @@ async function handleAnnotationClick(annotationId: string) {
|
|||||||
function handleParagraphClick(annotationId: string) {
|
function handleParagraphClick(annotationId: string) {
|
||||||
activeAnnotationId = annotationId;
|
activeAnnotationId = annotationId;
|
||||||
flashAnnotationId = annotationId;
|
flashAnnotationId = annotationId;
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
flashAnnotationId = null;
|
() => {
|
||||||
}, 1500);
|
flashAnnotationId = null;
|
||||||
|
},
|
||||||
|
prefersReducedMotion ? 2000 : 1500
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load blocks when transcribe mode is entered and set default panel mode
|
// Load blocks when transcribe mode is entered and set default panel mode
|
||||||
|
|||||||
Reference in New Issue
Block a user