import { Component } from '@angular/core';
import { WrRippleGrid } from 'ngwr/animations/ripple-grid';
@Component({ imports: [WrRippleGrid] })
export class MyComponent {}Ripple Grid
A faint square grid of thin lines behind a page's content. A click or a tap on the parent sends a ring out from that point: as the wave passes, the lines bend outward and brighten a little toward the primary colour; the ring widens, fades, and the grid lies flat again. Several rings add up where they cross.
Installation
Import the class, and @use the entry point in your global styles. Nothing warns you when the @use is missing.
Basic
Put <wr-ripple-grid /> first inside the region it should fill. The parent takes the click, passively and never stopped, so a link or a button over the grid works as before and still starts a ring under the pointer; a keyboard activation is a click with no point and rings nothing. One 2D canvas, drawn on the CPU every frame while something moves and idle otherwise: the frame loop runs only while a ring is alive, on screen and with the document visible; at rest the flat grid is drawn once, as straight lines.
<main class="not-found">
<wr-ripple-grid />
<wr-result-404 />
</main>The parent
The host is absolute, inset: 0, at z-index: -1, under no pointer and masked out at the edges. The parent provides two things: it is positioned, so the host fills it, and isolated, so the host's negative z-index lands at the floor of the parent's own stacking context, under the content and over the page background.
.not-found {
position: relative;
isolation: isolate;
}Spacing
spacing is the distance between two grid lines, in px, 32 unset; the grid is centred, so the margins at the edges are equal. A finer grid bends in more places as the wave passes, a coarser one reads as a few lines moving. The two colours are the --wr-ripple-grid-ink and --wr-ripple-grid-glow hooks, set on the host, where the defaults are declared, and read through resolveCssColorChannels and re-read when the scheme changes. The wave's speed, band, height and life are the model's numbers, not inputs.
<wr-ripple-grid spacing="20" />
<wr-ripple-grid spacing="48" />Accessibility
| Part | Behaviour |
|---|---|
| Screen readers | Decorative: the host is aria-hidden, holds no text, takes no pointer and is never focusable. The click listener on the parent is passive and stops nothing, so every control over the grid behaves as before. |
| Reduced motion | Under prefers-reduced-motion: reduce only the flat grid is drawn and a click rings nothing; a preference that flips mid-flight drops the live rings at once. |
| Contrast | A line shows its ink at a fifth, so the text over the grid reads as it does on the plain surface. Under forced-colors: active nothing is drawn. |
| Server | The server renders the empty canvas, absolutely positioned, so the browser's first draw shifts no layout. |
API
| Name | Description | Type | Default |
|---|---|---|---|
spacing | Distance between two grid lines, in px. | number | 32 |
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/ripple-grid declares each default on its own element.
| Variable | Default | Declared on |
|---|---|---|
--wr-ripple-grid-glow | var(--wr-color-primary) | .wr-ripple-grid |
--wr-ripple-grid-ink | var(--wr-color-on-surface-muted) | .wr-ripple-grid |