Motion
Motion confirms actions and creates continuity. Nothing else. No gratuitous animation. No entrance animations. No scroll-triggered reveals. Every transition exists because removing it would make the interface feel broken or ambiguous.
Signature easing
cubic-bezier(0.22, 1, 0.36, 1)
A decelerating settle curve. The element moves quickly at first and eases into its final position, like a physical object coming to rest. Used for spatial transitions where the shape of the curve is perceptible: underline sweeps, weight shifts, layout expansion, view transitions. Hover over any link on this page — the underline sweep uses this curve.
Secondary easing
ease
The CSS default. Used for simple property changes where the curve shape won’t be noticed: color shifts, opacity fades, border-color changes. There’s no reason to use the signature curve when the human eye can’t distinguish it from ease on a 0.2s color fade.
Animation inventory
Grouped by what triggers the motion.
Hover transitions
| Element | Properties | Duration | Easing |
|---|---|---|---|
| Prose link underline sweep | background-size | 0.3s | signature |
| Prose link color | color | 0.2s | ease |
| Nav nameplate weight shift | font-variation-settings | 0.4s | signature |
| Post title weight + color | font-variation-settings 0.4s signature, color 0.3s ease | ||
| Side rail link color | color | 0.2s | ease |
| Theme toggle color | color | 0.2s | ease |
| Accent dot border | border-color | 0.15s | ease |
| Accent option scale + border | transform, border-color | 0.15s | ease |
| Font size control | font-size | 0.15s | ease |
State changes
| Element | Properties | Duration | Easing |
|---|---|---|---|
| Toast show/hide | transform 0.3s signature, opacity 0.3s ease | ||
| Accent picker expand | max-width 0.3s signature, opacity 0.2s ease, margin-left 0.3s signature | ||
| Share modal overlay | opacity, visibility | 0.2s | ease |
| Share modal buttons | border-color, background | 0.25s | ease |
| Side rail visibility | opacity | 0.4s | ease |
| Body background (theme switch) | background | 0.3s | ease |
| Details toggle open | opacity, transform | 0.25s | ease |
Decorative
These are the only animations gated behind prefers-reduced-motion: no-preference:
| Element | Animation | Duration | Easing | Iteration |
|---|---|---|---|---|
| Live asterisk breathe | opacity 1 to 0.15 | 4s | ease-in-out | infinite |
| Accent hint pulse | scale 1 to 1.3 | 0.6s | ease | 2 |
Both are non-interactive. They communicate liveness (the asterisk) and invite discovery (the hint pulse). Neither conveys essential information.
View transitions
Cross-document view transitions are enabled globally:
@view-transition {
navigation: auto;
}
Two transition groups are defined:
| Group | Duration | Easing | Purpose |
|---|---|---|---|
root | 150ms | ease | Content crossfade between pages |
nameplate | 200ms | ease | Nameplate morphing (size/position shift between homepage and interior) |
The nameplate transition creates continuity as you move between pages. The content crossfade is fast enough to feel instant while preventing a hard cut.
Scroll progress
The scroll progress bar uses animation-timeline: scroll() (behind @supports) with a linear timing function. This is scroll-linked, not time-based, so easing curves don’t apply.
Reduced motion
Two levels of reduction.
Decorative animations (breathe, hint pulse, details toggle) are wrapped in @media (prefers-reduced-motion: no-preference) and only run when the user hasn’t requested reduced motion.
Blanket override catches everything else:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
This collapses all transitions to effectively instant. Hover state changes still occur (confirming user actions), they just skip the interpolation. View transitions get their own override, zeroing out animation-duration on all ::view-transition-* pseudo-elements.
The 0.01ms value rather than 0s avoids edge cases where some browsers skip transitionend events entirely when the duration is zero.
Do’s and don’ts
| Do | Don’t |
|---|---|
| Use signature easing for spatial transitions | Use linear or ease-in for UI transitions |
Use ease for simple color/opacity changes | Use signature easing where the curve won’t be perceived |
Gate non-interactive animation behind prefers-reduced-motion | Assume hover transitions need motion gating |
| Keep durations between 0.15s and 0.4s | Add animations longer than 0.5s |
Specimens
Signature Easing
Hover Demos
Breathing Asterisk
Reduced Motion
All decorative animations respect prefers-reduced-motion. Hover transitions do not need gating — they are responses to user action, not ambient decoration.