feat: fix reduce motion controls

This commit is contained in:
Jet 2026-03-27 13:15:34 -07:00
parent 3f74df0b3a
commit 38efffa9b9
No known key found for this signature in database
3 changed files with 26 additions and 70 deletions

View file

@ -1,8 +1,6 @@
const STORAGE_KEY = "background-motion-preference";
const MOTION_QUERY = "(prefers-reduced-motion: reduce)"; const MOTION_QUERY = "(prefers-reduced-motion: reduce)";
const STILL_STEPS = 5; const STILL_STEPS = 5;
type MotionPreference = "auto" | "off" | "on";
type BackgroundMode = "animated" | "still" | "failed"; type BackgroundMode = "animated" | "still" | "failed";
interface BackgroundActions { interface BackgroundActions {
@ -11,23 +9,7 @@ interface BackgroundActions {
renderStill: (steps: number) => void; renderStill: (steps: number) => void;
} }
function readPreference(): MotionPreference { function getMode(reducedMotion: boolean): BackgroundMode {
const stored = window.localStorage.getItem(STORAGE_KEY);
return stored === "off" || stored === "on" || stored === "auto"
? stored
: "auto";
}
function writePreference(preference: MotionPreference) {
window.localStorage.setItem(STORAGE_KEY, preference);
}
function getMode(
preference: MotionPreference,
reducedMotion: boolean,
): BackgroundMode {
if (preference === "on") return "animated";
if (preference === "off") return "still";
return reducedMotion ? "still" : "animated"; return reducedMotion ? "still" : "animated";
} }
@ -39,46 +21,22 @@ function applyCanvasState(mode: BackgroundMode) {
} }
} }
function updateControls(
preference: MotionPreference,
mode: BackgroundMode,
reducedMotion: boolean,
) {
const button = document.getElementById(
"background-toggle",
) as HTMLButtonElement | null;
const status = document.getElementById("background-status");
if (!button || !status) return;
button.textContent = `motion ${preference}`;
if (mode === "failed") {
status.textContent = "background unavailable";
return;
}
if (mode === "still") {
status.textContent =
preference === "auto" && reducedMotion
? "still frame"
: "background still";
return;
}
status.textContent = "background live";
}
export function initBackgroundControls(actions: BackgroundActions) { export function initBackgroundControls(actions: BackgroundActions) {
const media = window.matchMedia(MOTION_QUERY); const media = window.matchMedia(MOTION_QUERY);
let preference = readPreference(); let mode: BackgroundMode = getMode(media.matches);
let mode: BackgroundMode = getMode(preference, media.matches); let failed = false;
const applyMode = () => { const applyMode = (restartAnimation = false) => {
mode = getMode(preference, media.matches); if (failed) return;
mode = getMode(media.matches);
applyCanvasState(mode); applyCanvasState(mode);
updateControls(preference, mode, media.matches);
if (mode === "animated") { if (mode === "animated") {
if (restartAnimation) {
actions.stop();
}
actions.start(); actions.start();
return; return;
} }
@ -87,29 +45,34 @@ export function initBackgroundControls(actions: BackgroundActions) {
actions.renderStill(STILL_STEPS); actions.renderStill(STILL_STEPS);
}; };
const button = document.getElementById( const restartAnimation = () => {
"background-toggle", if (document.visibilityState === "hidden" || media.matches) {
) as HTMLButtonElement | null; return;
}
button?.addEventListener("click", () => { applyMode(true);
preference = };
preference === "auto" ? "off" : preference === "off" ? "on" : "auto";
writePreference(preference);
applyMode();
});
media.addEventListener("change", () => { media.addEventListener("change", () => {
applyMode(); applyMode();
}); });
document.addEventListener("visibilitychange", () => {
restartAnimation();
});
window.addEventListener("pageshow", () => {
restartAnimation();
});
return { return {
applyInitialMode() { applyInitialMode() {
applyMode(); applyMode();
}, },
setFailed() { setFailed() {
failed = true;
mode = "failed"; mode = "failed";
applyCanvasState(mode); applyCanvasState(mode);
updateControls(preference, mode, media.matches);
}, },
}; };
} }

View file

@ -38,9 +38,6 @@ export function renderFooter() {
<span aria-hidden="true">|</span> <span aria-hidden="true">|</span>
<a href="/ssh.txt" data-native-link>ssh</a> <a href="/ssh.txt" data-native-link>ssh</a>
<span aria-hidden="true">|</span> <span aria-hidden="true">|</span>
<button type="button" id="background-toggle" class="qa-inline-action">motion auto</button>
<span id="background-status" class="qa-meta" aria-live="polite"></span>
<span aria-hidden="true">|</span>
<a href="${mirror.href}">${mirror.label}</a> <a href="${mirror.href}">${mirror.label}</a>
</div> </div>
</div>`; </div>`;

View file

@ -352,10 +352,6 @@ a[aria-current="page"] {
color: var(--dark-gray); color: var(--dark-gray);
} }
#background-status {
margin-top: 0;
}
.sr-only { .sr-only {
position: absolute; position: absolute;
width: 1px; width: 1px;