blaze-motion
← All components

Radial Stagger

Stagger

Radial stagger — grid tiles spring in, ordered by their distance from a center point.

Click any tile to ripple outward from it.

Installation

One command drops the source into your repo — editable, with the provider and motion pulled in automatically.

npx shadcn add https://motion.asmitsah.dev/r/radial-stagger.json

Usage

Basic

Decorative by default — tiles are non-focusable role="presentation" cells. Clicking one re-ripples the cascade from it.

import { RadialStagger } from "@/components/motion/radial-stagger";

<RadialStagger columns={4} className="gap-2">
  {tiles.map((tile) => (
    <div key={tile.id} className="aspect-square rounded-sm border border-border bg-panel-2" />
  ))}
</RadialStagger>

Distance metric

Switch how ring distance from the origin is measured — "manhattan" ripples in diamonds, "chebyshev" in squares, "euclidean" (default) in circles.

<RadialStagger columns={5} distance="manhattan" defaultOrigin={12} className="gap-2">
  {tiles.map((tile) => (
    <div key={tile.id} className="aspect-square rounded-sm border border-border bg-panel-2" />
  ))}
</RadialStagger>

Interactive tiles

Opt into real focusable buttons — each tile needs an accessible name via getItemLabel, or you ship nameless controls (WCAG 4.1.2).

<RadialStagger
  columns={4}
  interactive
  getItemLabel={(index) => `Select swatch ${index + 1}`}
  onSelect={(index) => setActiveSwatch(index)}
  itemClassName="aspect-square rounded-sm border border-border bg-panel-2 focus-visible:outline-2 focus-visible:outline-offset-2"
>
  {swatches.map((swatch) => (
    <span key={swatch.id} style={{ background: swatch.color }} className="block size-full" />
  ))}
</RadialStagger>

Tuned step + origin

Raise `step` for a slower, more dramatic cascade; set `defaultOrigin` to start the first ripple somewhere other than tile 0.

<RadialStagger columns={6} step={0.14} defaultOrigin={17} className="gap-1.5">
  {tiles.map((tile) => (
    <div key={tile.id} className="aspect-square rounded-sm bg-panel-2" />
  ))}
</RadialStagger>

Props

PropTypeDefaultDescription
childrenReactNodeOne child per grid tile — each pops in on its own ring delay.
classNamestringClasses for the grid container (RadialStagger sets display:grid + columns via style).
styleCSSPropertiesInline style passthrough, merged onto the grid.
itemClassNamestringClasses applied to every tile (the grid cell).
columnsnumber4Grid columns — drives both the layout and the ring-distance geometry.
stepnumberfeel.stagger (0.08)Seconds of delay added per unit of distance from the origin tile.
distance"euclidean" | "manhattan" | "chebyshev""euclidean"How ring distance from the origin cell is measured.
defaultOriginnumber0Index the first cascade ripples out from, before any click.
interactivebooleanfalseRenders tiles as focusable `<button>`s instead of decorative role="presentation" elements. Requires `getItemLabel`.
getItemLabel(index: number) => stringAccessible name for the tile at `index` — required when `interactive`.
onSelect(index: number) => voidFires with the tile index when an interactive tile is selected.