Skip to content

Particles

A canvas of small dots that drift and lean toward the pointer, for a background. They pause when off-screen and stop for reduced motion, leaving a still scatter.

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.

Move the pointer here
import { Particles } from "@rdloom/react";

export default function ParticlesBasicExample() {
  return (
    <div className="flex w-full justify-center">
      <div className="relative h-96 w-full overflow-hidden rounded-[var(--rd-radius-overlay)] border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)]">
        <Particles />
        <div className="relative flex h-full items-center justify-center text-sm font-medium">Move the pointer here</div>
      </div>
    </div>
  );
}

Installation

npx rdloom add particles

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/particles.json

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

Usage

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

<div className="flex w-full justify-center">
  <div className="relative h-96 w-full overflow-hidden rounded-[var(--rd-radius-overlay)] border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)]">
    <Particles />
    <div className="relative flex h-full items-center justify-center text-sm font-medium">Move the pointer here</div>
  </div>
</div>

Sparse

A few larger dots
import { Particles } from "@rdloom/react";

export default function ParticlesSparseExample() {
  return (
    <div className="flex w-full justify-center">
      <div className="relative h-96 w-full overflow-hidden rounded-[var(--rd-radius-overlay)] border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)]">
        <Particles quantity={24} size={2.5} color="var(--rd-color-action-primary)" />
        <div className="relative flex h-full items-center justify-center text-sm font-medium">A few larger dots</div>
      </div>
    </div>
  );
}

API Reference

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

PropTypeDefault
quantity

How many dots.

number60
size

Dot radius in pixels.

number1.5
color

Colour of the dots, any CSS colour. Defaults to the muted text colour.

stringnone
isPaused

Stops the effect and shows it still.

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

  • Not announced: the canvas is hidden from assistive technology

What your code must do

  • Decoration only: the effect is aria-hidden, never takes focus and never blocks the pointer
  • Stands still for visitors who ask for less motion (prefers-reduced-motion) and inside an rdm-still container, and isPaused stops it too
  • Does not flash: nothing changes more than three times in a second (WCAG 2.3.1)
  • Paused while off-screen, and its animation frame is cancelled when it unmounts
  • A still scatter is drawn for reduced motion, inside rdm-still and with isPaused
  • Hidden in forced-colors (high contrast) mode
  • Follows the pointer through the container, so it never needs pointer events of its own

Guidelines

Use it when

  • A hero background
  • Giving a quiet section some depth

Avoid it when

  • Behind small text or dense forms
  • Low-power or small screens with very high counts

Don't

  • Counts in the hundreds
  • Dots in strong colours that fight with the text

Design tokens

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

  • --rd-color-text-muted