Light

Motion Tokens

[!TIP] For machine-parseable token values, see tokens.json.

Animation and transition specifications for consistent UI motion.


Duration Scale

TokenValueUse For
duration-7575msMicro-interactions (opacity changes)
duration-100100msQuick feedback (hover states)
duration-150150msStandard transitions (DEFAULT)
duration-200200msSlightly slower transitions
duration-300300msMedium transitions (accordion, collapse)
duration-500500msSlow transitions (page elements)
duration-700700msVery slow (dramatic reveals)
duration-10001000msExtended animations

Recommended Defaults

ContextDuration
Hover statesduration-150
Focus ringsduration-150
Button statesduration-150
Accordion expand/collapseduration-200
Dialog/Sheet openduration-200
Page transitionsduration-300

Easing Functions

TokenValueUse For
ease-linearlinearProgress bars, constant motion
ease-incubic-bezier(0.4, 0, 1, 1)Elements leaving viewport
ease-outcubic-bezier(0, 0, 0.2, 1)Elements entering viewport (DEFAULT)
ease-in-outcubic-bezier(0.4, 0, 0.2, 1)Elements moving within viewport

Recommended Defaults

ContextEasing
Hover/focus statesease-out or ease-in-out
Opening dialogsease-out
Closing dialogsease-in
Accordionease-in-out
General UIease-in-out

Common Animations

Spinner

<LoaderCircle className="size-4 animate-spin" />

Built-in Tailwind animation: rotates 360° in 1 second, linear, infinite.

Pulse (Skeleton)

<Skeleton className="animate-pulse" />

Built-in Tailwind animation: opacity fades between 1 and 0.5.

Bounce (Attention)

<div className="animate-bounce">
  <ArrowDown className="size-4" />
</div>

Use sparingly for attention-grabbing elements.

Ping (Notification Dot)

<span className="relative flex size-3">
  <span className="animate-ping absolute inline-flex size-full rounded-full bg-primary opacity-75" />
  <span className="relative inline-flex rounded-full size-3 bg-primary" />
</span>

Transition Properties

Standard Transition

// Default transition for most UI elements
className="transition-colors duration-150 ease-in-out"

// Transform transitions (scale, rotate, translate)
className="transition-transform duration-200 ease-in-out"

// All properties
className="transition-all duration-150 ease-in-out"

Specific Properties

Tailwind ClassProperties
transition-noneNo transition
transition-allAll properties
transition-colorscolor, background-color, border-color, text-decoration-color, fill, stroke
transition-opacityopacity
transition-shadowbox-shadow
transition-transformtransform

Component-Specific Animations

Accordion Chevron

<ChevronDown 
  className="size-4 shrink-0 text-muted-foreground transition-transform duration-200 
             data-[state=open]:rotate-180" 
/>

Collapsible Content

// Height animation handled by Radix
// Duration is set via CSS or data attributes

Dialog/Sheet

// Overlay fade
<DialogOverlay className="data-[state=open]:animate-in data-[state=closed]:animate-out 
                          data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" />

// Content slide
<DialogContent className="data-[state=open]:animate-in data-[state=closed]:animate-out
                          data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
                          data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95" />

Dropdown/Popover

<DropdownMenuContent 
  className="data-[state=open]:animate-in data-[state=closed]:animate-out
             data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
             data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95
             data-[side=bottom]:slide-in-from-top-2
             data-[side=left]:slide-in-from-right-2
             data-[side=right]:slide-in-from-left-2
             data-[side=top]:slide-in-from-bottom-2"
/>

CSS Custom Properties

:root {
  /* Durations */
  --duration-fast: 150ms;
  --duration-normal: 200ms;
  --duration-slow: 300ms;
  
  /* Easing */
  --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
}

Tailwind Configuration

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      transitionDuration: {
        DEFAULT: '150ms',
      },
      transitionTimingFunction: {
        DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)',
      },
      keyframes: {
        'accordion-down': {
          from: { height: '0' },
          to: { height: 'var(--radix-accordion-content-height)' },
        },
        'accordion-up': {
          from: { height: 'var(--radix-accordion-content-height)' },
          to: { height: '0' },
        },
      },
      animation: {
        'accordion-down': 'accordion-down 0.2s ease-out',
        'accordion-up': 'accordion-up 0.2s ease-out',
      },
    },
  },
}

Accessibility Considerations

Reduced Motion

Always respect user preferences for reduced motion:

// In your component
<div className="motion-safe:animate-bounce motion-reduce:animate-none">

// Or via media query in CSS
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Guidelines

  • Keep animations under 300ms for UI feedback
  • Avoid animations that flash, blink, or have rapid motion
  • Provide static alternatives for essential animations
  • Test with prefers-reduced-motion enabled

Gotchas

ProblemSolution
Animation feels slowUse 150ms for micro-interactions, 200ms max for most UI
Jerky accordionEnsure ease-in-out easing is applied
Spinner not animatingUse animate-spin class (includes infinite)
Skeleton flashinganimate-pulse uses 2s duration by default
Dropdown appears abruptlyAdd Radix animate-in/out classes

See Also

  • Related Components:
    • Accordion - Uses motion for expand/collapse
    • Dialog - Uses motion for open/close

Last updated: January 16, 2026