Spindle

self-contained pixel loaders, one component

Docs

Spindle is a client component that renders a self-contained grid of animated pixels. It injects its own keyframes once per page, needs no CSS from your app, honors prefers-reduced-motion, and ships an aria-label plus an sr-only status for screen readers.

Install

Spindle ships in the facedye package under the /loaders entry. React is an optional peer dependency.

bash
pnpm add facedye

Usage

Drop it in wherever you'd reach for a spinner. Every prop is optional — the defaults give you a gentle diagonal pulse.

tsx
import { Spindle } from "facedye/loaders";

export function Saving() {
  return <Spindle pattern="orbit" label="Saving changes" />;
}

Props

proptypedefault
sizenumber4
rows / colsnumbersize
patternSpindlePattern"diagonal"
animationStyleSpindleStyle"pulse"
speednumber0.75
accentDensitynumber0.35
labelstring"Loading"

Patterns

The pattern decides how the wavefront moves. Edge-travel patterns (diagonal, columns, snake) scroll in a direction; radial ones (ripple, diamond, radar, orbit) pulse out from or circle the center.

diagonalsnakearrow-upripplediamondcolumnsrowscheckerrainradarspiraltwinkleorbit

Animation styles

The style is what each cell does as the wave reaches it — from a soft opacity pulse to a flip, squash, or neon glow. Mix any style with any pattern.

pulsebreathepopblinkglowflipsquashspin

Every prop

One loader wired up with every prop at once. Only rows/cols and size are mutually exclusive — everything else composes freely.

Placing your order
tsx
<Spindle
  rows={4}               // grid height
  cols={8}               // grid width (rows + cols → rectangle)
  pattern="snake"        // how the wave travels
  animationStyle="flip"  // what each cell does
  speed={0.8}            // seconds per cycle
  accentDensity={0.5}    // share of bright cells
  className="opacity-90" // forwarded to the grid element
  label="Placing your order" // screen-reader status
/>

Recipes

Common recipes, each rendered live next to its code. Mix any pattern with any animation style — every combination just works.

MinimalNo props — a gentle diagonal pulse on the default 4×4 grid.
Loading
tsx
<Spindle />
Square sizesize sets both dimensions at once.
Loading
tsx
<Spindle size={6} pattern="ripple" />
Rectangular striprows + cols make a wide bar — handy as a typing indicator.
Loading
tsx
<Spindle rows={3} cols={8} pattern="snake" animationStyle="flip" speed={0.8} />
Ring spinnerorbit hollows out the center for a classic spinner.
Loading
tsx
<Spindle size={5} pattern="orbit" animationStyle="glow" />
Twinkle glowA random per-cell shimmer — no directional wave.
Loading
tsx
<Spindle size={5} pattern="twinkle" animationStyle="glow" />
Denser accentsaccentDensity raises the share of bright cells (0–1).
Loading
tsx
<Spindle pattern="diamond" accentDensity={0.6} />
Slower cyclespeed is seconds per loop — larger is calmer.
Loading
tsx
<Spindle pattern="rain" speed={1.3} />
Label + classNamelabel sets the a11y status; className forwards styles.
Uploading
tsx
<Spindle
  pattern="columns"
  label="Uploading"
  className="opacity-80"
/>