Staggered reveal
Children rise in sequence when the section enters the viewport.
The headline
Rises second.
Rises third.
<section data-sv data-sv-once class="py-24">
<h2 class="sv-rise text-4xl font-bold">Title</h2>
<p class="sv-rise" data-sv-order="1">Copy</p>
<p class="sv-rise" data-sv-order="2" data-sv-distance="3rem">More</p>
</section>
<!-- once: import 'scrollvars/styles/core.css' + <ScrollVarsBoot /> in the layout -->
<!-- data-sv-order/-distance/-from/-to become the CSS vars on mount. No
style attr, works with mapped CMS content, adds zero global CSS.
Also fine: [--sv-order:1] arbitrary classes for static one-offs;
sv-stagger on the parent for sequential lists (no attrs at all). -->
<section data-sv> <!-- tracked; sets --sv-live in the band -->
<h2 class="sv-rise">Title</h2>
<p class="sv-rise" style="--sv-order: 1">Copy</p>
</section>
/* needs styles/core.css (or paste the preset): */
.sv, [data-sv] { --sv-live: 0; }
.sv.sv-live { --sv-live: 1; }
.sv-on .sv .sv-rise { opacity: var(--sv-live);
translate: 0 calc((1 - var(--sv-live)) * var(--sv-distance, 6rem));
transition: opacity .8s var(--sv-ease, cubic-bezier(0.28, 0.84, 0.42, 1)),
translate .8s var(--sv-ease, cubic-bezier(0.28, 0.84, 0.42, 1));
transition-delay: calc(var(--sv-order, 0) * var(--sv-stagger, 90ms)); }
/* the same sheet's reduced-motion override, and it has to sit AFTER the rule
it beats: equal specificity, later wins */
@media (prefers-reduced-motion: reduce) {
.sv-on :is(.sv, [data-sv]) :is(.sv-rise, .sv-fade, .sv-slide-l, .sv-slide-r, .sv-drift) {
transition: none; animation: none; opacity: 1; translate: none; }
}
/* the same under html[data-sv-motion="reduce"], the site's own switch */
.sv-on:where([data-sv-motion="reduce"]) :is(.sv, [data-sv]) :is(.sv-rise, .sv-fade, .sv-slide-l, .sv-slide-r, .sv-drift) { transition: none; animation: none; opacity: 1; translate: none; }
/* a focused entrance child must be visible now, not after its stagger
delay (WCAG 2.4.7): must sit AFTER the reduce blocks above, same
source-order rule */
.sv-on .sv .sv-rise:focus-within { transition: none; opacity: 1; translate: none; }
<Reveal auto> {/* every direct child, stagger for free */}
<h2>Title</h2>
<p>Copy</p>
</Reveal>
// or per-child knobs as props:
<Reveal stagger={140}>
<Item order={0}>first</Item>
<Item order={1} effect="slide-l" distance="4rem">second</Item>
</Reveal>