AnimationDirectiveCSSAdded in v15.7

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.

Installation

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

import { Component } from '@angular/core';
import { WrShimmer } from 'ngwr/animations/shimmer';

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

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.

<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>

A muted label, toward the ink

New in 15.7: the animations family.

A heading, toward the primary ink

A band of light across the glyphs.

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.

<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>

On a tint, toward -contrast

Signals-first

On a fill, toward -soft

Signals-first

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.

<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.

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

Default: band 1.5em, strength 60%

A narrow band, part way to the ink.

--wr-shimmer-band: 5em, --wr-shimmer-strength: 100%

A wide band, all the way to the ink.

Default: pass 1.2s, pause 5s

One glide every six seconds.

--wr-shimmer-pass: 2.4s, --wr-shimmer-pause: 1s

A slow glide, over and over.

Accessibility

PartBehaviour
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.
DirectionThe 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.

VariableDefaultDeclared on
--wr-shimmer-band1.5em.wr-shimmer
--wr-shimmer-delay0.4s.wr-shimmer
--wr-shimmer-easevar(--wr-ease-in-out).wr-shimmer
--wr-shimmer-lightcolor-mix(in srgb, var(--wr-shimmer-peak) var(--wr-shimmer-strength), currentColor).wr-shimmer
--wr-shimmer-pass1.2s.wr-shimmer
--wr-shimmer-pause5s.wr-shimmer
--wr-shimmer-peakvar(--wr-color-on-surface).wr-shimmer
--wr-shimmer-strength60%.wr-shimmer

See also