Micro-interactions
Segmented
A segmented control whose indicator travels on a shared layout id and smears in the direction it is moving.
Install
bun add motion clsx tailwind-mergeThen copy 2 files into your project:
- components/registry/segmented/segmented.tsx
- lib/utils.ts
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| indicator | "solid" | "outline" | "underline" | "solid" | How the active segment is marked. All three are the same travelling element at different settings — the shared layout id does not change, so switching between them costs no extra animation code. |
| sizing | "auto" | "equal" | "auto" | Auto sizes each segment to its label, so the indicator resizes as well as travels — the half of the FLIP that equal-width tabs never show. Equal splits the row evenly and fills the container. |
| density | "comfortable" | "compact" | "comfortable" | Padding and type size. Compact is the toolbar setting; it shortens the travel too, which is why the smear reads more subtly there. |
| radius | number | 8 | Corner radius of the indicator and the segments. motion corrects the radius through the layout animation, so a rounded indicator does not go oval while it is resizing. |
| stiffness | number | 520 | Spring tension on the travel. It also drives the proxy spring the deformation is read from, so the smear stays in phase with the movement at every setting rather than at the one it was tuned on. |
| damping | number | 34 | Spring friction. Low lets the indicator ring past the segment and come back, which the smear follows in both directions; high arrives dead and barely deforms at all. |
| smear | number | 0.5 | How hard the travel deforms the indicator. The stretch is along the direction of movement and anchored at the leading edge, so it trails behind rather than running ahead. Zero is a rigid box that slides. |
| glow | boolean | true | Accent bloom under the indicator. It travels with the box and takes the same deformation, so it reads as light coming off the thing rather than a separate shape following it. |
| accentColor | string (hex) | "#a855f7" | The indicator, its bloom, and the label of the active segment when the indicator is not filling it. |
What It Demonstrates
- Direction-aware transition
- Content slides one way going forward and the opposite way going back, so navigation has a sense of direction.
- Layout animation
- When an element's size or position changes, it animates to the new spot instead of snapping.
- Squash & stretch
- Deforming an element as it moves to convey weight, speed, and flexibility.
- Velocity
- How fast and in which direction an element is moving. A spring carries it into the next animation when interrupted, so a flicked element keeps its speed.
- Spring
- Motion driven by physics (tension, mass, damping) rather than a set duration.
Related Components
Hover List
A display-type list where a shared accent block springs between rows on hover while the text indents and inverts.
Reorder
A sortable list that answers to a finger and to a keyboard equally — grab a row, and the rest step aside to make the space.
Dial
A machined rotary knob: angular drag with detents you can feel on the way past, inertia on a flick, and a spring that settles into the nearest stop. The angle lives in a motion value rather than in React state — a drag writes it sixty times a second and a spring more often than that — so the only thing React holds is the detent index, and only when that integer actually changes. Arrow keys step through the same spring the hand does, so the keyboard is not a second, worse path to the same control.
Morph Dialog
A dialog that morphs open from its trigger via shared-layout animation, with elements that travel between card and panel.
Stagger Menu
A menu overlay that blooms open as a clip-path circle spring while items stagger in with blur and rotation, then collapses back to its pill.
Magnetic Button
A button that leans into your cursor — caught in a magnetic field it slides toward the pointer while an amethyst aura swells with proximity, then snaps home on a spring and answers every click with a dark ripple bursting outward from the point of contact.