Skip to content

fix(ui): respect prefers-reduced-motion in Dialog and Toast - #864

Merged
pasqualevitiello merged 1 commit into
cosscom:mainfrom
westernCasa:fix/reduced-motion-dialog-toast
Oct 15, 2026
Merged

pasqualevitiello merged 1 commit into
cosscom:mainfrom
westernCasa:fix/reduced-motion-dialog-toast

Conversation

@westernCasa

Copy link
Copy Markdown
Contributor

What happens today

With prefers-reduced-motion: reduce set, Dialog and Toast play the same enter and exit motion as with it off:

  • Toast slides in from off-screen (100% of its height plus the inset) over 500 ms.
  • Dialog scales from 0.98 on desktop and slides up 16 px on mobile (bottomStickOnMobile is on by default), over 200 ms.

There is a reduced-motion rule in packages/ui/src/styles/globals.css that stops Toast's update shake. It is not part of the toast registry item, so projects that install Toast from the registry don't get it.

What this changes

Under reduced motion, both components keep their fades and drop their movement. With reduced motion off, nothing changes.

  • Dialog: the scale and translate start/end values only apply under motion-safe. motion-reduce:transition-opacity stops the nested-dialog scale from animating.
  • Toast: the enter and exit slide values only apply under motion-safe. Under motion-reduce the transition list drops transform, and the toast fades in instead of sliding (motion-reduce:data-starting-style:opacity-0). The anchored toast's scale also only applies under motion-safe.
  • Toast shake: motion-reduce:animate-none is added in upsertReplayClassName, so the guard ships with the component. The rule in globals.css is left in place.

I kept the fades rather than turning transitions off. A fade still tells you something appeared or went away; reduced motion is about movement, so movement is what's removed.

Measured

Measured in Chromium at 1280×800 and 390×844, with reduced motion off and on, reading every running transition from document.getAnimations(). The harness also records where each element sits on every frame while it's visible.

Component Viewport Before: transitions under reduced motion After Moves while visible (before → after)
Dialog desktop opacity 200ms, scale 200ms opacity 200ms yes → no
Dialog mobile opacity 200ms, translate 200ms opacity 200ms yes → no
Toast desktop transform 500ms, opacity 500ms opacity 500ms yes → no
Toast mobile transform 500ms, opacity 500ms opacity 500ms yes → no

"Moves while visible" checks whether the element's translate, scale or transform differs from its resting position on any frame where it can be seen. Every other primitive, and both components with reduced motion off, measured identical before and after.

Toast shake, with the globals.css reduced-motion block removed (what a registry install has):

Reduced motion off Reduced motion on
Before shake runs shake runs
After shake runs no shake

Harness, if you want to rerun it: https://github.com/westernCasa/coss-motion-audit

Not in this PR

  • Alert Dialog, Sheet, Drawer, Popover, Tooltip, Preview Card, Accordion and Collapsible behave the same way under reduced motion. I kept this to two components so it stays easy to review, and can follow up on the others if this approach works for you.
  • Toast swipe-to-dismiss: under reduced motion the toast no longer animates away after release; it leaves from where it was let go. Stack expand/collapse offsets also change without animating.
  • The Toast rule in globals.css is now redundant but left alone.

Checks

bun run format:all, registry:validate-deps, registry:build, ui:sync and typecheck, all from apps/ui. All pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CSAAqwUKTFSvgnbcPTL3Pp

With reduced motion on, Dialog and Toast still played their full
enter/exit movement. Under prefers-reduced-motion: reduce they now
keep their fades and drop the movement.

The Toast shake guard moves into the component so it ships with the
registry item; previously it lived only in packages/ui globals.css.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSAAqwUKTFSvgnbcPTL3Pp
@vercel

vercel Bot commented Oct 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
coss-com-origin Skipped Skipped Oct 12, 2026 2:02pm UTC

Request Review

@vercel
vercel Bot temporarily deployed to Preview – coss-com-origin October 12, 2026 14:02 Inactive
@vercel

vercel Bot commented Oct 12, 2026

Copy link
Copy Markdown

@westernCasa is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@pasqualevitiello pasqualevitiello left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@pasqualevitiello
pasqualevitiello merged commit 8929092 into cosscom:main Oct 15, 2026
10 of 13 checks passed

This branch was previously deployed

1 inactive deployment
Preview – coss-com-origin — 5969cdf1 Deployed Oct 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants