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 {
|
||||
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>
|
||||
|
||||
@@ -48,4 +48,11 @@ let sorted = $derived([...blocks].sort((a, b) => a.sortOrder - b.sortOrder));
|
||||
.flash-highlight {
|
||||
animation: flash 1.2s ease-out;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.flash-highlight {
|
||||
animation: none;
|
||||
background-color: rgba(0, 199, 177, 0.18);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -56,6 +56,10 @@ let activeAnnotationId = $state<string | null>(null);
|
||||
let highlightBlockId = $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 ─────────────────────────────────────────────────────
|
||||
|
||||
let transcriptionBlocks = $state<TranscriptionBlockData[]>([]);
|
||||
@@ -162,16 +166,20 @@ async function handleAnnotationClick(annotationId: string) {
|
||||
const block = transcriptionBlocks.find((b) => b.annotationId === annotationId);
|
||||
if (block) {
|
||||
highlightBlockId = block.id;
|
||||
setTimeout(() => {
|
||||
highlightBlockId = null;
|
||||
}, 1500);
|
||||
setTimeout(
|
||||
() => {
|
||||
highlightBlockId = null;
|
||||
},
|
||||
prefersReducedMotion ? 2000 : 1500
|
||||
);
|
||||
}
|
||||
|
||||
// Wait for DOM to render, then scroll to the matching block
|
||||
const scrollBehavior = prefersReducedMotion ? 'instant' : 'smooth';
|
||||
requestAnimationFrame(() => {
|
||||
if (block) {
|
||||
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) {
|
||||
activeAnnotationId = annotationId;
|
||||
flashAnnotationId = annotationId;
|
||||
setTimeout(() => {
|
||||
flashAnnotationId = null;
|
||||
}, 1500);
|
||||
setTimeout(
|
||||
() => {
|
||||
flashAnnotationId = null;
|
||||
},
|
||||
prefersReducedMotion ? 2000 : 1500
|
||||
);
|
||||
}
|
||||
|
||||
// Load blocks when transcribe mode is entered and set default panel mode
|
||||
|
||||
Reference in New Issue
Block a user