Shell's page content now remounts on every navigation and plays a scale+fade pop-in entrance. The active nav link's underline is now a single shared element that slides to its new position instead of snapping, re-colouring to a per-tab accent — a discrete cycle through the existing base16 chromatic slots, not a continuous hue rotation. Both are gated on the data-motion/prefers-reduced-motion plumbing lib/motion-apply.ts already had wired and waiting for a first real consumer.
40 lines
1.7 KiB
TypeScript
40 lines
1.7 KiB
TypeScript
// Applies the stored reduced-motion override (see `settings-storage.ts`)
|
|
// as a `data-motion="reduce"|"allow"` attribute on `<html>`, cleared
|
|
// when the override is "system" (letting `prefers-reduced-motion`
|
|
// alone decide, same as today).
|
|
//
|
|
// Built ahead of swarm-ui having any CSS animation to gate — the
|
|
// settings surface itself was scoped to cover theme *and* motion
|
|
// together (both named in the same go-ahead), and the marginal cost of
|
|
// the storage/attribute plumbing was near zero riding alongside the
|
|
// theme override's real wiring. `Shell`'s page-switch animation (the
|
|
// nav indicator's slide + the page-body pop) is the first real
|
|
// consumer — see `shell/Shell.css`'s file-top comment for the
|
|
// three-rule gate shape every animation here follows:
|
|
// @media (prefers-reduced-motion: reduce) { ... }
|
|
// :root[data-motion='reduce'] { ... same rule ... }
|
|
// :root[data-motion='allow'] { /* opt back in despite OS-level reduce */ }
|
|
import { useEffect } from 'preact/hooks';
|
|
import { useLocalSetting } from './settings-storage.js';
|
|
|
|
export type MotionOverride = 'system' | 'reduce' | 'allow';
|
|
|
|
export const MOTION_OVERRIDE_KEY = 'swarm-ui:motion-override';
|
|
|
|
export function useMotionOverride() {
|
|
return useLocalSetting<MotionOverride>(MOTION_OVERRIDE_KEY, 'system');
|
|
}
|
|
|
|
// Mounted once alongside `useApplyThemeOverride` — see that module's
|
|
// comment for why `Shell` is the right single mount point.
|
|
export function useApplyMotionOverride(): void {
|
|
const [override] = useMotionOverride();
|
|
useEffect(() => {
|
|
const root = document.documentElement;
|
|
if (override === 'system') {
|
|
delete root.dataset.motion;
|
|
} else {
|
|
root.dataset.motion = override;
|
|
}
|
|
}, [override]);
|
|
}
|