* studio: respect prefers-reduced-motion across animations
Tailwind animate-in/out, Radix dialog/popover zoom-in/slide-in transforms,
and the infinite shine / shiny-text / icon-pop keyframes all run at their
full duration regardless of the user's OS-level reduced-motion preference.
A Playwright probe that emulated the media query confirmed every measured
transition was identical between no-preference and reduce, so users with
vestibular triggers see the same scaling overlays and continuous shimmers.
Add the canonical universal-selector override so animation-duration,
animation-iteration-count, and transition-duration collapse to ~0ms when
the preference is set, leaving end states intact. Probe re-run shows
settings-dialog animationDuration drop from 0.1s to 1e-05s and the 50ms
mid-open screenshot is byte-identical to the settled one.
* studio: exempt .animate-spin from reduced-motion collapse
The universal-selector rule from the previous commit froze every
animation including .animate-spin, which is used as the canonical
in-progress indicator across Studio: tool execution loaders
(tool-ui-python/terminal/web-search/code-execution/fallback/group),
sonner toast spinners, Tauri startup + update screens, and the
generic <Spinner /> primitive in components/ui/spinner.tsx.
Freezing those leaves reduced-motion users with no visual signal
that work is in flight, which trades one accessibility win for
another. WCAG treats progress indicators as "essential motion"
that should keep moving.
Restore .animate-spin with a 1.5s cadence (instead of the default
1s) so the rotation is still perceptible but less aggressive than
the no-preference path. animation-iteration-count goes back to
`infinite` so the spinner doesn't halt after one rotation.
Verified via a focused probe that injects a .animate-spin element
and a .animate-in fade element side by side:
no-preference spin=1s infinite fade=0.15s
reduce spin=1.5s infinite fade=1e-05s
---------
Co-authored-by: danielhanchen <michaelhan2050@gmail.com>