← Overview

revealOnScroll

Reveals elements as they enter the viewport — an observer marks, CSS animates.

Scroll this page. Every card below fades and rises into place the moment it enters the viewport — and stays there. The script only ever marks what the browser reports as out of sight, so nothing you can already see is taken away; the six examples add a stagger, a selector instead of a marker per item, the four directions, a late trigger line, the single-element lane and a reveal that replays inside its own scroll box.

1 — Marked items, default look

The section carries data-js-behavior="revealOnScroll", each element to be revealed carries data-reveal-item, and the look comes from .c-reveal. Three items, three reveals — the heading included.

This card was transparent and 32px lower a moment ago. The distance is --reveal-distance, the timing --reveal-duration / --reveal-ease — all declared on .c-reveal itself, so a single item can be retuned with an inline custom property.

Reveal-once is the default: the attribute is gone, the item is unobserved, and when the last item of an instance is done both of its observers are disconnected. Scrolling back up leaves everything in place.

2 — A selector instead of a marker, and a stagger

The cards below carry no data-reveal-item: data-reveal-items=":scope > li" names them from the container instead — the lane for markup you cannot edit, a CMS list or a generated grid. With data-reveal-stagger="90" the four of them are released 90ms apart, in document order, so the grid arrives as a sequence. A reader who asked for reduced motion gets no sequence and no reveal at all: the content is simply there.

  • First
  • Second
  • Third
  • Fourth

3 — The four directions

One instance, four modifiers of the same component: --fade does not move at all (for a box that must not shift), --left and --right slide in sideways, --zoom grows into place. Each of them sets exactly one custom property, so a fifth direction is one declaration on the item — no new modifier needed.

--fade — opacity only

--zoom — from 0.94

--left — from the inline start

--right — from the inline end

4 — A later trigger line

data-reveal-offset="200" moves the trigger line 200px above the bottom edge of the viewport: the panel is not revealed when its first pixel appears but once it has come that far in. Useful for a tall element whose top edge would otherwise arrive long before the reader is looking at it.

Watch the bottom edge of the window as this panel comes up: it stays transparent for 200 more pixels of scrolling, then travels its 64px into place. The offset is also what keeps the repeat lane honest — an item that stops crossing the line is only hidden again once it is completely out of sight, never while it is still visible below the line.

5 — The single-element lane

The blockquote below has no marked item inside it, so the container is the item itself: one attribute on one element, and no wrapper to write. It is the same code path as everywhere else — the fallback is in the item lookup, not in a second mode.

“Pick the least powerful language that does the job.” The watching is the browser's (IntersectionObserver), the animating is the stylesheet's, and what is left for the script is one attribute per item.

6 — Inside a scroll box, replaying

The items below scroll inside the framed overflow: auto box — and there is no root option to set: an observer on the default root intersects an item with the clip box of every ancestor, so the box is the browser's business, not the module's. data-reveal-repeat puts an item back into its waiting state once it has left the view, so scroll the box up and down and the reveal replays as often as you like. Tab into it as well: the keyboard reveals an item before the scrolling does, and whatever holds the focus is never hidden again.

First item in the box. A link so there is something to tab to.

Second item. What decides here is whether the box shows it and the box itself is on screen — both are one intersection to the browser.

Third item. Another link — tab past the fold and watch it arrive before it is scrolled to.

Fourth item, and the one that shows the stagger inside the box: 60ms apart, in document order.

Fifth item. Scroll back to the top of the box and down again — every one of these replays, because the container carries data-reveal-repeat.