Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/src/lib/motion-apply.ts
iris 6be1bbd4f7 swarm-ui: page-switch animation (pop entrance + nav underline travel)
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.
2026-08-19 19:54:52 +02:00

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]);
}