Skip to content

Spinner

Terminal-inspired TUI spinner with four animation styles. The default snake variant renders a 3×3 pixel grid with a trailing dot that moves clockwise — like a pixelated snake.

Preview

Installation

$pnpm exec dgadd add ui/spinner
[Installs to]src/components/ui/spinner[Item]ui/spinner

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import { Spinner } from "@/components/ui/spinner";export default function SpinnerDefault() {  return (    <div className="flex flex-col items-start gap-6">      <Spinner />      <Spinner>        <span className="text-muted-foreground">Loading...</span>      </Spinner>    </div>  );}

Examples

Variants

Preview

Sizes

Preview

Label Positions

Preview

Gap and Speed

Preview

API Reference

Spinner

NameTypeDefaultDescription
variant"snake" | "braille" | "dots" | "pulse""snake"Animation style. Snake renders a 3x3 pixel grid; braille, dots, and pulse render text glyph sequences.
size"sm" | "md" | "lg""md"Font size token applied to the glyph and label.
labelPosition"right" | "left" | "top" | "bottom""right"Placement of the children label relative to the spinner glyph.
gap"none" | "sm" | "md" | "lg""md"Space between the spinner glyph and its label.
speednumberFrame interval in milliseconds. Overrides the variant default (snake 100, braille 80, dots 300, pulse 80).
childrenReactNodeOptional label. When omitted, the spinner uses aria-label="Loading".

CSS variables

NameDefaultDescription
--spinner-trail-head1Opacity of the snake head dot.
--spinner-trail-body0.6 (light: 0.7)Opacity of the dot one step behind the head.
--spinner-trail-tail0.3 (light: 0.45)Opacity of the dot two steps behind the head.
--spinner-trail-idle0.15 (light: 0.32)Opacity of the perimeter dots the trail has passed.

Accessibility

Notes

Labels

Pass children to Spinner to render a label. The labelPosition prop controls where the label appears relative to the glyph: right (default), left, top, or bottom.

Gap Control

The gap prop controls the space between the spinner glyph and its label. Values: "none" (0), "sm" (4px), "md" (8px, default), "lg" (12px). Works for both horizontal and vertical label positions.

Fixed Glyph Box

All four variants animate inside one 2em-square box that scales with the size prop, so swapping variant or cycling frames never moves the label or reflows the row. 2em is the largest footprint any variant needs — the lg snake grid measures 32px at its 16px font size, and the widest dots frame reserves 3ch.

Snake Variant

A 3×3 grid of dots with a 3-dot trail moving clockwise around the 8-position perimeter. The head, body, tail, and idle alphas come from the --spinner-trail-* variables in spinner.css; the light palette raises the ramp (idle 32% instead of 15%) so the trail stays legible on a light background.

Reduced Motion

When prefers-reduced-motion is active, the animation stops at the first frame. If the preference changes at runtime, the animation responds accordingly.

Speed Override

The speed prop overrides the default frame interval in milliseconds. Default speeds: snake 100ms, braille 80ms, dots 300ms, pulse 80ms.

Source

Install via CLI: pnpm exec dgadd add ui/spinner.

Highlighted source loads after this disclosure opens. Browse the source repository.