Foundations
Motion
Four durations, three curves, and one rule: if an animation doesn’t explain something, it doesn’t ship.
Principles #
- Enter slow, leave fast. Arrivals get just enough time to be understood; departures get out of the way.
- Travel short distances. Things move 2–12px, from where they came from. Nothing flies across the screen.
- Animate only transform and opacity. They’re composited by the GPU and never trigger layout.
- Colour changes are fast. Hover and focus respond within 120ms so the interface feels immediate.
Durations #
| Token | Value | Use |
|---|---|---|
| --juxt-duration-instant | 80ms | Leaving, pressing |
| --juxt-duration-fast | 120ms | Hover, colour, focus |
| --juxt-duration-normal | 180ms | Entering, toggles |
| --juxt-duration-slow | 240ms | Travel: indicators, sheets |
Easing #
Press play to compare the three curves over the same distance.
--juxt-ease-standardcubic-bezier(0.2, 0, 0, 1)
--juxt-ease-outcubic-bezier(0.16, 1, 0.3, 1)
--juxt-ease-incubic-bezier(0.4, 0, 1, 1)
Component choreography #
| Button | Press: scale 0.97 in 80ms. Release eases back in 120ms. |
| Input, Select | Focus: border takes the accent and a 3px halo fades in over 180ms. |
| Checkbox | The box fills in 120ms; the tick draws itself 40ms later. |
| Switch | The thumb travels in 180ms with ease-out, stretching 3px while pressed. |
| Menus, Select | Enter: fade in 120ms while rising 4px and scaling from 0.97 toward the trigger. Leave: 80ms. |
| Tooltip | Enter: 120ms, 2px toward its trigger. Leave: 80ms fade. Instant when moving between tooltips. |
| Dialog | Backdrop fades in 180ms. Panel fades and settles from 8px below at 0.97 scale over 240ms. Leave: 120ms. |
| Command palette | Faster than a dialog: 120ms fade, 180ms settle, 80ms leave. |
| Toast | Rises 12px from the edge over 240ms; others glide to make room. Leaves in 120ms. |
| Tabs | One indicator slides in 240ms. Panels only fade — content never slides. |
Reduced motion #
When the operating system asks for reduced motion, juxt.ui sets --juxt-motion-shift to 0 and --juxt-motion-scale to 1. Things still fade — a fade conveys a change of state without motion — but nothing travels, scales, stretches or slides. Spinners slow down, and the tab indicator jumps instead of gliding.
Use the same two tokens in your own transitions and you get the same behaviour for free.