# Shimmer

> A soft band of light crosses the glyphs of a short label, waits, and crosses again for as long as the label is on screen. It has no inputs: the cadence and the paint are CSS variables.

Source: https://ngwr.dev/animations/shimmer  
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 { WrShimmer } from 'ngwr/animations/shimmer';

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

```scss
@use 'ngwr/animations/shimmer';
```

## Basic

Put `wrShimmer` on an element that holds only text and paints no background of its own, such as a `<span>` in a paragraph: the band is a background clipped to the glyphs, and a background on the host would be covered while it passes. The band pulls the text's colour toward `--wr-shimmer-peak`, the ink of the surface by default, so it shows on text that is not that ink already: a muted label lights toward the ink, and a heading, which is the ink, takes a peak of its own, such as the primary ink. The text itself is never touched. The cycle runs only while most of the element is in view and the page is visible.

```angular-html
<p class="muted"><span wrShimmer>New in 15.7: the animations family.</span></p>
<h2><span wrShimmer style="--wr-shimmer-peak: var(--wr-color-primary-ink)">A band of light across the glyphs.</span></h2>
```

## On a fill

A chip on a tint reads its label in the intent's `-ink`; set the peak to the same intent's `-contrast` and the band is a glint of light over the letters. On a solid fill the label is the `-contrast` colour already, so the band has nowhere lighter to go: set the peak to the intent's `-soft` instead, and the band is the fill showing through.

```angular-html
<wr-tag color="primary" outlined style="--wr-shimmer-peak: var(--wr-color-primary-contrast)">
  <span wrShimmer>Signals-first</span>
</wr-tag>
<wr-tag color="primary" style="--wr-shimmer-peak: var(--wr-color-primary-soft); --wr-shimmer-strength: 100%">
  <span wrShimmer>Signals-first</span>
</wr-tag>
```

## Hover and focus

A pointer entering, or focus landing on, the element or its closest focusable ancestor plays one pass at once while the label is waiting, and is ignored while a pass is in flight.

```angular-html
<button wr-btn outlined type="button">
  <span wrShimmer style="--wr-shimmer-peak: var(--wr-color-primary)">Hover or focus me</span>
</button>
```

## Band, strength and cadence

`--wr-shimmer-band` is the lit core of the band, in em, with a fade of the same width on either side. `--wr-shimmer-strength` is how far the band's centre gets toward the peak; the rest is the text's own colour, so at 60%, the default, a muted label never lights to full ink. `--wr-shimmer-pass` is how long the band takes to cross, `--wr-shimmer-pause` the wait between two passes and `--wr-shimmer-delay` the wait before the first one after the element comes into view. Every label below is the same muted text with one pair of hooks changed.

```scss
.promo span {
  --wr-shimmer-band: 5em;
  --wr-shimmer-strength: 100%;
  --wr-shimmer-pass: 2.4s;
  --wr-shimmer-pause: 1s;
}
```

## Accessibility

| Part | Behaviour |
| --- | --- |
| Screen readers | Nothing changes: no split, no copy, no pseudo-element content. The band is paint over the text's own glyphs, so what is announced, selected and copied is the text as authored. |
| Reduced motion | Under `prefers-reduced-motion: reduce` the cycle never starts and the stylesheet paints no band. The demos on this page follow the same setting. |
| Forced colours | Under `forced-colors: active` the band is not painted and the directive does not run, so the label keeps the system colour. |
| Server | The server renders the label as it is. The cycle starts in the browser, after the first render, and only where `IntersectionObserver` exists. |
| Direction | The band travels the reading direction: under `dir="rtl"` it crosses right to left. |

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

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-shimmer-band` | `1.5em` | `.wr-shimmer` |
| `--wr-shimmer-delay` | `0.4s` | `.wr-shimmer` |
| `--wr-shimmer-ease` | `var(--wr-ease-in-out)` | `.wr-shimmer` |
| `--wr-shimmer-light` | `color-mix(in srgb, var(--wr-shimmer-peak) var(--wr-shimmer-strength), currentColor)` | `.wr-shimmer` |
| `--wr-shimmer-pass` | `1.2s` | `.wr-shimmer` |
| `--wr-shimmer-pause` | `5s` | `.wr-shimmer` |
| `--wr-shimmer-peak` | `var(--wr-color-on-surface)` | `.wr-shimmer` |
| `--wr-shimmer-strength` | `60%` | `.wr-shimmer` |

## See also

- [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.
- [Badge](https://ngwr.dev/reference/components/badge) — A small label for a count or a status.
- [Motion](https://ngwr.dev/guides/tokens/motion) — Easing and duration tokens for your transitions and animations.
