# Rotate Reveal

> One word of a sentence gives way to the next from a list, in place: the letters leaving rise out of the line while the letters arriving climb in under them, and the slot eases to the new width so the rest of the sentence glides.

Source: https://ngwr.dev/animations/rotate-reveal  
Kind: Animation, Component, 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 { WrRotateReveal } from 'ngwr/animations/rotate-reveal';

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

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

## Basic

`<wr-rotate-reveal>` is inline: put it in the sentence where the word goes and pass the words as `words`. The first word is what the server and a still page show. The list plays once, and after the last word the first comes back and stays. The widths come from one measurement in the browser, never from a layout read per swap.

```angular-html
<p>Zoneless, SSR-safe, <wr-rotate-reveal [words]="qualities" /> with no accessor layer in between.</p>
```

## Loop and hold

`hold` is how long a word stands before the next replaces it, 2400 ms unset. `loop` keeps cycling instead of stopping on the first word after one pass. A pointer over the sentence, or focus in it, pauses the hold so the word being read does not change under the reader; leaving resumes it.

```angular-html
<p>Signal Forms <wr-rotate-reveal [words]="verbs" loop hold="1200" /> on every keystroke.</p>
```

## Delay

`delay` is the pause before the first word's hold starts counting, from the moment the host is in view: room for an entrance above it to finish. The offset a letter travels and the gap between letters are CSS variables.

```angular-html
<p>Built to be <wr-rotate-reveal [words]="tones" delay="1700" />.</p>
```

```scss
.hero p {
  --wr-rotate-reveal-offset: 1.4em;
  --wr-rotate-reveal-gap: 40ms;
}
```

## The phrase a screen reader hears

The slot is `aria-hidden` and a visually hidden phrase beside it names every word once: "A, B and C". The joining word comes from the i18n catalog (`rotateReveal.conjunction`), so an app with `ngwr/i18n/de` hears "und"; `conjunctionLabel` overrides it for one instance.

```angular-html
<p>Built to be <wr-rotate-reveal [words]="tones" conjunctionLabel="or" />.</p>
```

## Accessibility

| Part | Behaviour |
| --- | --- |
| Screen readers | The slot is `aria-hidden` and a visually hidden phrase beside it names every word once, so a reader hears the sentence with "A, B and C" in it and never a word mid-flight. The phrase is out of the selection, so a copy of the sentence gives the word on show. |
| Motion that stops | The list plays once and stops on the first word, as WCAG 2.2.2 asks of content that moves on its own; `loop` lifts that. A pointer over the sentence or focus in it pauses the hold, and so does the host leaving the viewport or the document being hidden. |
| Reduced motion | Under `prefers-reduced-motion: reduce` nothing swaps, not even without motion: the first word stands as text. The demos on this page follow the same setting. |
| Server | The server renders the first word as text, and so does a browser without `IntersectionObserver`. The cycle starts in the browser once most of the host is in view. |
| Layout | At rest the slot holds nothing but the text, so the line reads, breaks, selects and copies as if the word were typed there. While swapping it is clipped to its line, so a letter on its way never crosses a neighbouring line. |

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `words`required | The words, in the order they show. The first is what the server and a still page show. | `readonly string[]` | — |
| `hold` | How long a word stands before the next replaces it, in ms. | `number` | `2400` |
| `delay` | Pause before the first word's hold starts counting, in ms, from the moment the host is in view: room for an entrance above it to finish. | `number` | `0` |
| `loop` | Keep cycling instead of stopping on the first word after one pass through the list. | `boolean` | `false` |
| `conjunctionLabel` | The word between the last two words of the phrase a screen reader hears: "A, B and C". Unset, the `rotateReveal.conjunction` string of the i18n catalog, "and" in English. | `string \| null` | `null` |

## 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/rotate-reveal` declares each default on its own element.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-rotate-reveal-gap` | `24ms` | `.wr-rotate-reveal` |
| `--wr-rotate-reveal-in` | `0.38s` | `.wr-rotate-reveal` |
| `--wr-rotate-reveal-lead` | `50ms` | `.wr-rotate-reveal` |
| `--wr-rotate-reveal-offset` | `1.1em` | `.wr-rotate-reveal` |
| `--wr-rotate-reveal-out` | `0.32s` | `.wr-rotate-reveal` |
| `--wr-rotate-reveal-swap` | `calc(var(--wr-rotate-reveal-lead) + (var(--wr-rotate-reveal-count, 1) - 1) * var(--wr-rotate-reveal-gap) + var(--wr-rotate-reveal-in))` | `.wr-rotate-reveal__slot` |

## See also

- [Typewriter](https://ngwr.dev/animations/typewriter) — A line typed character by character behind a caret, then held, deleted and followed by the next, or typed once and left standing.
- [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.
- [Translate](https://ngwr.dev/guides/translations/overview) — Translate your app and ngwr's own labels, and switch the locale at runtime.
