Speed
Slow motion multiplier
1×
Reduced motion
Show

Preview plan

60 rows
Every component in the audit, what its preview has to demonstrate, and the controls it needs. The hard case is the column that matters: a preview that only shows the easy case is worth nothing.Decide here: What every remaining preview has to show before it is worth building.

60

Rows across six surfaces

29

Already have a rig in the lab

31

Specified, not built

8

Rows where the brief and the audit disagree
60 of 60

Overlays & layers

15
TooltipPARTIALbuilt · LayerRigP1 · decides an open question
Today: Direction-aware fade and scale in.
Gap: Hard-cuts on every dismiss, on the highest-frequency surface in the system.
The preview must showEnter and exit at four placements, with the exit curve and duration separately tunable from the entry.
The hard case that decides the techniquePointer moves from trigger to trigger. The exit of one overlaps the entry of the next, and the frequency argument says this surface may deserve no exit at all — the preview has to make that case answerable rather than assumed.
enter durationexit durationexit curveplacementreduced motion
HoverCardPARTIALbuilt · LayerRigP2 · high-frequency surface
Today: Fade and scale in.
Gap: No exit; the most visible instance of the gap because the surface is large.
The preview must showA card-sized surface entering and leaving, beside a tooltip-sized one on the same timings.
The hard case that decides the techniqueLarge surfaces need longer than small ones at the same perceived speed. The preview has to show whether one --duration-enter can serve both, or whether size has to scale it.
enter durationexit durationsurface sizeopen delay
PopoverPARTIALbuilt · LayerRigP1 · decides an open question
Today: Fade and scale in.
Gap: No exit.
The preview must showClick-triggered presence, with transform-origin anchored to the trigger rather than the centre.
The hard case that decides the techniqueDismissal by outside-press while focus is inside the popover. Focus has to leave before the element does, and the preview is where that ordering gets agreed.
enter durationexit durationorigindismiss method
DropdownMenuPARTIALbuilt · LayerRigP1 · decides an open question
Today: Fade and scale in, submenus too.
Gap: No exit; menus snap shut.
The preview must showA menu and a submenu, both directions, with the submenu inheriting the parent timing.
The hard case that decides the techniqueSelecting an item closes the menu and performs the action at the same time. If the exit is not interruptible the action feels late; if it is skipped the menu blinks.
enter durationexit durationsubmenu delayselection behaviour
ContextMenuPARTIALbuilt · LayerRigP3 · completeness
Today: Fade and scale in.
Gap: No exit.
The preview must showPointer-anchored entry, where the origin is a coordinate rather than an element.
The hard case that decides the techniqueRight-click while a menu is already open. The old one has to leave while the new one arrives, from a different origin.
enter durationexit durationorigin
Selector / ComplexSelector / MultiSelectorPARTIALbuilt · LayerRigP2 · high-frequency surface
Today: Chevron rotate plus layer entry.
Gap: No exit. The brief also claims three dead no-op transition rules here.
The preview must showChevron and layer on one timeline, so the two do not disagree about how long opening takes.
The hard case that decides the techniqueKeyboard-driven open and close, which is the frequency band the rubric says gets no motion at all. The preview has to show the pointer path and the keyboard path side by side.
enter durationexit durationchevron durationinput method
PowerSearch / Typeahead / MoreMenuPARTIALspecifiedP2 · high-frequency surface
Today: Layer entry only.
Gap: No exit, and no motion when tokens or results are added and removed.
The preview must showThe layer, and separately the list inside it changing as the query changes.
The hard case that decides the techniqueResults replacing results while the layer stays open. Animating the container is easy; animating a filtered list without making typing feel laggy is the decision.
enter durationexit durationlist item staggerfilter debounce
DialogPARTIALbuilt · DialogRigP1 · decides an open question
Today: Directional keyframe entry.
Gap: No exit, and the backdrop has no transition at all, so the scrim hard-cuts.
The preview must showPanel and backdrop as two separately tunable timelines, plus focus return timing.
The hard case that decides the techniqueFocus return during the exit. Dialog returns focus the moment it closes, so with a real exit the page can scroll to the trigger while the dialog is still on screen.
panel durationbackdrop durationfocus return delayreduced motion
AlertDialogPARTIALbuilt · DialogRigP3 · completeness
Today: Inherits the Dialog entry.
Gap: No exit. Destructive confirmation is the one dialog where a slow dismissal is wrong.
The preview must showThe same rig as Dialog with the exit deliberately shorter, to test whether one token can serve both.
The hard case that decides the techniqueConfirming a destructive action. The dialog should be gone before the consequence appears, which is an argument for exit being shorter than enter rather than symmetric.
panel durationexit durationconfirm vs cancel path
CommandPalettePARTIALspecifiedP2 · high-frequency surface
Today: Inherits the Dialog entry.
Gap: No exit, and no motion on the result list as it filters.
The preview must showOpening, typing, and the list reordering underneath, all at once.
The hard case that decides the techniqueKeystroke-rate list changes. This is the clearest case in the system for animating nothing: the rubric says 100+/day actions get none, and a palette is used by exactly those people.
enter durationlist staggerreduced motiontyping speed
LightboxADDspecifiedP2 · high-frequency surface
Today: Image zoom and pan only.
Gap: A full-screen surface with no open or close at all.
The preview must showOpening from the thumbnail that triggered it, not from the centre of the screen.
The hard case that decides the techniqueThe shared-element case: the image should appear to be the thumbnail growing. Everything else is a crossfade, and a crossfade on a full-screen surface is the thing the brief calls jarring.
open durationclose durationoriginbackdrop opacity
BottomSheetGOODspecifiedP1 · decides an open question
Today: Backdrop and panel, drag-aware, with its own exit curve.
Gap: None — this is the reference implementation, and the source of --ease-exit.
The preview must showIts two hardcoded curves beside the proposed tokens, to prove the tokens reproduce them exactly.
The hard case that decides the techniqueDrag-to-dismiss released mid-gesture. The spring has to carry the velocity the finger had; a keyframe restarts from zero and the sheet stutters.
panel durationexit curvedrag velocitybounce
MobileNavGOODspecifiedP2 · high-frequency surface
Today: Slide plus backdrop; reads its own computed duration to time the close.
Gap: None — the best-reasoned close timing in the repo, and an argument for the JS mirror.
The preview must showThe getComputedStyle read that times the close, and the same thing done through the mirror.
The hard case that decides the techniqueThe close timing has to survive a theme that retunes the duration scale. That is exactly what the JS mirror is for, and this component is the existing proof it is needed.
slide durationbackdrop durationtheme scale
Overlay / scrimGOODspecifiedP3 · completeness
Today: Per-edge slide, both directions.
Gap: None — the template the presence primitive should generalise.
The preview must showAll four edges, so the primitive’s API is designed against the hardest of them rather than the first.
The hard case that decides the techniqueEdge-dependent transform origin with a scrim that must not slide with the panel. Two elements, one dismissal, different curves.
edgepanel durationscrim durationscrim curve
Drawer (lab)PARTIALspecifiedP2 · high-frequency surface
Today: Slide plus backdrop.
Gap: Closes after a hardcoded 250ms while the slide is 410ms, so the exit is cut at ~60%.
The preview must showThe cut, at 1x and slowed down, beside the same close awaiting its transition.
The hard case that decides the techniqueProving the bug is visible at 1x. If it is not, the finding is a code-hygiene issue rather than a user-facing one, and it should be scheduled as such.
slide durationunmount delayslow-mo
Measured: lab is a workspace package here, so this row is checked rather than assumed.

Disclosure & expansion

6
CollapsibleGOODbuilt · DisclosureRigP1 · decides an open question
Today: Height interpolation, offset content fade, theme-tunable.
Gap: None, but it is the only component doing it this way.
The preview must showHeight interpolation against grid-track expansion on identical content.
The hard case that decides the techniqueContent whose height is unknown until it renders, and content that changes height while open. Height interpolation needs a measurement; grid tracks do not.
durationcurvecontent heighttechnique
CodeBlockPARTIALbuilt · DisclosureRigP2 · high-frequency surface
Today: Grid-track expand plus chevron.
Gap: A different technique from Collapsible, for the same job.
The preview must showThe same expansion in both techniques, with the chevron on a shared timeline.
The hard case that decides the techniqueLong content that overflows. Grid tracks animate to a content-derived size; a max-height fallback does not, and the difference only shows on the long case.
durationcurvecontent lengthtechnique
Table row expansionADDspecifiedP1 · decides an open question
Today: Chevron rotate only.
Gap: The revealed content appears instantly.
The preview must showA row expanding inside a real table, in both techniques.
The hard case that decides the techniqueThis is the row that decides the whole disclosure question: a table row cannot be a grid container, so the grid-track technique is unavailable here. If the answer is "one technique everywhere", it has to be height — and this preview is the evidence.
durationcurvetechniquerow count
Table tree / grouped rowsADDspecifiedP2 · high-frequency surface
Today: Chevron rotate only.
Gap: Same as row expansion, plus the hardcoded 150ms values.
The preview must showExpanding a group of rows rather than one, with the 150ms and the token side by side.
The hard case that decides the techniqueExpanding many rows at once. Per-row stagger is the obvious idea and the wrong one: at 30ms across twenty rows the last row arrives 600ms late.
durationstaggerrow countreduced motion
TreeListADDspecifiedP3 · completeness
Today: Row tint plus chevron.
Gap: The child list reveal is instant.
The preview must showNested reveal, where expanding a child moves everything below it.
The hard case that decides the techniqueExpanding a deep node near the bottom of the viewport. The reveal and the scroll compensation fight each other, and doing neither is better than doing both badly.
durationdepthscroll compensation
Chat tool calls / reasoningPARTIALspecifiedP1 · decides an open question
Today: Grid-track expand; a 1.5s reasoning shimmer.
Gap: A third disclosure technique, and the shimmer has no reduced-motion escape.
The preview must showThe shimmer with and without the reduced-motion branch, and the disclosure in both techniques.
The hard case that decides the techniqueContent streaming in while the container is animating open. The height target moves during the transition, which is where height interpolation fails and grid tracks do not.
durationtechniquestream ratereduced motion

Navigation

8
SideNavADDbuilt · RailRigP2 · high-frequency surface
Today: Sub-list expand, chevron, collapse button.
Gap: The rail’s own collapse is not animated; only the chevron rotates.
The preview must showThe rail collapsing with the labels, at several widths.
The hard case that decides the techniqueWidth is a layout property, so the honest version needs transform or clip-path. The preview has to show whether the cheap version — animating width — is visibly worse, because it is much simpler.
durationcurvetechniquelabel fade offset
TabListADDbuilt · TabIndicatorRigP1 · decides an open question
Today: Per-tab indicator cross-fade.
Gap: The indicator does not slide between tabs; each tab owns its own bar.
The preview must showOne indicator that travels, against the per-tab cross-fade, on the same tabs.
The hard case that decides the techniqueTabs of different widths. The indicator has to change size while it moves, which is a scale on a transform rather than a width, or it thrashes layout on every frame.
durationcurvespring vs curvetab widths
SegmentedControlADDbuilt · TabIndicatorRigP1 · decides an open question
Today: Per-item background tint.
Gap: No sliding thumb, so the selected pill jumps.
The preview must showThe same travelling-indicator technique as TabList, to confirm one implementation serves both.
The hard case that decides the techniqueRapid clicking between segments. The thumb has to retarget mid-flight, which a transition does and a keyframe does not.
durationspring bounceclick rate
TopNavPARTIALspecifiedP1 · decides an open question
Today: Mega-menu with a real exit, sub-lists, chevrons.
Gap: The only component animating a popover exit, and it never checks reduced motion.
The preview must showIts exit as the existing proof the pattern works, plus the missing reduced-motion branch.
The hard case that decides the techniqueA mega-menu is large and frequently traversed. It is the one place where the published "no exit needed" guidance is most obviously wrong, and this component already disagrees with the docs.
enter durationexit durationreduced motiontraverse speed
NavItem / NavMenuADDbuilt · TintRigP3 · completeness
Today: Nothing.
Gap: No hover or active transition, unlike every sibling nav item.
The preview must showA tint transition at the state token, beside the untransitioned original.
The hard case that decides the techniqueHover on a touch device, where a tap fires a false hover. The rubric requires this to be gated to fine pointers, and this is the cheapest place to prove the gate works.
durationpointer typereduced motion
OutlinePARTIALbuilt · OutlineMarkerRigP1 · decides an open question
Today: Marker slide plus item tint.
Gap: Animates top and height, which thrashes layout.
The preview must showThe same marker on transform and on top/height, with a paint-cost readout.
The hard case that decides the techniqueScroll-linked movement. The marker moves on every scroll frame, so a layout-triggering property here costs more than anywhere else in the audit.
durationtechniquescroll rate
BreadcrumbsPARTIALspecifiedP3 · completeness
Today: Overflow menu entry.
Gap: Crumb collapse on resize is instant.
The preview must showCrumbs collapsing into the overflow as the container narrows.
The hard case that decides the techniqueResize is continuous, so the collapse can fire many times a second. This is a strong candidate for animating nothing, and the preview should make that easy to conclude.
container widthdurationdebounce
PaginationADDspecifiedP2 · high-frequency surface
Today: Cell tint.
Gap: Page change is an instant content swap.
The preview must showDirectional content movement: forward goes one way, back goes the other.
The hard case that decides the techniqueDirectionality is a published principle that the rubric drops entirely. This preview and Calendar are the two rows that would be measured against it, so they are the argument for adding the criterion.
durationdirectionreduced motion

Inputs & controls

10
ButtonGOODbuilt · PressRigP1 · decides an open question
Today: Tint, press scale, loading swap.
Gap: The brief calls the press transform transition the main Motion conflict.
The preview must showPress feedback on transform, on the independent scale property, and not at all.
The hard case that decides the techniqueA motion library writing transform every frame while the CSS transition re-eases it. This is the whole of blocker 2, and it is answerable by dragging one button.
press durationtechniquelibrary drag
IconButton / ToggleButtonGOODbuilt · PressRigP3 · completeness
Today: Tint and press feedback inherited from Button.
Gap: None named, but the toggle state change is not distinguished from a press.
The preview must showPress feedback and state change as two separate timings on the same control.
The hard case that decides the techniqueA toggle that is pressed and then changes state. Two animations on one gesture: if they share a duration the state change is invisible.
press durationstate durationtoggle rate
SwitchGOODspecifiedP2 · high-frequency surface
Today: Track tint plus thumb travel.
Gap: Animates the thumb width and height, which are layout properties.
The preview must showThe thumb on width/height against the same thumb on transform.
The hard case that decides the techniqueFast repeated toggling. Layout-property animation is cheap on one switch and expensive in a settings list of thirty, which is where this component actually lives.
durationtechniqueswitch counttoggle rate
CheckboxInput / RadioList / IndicatorADDbuilt · CheckTickRigP2 · high-frequency surface
Today: Box tint and border.
Gap: The tick and the dot never draw or scale in.
The preview must showThree techniques on the tick: hard swap, stroke draw, and scale-in.
The hard case that decides the techniqueSelect-all across twenty checkboxes. A tick that draws in 200ms is charming once and a mess twenty times at once, which is the frequency argument made visible.
durationtechniquestaggeritem count
Field / TextInput / TextArea / NumberInputGOODspecifiedP3 · completeness
Today: Border and focus ring transitions.
Gap: TextArea auto-grow is not animated.
The preview must showFocus ring timing at the state token, and auto-grow with and without a height transition.
The hard case that decides the techniqueAuto-grow while typing. The height changes on a keystroke, so any duration above about 100ms means the field is still growing when the next character lands.
state durationgrow durationtyping speed
SliderPARTIALspecifiedP2 · high-frequency surface
Today: Thumb tint and shadow.
Gap: The thumb does not transition on keyboard step.
The preview must showKeyboard stepping with and without a transition, beside a pointer drag.
The hard case that decides the techniqueThe same thumb must be transitioned for keyboard and untransitioned for drag, or dragging lags the pointer. One control, two input paths, opposite answers.
step durationinput methodstep size
Calendar / DateInputGOODspecifiedP2 · high-frequency surface
Today: Cell tint; the touch wheel is scroll-driven and genuinely advanced.
Gap: No month-change transition.
The preview must showMonth change with direction, beside the existing instant swap.
The hard case that decides the techniqueHolding the arrow to move six months. Each step has to be interruptible and the direction has to stay legible, which rules out a keyframe.
durationdirectionrepeat rate
Tokenizer / TokenADDbuilt · ChipRigP1 · decides an open question
Today: Background transition on the token only.
Gap: Chip add and remove, the canonical scale-in case, is instant.
The preview must showAdd and remove with the surrounding chips reflowing, not just the chip itself appearing.
The hard case that decides the techniqueRemoving a chip from the middle of a wrapped row. Everything after it moves, and moving them is the animation — the chip leaving is the easy half.
enter durationexit durationreflow durationchip count
FileInputPARTIALspecifiedP3 · completeness
Today: Dropzone border and tint.
Gap: File chips add and remove with no motion.
The preview must showChips arriving as files are dropped, with upload progress on each.
The hard case that decides the techniqueA chip that arrives and then changes state while it is still arriving. Entry and progress on one element, which is the case ChipRig deliberately does not cover.
enter durationstaggerprogress ratefile count
ResizablePARTIALspecifiedP3 · completeness
Today: Handle tint and grip.
Gap: Panel snap and collapse are instant.
The preview must showSnap-to-position and collapse animated, with the drag itself left untransitioned.
The hard case that decides the techniqueThe drag must not be transitioned and the release must be. The transition has to be added on release and removed on grab, which is a state machine rather than a style.
snap durationcurvesnap points

Feedback & status

9
ToastGOODbuilt · InterruptRigP2 · high-frequency surface
Today: Entry, exit, and stack collapse.
Gap: None, though it animates grid tracks and padding.
The preview must showEntry and exit interrupted mid-flight, since this is the most interruptible surface in the system.
The hard case that decides the techniqueA toast dismissed while another is entering and a third is collapsing the stack. Three animations on one list, and the reference case for the interruptibility criterion.
enter durationexit durationcollapse durationtoast rate
BannerADDspecifiedP2 · high-frequency surface
Today: Chevron rotate.
Gap: A dismissible banner appearing and leaving is unanimated.
The preview must showDismissal with the content below closing the gap, rather than snapping up.
The hard case that decides the techniqueThe banner leaving is easy; the page reflowing underneath it is the animation. Height on the container, or transform on everything below.
exit durationreflow durationtechnique
SkeletonADDbuilt · SkeletonSwapRigP1 · decides an open question
Today: Staggered pulse.
Gap: No skeleton-to-content crossfade; content hard-swaps.
The preview must showThe swap at several load times, including one fast enough that the skeleton should never have appeared.
The hard case that decides the techniqueContent arriving in 80ms. The crossfade makes a fast load look slower than no skeleton at all, so the preview has to expose the minimum-display-time decision, not just the fade.
crossfade durationload timeminimum display time
SpinnerPARTIALbuilt · LoopRigP1 · decides an open question
Today: Rotation.
Gap: Reduced motion slows the loop to 3s rather than stopping it.
The preview must showToday, delete, and degrade as three policies on the same spinner.
The hard case that decides the techniqueA stopped spinner communicates nothing. Degrading a loop means replacing it with a determinate state, which is a component change rather than a CSS branch — the reason this decision is not free.
loop durationpolicyreduced motion
ProgressBarPARTIALbuilt · LoopRigP2 · high-frequency surface
Today: Width fill plus an indeterminate slide.
Gap: Animates width; reduced motion slows rather than stops.
The preview must showWidth against transform: scaleX, and the indeterminate loop under all three policies.
The hard case that decides the techniquescaleX distorts the bar’s end caps and any label inside it. The compositor-only rule is right in general and needs an exception here, or a different structure.
fill durationtechniquepolicy
StatusDotPARTIALbuilt · LoopRigP2 · high-frequency surface
Today: A pulse at a hardcoded 2s with ease-in-out.
Gap: Bypasses the scale entirely, because the scale stops at 1300ms.
The preview must showThe pulse at 2s beside the longest token, so the missing band is visible rather than argued.
The hard case that decides the techniqueAmbient motion is not interface motion. Either the scale grows a band for it or the system says ambient loops are out of scope — this row and Chat reasoning are the only two asking.
loop durationpolicydot count
FieldStatusPARTIALspecifiedP2 · high-frequency surface
Today: Slide-down entry, via the shared entry hook.
Gap: Error text leaves instantly.
The preview must showError text arriving and leaving, and text replacing text without leaving first.
The hard case that decides the techniqueValidation firing on every keystroke. The error message changes while it is still entering, which is the interruptibility criterion on a surface nobody thinks of as animated.
enter durationexit durationvalidation rate
BadgeADDbuilt · StatCountRigP3 · completeness
Today: Nothing.
Gap: Count changes pop in.
The preview must showA count changing with and without a digit transition.
The hard case that decides the techniqueA count that changes several times a second, which is what a notification badge does. The rubric’s frequency criterion probably says do nothing, and the preview should be allowed to conclude that.
durationchange ratetechnique
Avatar / AvatarGroupADDspecifiedP3 · completeness
Today: Nothing.
Gap: Image load and member enter/leave both pop in.
The preview must showImage fade-in on load, and members joining and leaving a stacked group.
The hard case that decides the techniqueA cached image loads in the same frame as the component mounts, so a fade-in animates something that was never absent. The preview has to include the cached case or it will ship a flash.
fade durationstaggercache stategroup size

Content & data

12
MarkdownGOODspecifiedP3 · completeness
Today: Streaming chunk fade-in.
Gap: None — well built, and the only streaming-aware motion in the system.
The preview must showChunk fade-in at several stream rates, including faster than the fade.
The hard case that decides the techniqueChunks arriving faster than the fade duration. Overlapping fades either stack into a flicker or have to be dropped, and the current code has an answer worth documenting.
fade durationstream ratechunk size
ThumbnailGOODspecifiedP3 · completeness
Today: Hover-intent reveal for actions.
Gap: None — the only consumer of the most sophisticated motion code in the repo.
The preview must showHover intent with the delay exposed, and the same interaction on a touch device.
The hard case that decides the techniqueA tap on touch fires a false hover. Hover-intent logic that is correct with a pointer reveals actions that cannot be dismissed on touch.
intent delayreveal durationpointer type
Table rowsPARTIALbuilt · TintRigP1 · decides an open question
Today: Row tint.
Gap: No reduced-motion handling anywhere in Table, and nine hardcoded 150ms values.
The preview must showThe 150ms tint against the state token, over enough rows to feel it.
The hard case that decides the techniqueRow hover is the published page’s own example of where motion hurts. The preview should make deleting the transition a visible option, not just retokenising it.
durationrow countpointer speedreduced motion
CarouselPARTIALspecifiedP3 · completeness
Today: Edge fade plus control opacity.
Gap: A paint-heavy mask transition, and no explicit slide.
The preview must showThe mask transition's cost beside a transform-based slide.
The hard case that decides the techniqueScroll-linked edge fades repaint on every scroll frame. This is the one place in the audit where a mask is animated, and it needs a measurement rather than an opinion.
slide durationtechniquescroll rate
List / OverflowListADDbuilt · ListReorderRigP2 · high-frequency surface
Today: Item tint only.
Gap: Add, remove and reorder are all instant.
The preview must showReorder with FLIP, against reorder with View Transitions, on the same list.
The hard case that decides the techniqueReordering while the list is scrolled. Items that move out of view still animate, and the technique that handles that well is the one to standardise on.
durationtechniqueitem countscroll position
EmptyStateADDspecifiedP2 · high-frequency surface
Today: Nothing.
Gap: The archetypal content-swap surface, entirely static.
The preview must showEmpty to populated and back, as a crossfade and as a replace.
The hard case that decides the techniqueGoing from empty to populated after a search. The empty state and the results are different heights, so the container moves — and that movement is the animation people actually notice.
crossfade durationheight techniqueresult count
StepperGOODspecifiedP3 · completeness
Today: Connector fill and node tint.
Gap: Step content swap is instant.
The preview must showThe connector fill with the content swap on the same timeline, and directionality on back.
The hard case that decides the techniqueGoing backwards. The connector has to unfill and the content has to arrive from the other side, or back feels like forward.
fill durationswap durationdirection
ListInput (lab)GOODbuilt · ListReorderRigP2 · high-frequency surface
Today: View Transitions reorder.
Gap: None — the only View Transitions user in the repo.
The preview must showIts View Transitions reorder beside a FLIP implementation of the same thing.
The hard case that decides the techniqueView Transitions are document-scoped, so two of them cannot run at once. A reorder while a route change is animating is where the technique breaks, and it is the reason not to standardise on it blindly.
durationtechniqueconcurrent transition
Measured: lab is a workspace package here, so this row is checked rather than assumed.
TransferList (lab)ADDspecifiedP2 · high-frequency surface
Today: Nothing.
Gap: Moving an item between lists — the whole point of the component — is instant.
The preview must showAn item leaving one list and arriving in the other as one continuous movement.
The hard case that decides the techniqueMoving several items at once. Individually they are a flight; together they are either a stagger or a swarm, and only one of those is legible.
durationstaggeritem countcurve
Measured: lab is a workspace package here, so this row is checked rather than assumed.
Stat (lab)ADDbuilt · StatCountRigP3 · completeness
Today: Nothing.
Gap: Numeric value changes with no motion.
The preview must showA counting transition against a straight swap, at several magnitudes of change.
The hard case that decides the techniqueA value that changes while it is still counting, on a live dashboard. Counting animations that cannot retarget end up showing numbers that were never true.
count durationchange ratemagnitude
Measured: lab is a workspace package here, so this row is checked rather than assumed.
Chart / Radial / Sankey / Schedule (lab)ADDspecifiedP1 · decides an open question
Today: Axis ticks only, via inline styles.
Gap: No transition on data change, and outside the token system entirely.
The preview must showA data change animated through the JS token mirror, since these draw rather than style.
The hard case that decides the techniqueChart code cannot read var(--duration-fast). This family is the reason the JS mirror exists, so its preview is the mirror’s acceptance test rather than a motion demo.
durationcurvedata change size
Measured: lab is a workspace package here, so this row is checked rather than assumed.
Layout primitives (Stack, Grid, Card, Divider, Text, Heading, Kbd + 19 more)NONEspecifiedP3 · completeness
Today: Nothing, correctly.
Gap: None. Motion on a layout primitive is motion on everything that uses it.
The preview must showNothing. This row exists so the rubric can record a deliberate no rather than an omission.
The hard case that decides the techniqueThe only hard case is resisting the request. When someone asks for a Card hover lift, the answer is that Card is used 400 times and the cost is paid 400 times.

Build queue

The 31 specified previews in priority order. P1 rows each settle an open question, so they are worth building before the decision meeting rather than after it.
PriorityComponentSurfaceWhy it is worth building
P1BottomSheetOverlays & layersDrag-to-dismiss released mid-gesture. The spring has to carry the velocity the finger had; a keyframe restarts from zero and the sheet stutters.
P1Table row expansionDisclosure & expansionThis is the row that decides the whole disclosure question: a table row cannot be a grid container, so the grid-track technique is unavailable here. If the answer is "one technique everywhere", it has to be height — and this preview is the evidence.
P1Chat tool calls / reasoningDisclosure & expansionContent streaming in while the container is animating open. The height target moves during the transition, which is where height interpolation fails and grid tracks do not.
P1TopNavNavigationA mega-menu is large and frequently traversed. It is the one place where the published "no exit needed" guidance is most obviously wrong, and this component already disagrees with the docs.
P1Chart / Radial / Sankey / Schedule (lab)Content & dataChart code cannot read var(--duration-fast). This family is the reason the JS mirror exists, so its preview is the mirror’s acceptance test rather than a motion demo.
P2PowerSearch / Typeahead / MoreMenuOverlays & layersResults replacing results while the layer stays open. Animating the container is easy; animating a filtered list without making typing feel laggy is the decision.
P2CommandPaletteOverlays & layersKeystroke-rate list changes. This is the clearest case in the system for animating nothing: the rubric says 100+/day actions get none, and a palette is used by exactly those people.
P2LightboxOverlays & layersThe shared-element case: the image should appear to be the thumbnail growing. Everything else is a crossfade, and a crossfade on a full-screen surface is the thing the brief calls jarring.
P2MobileNavOverlays & layersThe close timing has to survive a theme that retunes the duration scale. That is exactly what the JS mirror is for, and this component is the existing proof it is needed.
P2Drawer (lab)Overlays & layersProving the bug is visible at 1x. If it is not, the finding is a code-hygiene issue rather than a user-facing one, and it should be scheduled as such.
P2Table tree / grouped rowsDisclosure & expansionExpanding many rows at once. Per-row stagger is the obvious idea and the wrong one: at 30ms across twenty rows the last row arrives 600ms late.
P2PaginationNavigationDirectionality is a published principle that the rubric drops entirely. This preview and Calendar are the two rows that would be measured against it, so they are the argument for adding the criterion.
P2SwitchInputs & controlsFast repeated toggling. Layout-property animation is cheap on one switch and expensive in a settings list of thirty, which is where this component actually lives.
P2SliderInputs & controlsThe same thumb must be transitioned for keyboard and untransitioned for drag, or dragging lags the pointer. One control, two input paths, opposite answers.
P2Calendar / DateInputInputs & controlsHolding the arrow to move six months. Each step has to be interruptible and the direction has to stay legible, which rules out a keyframe.
P2BannerFeedback & statusThe banner leaving is easy; the page reflowing underneath it is the animation. Height on the container, or transform on everything below.
P2FieldStatusFeedback & statusValidation firing on every keystroke. The error message changes while it is still entering, which is the interruptibility criterion on a surface nobody thinks of as animated.
P2EmptyStateContent & dataGoing from empty to populated after a search. The empty state and the results are different heights, so the container moves — and that movement is the animation people actually notice.
P2TransferList (lab)Content & dataMoving several items at once. Individually they are a flight; together they are either a stagger or a swarm, and only one of those is legible.
P3Overlay / scrimOverlays & layersEdge-dependent transform origin with a scrim that must not slide with the panel. Two elements, one dismissal, different curves.
P3TreeListDisclosure & expansionExpanding a deep node near the bottom of the viewport. The reveal and the scroll compensation fight each other, and doing neither is better than doing both badly.
P3BreadcrumbsNavigationResize is continuous, so the collapse can fire many times a second. This is a strong candidate for animating nothing, and the preview should make that easy to conclude.
P3Field / TextInput / TextArea / NumberInputInputs & controlsAuto-grow while typing. The height changes on a keystroke, so any duration above about 100ms means the field is still growing when the next character lands.
P3FileInputInputs & controlsA chip that arrives and then changes state while it is still arriving. Entry and progress on one element, which is the case ChipRig deliberately does not cover.
P3ResizableInputs & controlsThe drag must not be transitioned and the release must be. The transition has to be added on release and removed on grab, which is a state machine rather than a style.
P3Avatar / AvatarGroupFeedback & statusA cached image loads in the same frame as the component mounts, so a fade-in animates something that was never absent. The preview has to include the cached case or it will ship a flash.
P3MarkdownContent & dataChunks arriving faster than the fade duration. Overlapping fades either stack into a flicker or have to be dropped, and the current code has an answer worth documenting.
P3ThumbnailContent & dataA tap on touch fires a false hover. Hover-intent logic that is correct with a pointer reveals actions that cannot be dismissed on touch.
P3CarouselContent & dataScroll-linked edge fades repaint on every scroll frame. This is the one place in the audit where a mask is animated, and it needs a measurement rather than an opinion.
P3StepperContent & dataGoing backwards. The connector has to unfill and the content has to arrive from the other side, or back feels like forward.
P3Layout primitives (Stack, Grid, Card, Divider, Text, Heading, Kbd + 19 more)Content & dataThe only hard case is resisting the request. When someone asks for a Card hover lift, the answer is that Card is used 400 times and the cost is paid 400 times.