Scroll Weight
Typographic weight animated from 100 to 900 by the native browser scroll — animation-timeline: scroll(), zero JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Text has 56 effects, including 4 free. Explore the category →
3 usage examples



How it works
animation-timeline: scroll() (CSS Scroll-driven Animations, Level 1) binds a @keyframes animation's progress to a scroll container's scroll position, with no requestAnimationFrame or JavaScript event. On .vf-scroll-text, the declaration automatically targets the nearest ancestor marked as a scroll container — here .vf-scroll-container (overflow-y: auto). The browser maps the scroll position (0% at top, 100% at bottom) to the animation state on the compositor thread, off the main thread.
The vfScrollWeight animation is a symmetric round-trip in three steps: at 0% (no scroll), font-weight: 100, letter-spacing: 8px, opacity: 0.4; at 50% (mid-scroll), font-weight: 900, letter-spacing: -2px, opacity: 1; at 100% (end), back to the initial state. The weight rises then falls symmetrically — interpolation between 100 and 900 is smooth with a variable font covering the full weight axis.
The text is sticky-positioned at the vertical center of .vf-scroll-container — it stays visible throughout the scroll despite .vf-scroll-content being 800 px tall. The effective scroll range (800 − 280 = 520 px) determines the amplitude and perceived "duration" of the transformation.
If animation-timeline is not supported, font-weight stays frozen at 100 — text is readable but static. No JavaScript error is thrown since the effect is entirely CSS.
Accessibility
- prefers-reduced-motion: absent from the code. No
@media (prefers-reduced-motion: reduce)rule is defined — the animation stays active even when the OS requests reduced motion. Add it if needed:@media (prefers-reduced-motion: reduce) { .vf-scroll-text { animation: none; font-weight: 400; } }. - No ARIA attributes in the provided structure — neither
rolenoraria-labelon the container. Add them if the scene is the sole semantic content of a section. - Contrast: white text (
#fff) on#0a0a0fexceeds a 18:1 ratio — including atopacity: 0.4(effective ratio ~7:1), well above the WCAG AA threshold (4.5:1). - Text legibility is maintained at every weight: even at
font-weight: 100, the word remains perceptible on a dark background. - No interactive elements are introduced — no keyboard interaction or focus management required for the effect itself.
Browser compatibility
Requires animation-timeline: scroll() (CSS Scroll-driven Animations, Level 1). No JavaScript dependency.
On Safari < 17.2 and Firefox < 114, <code>font-weight</code> stays frozen at <code>100</code> — text is visible but static. No JS error (100% CSS effect).
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="vf-preview">
<div class="vf-scroll-container">
<div class="vf-scroll-content">
<span class="vf-scroll-text">YOUR TEXT</span>
</div>
</div>
<span class="vf-scroll-hint">Scroll ↓</span>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| font-size on .vf-scroll-text | 3rem | Size of the animated text. Large for a hero (5–8 rem), small for a component integration (1–1.5 rem). |
| font-family on .vf-scroll-text | Inter, sans-serif | Font. Must cover weights 100–900 for smooth interpolation — variable font recommended (Inter Variable, DM Sans, Plus Jakarta Sans, system-ui). |
| color on .vf-scroll-text | #fff | Text color. Adjust to your palette — contrast is maintained on dark backgrounds at every weight. |
| font-weight at 0% and 100% in @keyframes vfScrollWeight | 100 | Starting weight (no scroll) and ending weight (full scroll). Raise to 300 for a less extreme start. |
| font-weight at 50% in @keyframes vfScrollWeight | 900 | Peak weight at mid-scroll. 700 = classic Bold, 900 = Black — depends on your variable font's available axes. |
| letter-spacing in @keyframes vfScrollWeight | 8px → -2px | Starting and ending letter spacing. Narrow to 2px → 0px for multi-word titles legible at all stages. |
| height on .vf-scroll-container | 280px | Height of the visible scroll area. Increase for a hero section, decrease for a compact component. |
| height on .vf-scroll-content | 800px | Total height of the scrollable content. The effective range (800 − 280 = 520 px) determines the scroll effort needed to traverse the animation. Decrease to 400px for a faster trigger. |
FAQ
font-weight: 100 — readable but unanimated. No JavaScript error is thrown because the effect is 100% CSS.animation-timeline: scroll() with animation-timeline: scroll(root) on .vf-scroll-text, and remove overflow-y: auto from the container. The text will then animate based on the global page scroll — no oversized .vf-scroll-content needed.wght axis 100–900 gives continuous, pixel-perfect interpolation.