Frontend motion & touch: honor prefers-reduced-motion; contain overscroll in drawer/dialog #71

Closed
opened 2026-06-09 21:27:06 +00:00 by logaritmisk · 1 comment
Owner

prefers-reduced-motion is not honored anywhere

grep -rn 'prefers-reduced-motion' web/src web/index.html → zero hits. Every animation runs unconditionally for users who've asked the OS for reduced motion:

  • AlertDialog zoom/fade, Drawer slide, Menu/Select fade (data-open:animate-in … in src/components/ui/)
  • animate-pulse skeletons
  • the sidebar transition-[width] expand/collapse (src/shell/sidebar.tsx)

Simplest global fix in src/index.css:

@media (prefers-reduced-motion: reduce) {
  *, ::before, ::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

(or the more surgical Tailwind motion-safe:/motion-reduce: prefixes on the data-open/data-closed animation classes — the global rule is less churn and covers future additions.)

Modal surfaces don't contain overscroll

grep -rn overscroll web/src → zero hits. On touch devices, scrolling past the end of the drawer body or a scrollable dialog chains to the page beneath, breaking the modal illusion:

  • web/src/components/ui/drawer.tsx — add overscroll-y-contain to the DrawerContent popup (the detail drawer scrolls long object records).
  • web/src/components/ui/alert-dialog.tsx — same on AlertDialogContent.
  • web/src/components/ui/combobox.tsx / menu.tsx / select.tsx popups (overflow-auto lists) — same, so flicking past the end of a long term list doesn't scroll the page.

Both changes are CSS-only inside src/components/ui/, no bundle-size impact.

## prefers-reduced-motion is not honored anywhere `grep -rn 'prefers-reduced-motion' web/src web/index.html` → zero hits. Every animation runs unconditionally for users who've asked the OS for reduced motion: - AlertDialog zoom/fade, Drawer slide, Menu/Select fade (`data-open:animate-in …` in `src/components/ui/`) - `animate-pulse` skeletons - the sidebar `transition-[width]` expand/collapse (`src/shell/sidebar.tsx`) Simplest global fix in `src/index.css`: ```css @media (prefers-reduced-motion: reduce) { *, ::before, ::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } ``` (or the more surgical Tailwind `motion-safe:`/`motion-reduce:` prefixes on the `data-open`/`data-closed` animation classes — the global rule is less churn and covers future additions.) ## Modal surfaces don't contain overscroll `grep -rn overscroll web/src` → zero hits. On touch devices, scrolling past the end of the drawer body or a scrollable dialog chains to the page beneath, breaking the modal illusion: - `web/src/components/ui/drawer.tsx` — add `overscroll-y-contain` to the `DrawerContent` popup (the detail drawer scrolls long object records). - `web/src/components/ui/alert-dialog.tsx` — same on `AlertDialogContent`. - `web/src/components/ui/combobox.tsx` / `menu.tsx` / `select.tsx` popups (`overflow-auto` lists) — same, so flicking past the end of a long term list doesn't scroll the page. Both changes are CSS-only inside `src/components/ui/`, no bundle-size impact.
Author
Owner

Fixed in 9a896bb (merged as 3a57c0a).

  • Global prefers-reduced-motion: reduce rule in index.css @layer base — collapses all animation/transition durations to 0.01ms, covering the kit's data-open/closed animations, animate-pulse skeletons, and the sidebar transition-[width] with one rule (and any future additions for free).
  • overscroll-y-contain on DrawerContent, SelectContent, and ComboboxPopup (the scrollable surfaces), plus AlertDialogContent defensively for when it gains scrollable content.
  • MenuPopup was skipped deliberately: it has no max-height/overflow, so it can never scroll — containment there would be a no-op.

Verified in the built output: dist/assets/*.css contains both the prefers-reduced-motion media query and overscroll-behavior-y:contain.

Fixed in 9a896bb (merged as 3a57c0a). - Global `prefers-reduced-motion: reduce` rule in `index.css` `@layer base` — collapses all animation/transition durations to 0.01ms, covering the kit's `data-open/closed` animations, `animate-pulse` skeletons, and the sidebar `transition-[width]` with one rule (and any future additions for free). - `overscroll-y-contain` on `DrawerContent`, `SelectContent`, and `ComboboxPopup` (the scrollable surfaces), plus `AlertDialogContent` defensively for when it gains scrollable content. - `MenuPopup` was skipped deliberately: it has no `max-height`/`overflow`, so it can never scroll — containment there would be a no-op. Verified in the built output: `dist/assets/*.css` contains both the `prefers-reduced-motion` media query and `overscroll-behavior-y:contain`.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logaritmisk/biggus-dickus#71