Scroll reveal
Reveal content once as it enters the viewport with a reduced-motion fallback.
Live preview
Elements fade or slide into place as they enter the viewport.
Definition and close-term distinctions
English definitions are preserved verbatim from Animation Vocabulary; translations and distinctions help you choose precisely.
scroll-revealScroll reveal
滚动揭示Elements fade or slide into place as they enter the viewport.
元素进入视口时淡入或滑动到位。
Decide before this motion ships
Purpose, frequency, trigger, timing, interruptibility, and gesture rules shape the implementation.
Signal a new reading section on first viewport entry while preserving scroll continuity.
Trigger each section at most once and select only structurally meaningful nodes on long pages.
Trigger from an IntersectionObserver threshold, showing initially visible content immediately.
Enter with brief travel and fade, then remain stable after leaving the viewport.
Complete the current reveal during fast scrolling to avoid animation queues.
Keep scrolling native and never capture wheel, touch, or keyboard scroll input for the reveal.
Show content immediately on viewport entry or keep one very brief fade.
Copy into your interface
Current parameters update visual-state CSS, semantic HTML, the required vanilla JS runtime, and an interaction prompt.
.motion-demo.motion-demo--scroll-reveal {
position: relative;
display: grid;
place-items: center;
width: min(100%, 34rem);
min-height: 15rem;
padding: 2rem;
overflow: hidden;
color: #18181b;
background: #fafafa;
border: 1px solid #e4e4e7;
border-radius: 12px;
isolation: isolate;
}
.motion-demo.motion-demo--scroll-reveal *, .motion-demo.motion-demo--scroll-reveal *::before, .motion-demo.motion-demo--scroll-reveal *::after {
box-sizing: border-box;
}
.motion-demo.motion-demo--scroll-reveal .motion-surface {
position: relative;
display: grid;
gap: 0.65rem;
width: min(100%, 18rem);
padding: 1.25rem;
background: #ffffff;
border: 1px solid #e4e4e7;
border-radius: 8px;
box-shadow: 0 1px 2px rgb(0 0 0 / 6%), 0 10px 28px rgb(0 0 0 / 7%);
}
.motion-demo.motion-demo--scroll-reveal .motion-line {
display: block;
width: 100%;
height: 0.5rem;
background: #e4e4e7;
border-radius: 999px;
}
.motion-demo.motion-demo--scroll-reveal .motion-line:last-child { width: 62%; }
.motion-demo.motion-demo--scroll-reveal .motion-button {
position: relative;
min-height: 2.75rem;
padding: 0.75rem 1.1rem;
color: white;
font: inherit;
font-weight: 700;
background: #18181b;
border: 0;
border-radius: 7px;
cursor: pointer;
}
.motion-demo.motion-demo--scroll-reveal .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }
.motion-demo.motion-demo--scroll-reveal .motion-action-row {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 0.85rem;
}
.motion-demo.motion-demo--scroll-reveal .motion-replay-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--scroll-reveal .motion-pausable-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--scroll-reveal button.motion-surface {
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
.motion-demo.motion-demo--scroll-reveal .motion-replay-button,
.motion-demo.motion-demo--scroll-reveal .motion-undo-button,
.motion-demo.motion-demo--scroll-reveal .motion-dismiss-button,
.motion-demo.motion-demo--scroll-reveal .motion-pause-button {
min-height: 2.75rem;
padding: 0.65rem 0.9rem;
color: #18181b;
font: 650 0.78rem/1 ui-sans-serif, system-ui, sans-serif;
background: #ffffff;
border: 1px solid #d4d4d8;
border-radius: 7px;
cursor: pointer;
}
.motion-demo.motion-demo--scroll-reveal .motion-replay-button:focus-visible,
.motion-demo.motion-demo--scroll-reveal .motion-undo-button:focus-visible,
.motion-demo.motion-demo--scroll-reveal .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--scroll-reveal .motion-pause-button:focus-visible,
.motion-demo.motion-demo--scroll-reveal [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--scroll-reveal [data-swipe-target]:focus-visible {
outline: 3px solid rgb(37 99 235 / 32%);
outline-offset: 3px;
}
.motion-demo.motion-demo--scroll-reveal .motion-status {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.motion-demo.motion-demo--scroll-reveal .motion-ripple-ink {
position: absolute;
width: 12px;
height: 12px;
pointer-events: none;
background: rgb(255 255 255 / 72%);
border-radius: 50%;
transform: translate(-50%, -50%) scale(0.95);
opacity: 0;
}
.motion-demo.motion-demo--scroll-reveal .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--scroll-reveal .motion-state { position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; background: #ffffff; border: 1px solid #e4e4e7; border-radius: 8px; }
.motion-demo.motion-demo--scroll-reveal .motion-state--to { background: #18181b; color: white; }
.motion-demo.motion-demo--scroll-reveal .motion-scroll-track > span { color: #71717a; font: 600 0.72rem/1.2 ui-monospace, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
.motion-demo.motion-demo--scroll-reveal .motion-orbit-item { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; color: white; background: #2563eb; border-radius: 50%; }
.motion-demo.motion-demo--scroll-reveal .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--scroll-reveal svg { width: min(100%, 20rem); height: auto; }
.motion-demo.motion-demo--scroll-reveal { max-height: 15rem; overflow-y: auto; place-items: start center; }
.motion-demo.motion-demo--scroll-reveal .motion-scroll-track { display: grid; place-items: center; gap: 10rem; width: 100%; min-height: 48rem; padding-block: 5rem; }
.motion-demo.motion-demo--scroll-reveal .motion-surface { animation: motion-scroll-reveal 260ms cubic-bezier(0.23, 1, 0.32, 1) both; }
@supports (animation-timeline: view()) {
.motion-demo.motion-demo--scroll-reveal .motion-surface { animation-timeline: view(); animation-range: entry 20% cover 55%; }
}
@keyframes motion-scroll-reveal { from { opacity: 0; transform: translateY(28px); } to { opacity: 1; transform: none; } }
@keyframes motion-scroll-reveal-reduced-fade {
from { opacity: 0.84; }
to { opacity: 1; }
}
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal {
animation: motion-scroll-reveal-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal *,
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal *::before,
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal .motion-button,
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal [data-spring-target] {
transform: none !important;
}
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--scroll-reveal .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
@media (prefers-reduced-motion: reduce) {
.motion-demo.motion-demo--scroll-reveal {
animation: motion-scroll-reveal-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.motion-demo.motion-demo--scroll-reveal *, .motion-demo.motion-demo--scroll-reveal *::before, .motion-demo.motion-demo--scroll-reveal *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.motion-demo.motion-demo--scroll-reveal .motion-button,
.motion-demo.motion-demo--scroll-reveal [data-reorder-item],
.motion-demo.motion-demo--scroll-reveal [data-swipe-target],
.motion-demo.motion-demo--scroll-reveal [data-spring-target] {
transform: none !important;
}
.motion-demo.motion-demo--scroll-reveal .motion-path { stroke-dashoffset: 0 !important; }
.motion-demo.motion-demo--scroll-reveal .motion-word--from { opacity: 0 !important; visibility: hidden; }
.motion-demo.motion-demo--scroll-reveal .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}Pre-delivery review
- Keep content visible when JavaScript is unavailable.
- Do not make critical above-the-fold content wait for an observer callback.
Keep information accessible
Show content immediately on viewport entry or keep one very brief fade.
Parameter reference
| Parameter | Range / options | Default | Purpose |
|---|---|---|---|
duration | 80ms–1200ms | 260ms | Controls the time required for one motion cycle. |
threshold | 0%–80% | 20% | Adjusts threshold. |
distance | 0px–120px | 28px | Controls how far the element travels. |
ease | Soft / Snap / Calm / Linear | Soft | Controls how velocity changes over time. |