Accessibility

This page documents the specific decisions made in this build, not a generic policy. If something below stops being true, it's a bug — please report it.

Motion

The motion toggle in the header (Motion: on/off) is a real CSS state — html[data-motion] — not a one-time sweep over running animations. It defaults to your OS's prefers-reduced-motion setting and is remembered across visits via localStorage.

Under reduced motion or the toggle set to off:

  • The fridge door is already open on load. There is no swing animation to sit through.
  • The pull-tab's idle "breathing" glow animation is disabled.
  • The shelf-item physics nudge (hover-to-jostle neighbours) is a no-op.
  • The condensation effect's SVG <animate> is explicitly paused via SVGSVGElement.pauseAnimations() — CSS media queries cannot reach SMIL animations inside an SVG filter, so this one is paused with JavaScript, watching both the toggle and the OS setting.
  • View Transition morphs (shelf item → story panel) fall back to an instant swap.

Keyboard

  • Opening the fridge door is drag-operable, but dragging is never the only way — Enter or Space on the pull-tab toggles it fully open or closed. This satisfies WCAG 2.5.7 Dragging Movements (AA): every dragging interaction on this page has a single-pointer, non-dragging equivalent.
  • Shelf items are laid out as a real 2D grid widget: arrow keys move focus row/column, Home/End jump to the first/last item, and only the currently-focused item is in the tab sequence (roving tabindex).
  • The jar-in-the-back puzzle in the freezer is two ordinary buttons — no drag, no timing requirement. Moving both reveals a third button; there's no pointer-only path through it.
  • The story panel is a real role="dialog": opening it moves focus to its close button, and Escape closes it and returns focus to the item that opened it.

Structure

Every "shelf" is a <section> with its own <h2>. The page reads as a linear document top to bottom with CSS off — the dark kitchen and the fridge light are entirely decorative layers on top of ordinary content, not a replacement for it. Decorative elements (the door panel, the lightwell glow, the condensation) all carry aria-hidden="true".

The fridge door's open/closed state is announced through a polite aria-live region ("Fridge door open. Light spills into the kitchen.") — the light itself is a visual effect, so the state is also available as text.

Contrast & the lit-kitchen mode

The default dark palette already clears 7:1 (AAA) at its worst text pair — measured in sRGB, not eyeballed. "Turn on the kitchen light" isn't there to rescue the contrast; it's for anyone who needs or prefers a light background. "Turn on the kitchen light" flattens the entire page — background, shelf cards, freezer, everything — to a token pair that also clears 7:1. This isn't a separate theme maintained on the side: it's the same tokens, reassigned. prefers-contrast: more triggers that same authored palette automatically. forced-colors: active (Windows Contrast Themes, tested) is handled differently on purpose: the OS is going to override text, background and link colors with its own system palette regardless of what we author, so those tokens map to Canvas/CanvasText/LinkText instead of literal colors the browser would ignore anyway — and the purely decorative layers (the light glow, the condensation, the cast shadows) are hidden outright, since they'd otherwise render unforced on top of a forced palette.

Palette values are authored in OKLCH for perceptually even steps, but contrast ratios were verified with an sRGB-based checker — OKLCH lightness is not a stand-in for WCAG's sRGB relative luminance.

What this page doesn't do

  • No audio autoplays anywhere.
  • No content relies on DeviceMotionEvent or device tilt.
  • Nothing requires hover alone — every hover-revealed state has a focus-visible equivalent.
  • No interactive target is smaller than 24×24 CSS px (WCAG 2.5.8).

The closing line

The sentence at the end of the page is chosen from a small, hand-written set based on what you actually did during this visit — which shelves you opened, whether you found the jar pushed to the back of the freezer, whether you closed the fridge door and opened it again, and whether you navigated only by keyboard. All of that is tracked in memory, for this visit only. Nothing is sent anywhere, nothing is written to localStorage, and none of it survives a reload. The paragraph's default text, already present in the page's HTML, is what you see if JavaScript doesn't run at all.