Animation
Motion should make an interface easier to read: acknowledge an action, show where a change came from, or preserve context while the interface changes. If it does none of those things, leave it still.
The three jobs of motion
Confirm input
A press state, selected option, or saved toast confirms that the system received the action. Keep it immediate and short.
Connect states
Menus, panels, and reordered content should make their origin or destination legible, so people do not have to re-orient themselves.
Direct attention
Use motion sparingly for a meaningful new result. Do not animate every item competing for attention on a busy screen.
Decide before you animate
- What changed, and does the change need help being understood?
- Can the motion follow the user’s input or a visible source element?
- Will the final state be usable immediately for mouse, keyboard, and assistive-technology users?
- Is the same information clear when motion is reduced or absent?
Timing and easing
| Interaction | Duration | Use it for |
|---|---|---|
|
Press feedback
|
100–160ms | Small scale, elevation, or color feedback while a control is pressed. |
|
Popover or tooltip
|
125–200ms | A compact layer appearing near the control that opened it. |
|
Menu or select
|
150–250ms | A spatial relationship that should be noticed but not hold up selection. |
|
Dialog or drawer
|
200–300ms | A larger context shift; keep the path short and the final focus ready. |
|
Shared token rule
|
--duration-100, --duration-120, --duration-160, --duration-180, --duration-300Use these instead of one-off values. |
--ease-out for entering or settling.--ease-fade-out for a deliberate fade-out.
|
Prefer a quick ease-out curve for elements entering their resting state: var(--ease-out). It feels responsive because most of the distance is covered early. Use linear timing only when something is intentionally continuous, such as a progress indicator.
Motion tokens
Motion tokens are global design-system values. Use them directly in components rather than introducing local durations, duplicated curves, or fallback values. This keeps feedback consistent and lets one decision change the whole interface.
| Token | Value | Use |
|---|---|---|
--duration-100
|
100ms | Very small selected-state or icon feedback. |
--duration-120
|
120ms | Press feedback on controls. |
--duration-160
|
160ms | Compact controls, icons, and content details. |
--duration-180
|
180ms | Default component transition and nearby layer motion. |
--duration-300
|
300ms | Larger context shifts such as drawers. |
| Token | Curve | Use |
|---|---|---|
--ease-out
|
cubic-bezier(0.23, 1, 0.32, 1)
|
Elements entering or settling into their final state. |
--ease-fade-out
|
cubic-bezier(0.4, 0, 0.8, 0.4)
|
Intentional exits and fade-outs. |
Use tokens in component motion
Choose the duration based on the size of the context change. Use the enter curve to settle a visible element; use the exit curve only when an element is leaving.
/* Entering or settling into place */ transition: transform var(--duration-180) var(--ease-out); /* A deliberate exit */ animation: fade-out var(--duration-180) var(--ease-fade-out) forwards;
Live patterns
Press feedback
Press the existing Button component. Its response should be immediate, subtle, and end exactly at its resting position; it is confirmation, not a loading indicator.
Origin-aware reveal
This uses the existing Popover and Card components. Its compact entry stays connected to the trigger, so new actions do not appear from nowhere.
Actions
Edit settings, duplicate item, or archive item.
Expand content in place
Use the Accordion component when content should remain connected to its heading. The control already provides focus behavior and an explicit expanded state.
Motion checklist
Result, not spectacle
A Toast communicates a completed result in text first. If it enters, travel only a small distance and do not dismiss it before someone can read it.
Implementation recipes
Enter a nearby layer
.c-popover { transform-origin: top center; animation: popover-enter var(--duration-180) var(--ease-out) both; } @keyframes popover-enter { from { opacity: 0; transform: translateY(-0.25rem) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
Animate cheap properties
Prefer opacity and transform. Avoid animating layout dimensions, large blurs, and transition: all.
Match exit to intent
Exit motion can be faster than entry because the person initiated it. Do not make close actions wait for a long animation.
Do not create a fake delay
Do not delay navigation, focus, selection, or validation just to let motion finish. UI state changes immediately.
Reduced motion is the baseline fallback
Respect the operating-system preference. Keep the final state, focus management, and written feedback exactly the same; remove the travel and timing. Do not use motion as the only confirmation that something happened.
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-duration: 1ms !important; } }