eleven / messenger Public

prototype: complete the prefers-reduced-motion coverage (#366)

Dust, confetti, and card shake were already skipped; now the strobing 11
LED, the VU flicker jitter, and the LED/eleven-number transitions go
still too — states change instantly instead of animating. A mid-session
toggle of the OS setting reloads the page (it's stateless) so the new
preference applies everywhere.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell a2d19b51cef1610c0c636a8cb8a3ab0c169d79c0 parent 4fcf2e7
1 file changed, +14 −3
  • prototypes/space-lever.html +14 −3
diff --git a/prototypes/space-lever.html b/prototypes/space-lever.html
index c4c8da9..6a11085 100644
--- a/prototypes/space-lever.html
+++ b/prototypes/space-lever.html
@@ -79,6 +79,13 @@
.led.on-r { background: var(--led-r); box-shadow: 0 0 8px color-mix(in srgb, var(--led-r) 70%, transparent); }
@keyframes led-fury { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
.led.fury { animation: led-fury .14s linear infinite; }
+ /* Reduced motion: no strobe, no colour/scale transitions — states still
+ change, they just change instantly. (The dust, confetti, shake, and VU
+ flicker are skipped in the script under the same query.) */
+ @media (prefers-reduced-motion: reduce) {
+ .led.fury { animation: none; }
+ .led, .num.eleven { transition: none; }
+ }
/* The fader: a recessed slot, ticks above it, numbers below, and the cap. */
.fader { position: relative; height: 46px; margin: 0 2px; touch-action: none; cursor: grab; }
@@ -420,10 +427,11 @@ function paint() {
nums[10].classList.toggle("hit", past);
// VU: lit up to the current notch, with a one-LED flicker while the signal's
- // hot so it reads as a live meter, not a progress bar. At eleven: all of it,
- // and the last LED strobes.
+ // hot so it reads as a live meter, not a progress bar (steady under reduced
+ // motion). At eleven: all of it, and the last LED strobes.
const hot = levelNow();
- const lit = past ? 11 : Math.round(value) + (hot > 0.4 && Math.random() < 0.35 ? (Math.random() < 0.5 ? -1 : 0) : 0);
+ const flicker = !reduced && hot > 0.4 && Math.random() < 0.35 ? (Math.random() < 0.5 ? -1 : 0) : 0;
+ const lit = past ? 11 : Math.round(value) + flicker;
leds.forEach((led, i) => {
const on = i < lit;
led.className = "led" + (on ? " on-" + led.dataset.c : "") +
@@ -561,6 +569,9 @@ bind("kick", "kick", (v) => v.toFixed(2)); bind("motes", "motes"); bind("build",
addEventListener("resize", initDust);
matchMedia("(prefers-color-scheme: dark)").addEventListener("change", initDust);
+// `reduced` is read once at load; if the OS setting flips mid-session, reload —
+// the page is stateless, and a fresh boot honours the new preference everywhere.
+matchMedia("(prefers-reduced-motion: reduce)").addEventListener("change", () => location.reload());
initDust();
rafId = requestAnimationFrame(tick);
</script>