# Blur Reveal

> A text comes in piece by piece the first time it scrolls into view: each word, or each letter, from blurred and a little below its place to sharp and in place.

Source: https://ngwr.dev/animations/blur-reveal  
Kind: Animation, Directive, CSS  
Added in v15.7

## Installation

Import the class, and `@use` the entry point in your global styles. Nothing warns you when the `@use` is missing.

```typescript
import { Component } from '@angular/core';
import { WrBlurReveal } from 'ngwr/animations/blur-reveal';

@Component({ imports: [WrBlurReveal] })
export class MyComponent {}
```

```scss
@use 'ngwr/animations/blur-reveal';
```

## Basic

Put `wrBlurReveal` on the element that holds the text. It plays once, when most of the element is in view, and leaves the element exactly as it was: the same nodes, bindings included, go back when the last piece lands.

```angular-html
<h2 wrBlurReveal>Signal Forms, with nothing in between.</h2>
```

## Letters

`by="letters"` enters one grapheme at a time. The letters of a word sit in a no-wrap wrapper, so a word never breaks across lines mid-word, and an emoji or an accented letter is one piece.

```angular-html
<h2 wrBlurReveal by="letters">Built on the new Angular.</h2>
```

## From above

`from="above"` settles each piece down into its place instead of up.

```angular-html
<h2 wrBlurReveal from="above">The checks behind ngwr.</h2>
```

## Timing

`delay` is the pause between the element entering the viewport and the first piece. `stagger` is the gap from one piece to the next; unset, it is 70 ms for words and 28 ms for letters. Whatever the gap, the whole reveal spreads over 1.2 s at most, so a long paragraph does not keep the reader waiting. The blur, the offset, the duration and the easing are CSS variables.

```angular-html
<p wrBlurReveal delay="300" stagger="120">Each word waits for the one before it.</p>
```

```scss
.hero h2 {
  --wr-blur-reveal-blur: 0.4em;
  --wr-blur-reveal-duration: 1s;
}
```

## Accessibility

| Part | Behaviour |
| --- | --- |
| Screen readers | While the pieces play they are `aria-hidden`, and a visually hidden copy of the text stands beside them, so the sentence is announced once and never letter by letter. At rest the element holds its original text nodes. |
| Reduced motion | Under `prefers-reduced-motion: reduce` nothing is split and nothing moves: the text is shown as authored. The demos on this page follow the same setting. |
| Server | The server renders the plain text. The split happens in the browser, after the first render, and only where `IntersectionObserver` exists. |
| Selection and search | Spaces between pieces stay real text, so the words wrap where they did, and find-in-page and copy work once the reveal has ended. |

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `by` | What enters one at a time: a word, or a grapheme. In letter mode the letters of one word sit in a no-wrap wrapper, so a word never breaks across lines mid-word. | `WrBlurRevealBy` | `'words'` |
| `from` | The side a piece settles in from. | `WrBlurRevealFrom` | `'below'` |
| `stagger` | Gap between the start of one piece and the next, in ms. Unset, the mode decides: 70 for words, 28 for letters. Either way the whole reveal spreads over 1.2 s at most, so for a long text the gap shrinks until the last piece starts by then. | `number \| null` | `null` |
| `delay` | Pause between the host entering the viewport and the first piece, in ms. | `number` | `0` |

## CSS variables

Set these on the element itself, with a rule more specific than its Declared on selector. In a global stylesheet, put a class of yours on an ancestor it really has: `<body>` for an overlay panel, which renders outside your markup. In a component stylesheet the selector alone works, but only for an element in that component's own template. A value on a wrapper or `:root` is shadowed, because `ngwr/animations/blur-reveal` declares each default on its own element.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-blur-reveal-blur` | `0.2em` | `.wr-blur-reveal` |
| `--wr-blur-reveal-delay` | `0ms` | `.wr-blur-reveal` and inline on the element, written by the component from its inputs or its state — a rule holds only until the component writes |
| `--wr-blur-reveal-direction` | `1` | `.wr-blur-reveal` +1 variant override |
| `--wr-blur-reveal-duration` | `0.6s` | `.wr-blur-reveal` |
| `--wr-blur-reveal-ease` | `var(--wr-ease-out)` | `.wr-blur-reveal` |
| `--wr-blur-reveal-offset` | `0.35em` | `.wr-blur-reveal` |
| `--wr-blur-reveal-stagger` | `70ms` | `.wr-blur-reveal` and inline on the element, written by the component from its inputs or its state — a rule holds only until the component writes |

## See also

- [Reveal](https://ngwr.dev/animations/reveal) — A headline whose letters rise into place one after another from first paint, with no script: built for above the fold, where the server HTML already holds the pieces and the motion starts before the bundle arrives.
- [Decode](https://ngwr.dev/animations/decode) — A readout locking on: every character shows random glyphs that change quickly, then settles on the real one, left to right.
- [Motion](https://ngwr.dev/guides/tokens/motion) — Easing and duration tokens for your transitions and animations.
