Navigation

v0.0.4
Structure

Reveal

Content that arrives as it scrolls into view.

On this page

A reveal is content that arrives as it scrolls into view: it fades in and rises the last few pixels while its first stretch crosses the edge of the viewport, and is at rest from then on. It is one class and a scroll-driven animation. Nothing observes the element and no class is added to it: the position of the scroll is the clock.

Reveal

<div class="flex flex-col gap-md max-w(420) mx-auto">  <p class="m(0) fg-text-muted text-sm text-center py-xl">Scroll down</p>  <article class="card mt(240)">    <header class="card-header">      <h3 class="card-title">Native first</h3>      <p class="card-description">Every overlay is a dialog or a popover, opened without script.</p>    </header>  </article>  <article class="card reveal">    <header class="card-header">      <h3 class="card-title">One material</h3>      <p class="card-description">Surfaces step apart on a ladder of light, in both themes.</p>    </header>  </article>  <article class="card reveal">    <header class="card-header">      <h3 class="card-title">Contracts</h3>      <p class="card-description">Each stylesheet says what it promises in a file beside it.</p>    </header>  </article>  <article class="card reveal" style="--reveal-scale: .96">    <header class="card-header">      <h3 class="card-title">Yours to change</h3>      <p class="card-description">Tokens retune every component; the source is there to own.</p>    </header>  </article>  <p class="m(0) fg-text-muted text-sm text-center py-xl">The end</p></div>
Scroll the preview: each card arrives as it enters.

#How it works

The element's animation runs on its own view timeline, animation-timeline: view(), over the range in which it enters the scroller. Three consequences follow from the scroll being the clock:

  • What is already in view when the page opens does not move at all: its entry is over before the first frame.
  • Scrolling back plays the animation backwards. Nothing has to be reset.
  • There is no delay to tune and nothing to stagger: elements that enter one after another arrive one after another.

The animation is finished within the first 12rem of the element entering, set by --reveal-range, so a tall section does not fade for a whole screen. An element shorter than that has arrived once all of it is in view, which means the last thing on a page always arrives.

#Tuning

Three properties tune it, on the element or on a parent of several:

  • --reveal-distance is how far it rises, 16px by default. Set 0 for a fade alone.
  • --reveal-scale is the size it arrives from. .96 suits a picture or a card; text should not be scaled.
  • --reveal-range is how much of the element has entered when it has arrived.

#When to use it

Use a reveal on whole blocks of a long page: a section of a landing page, a row of cards, a figure. Use it sparingly. A page on which everything arrives is a page on which nothing is at rest, and a reader who scrolls fast waits for it.

Do not put it on text a reader needs at once, on anything inside a dialog or a menu, or on content that changes size as it loads.

#Without scroll-driven animations

In a browser without animation-timeline: view(), and for a reader who asks the system for reduced motion, the class does nothing and the content is simply there. The same holds in print. So the content never depends on the animation: no opacity is set outside it.

#Accessibility

  • The content is in the document and in the accessibility tree from the start; only its opacity and its position change while it enters.
  • With prefers-reduced-motion: reduce there is no fade and no travel.
  • Keep the travel small. A large distance on a large block is motion across much of the viewport, which some readers find hard to look at.

#Classes

ClassDescription
revealAn element that fades in and rises 16px while its first stretch enters the viewport, and is at rest from then on

#Custom properties

PropertyDefaultDescription
--reveal-distance1remHow far it rises; 0 for a fade alone
--reveal-scale1The size it arrives from, such as .96 for a picture
--reveal-range12remHow much of the element has entered when it has arrived; an element shorter than this has arrived once all of it is in view

#Source

@master/ui/components/reveal.css
/* * Reveal: content that arrives as it scrolls into view. * A scroll-driven animation on the element's own view timeline: it fades in and rises the last few pixels * while its first stretch crosses the edge of the viewport, and is at rest from then on. Nothing observes * it and no class is added: the position of the scroll is the clock, so scrolling back plays it backwards * and what is already in view when the page opens does not move at all. * Without scroll-driven animations, and for a reader who asks for less motion, the content is simply there. * Geometry: 16px of travel, finished within the first 12rem of the element entering. */@layer components {  .reveal {    --reveal-distance: 1rem;    --reveal-scale: 1;    --reveal-range: 12rem;    @supports (animation-timeline: view()) {      @media (prefers-reduced-motion: no-preference) {        animation: reveal-arrive var(--easing-glide) both;        animation-timeline: view();        /* An element shorter than the range has arrived once all of it is in view. */        animation-range: entry 0% entry min(100%, var(--reveal-range));      }    }    @media print {      animation: none;    }  }}@keyframes reveal-arrive {  from {    opacity: 0;    scale: var(--reveal-scale);    translate: 0 var(--reveal-distance);  }}