Scroll✨ Premium

Blur to Clear

Three blocks in a 280 px area: each goes from blur(10px) and opacity 0 to sharp and opaque between entering the frame and reaching its middle — animation-timeline: view(), animation-range: entry cover 50%. No scale, no translation, zero JavaScript: focusing is the only motion.

CSSBlurView

Updated

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Scroll has 70 effects, including 12 free. Explore the category →

3 usage examples

Vision zones of a progressive lens — an optician's product page — Blur to Clear example 1

① Vision zones of a progressive lens — an optician's product page

WhenProduct page of a progressive lens: three blocks (distance, intermediate, near vision) with a short text, each blurred then sharp as you scroll down.
WhyBlur is the subject here: a corrective lens makes sharp what was blurred, without distorting the image; fx-0543 adds a scale(1.1) → 1 that would make the lens illustration “breathe”, the opposite of what an optician wants to say.
Settings296 px area on a light background, stage set to height: auto with 118 px of bottom margin; white 95 px blocks (1 px border, #0f172a text, SVG pictogram of the lens zone) instead of the gradients; blur(10px), opacity: 0 and entry cover 50% untouched. Measured at rest: block 2 at .64, block 3 at .09, sharp at 178 px (237 px travel). The automatic scrolling is a demo device.
Excerpts from a crime novel — a publisher's page for a book launch — Blur to Clear example 2

② Excerpts from a crime novel — a publisher's page for a book launch

WhenLaunch page: the cover, then three excerpts (the body, the alibi, the detail that doesn't fit) on a colored background, each excerpt coming out of the blur as you read.
WhyA clue that “comes into focus” reads literally in blur(10px) → 0, and entry → cover 50% guarantees sharp text as soon as it reaches the middle of the frame; fx-0553 Clip Path would unveil it from one edge to the other, a curtain rather than a focus pull.
Settings302 px area on a burgundy background, translucent 101 px cards (rgba(255,255,255,.07)), excerpts in 12.5 px italic Georgia (#fbe9ee, 14.4:1); stage set to height: auto, 120 px of bottom margin. Measured at rest: block 2 at .74, block 3 at .18, sharp at 165 px (215 px travel).
Film posters in a catalog — the TV interface of a streaming platform — Blur to Clear example 3

③ Film posters in a catalog — the TV interface of a streaming platform

WhenTV screen: a vertical row of posters (title, running time, genre) browsed with the remote, each blurred poster becoming sharp as it approaches the center.
WhyFrom three meters away, the center of the screen is the remote's focus zone: the entry → cover 50% range makes the poster sharp exactly there; fx-0543 finishes it at entry 60%, too early for a navigation where the eye settles at the center.
Settings294 px area, six 86 px posters (50 × 68 thumbnail, title, running time · genre · year), padding: 99px 6px top and bottom so the first and last pass through the center; a fixed 96 px focus frame (pointer-events: none) marks the spot where cover 50% makes the poster sharp. One poster becomes sharp every 96 px (470 px travel).

How it works

Four levels: the frame .demo-preview (centered flex, min-height: 300px, position: relative, #0a0a0f background, white text), the scrolling area .sdc-scroll-container (100% × 280px, overflow-y: auto, scroll-behavior: smooth, 4 px WebKit scrollbar), the stage .sb-blurtoclear-content (a fixed 900 px, padding: 60px 16px, flex column, gap: 30px) and three .sb-blurtoclear-box: padding: 24px, 16 px radius, 135° gradients cyan → violet, violet → pink, green → cyan, a 1.1 rem h4 at weight 800 (Inter set inline, a catalog leftover) and a 0.75 rem p in white at 90%. Measured in Chromium 151 on a blank page: 91 px blocks at 60, 181 and 302 px from the top of the stage, travel 900 − 280 = 620 px. The .sdc-scroll-hint hint “Scrollez ↓” (absolute, 8 px from the bottom of the frame, 10.4 px, white at 30%) pulses from .3 to .7 every 2 s. Empty JavaScript field, no site variable, no console error, same rendering at 390 px and on a white page.

Each block starts at filter: blur(10px); opacity: 0; a single keyframe brings it back, @keyframes sbBlurClear { to { filter: blur(0); opacity: 1 } }. The engine is not time but position: animation-timeline: view() ties progress to the block's journey through the nearest scrolling container — the area, not the page — and animation-range: entry cover 50% bounds the useful window: 0% when the block's top edge crosses the bottom of the area (entry 0%), 100% when its center reaches the center of the area, at 140 px (cover 50%). animation-duration: auto, linear and animation-fill-mode: both do the rest: proportional progress, start state held before the range, end state held after it. For 91 px in 280, the range is 185 px (half block + half area), with opacity = p and blur = 10 × (1 − p): measured with the wheel on the third block, opacity .42 and blur(5.8px) at 100 px of scroll, .69 and 3.1 px at 150, sharp at 207.

The shipped geometry does not treat the three blocks alike. The first (center at 105 px) has already passed the middle at rest: fill-mode: both holds it sharp before any scrolling. The second is caught mid-range — measured at rest opacity .53, blur(4.7px), sharp from 86 px. Only the third plays the whole sequence, from 22 to 207 px. Then nothing over the remaining 413 px and, from 393 px on, the area shows nothing but its background — 227 px of empty travel, due to the stage's fixed 900 px. No transform: a block only moves because the area scrolls, which sets it apart from Blur Reveal (fx-0543, scale(1.1) → 1) and Zoom Into Content (fx-0570, scale(.3) → 1). The animated filter puts each block on its own compositing layer: three blocks cost nothing, fifty are worth measuring.

Accessibility

  • prefers-reduced-motion not in the code: measured under emulation, nothing changes — third block at opacity 0 at rest, .42 at 100 px, pulsing hint. The animation is driven by the reader, with no displacement or zoom: a mild case under WCAG 2.3.3, but a block that stays invisible until you scroll is still a nuisance. Add @media (prefers-reduced-motion: reduce) { .sb-blurtoclear-box { animation: none; filter: none; opacity: 1 } .sdc-scroll-hint { animation: none } }: everything is flat and sharp, as in Firefox.
  • Keyboard: measured, Tab focuses the area without a tabindex in Chromium (outline: auto ring) as in Firefox; Arrow Down moves 40 px in Chromium (third block at .10), 51 px in Firefox; PageDown 245 px (block sharp), End goes to 620. Safari does not focus a scrolling box without tabindex="0": add it, with role="region", an aria-label and a visible :focus-visible.
  • Contrast (17.6 px title at weight 800, below the 18.66 px “large text” threshold: 4.5:1 required): white on #06b6d4 2.43:1, #8b5cf6 4.23:1, #ec4899 3.53:1, #10b981 2.54:1 — no gradient end passes; the 12 px paragraph at 90% ranges from 2.22 to 3.75:1. Mid-range, text and block both at 50% over #0a0a0f drop to 1.9-2.5:1, a transient acceptable if the end state is readable: darken the gradients (#0e7490 → #6d28d9 gives 5.4 and 7.1:1). The hint at 30% reads 2.62:1: decorative, aria-hidden="true".
  • Screen readers and fallback: three divs with an h4 and a p, read whatever the opacity — opacity: 0 removes nothing from the accessibility tree, unlike visibility: hidden. An h4 with no h2/h3 above breaks the page outline. “Scrollez ↓” is hard-coded in French. Where animation-timeline does not exist (Firefox, Safari before 26), the blocks are sharp from the start: the content never depends on the effect.

Browser compatibility

CSS only, no JavaScript, no dependency: animation-timeline: view(), animation-range with entry and cover, animation-duration: auto, animated filter: blur(). Chrome and Edge 115+, Safari and iOS 26+. Firefox: no version — measured in Firefox 153, CSS.supports('animation-timeline: view()') returns false, animation-duration: auto is rejected and the duration falls back to 0 s: the animation jumps to its to keyframe, the three blocks are sharp and opaque from load (opacity 1, blur(0) over the whole travel), the area scrolls, nothing is hidden or overlapping. No @supports in the code, and none needed: the end state is the readable state. A buying fact: on Firefox, your visitor sees a list of cards with no focusing.

Chrome 115+✓ Full
Firefox✓ No version (blocks sharp and readable from the start, no animation — measured in Firefox 153)
Safari 26+✓ Full (before 26: blocks sharp from the start, no animation)
Edge 115+✓ Full
Mobile iOS 26+✓ Full (touch scrolling of the area)
Android Chrome 115+✓ Full

Without animation-timeline (Firefox, Safari and iOS before 26, Chrome and Edge before 115), the declaration is dropped, animation-duration: auto too, and the 0 s animation ends at once: fill-mode: both applies the final keyframe, sharp and opaque blocks, a scrolling area. No script to enable.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="demo-preview">
  <div class="sdc-scroll-container">
    <div class="sb-blurtoclear-content">
      <div class="sb-blurtoclear-box">
        <h4>…</h4>
        <p>…</p>
      </div>
      <div class="sb-blurtoclear-box">…</div>
      <div class="sb-blurtoclear-box">…</div>
    </div>
  </div>
  <span class="sdc-scroll-hint">…</span>
</div>
Unlock the full code — €9.90/mo excl. tax

The full HTML + CSS + JS of Blur to Clear, ready to paste — plus all 811 effects. Or €299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Reveal range (CSS — animation-range: entry cover 50%) entry 0% → cover 50% (185 px for a 91 px block in 280) entry 0% entry 100%: sharp as soon as the block is fully inside (91 px of travel). cover 20% cover 80%: focusing spread over most of the crossing. entry 25% cover 50%: block invisible for its first 23 pixels.
Starting blur (CSS — filter: blur(10px)) 10 px 20 px (fx-0543's value) erases the shape of the block, 4 px gives a veil. The blur covers text and background: 12 px text is unreadable from 3 px.
Starting opacity (CSS — opacity: 0) 0 At 0, a block outside its range is absent — hence the third one invisible at rest. .35 keeps a blurred silhouette that announces what follows; the keyframe still leads to 1.
Curve (CSS — animation-timing-function: linear) linear Proportional to the scroll (measured: opacity .42 at 42% of the range). ease-out makes it almost sharp within the first third, ease-in delays sharpness until the center.
Area height and stage length (CSS — .sdc-scroll-container { height: 280px }, .sb-blurtoclear-content { height: 900px }) 280 px / 900 px (620 px travel, 227 of them empty) A block's range is half block + half area: a 400 px area brings each focus pull to 245 px. Set the stage to height: auto so the travel ends at the last block, plus a padding-bottom of half an area if it must reach the center — the setting of the three scenes.
Blocks (CSS — .sb-blurtoclear-box, :nth-child(2), :nth-child(3)) 24 px padding, 16 px radius, three 135° gradients A fourth block reuses the base rule's cyan → violet: add its :nth-child(4) line. Every child is blurred with the block, images included.
Timeline container (HTML/CSS — .sdc-scroll-container) 280 px area with overflow-y: auto view() picks the nearest scrolling ancestor. Without the area (or with overflow: visible; height: auto), the page drives and each block becomes sharp in the middle of the window. view(block 15%) tightens the range by 15% at the top and bottom.
“Scrollez ↓” hint (HTML/CSS — .sdc-scroll-hint) 8 px from the bottom of the frame, 10.4 px, white at 30%, 2 s pulse Hard-coded French, positioned relative to .demo-preview: without that frame it drops to the bottom of the next positioned ancestor. Delete the span and sdcHintPulse with it.

FAQ

Because the range is relative to the block's position in the area, and the shipped stage puts the blocks at 60, 181 and 302 px in a 280 px area. For the first block (center at 105 px, already above the 140 px midpoint), the entry cover 50% range is entirely past at load and fill-mode: both holds the final keyframe. The second runs from −99 to 86 px: at rest it is at 53%, measured opacity .53 and blur(4.7px). Only the third (22 → 207 px) starts from zero. To make the first one play too, push it below the bottom of the area (padding-top: 280px on the stage) or shorten the range (entry 0% entry 100%, which ends before the center). In the three scenes on this page, the first block is deliberately sharp from the start: it shows the arrival state.
Same skeleton (280 px area, 900 px stage, three blocks, animation-timeline: view(), zero JavaScript), two settings that change everything. fx-0543 animates three properties — opacity: 0 → 1, blur(20px) → 0, scale(1.1) → 1 — over entry entry 60%: sharp once 60% of its height has entered, 55 px of travel for 91 px, before it is even fully visible, and it shrinks as it sharpens. fx-0551 animates only the opacity and a 10 px blur, with no transform, over entry cover 50%: 185 px, 3.4 times longer, sharpness in the middle of the area. Pick fx-0551 when the content must not deform (product photo, poster, text) or when sharpness must coincide with the reading zone; fx-0543 for a short, theatrical entrance. Zoom Into Content (fx-0570) is the third setting of the same technique: scale(.3) → 1 and blur(8px) over the same range, for a hero block. On Firefox, all three behave the same: content sharp from the start.
Yes to both. view() attaches to the nearest scrolling container: without .sdc-scroll-container, the window drives and cover 50% becomes “block center at the center of the screen” — on an 800 px tall phone, the range of a 91 px block is 445 px. The number of blocks is free: the rule is a class, each block carries its own timeline, only the backgrounds are in :nth-child. Two precautions: the last block only reaches the center if the page can still scroll half a window below it (footer or padding-bottom, otherwise prefer entry 0% entry 100%); and every blurred block lives on its own layer, light for a dozen cards, worth measuring beyond. view() reads the vertical axis: nothing reacts to horizontal scrolling.