Motion
Scroll-driven motion that respects reduced-motion preferences. cui-scroll-stage publishes focus and offset as CSS custom properties; cui-reveal layers entry animations on top.
Scroll stage
cui-scroll-stage is a sticky 3D track that publishes its focus as CSS custom
properties: --cui-stage-focus (0 → 1, peaks when centered),
--cui-stage-offset (-1 → 1, signed distance from center after the hold band), and
--cui-stage-active (0/1). Children animate with vanilla CSS by reading those values.
Scroll through the three stages below. Each applies a different transform recipe to the same
published variables. Stages with a tone attribute write
data-cui-tone to a scoped target while focused; the rig below is marked
data-cui-tone-root so the tint stays inside it instead of bleeding to the whole page.
Stage 01 · effect="wheel" · tone="dawn"
Z-translate + rotateY
This card reads --cui-stage-focus and --cui-stage-offset and turns
them into a depth-and-rotation move. The stage element holds focus through the hold band so
the panel can settle visually before scrolling on.
transform: translate3d(0, 0, calc(var(--cui-stage-focus) * 220px - 320px)) rotateY(calc(var(--cui-stage-offset) * 35deg));
Stage 02 · effect="slide" · tone="noon"
Signed offset slide
Same publisher, different recipe. Here the panel uses the signed
--cui-stage-offset for a horizontal slide that flips direction either side of
center. Notice how the page background tinted as this stage took focus.
transform: translate3d(calc(var(--cui-stage-offset) * -40%), 0, 0); opacity: calc(0.3 + var(--cui-stage-focus) * 0.7);
Stage 03 · effect="scale" · tone="dusk"
Scale + blur
One last recipe: a scale-up paired with a defocus blur that clears as focus approaches 1. None of these stages know about each other's transforms — they just expose state.
transform: scale(calc(0.72 + var(--cui-stage-focus) * 0.28)); filter: blur(calc((1 - var(--cui-stage-focus)) * 8px));
API reference
| Attribute / variable | Purpose |
|---|---|
focus-bias (-1..1) |
Shift what counts as "centered" in viewport space. |
hold (0..1) |
Dwell band where focus stays at 1 before falling off. |
threshold (0..1) |
Focus level at which --cui-stage-active flips to 1. |
sticky-top |
Top offset for the sticky inner track (match your navbar height). |
min-block-size |
Scroll runway length. Longer = slower wheel. |
tone |
Key written to data-cui-tone on the resolved target while this stage is most focused. |
tone-target |
Selector for where the tone is written. Defaults to the nearest [data-cui-tone-root] ancestor, falling back to the stage element itself. Use "document" to opt into the old page-wide behavior. |
--cui-stage-focus |
Published 0..1. Peaks when stage is centered. |
--cui-stage-offset |
Published -1..1. Signed distance from center after hold. |
--cui-stage-active |
Published 0 or 1. Mirror of the .is-active class. |
Reveal
cui-reveal wraps content that should fade and slide into place as its ancestor
stage becomes focused. Direction via from, timing via delay, travel
distance via distance. It's a pure-CSS element — it reads
--cui-stage-focus from its surrounding stage and interpolates. Outside a stage,
reveals stay fully visible.
API reference
| Attribute | Purpose |
|---|---|
from |
up · down · left · right · fade
|
delay |
Transition delay. Unitless numbers are treated as ms. |
distance |
Travel magnitude. Unitless numbers are treated as px. |
threshold |
Focus level at which the reveal starts. Defaults to 0.6. |