Foundations
Motion
Short motion with a reason: it shows what appeared, where it came from and where it went. Enter by slowing down, leave by speeding up.
Tokens
Press ▶ to play. The duration demos run 4× slower so they are easier to follow.
durationUltraFast
duration-ultra-fast · 50ms
Instant feedback (press)
durationFaster
duration-faster · 100ms
Hover, color, icons
durationFast
duration-fast · 150ms
Surfaces leaving
durationNormal
duration-normal · 200ms
Expanding content in place
durationGentle
duration-gentle · 250ms
Surfaces entering (Dialog, Drawer)
durationSlow
duration-slow · 300ms
Large, rare movement
curveDecelerateMid
ease-decelerate-mid
Elements entering
curveAccelerateMid
ease-accelerate-mid
Elements leaving
curveEasyEase
ease-easy-ease
State change in place
curveDecelerateMax
ease-decelerate-max
Entering over a long distance
curveAccelerateMax
ease-accelerate-max
Leaving over a long distance
curveEasyEaseMax
ease-easy-ease-max
Moving across the screen
curveLinear
ease-linear
Progress, spinners
Utilities & keyframes
duration-* sets transition-duration and ease-* sets transition-timing-function. The animate-* utilities below come with the theme (theme.css, theme-core.css). The keyframes live in @theme, so Tailwind writes only the ones a class or an animation declaration uses. To replace one with your own @keyframes of the same name, declare it inside @theme.
| Utility | Motion | Duration · curve |
|---|---|---|
animate-fade-in | opacity 0 → 1 | 200ms · decelerate-mid |
animate-fade-out | opacity 1 → 0 | 150ms · accelerate-mid |
animate-surface-in | opacity 0 → 1, scale 0.96 → 1 | 250ms · decelerate-mid |
animate-surface-out | opacity 1 → 0 | 150ms · accelerate-mid |
animate-slide-in-start / -end / -bottom | slides in from the start / end / bottom edge (mirrored in RTL) | 250ms · decelerate-mid |
animate-slide-out-start / -end / -bottom | slides out to that edge | 150ms · accelerate-mid |
animate-sweep | light sweep with transform (Skeleton) | 1.8s · linear · ∞ |
animate-indeterminate-slide | 1/3 bar sliding with transform (indeterminate ProgressBar) | 3s · linear · ∞ |
animate-shimmer / animate-indeterminate | older versions animating background-position / left (repaint every frame) | 1.8s / 3s · linear · ∞ |
By component
The enter / exit pair each component uses, next to the real component. Turn on the preview to see them with reduced motion.
Dialog
- Enter
- surface-in 250ms · fade-in (backdrop)
- Exit
- fade-out 150ms
- Reduced
- Unchanged: only a fade and a 4% scale.
Drawer / Sheet
- Enter
- slide-in-start | end | bottom 250ms
- Exit
- slide-out-* 150ms
- Reduced
- No slide: appears and hides at once; the backdrop still fades.
Menu / Popover
- Enter
- surface-in 250ms
- Exit
- fade-out 150ms
- Reduced
- Unchanged.
Tooltip
- Enter
- fade-in 200ms
- Exit
- fade-out 150ms
- Reduced
- Unchanged.
Toast
- Enter
- slide-in-end (end corners) · surface-in (centre)
- Exit
- slide-out-end · swipe to dismiss
- Reduced
- No slide: appears and hides at once.
Accordion
- Enter
- grid-template-rows 200ms · easy-ease
- Exit
- grid-template-rows 200ms · easy-ease
- Reduced
- Opens and closes at once; the chevron does not rotate.
Tabs
- Enter
- indicator color 100ms · easy-ease
- Exit
- no slide between tabs
- Reduced
- Unchanged (color only).
Playground
Pick a property, then a duration and a curve for two options; play them side by side and copy the classes.
A
B
/* A */ transition-[translate] duration-gentle ease-decelerate-mid
/* B */ transition-[translate] duration-gentle ease-linearReduced motion
With prefers-reduced-motion on, components drop slides, rotation and loops; short fades stay so changes remain visible.
| Component | With reduced motion |
|---|---|
| Drawer, Sheet, Toast | No slide; the backdrop still fades. |
| Accordion, Collapse, Fade, Slide, Scale | Change state at once. |
| Carousel | No autoplay; slides change without smooth scrolling. |
| Skeleton, ProgressBar, Spinner, Chart | No looping shimmer or drawing animation. |
| Stagger, Tour | Show together instead of one by one. |
| Dialog, Menu, Popover, Tooltip | Unchanged: a 150–250ms fade without movement. |
The switch under By component applies the motion-reduce:* classes the components declare. Behaviour written in JavaScript (Carousel, Stagger, Tour) only changes with the real setting, in the operating system or in DevTools (Rendering › Emulate CSS media feature prefers-reduced-motion).
When you write your own motion, add motion-reduce:transition-none or motion-reduce:animate-none right next to the motion class.
Choreography
When several things change, give them one order and one direction. Animate only when the motion helps people follow the change.
Staggered list
Stagger: 100ms apart, hidden in reverse order.
- Inbox
- Drafts
- Sent
- Archive
Skeleton → content
Fade in place, no slide.
Layout change
Collapse / Accordion move the content below at the same pace.
- Page changes: do not animate the whole page; the PageProgress bar is enough.
- Do not animate data that updates continuously (tables, logs, streaming chat).
- Surfaces opened by a button appear next to that button (Menu, Popover).
Performance
- Animate
transform/translate/scaleandopacity: browsers run them on the compositor without recalculating layout. - Avoid animating
width,height,top,left,margin. Accordion and Collapse are deliberate exceptions: small regions, 200ms, using grid-template-rows. will-changeonly right before the motion, removed afterwards; leaving it on costs GPU memory.- Long-running motion (shimmer, indeterminate) uses CSS animations rather than requestAnimationFrame, so it stays off the main thread.
Do and don't
- Leave faster than you enter (150ms out, 250ms in).
- Slide from the edge the surface is attached to, and mirror it in RTL.
- Use the duration-* and ease-* tokens so motion stays consistent.
- Bounce or overshoot on UI chrome.
- Animate on every hover or every data update.
- Go past 300ms for anything people wait on.