jatui

Reveal

IntersectionObserver scroll-reveal action — entrance variants on the --motion-* tokens

Scroll-triggered variants

Scroll the box below — each row below the fold reveals the first time it enters the viewport, then never again (the observer unobserves). Content already on screen when the action mounts is painted as-is with no entrance (below-fold-only entrances), so hydration never replays an animation over visible content. Uses the built-in variants remapped onto styles/motion.css tokens; no animista.css is shipped. Skipped entirely when prefers-reduced-motion is set — including live, if the preference flips while rows are still waiting.

fade-in
fade-in-bottom (default)
fade-in-top
fade-in-left
fade-in-right
scale-in-center
staggered row 1
staggered row 2
staggered row 3
staggered row 4
staggered row 5
staggered row 6
staggered row 7
staggered row 8
staggered row 9
staggered row 10
staggered row 11
staggered row 12
staggered row 13
staggered row 14

Threshold + delay

The default threshold is 0 — any intersection counts, so a tall element in a short viewport (a 9000px email preview in an 800px pane can only ever intersect ~8%) still reveals instead of sitting at opacity: 0 forever. threshold: 0.5 still opts into waiting until half the element is visible. delay is normalized: below 1 it is seconds (chimaro-compatible — 0.2 = 200ms), at 1 or more it is read as milliseconds (200 → 0.2s), and either way the wait is capped at 0.3s so a unit mix-up or an over-long stagger cannot hold content invisible.

scroll ↓
Reveals at 50% visibility, 150ms late

Hidden container (zero-area guard)

A routed pane that mounts inside a display: none subtree reports a 0×0 rect — that is not "below the fold", so the guard reveals it without the entrance instead of waiting for an intersection that may never come (the blank-pane bug). Open the pane below: the content appears immediately, never blank.

Opt-out surface: data-reveal="off"

Any ancestor carrying data-reveal="off" switches reveal off for its whole subtree — the action no-ops and the content simply renders visible. Useful for surfaces where entrances would hurt: dense panes, print, tests.

Row 1 — inside data-reveal="off", so always visible
Row 2 — inside data-reveal="off", so always visible
Row 3 — inside data-reveal="off", so always visible
Row 4 — inside data-reveal="off", so always visible
Row 5 — inside data-reveal="off", so always visible
Row 6 — inside data-reveal="off", so always visible

Usage

import { reveal } from 'jatui'
import 'jatui/styles/motion.css'

<div use:reveal>…</div>
<div use:reveal={{ animation: 'scale-in-center', delay: 0.2, threshold: 0.3 }}>…</div>

<!-- opt a whole surface out -->
<section data-reveal="off">…</section>