Foundations

Motion

What moves in the kit, read from the code: thirteen --animate-* tokens, what moves outside them, the transitions, the curves, three staggers, and what switches off with reduced motion.

Updated Oct 13, 2026 by Juan Pablo Turina

Tokens

Thirteen tokens in seven kinds (the Sheet has one pair per side). Replay each one; the line under it is where the kit uses it today.

animate-fade-in
450ms · ease-out

Something appearing in place, with no distance to travel: the scrim behind an overlay, an accordion or collapsible panel, a navigation menu panel, the shell on first paint.

Used by Sheet, Dialog and AlertDialog scrims · Accordion · Collapsible · NavigationMenu · Sidebar · TopBar

animate-fade-out
200ms · ease-in

The Sheet scrim leaving. It is the only fade with an exit: Dialog and AlertDialog scrims close at once.

Used by Sheet scrim

animate-dialog-in
240ms · enter curve

A floating surface opened from a trigger: a dialog, a popover, a menu, a select list, a hover card. Rises 8px and grows from 97%. Closes at once, with no exit.

Used by Dialog · AlertDialog · Popover · DropdownMenu · ContextMenu · Menubar · HoverCard · Select · AccountMenu, and what is built on them (Combobox, DatePicker, Command…)

animate-cascade
550ms · enter curve

A page's own content arriving on load: the header, then each section. Rises 10px.

Used by PageHeader · the sections of the Base and Insights templates

animate-slide-in-left
500ms · enter curve

The sidebar on first paint: every row slides 14px in from the left, one after another.

Used by Sidebar

animate-sheet-in-right
300ms · enter curve

A Sheet sliding in from the edge it is anchored to. The Sheet picks the token from its side prop.

Used by Sheet (one token per side: -right, -left, -top, -bottom)

animate-sheet-out-right
200ms · exit curve

The Sheet leaving through the same edge, faster and on the exit curve.

Used by Sheet (one token per side: -right, -left, -top, -bottom)

Outside the tokens

Eight things move, or don't, on timing that is not a kit token: two Tailwind loops, three libraries that bring their own motion, the agent orb and the tour with their own, and Tooltip with none. They are listed so nobody copies them as the rule.

WhatValueWhy
animate-pulse
Skeleton · the InputOTP caret
2s loop · TailwindTailwind's own loop. Says this is loading, or type here, and runs for as long as that lasts.
animate-spin
the loading toast · the Button loading recipe
1s loop · TailwindTailwind's own loop, on a loading icon: an action in progress.
Toast (Sonner)
Toast
400ms · ease · SonnerSonner slides toasts in and out with its own transitions, and switches them off with reduced motion by its own rule.
Chart (Recharts)
every Recharts chart, unless it passes isAnimationActive={false} (DonutCard, several Insights template charts)
Line, Area, Pie 1500ms · Bar 400ms · RechartsRecharts draws each series in when the chart mounts. Its default skips that with reduced motion.
Drawer (vaul)
Drawer, panel and scrim
500ms · cubic-bezier(0.32, 0.72, 0, 1) · vaulThe vaul library drives it so the panel can follow a finger when dragged. Its duration and curve are vaul's; vaul ignores reduced motion, so the kit's Drawer switches the panel and its scrim off itself.
AgentOrb
AgentOrb
halo 2.8s and 1.4s · breath 2.4s · ring 2.4s · its own curves · WebGL frame loopThe orb is a character, not a surface: each state has its own beat, tuned by hand, and the ball runs a frame loop in WebGL. With reduced motion nothing moves: the ball shows each state in one frame and the halo or ring holds still where it carries the state.
Tour
Tour: its cut-out, its card and its demo cursor
cut-out: a critically damped spring, about 350ms, no overshoot; its fill 260ms · card: fades in 300ms once the light has landed · cursor 420 to 1000ms by distance on cubic-bezier(0.22, 1, 0.36, 1) · click pulse: Tailwind ping, once · close: 300ms fadeA tour moves things across the screen between targets it does not know in advance, so a fixed token duration does not fit: a short hop and a trip across the page need different times. The cut-out moves on a spring because its target is measured again while it moves (a page scrolling, an answer growing): a transition restarts its curve every time and the light arrives in slowing steps. The cursor borrows the kit's landing curve. With reduced motion the cut-out and the card jump, and the cursor's demo does not play at all.
Tooltip
Tooltip
noneAppears and disappears at once. There is no animation to match.

Transitions

A transition eases a change of state (a hover, a switch, a caret turning, a width) instead of playing an entrance. Unless a class names a duration, it lasts Tailwind's 150ms.

WhatValueUsed by
Hover and focus: colour, background, border, shadow, opacity.150mstransition-colors, transition-opacity, transition-shadow, transition-all (cards that lift their shadow, Tabs and Accordion triggers, InputOTP slots)
A control changing state or reacting to hover.150msthe Switch thumb · the Progress fill · the Slider thumb shadow · ShortcutTile growing to 105%
A caret turning when its section opens or closes: always duration-200.200msAccordion · NavigationMenu · Sidebar · AccountMenu · InfoBlock · SectionHeader · CheckoutSelect
The sidebar collapsing to icons and back.200ms · ease-outSidebar

Curves

Token entrances run 240 to 550ms and exits 200ms: an exit is always shorter than the entrance it undoes.

CurveValueUse for
Entercubic-bezier(0.22, 1, 0.36, 1)Starts fast and settles softly. Every token entrance except the fades.
Exitcubic-bezier(0.55, 0, 1, 0.45)Accelerates out of view. The Sheet exits.
Fadeease-out / ease-inOpacity only: ease-out on the way in, ease-in on the way out.
Transitioncubic-bezier(0.4, 0, 0.2, 1)Tailwind's default, for every transition except the Sidebar collapse, which uses ease-out.

Stagger

Three places bring their pieces in one after another. Each has its own rhythm, set by how many pieces it has: a few sections, many sidebar rows, the fields of a sheet.

WhatTimingAnimation
Page: the header, then each section of a template.100ms, then every 150ms: 250 · 400 · 550animate-cascade
Sidebar: every slot in reading order (labels, separators, items); sub-items when their parent opens.120ms + 30ms per slot · sub-items 30ms eachanimate-slide-in-left
SheetBody: each direct child, starting while the sheet finishes landing (it lands at 300ms).220ms + 40ms per child · stops at the 10th (580ms)cascade-in · 500ms

The SheetBody stagger, running on the real rule:

Reduced motion

With prefers-reduced-motion, the kit's entrances switch off. Some things keep moving.
globals.css turns off the fades (every scrim included), dialog-in, cascade and the sidebar slide, and with them the page and sidebar staggers, including classes written behind a variant; the SheetBody stagger has its own rule. Sonner and Recharts switch theirs off by themselves, and the Drawer switches off vaul's. What still moves: Sheet slides, by decision (they are short, and the direction says which edge the panel lives on); the pulse and spin loops, which say something is loading; and transitions, which last 150 to 200ms. A new --animate-* token is added to that rule or declared to stay.

Usage

Do
  • Use the token that matches the job: dialog-in for a floating surface opened from a trigger, fade-in for scrims and panels that appear in place, sheet-* for sheets.
  • Keep exits shorter than entrances.
  • To bring pieces in one after another, reuse one of the kit's rhythms: 150ms between page sections, 30 to 40ms between rows.
  • Test with reduced motion on (in DevTools, the Rendering panel, prefers-reduced-motion).
Don't
  • Don't write a keyframe, a duration or a curve of your own in a prototype; if the kit lacks one, propose a token.
  • Don't use a bounce or an overshoot; no curve in the kit goes past its target.
  • Don't stagger a table or a long list row by row; the SheetBody stagger stops at the tenth child. The sidebar is the exception: it is the shell, drawn once per load.
  • Don't animate content that updates on its own: a new row, a refreshed number, a polled status.
  • Don't borrow classes from animation libraries the kit doesn't install (animate-in, fade-in-0, zoom-in-95): they compile to nothing.