Blur Reveal
Pure CSS animation that transitions text from blurry and invisible to sharp and readable — duration, blur intensity, and easing are fully adjustable.
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
The animation is driven entirely by CSS via @keyframes blurReveal. The initial state of .text-blur-reveal combines filter: blur(10px) and opacity: 0 — text is blurry and invisible. The to keyframe brings both values to filter: blur(0) and opacity: 1. The simultaneous interpolation of both properties creates the impression of text gradually emerging from a fog.
The forwards keyword in the animation shorthand is essential: it preserves the last keyframe state (blur(0), opacity: 1) once the duration has elapsed. Without it, the browser would instantly revert the text to its initial state (blur(10px), opacity: 0) at the end of the animation.
To replay the animation on click, the standard vanilla JS technique is to remove the .text-blur-reveal class, read element.offsetWidth (forces a browser reflow), then re-add the class: the cycle restarts cleanly from keyframe 0. No library needed — two lines of code.
Accessibility
- prefers-reduced-motion not implemented: the base version does not detect the system preference. Add
@media (prefers-reduced-motion: reduce) { .text-blur-reveal { animation: none; filter: none; opacity: 1; } }to display the text immediately without animation. - The text stays in the DOM and accessibility tree throughout the animation —
filterandopacitydo not affect screen reader output. opacity: 0in the initial state hides the text visually but is not equivalent tovisibility: hiddenordisplay: none: the content is already accessible to AT from page load.- At the final state (
filter: blur(0),opacity: 1), the effect does not alter any contrast ratio — WCAG AA compliance depends solely on the chosen colors. - If a 'Replay' button is exposed to the user, wire it to a native focusable
<button>, not a clickablediv— the keyboard must be able to trigger the replay.
Browser compatibility
CSS filter: blur() and @keyframes are supported in all modern browsers. Zero JS dependency for the base animation.
Without <code>filter</code> support (IE 11), the animation retains only the opacity transition (0 → 1) — the text fades in without blur. Without <code>@keyframes</code> at all, the text remains invisible (<code>opacity: 0</code>): add <code>.text-blur-reveal { opacity: 1; animation: none; }</code> as a fallback rule to protect that case.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="demo-preview">
<!-- Single text -->
<span class="demo-text text-blur-reveal">Your text</span>
</div>
<!-- Multiple lines with staggered delay -->
<div class="demo-preview">
<span class="demo-text text-blur-reveal">Headline</span>
<span class="demo-text text-blur-reveal"
style="animation-delay: 0.3s">Subheadline</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 |
|---|---|---|
| blur(10px) in .text-blur-reveal | 10px | Starting blur intensity. 4px = subtle, 10px = standard, 24px = dramatic. Change the value in the initial state of the selector (not in the keyframe, which already targets blur(0)). |
| animation: 1s ease in .text-blur-reveal | 1s ease | Duration and timing. Replace 1s with the target value (0.5s = snappy, 1.8s = cinematic); ease-out for elegant deceleration, linear for a constant unblur rate. |
| 0s (delay) in the animation shorthand | 0s | Delay before triggering. Set to 0.3s to let the page render before animating, or override per element via style="animation-delay: Xs" for staggered reveals. |
| opacity: 0 in .text-blur-reveal | 0 | Starting opacity. Set to 0.3 for a semi-visible start (text faintly perceptible on frame 1 — softer effect, less visual jump). |
| font-size: 2.5rem in .demo-text | 2.5rem | Text size — the animation adapts to any size. 1rem for a label, 6rem for a hero headline. |
| letter-spacing: -.02em in .demo-text | -0.02em | Letter spacing. Negative value for large headings (condensed look), 0 or slightly positive for body text. |
FAQ
.text-blur-reveal class from the element, read element.offsetWidth (forces a browser reflow), then re-add the class: el.classList.remove('text-blur-reveal'); void el.offsetWidth; el.classList.add('text-blur-reveal');. The browser restarts cleanly from keyframe 0 — two lines of JS, zero dependencies..text-blur-reveal class in HTML (or remove it on load via JS), then apply it inside an IntersectionObserver callback when isIntersecting becomes true: the animation fires at the exact moment the element becomes visible, without polling.<span class="text-blur-reveal"> and override the delay inline: style="animation-delay: 0.2s", style="animation-delay: 0.4s", and so on. Elements unblur in a cascade with no extra JS.