Skip to content

Flickering Grid

A grid of small squares that gently fade in and out, drawn on a canvas that fills its parent.

Motionv0.1.0experimentalWCAG 2.2 AAView spec

Optional, and kept apart.

Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.

Quiet texture

Squares fade slowly

import { FlickeringGrid } from "@rdloom/react";

export default function FlickeringGridBasic() {
  return (
    <div className="flex w-full justify-center">
      <div className="relative flex h-96 w-full items-center justify-center overflow-hidden rounded-xl border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)]">
        <FlickeringGrid />
        <div className="relative text-center">
          <p className="text-4xl font-semibold tracking-tight text-[var(--rd-color-text-default)]">Quiet texture</p>
          <p className="text-base text-[var(--rd-color-text-muted)]">Squares fade slowly</p>
        </div>
      </div>
    </div>
  );
}

Installation

npx rdloom add flickering-grid

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.

It also adds rdloom-motion.css next to your tokens and imports it from the tokens file, once: no setup, and no inline styles. The plain components you already use are untouched.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/flickering-grid.json

Works, but without upgrade tracking, and you import the motion CSS yourself.

Usage

import { FlickeringGrid } from "@rdloom/react";

<div className="flex w-full justify-center">
  <div className="relative flex h-96 w-full items-center justify-center overflow-hidden rounded-xl border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)]">
    <FlickeringGrid />
    <div className="relative text-center">
      <p className="text-4xl font-semibold tracking-tight text-[var(--rd-color-text-default)]">Quiet texture</p>
      <p className="text-base text-[var(--rd-color-text-muted)]">Squares fade slowly</p>
    </div>
  </div>
</div>

Colored

Ember squares

Larger and a little warmer

import { FlickeringGrid } from "@rdloom/react";

export default function FlickeringGridColored() {
  return (
    <div className="flex w-full justify-center">
      <div className="relative flex h-96 w-full items-center justify-center overflow-hidden rounded-xl border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)]">
        <FlickeringGrid color="var(--rd-color-ember-500)" maxOpacity={0.35} squareSize={6} gridGap={4} />
        <div className="relative text-center">
          <p className="text-4xl font-semibold tracking-tight text-[var(--rd-color-text-default)]">Ember squares</p>
          <p className="text-base text-[var(--rd-color-text-muted)]">Larger and a little warmer</p>
        </div>
      </div>
    </div>
  );
}

Still

Held still

One frame, no movement

import { FlickeringGrid } from "@rdloom/react";

export default function FlickeringGridStill() {
  return (
    <div className="flex w-full justify-center">
      <div className="relative flex h-96 w-full items-center justify-center overflow-hidden rounded-xl border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)]">
        <FlickeringGrid isStill />
        <div className="relative text-center">
          <p className="text-4xl font-semibold tracking-tight text-[var(--rd-color-text-default)]">Held still</p>
          <p className="text-base text-[var(--rd-color-text-muted)]">One frame, no movement</p>
        </div>
      </div>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
squareSize

Side of each square in pixels.

number4
gridGap

Space between squares in pixels.

number6
color

Colour of the squares: any CSS colour or token.

string"var(--rd-color-border-strong)"
maxOpacity

Brightest a square gets, from 0 to 1.

number0.3
isStill

Stops all movement. Moving content should be stoppable.

booleanfalse

Accessibility

Role presentation, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

Not interactive.

Screen readers announce

  • Ignored: it is not announced

What your code must do

  • Pure decoration: aria-hidden, never focusable, and pointer-events none so content on top stays usable
  • Drawn on a canvas, so it adds no DOM nodes and is safe in server HTML
  • Each square changes at most about 2 times a second and fades over most of a second, never a hard flash
  • Stands still for visitors who ask for less motion (prefers-reduced-motion), with isStill, and inside an rdm-still container
  • Nothing flashes more than 3 times a second (WCAG 2.3.1) and moving content can be stopped (WCAG 2.2.2)
  • Hidden in forced-colors (high contrast) mode so it never fights system colours
  • Keep text on top at 4.5:1 against the effect: use a low-contrast colour

Guidelines

Use it when

  • A quiet living texture behind a hero or empty state

Avoid it when

  • Behind long text
  • On pages with several other moving effects

Don't

  • A bright colour or a high maxOpacity that makes the squares compete with content
  • Several flickering grids on one screen

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-border-strong