Motion
Player 1 Inventory uses Tailwind’s default transition and animation utilities — no custom motion tokens are defined. Motion is applied selectively to interactive elements; the majority of the UI is static.
The design philosophy favors restraint: motion should confirm interactions, not decorate them.
Transition patterns
The following transition types appear in the codebase:
transition-colors — color state changes
Used on interactive elements where the color (background, border, text) changes on hover or focus. The most semantically targeted option.
<button className="transition-colors hover:bg-importance-primary-accessory" />transition-all — general transitions
Used where multiple properties may change simultaneously (e.g. a button that changes size, color, and opacity together). Apply with care — it captures every animatable property, which can be expensive.
<div className="transition-all" />transition-opacity and transition-transform
Each appears once, inside shadcn/ui base components (dialog.tsx close button and switch.tsx thumb). These are not patterns to reach for in feature code — use transition-colors for color-only changes or transition-all for everything else.
Prefer transition-colors over transition-all when possible: transition-all watches every animatable property (color, size, shadow, border, transform, etc.), which is more work for the browser compositor even when only one property is changing.
Animation utilities
Beyond transitions, Tailwind’s animation utilities appear in two contexts.
animate-spin — loading states
Used for spinner icons to signal in-progress operations. Applied to a Lucide icon inside a loading button or standalone spinner component.
<Loader2 className="animate-spin" />Appears in: LoadingSpinner, button.tsx (loading variant), DeleteButton, ItemCard (+/- buttons while a mutation is in flight), and several route-level loading states.
animate-in / animate-out — component entry and exit
Radix UI components (e.g. Select dropdown) use Tailwind’s animate-in / animate-out utilities combined with variant classes for enter/exit animations:
<div className="animate-in fade-in-0 zoom-in-95 slide-in-from-top-2" /><div className="animate-out fade-out-0 zoom-out-95" />These are applied by shadcn/ui component internals and are not written directly in feature code. The global prefers-reduced-motion rule collapses them along with all other animations.
Duration
The single duration value used across the codebase is duration-300 (300ms). This applies to all transition types.
<button className="transition-colors duration-300" />300ms is long enough to be perceptible without feeling sluggish — appropriate for panel reveals and color shifts. For near-instant feedback (hover highlights), the default Tailwind duration (150ms) is acceptable without an explicit duration-* class.
Easing
No custom ease-* classes are used. Tailwind’s default ease-in-out applies to all transitions.
Reduced-motion
The app respects the OS-level prefers-reduced-motion setting. This rule in apps/web/src/index.css collapses all animations and transitions to near-zero duration:
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }}This is a global catch-all — no per-component reduced-motion logic is needed. When adding new animated components, write them as normal Tailwind transitions and the global rule will handle accessibility automatically.
Guidelines
- Prefer
transition-colorsovertransition-allwhen only color changes — it is more performant and explicit. - Use
duration-300for panel-level transitions (collapsibles, drawers). Omit the duration class for hover highlights to get the faster default. - Use
animate-spinon a Lucide icon for loading states — do not build custom spinner animations. - Do not add motion for decoration — transitions should confirm an interaction or reveal a state change, not attract attention.
- Do not worry about reduced-motion per-component — the global CSS rule handles it.