Motion
Spatial motion
Depth as a coordinate system, not a decoration. Six capability families, each one a public @luc/ui export, all driven by the control board below — change the depth, switch the material, freeze the orbit, and watch the page answer.
Control board
One panel drives every tile, so two treatments can be compared under identical settings instead of hunting for per-demo knobs.
Depth
24pxTilt
6°Orbit speed
1.0×Intensity scopes how loud everything below it may be. Reduced motion still resolves every level to none, and the arrangements stay intact.
Surfaces, materials and layered depth
ParallaxScene publishes one pointer offset as custom properties; each ParallaxLayer multiplies it by its own depth. One rAF loop, zero React renders per frame, and the whole scene still composes when nothing moves.
ParallaxScene
<ParallaxScene depth tilt> · <ParallaxLayer depth z axis>workspace
analyst / eu-west
depth 0
Text stays still
Moving the layer you read is the fastest way to make a page feel cheap. Depth belongs to decoration.
Readable content sits at depth 0 and never shifts. The grid and the aurora recede, the workspace card travels with the pointer, and the badge moves against it — the classic foreground read, all from the same two numbers.
One rAF loop
The scene owns the frame loop and stops it when the pointer leaves and the motion settles.
No per-frame renders
Positions travel as inherited custom properties, so layers are ordinary divs.
Flat, but composed
Without a pointer every offset resolves to zero and the layers keep their translateZ separation.
Geometric motion
An orbit is a layout: things are arranged on rings, the rings turn, and any seat can be a real control. Rotation runs on the compositor, and every label counter-rotates at the ring's exact period so it stays upright.
OrbitField
<OrbitField speed paused> · <OrbitFieldRing radius duration direction> · <OrbitFieldNode angle onSelect>APAC
412+6.4%
The outer ring is trackless — a ring without a visible track still arranges its seats, which is how a second tier is built without drawing a circle nobody asked for.
Why it reads as a system
Seats are real buttons: click, Tab and Enter all work, and aria-pressed carries the choice.
A seat is named with its value, so the orbit is fully legible to a screen reader.
Freezing the orbit keeps the arrangement — waiting for a turning chip is not an interaction.
Select a region on the middle ring; the hub reads the same identity.
Morphing and continuity
ExpandableSurface keeps a compact summary and its expanded detail in one grid cell with one shared layout identity, so the surface grows out of its own rectangle and settles back — in flow, with no portal, no scroll lock and no focus trap.
Design rules
In flow, never a modal: a dashboard card that reveals detail should not take the page away.
The trigger stays mounted — it is the focus target we return to and the element that carries aria-expanded.
Reduced motion drops the shared layout rather than slowing it, so the surface simply swaps.
Rejected: a full-screen morphing overlay for this content. It hides the surrounding context that makes the detail meaningful.
Kinetic typography
KineticHeadline animates a change of words with a direction and a reading rhythm. Word granularity, real text nodes, and one accessible string — never a character split across nine spans.
<KineticHeadline text mode stagger live />Built for dense interfaces
The animated layer is aria-hidden; a visually hidden copy carries the text and announces it politely when live is on, so a screen reader hears a sentence rather than a spelling.
The same string, static
Built for dense interfaces
Read both. The kinetic version must never be harder to read than the plain one — a headline that only works in motion is not a headline.
Depth and spatial navigation
CardStack makes depth the navigation: the active card is largest and closest, the deck behind it recedes on three axes, and stepping moves the whole deck. Arrow keys, Home and End, and a 48px touch drag all work.
<CardStack visible offsetY shrink spread> · <CardStackItem> · <CardStackControls>1 / 3Depth is state
The deck answers how much is left without a progress bar.
CardStackControls is a separate component rather than a controls boolean: it reads the same context and can be placed under the deck, beside it, or in a toolbar. The deck is also the one place the glass material is refused — overlapping translucent surfaces show each other's text.
What it deliberately is not
Not a carousel: there is no track, no autoplay and no slide transition.
Not a modal: the deck stays in the document, so the page keeps its scroll position.
Not pointer-only: every position is reachable from the keyboard and from the controls.
Interactive data motion
SelectionBeam measures two live DOM nodes and draws a travelling packet between them. Selection, marker and inspector stop being three separate highlights and become one gesture.
<SelectionBeam from={rowRef} to={inspectorRef} active tone />Arrow through the rows — the link follows the selection
APAC
EMEA
Americas
LATAM
Inspector
APAC
412M+6.4% quarter over quarter
Nothing here is a finance calculation: the beam takes two refs and knows nothing about tables or charts.
The overlay is position: fixed, so it needs no positioned ancestor and cannot be clipped by one. Measurement is rAF-coalesced and re-runs on resize and on any scroll, captured at the window so a beam inside a scrolling pane stays attached.
The contract behind all six
Spatial motion is expensive if it is careless. These are the rules these primitives are built to, and the ones a product can rely on.
Compositor only
Transform and opacity. No animated filters, no per-frame layout reads, no React render per pointer move.
Reduced motion keeps the composition
Positions, rings and layers still resolve to a deliberate arrangement — the motion stops, the design does not disappear.
Nothing decorative carries meaning
Every effect layer is aria-hidden and pointer-events-none; state is carried by semantics, colour and text.