Motion
Motion communicates relationships and reinforces hierarchy. Use the right token for the right moment — fast for press states, slow for reveals — and combine duration with easing to shape how an element arrives or departs.
Duration
Five steps, each tuned to a specific interaction type. Heavier geometry and content reveals warrant longer durations; micro-interactions should be nearly invisible.
Reference — Duration
| Token | Value | Use |
|---|---|---|
--duration-fast | 80ms | Press / active states. The user should feel the response, not watch it. |
--duration-base | 120ms | The default for hover colour and background shifts. |
--duration-slow | 200ms | Fill and progress indicators; anything tracking a measured change. |
--duration-enter | 250ms | Panel slide-ins, drawer opens, nav transitions. Default for geometry. |
--duration-reveal | 400ms | Content that appears in stages. Use sparingly. |
--duration-loop | 800ms | Repeating cycle animations — spinner, skeleton shimmer. |
Easing
All easings are one-directional — they describe how something arrives. The preview shows each curve in the direction it is designed to be used, then snaps back to reset.
Reference — Easing
| Token | Value | Use |
|---|---|---|
--ease-default | ease | Safe choice when direction is neutral (colour shifts, opacity fades). |
--ease-out | ease-out | Anything sliding or fading into view; standard pairing with --duration-enter. |
--ease-in-out | ease-in-out | Elements that travel a significant distance and need to feel settled at both ends. |
--ease-spring | cubic-bezier(0.16, 1, 0.3, 1) | Use for one expressive moment per interaction. Not for loops or repeated micro-interactions. |
prefers-reduced-motion: base.css sets transition-duration and animation-duration to 0.01ms when the user has requested reduced motion; no extra guard is needed if you import farn.css. In custom animation code, always add an explicit @media (prefers-reduced-motion: reduce) guard.
Pairing Guide
Each scenario calls for a specific duration and easing combination. The demos below loop continuously to show the intended feel.
--duration-fast + --ease-default--duration-base + --ease-out--duration-enter + --ease-spring--duration-enter + --ease-out--duration-reveal + --ease-outReference — Pairing Guide
| Situation | Duration | Easing |
|---|---|---|
| Button press / active | --duration-fast | --ease-default |
| Hover colour shift | --duration-base | --ease-out |
| Panel / drawer enter | --duration-enter | --ease-spring |
| Panel / drawer exit | --duration-enter | --ease-out |
| Content reveal | --duration-reveal | --ease-out |
Code example
Combining duration and easing in a single transition declaration. Transform and opacity use different easing functions to feel natural — spring for the slide, ease-out for the fade.
.panel {
transform: translateY(8px);
opacity: 0;
transition:
transform var(--duration-enter) var(--ease-spring),
opacity var(--duration-enter) var(--ease-out);
}
.panel.is-open {
transform: translateY(0);
opacity: 1;
}