import { Component } from '@angular/core';
import { WrRotateReveal } from 'ngwr/animations/rotate-reveal';
@Component({ imports: [WrRotateReveal] })
export class MyComponent {}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.
Installation
Import the class, and @use the entry point in your global styles. Nothing warns you when the @use is missing.
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.
<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.
<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.
<p>Built to be <wr-rotate-reveal [words]="tones" delay="1700" />.</p>.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.
<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 |
|---|---|---|---|
wordsrequired | 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
Animation TypewriterA line typed character by character behind a caret, then held, deleted and followed by the next, or typed once and left standing.Animation RevealA 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.Guide TranslateTranslate your app and ngwr's own labels, and switch the locale at runtime.