Reveals · effect

Riser

Children arrive on a stagger, held until the container is on screen.

press replay to bring them in again

Paste into Claude Code, Cursor or any agent. Pinned to a tag, so it will not change under you.

Options

select empty

CSS selector for the children to animate, scoped to the container. Empty means every direct child, which is what you want most of the time.

order forward

forward · reverse · centre

The order children arrive in. `centre` starts in the middle and works outwards, which suits a row of three or five.

stagger 90

0 to 400 ms, good: 60 to 140

Milliseconds between one child and the next. Past about 150 a list of eight takes over a second to finish and people start scrolling away from it.

duration 900

100 to 3000 ms, good: 600 to 1100

Milliseconds each child takes on its own.

rise 34

-120 to 120 px, good: 20 to 50

How far each child travels. Negative falls from above. Large values read as a page that has not finished loading.

scale 1

0.5 to 1.5, good: 0.94 to 1.06

Scale each child starts at. 1 is no scaling. Anything below 0.9 makes text resample and look soft on the way in.

blur 0

0 to 20, good: 0 to 6

Blur each child starts at. Off by default: on a card with an image inside, a blur costs far more than the transform does.

reducedMotionTime 999

0 to 9999 s

The single frame shown when the user prefers reduced motion. For a one-shot reveal this should be a time after the animation has finished, so the content simply appears.

What it needs

Dependencies
None at all
Browser
No WebGL
Frame budget
~0.3 ms · 0 context
Pinned to
v0.2.0
  • No canvas and no WebGL. It moves the children already in the element
  • No IntersectionObserver of its own: the runtime already holds the loop until the element is on screen
  • destroy() clears every style it set, leaving the children as they were found
  • One transform and one opacity per child, composited
  • Nothing here reads layout, so it cannot cause a synchronous reflow