Dot Grid

An even field of small, faint dots behind a section's content. Near the pointer the dots light up toward the primary colour and are pushed gently away; each one follows a damped spring back to its place when the pointer moves on, so a fast sweep leaves a short, soft wake that settles.

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 { WrDotGrid } from 'ngwr/animations/dot-grid';

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

Basic

Put <wr-dot-grid /> first inside the region it should fill. One 2D canvas, drawn on the CPU every frame while something moves and idle otherwise: the frame loop runs while the pointer is over the parent or a dot is still settling, and only while the host is on screen and the document is visible; at rest the still field is drawn once. The parent takes the pointer, the host and its canvas never do. Touch never pushes, so a scroll over the region is never fought; a tap sends one ring of light out from the finger instead.

<section class="bento">
  <wr-dot-grid />
  <h2>Every component by name.</h2>
</section>

Move the pointer over this box.

The dots near it light up and lean away, then spring back.

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.

.bento {
  position: relative;
  isolation: isolate;
}

Spacing and reach

spacing is the distance between two dots and reach how far from the pointer a dot still lights and moves, both in px. The field is laid out centred, so the margins at the edges are equal. The two colours are the --wr-dot-grid-ink and --wr-dot-grid-glow hooks, set on the host, where the defaults are declared, and read through resolveCssColorChannels and re-read when the scheme changes.

<wr-dot-grid spacing="40" reach="220" />

A wider grid, a longer reach.

Accessibility

PartBehaviour
Screen readers Decorative: the host is aria-hidden, holds no text, takes no pointer and is never focusable.
Reduced motion Under prefers-reduced-motion: reduce only the still field is drawn; a preference that flips mid-flight puts every dot back at once.
Contrast A dot shows its ink at about a third, so the text and the cards over the field read as they do 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

NameDescriptionTypeDefault
spacingDistance between two dots, in px.number26
reachHow far from the pointer a dot still lights and moves, in px.number140

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/dot-grid declares each default on its own element.

VariableDefaultDeclared on
--wr-dot-grid-glowvar(--wr-color-primary).wr-dot-grid
--wr-dot-grid-inkvar(--wr-color-on-surface-muted).wr-dot-grid

See also