diff --git a/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte b/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte index 310ed0b..cfa81ae 100644 --- a/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte +++ b/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte @@ -70,6 +70,19 @@ const SLIDER_PERSIST_DEBOUNCE_MS = 100; const SLIDER_PADDING_MOBILE_PX = 48; const SLIDER_PADDING_DESKTOP_PX = 96; +/** + * Position bounds (percent of container width). + */ +const SLIDER_MIN = 0; +const SLIDER_MAX = 100; + +/** + * Fine and coarse keyboard step sizes. Shift / Page keys use the coarse + * step; bare arrow keys use the fine step. + */ +const SLIDER_STEP_FINE = 1; +const SLIDER_STEP_COARSE = 10; + const fontA = $derived(comparisonStore.fontA); const fontB = $derived(comparisonStore.fontB); const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady); @@ -130,6 +143,46 @@ function startDragging(e: PointerEvent) { handleMove(e); } +/** + * Keyboard control for the comparison slider. Implements the standard + * ARIA slider keyboard contract: arrows step the position, Shift+arrow + * and PageUp/PageDown jump by the coarse step, Home/End snap to bounds. + */ +function handleKeydown(e: KeyboardEvent) { + const coarse = e.shiftKey; + const step = coarse ? SLIDER_STEP_COARSE : SLIDER_STEP_FINE; + const current = sliderSpring.target; + let next = current; + + switch (e.key) { + case 'ArrowLeft': + case 'ArrowDown': + next = current - step; + break; + case 'ArrowRight': + case 'ArrowUp': + next = current + step; + break; + case 'PageDown': + next = current - SLIDER_STEP_COARSE; + break; + case 'PageUp': + next = current + SLIDER_STEP_COARSE; + break; + case 'Home': + next = SLIDER_MIN; + break; + case 'End': + next = SLIDER_MAX; + break; + default: + return; + } + + e.preventDefault(); + sliderSpring.target = Math.max(SLIDER_MIN, Math.min(SLIDER_MAX, next)); +} + const storeSliderPosition = debounce((value: number) => { comparisonStore.sliderPosition = value; }, SLIDER_PERSIST_DEBOUNCE_MS); @@ -271,8 +324,12 @@ const paddingClass = $derived( role="slider" tabindex="0" aria-valuenow={Math.round(sliderPos)} + aria-valuemin={SLIDER_MIN} + aria-valuemax={SLIDER_MAX} + aria-orientation="horizontal" aria-label="Font comparison slider" onpointerdown={startDragging} + onkeydown={handleKeydown} class=" relative w-full max-w-6xl h-full flex flex-col justify-center