Skip to content

agilek/cli-loaders

Repository files navigation

cli-loaders for react

Animated braille-glyph loading spinners for React. 19 unique animations, zero dependencies, fully accessible. — Live demo → · GitHub →

braille orbit breathe braillewave dna waverows helix fillsweep diagswipe scanline line cascade scan rain pulse snake sparkle columns checkerboard

cli-loaders gives you 19 animated braille-glyph spinners as a set of composable, accessible React components. Zero runtime dependencies beyond React itself.

Features

  • 19 spinner animations with unique braille Unicode patterns
  • Zero runtime dependencies — pure React, no external libraries
  • Fully accessible — WCAG 2.1 AA, prefers-reduced-motion support, semantic HTML
  • TypeScript first — complete type definitions, DotShape export for custom rendering
  • Composable — 7 components + 2 hooks for any use case
  • Small bundle — ~15KB gzipped (lib) + optional SVG rendering
  • Shape customization — render as circles, squares, or diamonds (SVG mode)

Why cli-loaders?

vs. react-spinners: Smaller bundle, braille Unicode art for terminal-like aesthetics, context API for global defaults, shape customization (circles/squares/diamonds)

vs. react-loading: Zero dependencies, comprehensive accessibility testing, 19+ unique animations, SVG rendering mode

vs. custom CSS: No @keyframes to maintain, out-of-the-box reduced-motion support, semantic HTML, accessible by default

Install

npm install @agilek/cli-loaders
# or
pnpm add @agilek/cli-loaders
# or
yarn add @agilek/cli-loaders

Requires React ≥ 18.

Quick start

import { Spinner } from '@agilek/cli-loaders';

// Minimal — defaults to the "braille" spinner
<Spinner />

// Named + coloured
<Spinner name="helix" color="#7c3aed" size="1.5rem" />

// With custom dot shape (renders as SVG instead of braille text)
<Spinner name="scan" color="#00ff99" shape="square" size="2rem" />

Components

<Spinner>

The base animated glyph. Renders as an inline <span> so it drops naturally into any line of text.

Prop Type Default Description
name SpinnerName "braille" Which spinner animation to use
color string currentColor CSS color value
size string | number undefined Font size of the glyph, e.g. "1.5rem" or 24
speed number 1 Playback multiplier — 2 = twice as fast
paused boolean false Freeze the animation
ignoreReducedMotion boolean false Override prefers-reduced-motion and always animate
shape "circle" | "square" | "diamond" undefined Render dots as SVG shapes instead of braille text
label string "Loading" Accessible label announced by screen readers
className string Applied to the outer <span>
style CSSProperties Inline styles for the outer <span>
ref Ref<HTMLSpanElement> Forwarded to the outer <span>

Any additional HTML span attributes are forwarded.


<SpinnerInline>

Spinner + children in a flex row — handy for inline status messages.

<SpinnerInline name="braille" color="#00ff99">
  Fetching data…
</SpinnerInline>

Accepts all Spinner props plus:

Prop Type Default Description
gap string | number "0.4em" Gap between spinner and children
children ReactNode Content rendered after the spinner

<SpinnerText>

Decorates a text string with spinners — supports a bookend mode that mirrors one on each end.

<SpinnerText text="Deploying" color="#f59e0b" bookend />
// ⠿ Deploying ⠿
Prop Type Default Description
text string required The text to decorate
bookend boolean false Place a spinner on both sides
gap string | number "0.4em" Gap between spinners and text

Plus all Spinner props.


<SpinnerBadge>

A tinted pill badge with a spinner — good for live status indicators.

<SpinnerBadge label="Live"     color="#ef4444" />
<SpinnerBadge label="Syncing"  color="#38bdf8" />
<SpinnerBadge label="Building" color="#f59e0b" />
Prop Type Default Description
label string required Badge text (also used as the a11y label)
paddingY string "0.4em" Vertical padding
paddingX string "0.8em" Horizontal base padding
balanceRatio number 0.3 Extra right padding fraction for optical balance
borderRadius string | number "999px" Border radius
gap string | number "0.35em" Gap between glyph and label

Plus all Spinner props.


<SpinnerTrail>

Renders a sliding window of recent frames with decreasing opacity — creates a ghost/motion-blur trail effect.

<SpinnerTrail name="helix" color="#7c3aed" size="2rem" trailLength={6} />
Prop Type Default Description
trailLength number 4 Number of ghost frames (including the current one)
minOpacity number 0.1 Opacity of the oldest ghost frame
reverse boolean false Flip the opacity gradient (bright on left, fading right)

Plus all Spinner props.


<SpinnerButton>

A <button> with a built-in loading state. Disables itself and shows a spinner when loading is true. Fully forwarded ref.

<SpinnerButton
  loading={isSaving}
  onClick={handleSave}
  spinnerProps={{ name: 'braille', color: '#00ff99' }}
>
  Save changes
</SpinnerButton>
Prop Type Default Description
loading boolean false Show spinner and disable the button
spinnerPosition "left" | "right" "left" Which side the spinner appears on
spinnerGap string | number "0.45em" Gap between spinner and button label
spinnerProps Omit<BaseSpinnerProps, "size"> Props forwarded to the inner <Spinner>

All standard <button> attributes are forwarded.


<SpinnerOverlay>

Wraps any content and renders a centered spinner overlay when active. Uses the native inert attribute to block keyboard/pointer access to the content beneath.

<SpinnerOverlay
  active={isLoading}
  name="orbit"
  color="#7c3aed"
  size="2.5rem"
  backdrop="rgba(0,0,0,0.4)"
>
  <YourContent />
</SpinnerOverlay>
Prop Type Default Description
active boolean true Show the overlay
backdrop string "rgba(0,0,0,0.35)" CSS background for the backdrop
size string | number "2rem" Spinner glyph size
containerStyle CSSProperties Style applied to the outer wrapper <div>
containerClassName string Class applied to the outer wrapper <div>
children ReactNode Content that gets overlaid

Plus all Spinner props.


<SpinnerProvider>

Sets global defaults for every Spinner-family component in the subtree. Individual props override context values.

<SpinnerProvider
  defaultName="orbit"
  defaultColor="#7c3aed"
  defaultSpeed={1.2}
  respectReducedMotion={true}
>
  <App />
</SpinnerProvider>

// Inside: no props needed — picks up context defaults
<Spinner />
<SpinnerBadge label="Live" />
Prop Type Default Description
defaultName SpinnerName "braille" Default spinner name
defaultColor string undefined Default color
defaultSize string | number undefined Default glyph size
defaultSpeed number 1 Default speed multiplier
defaultShape "circle" | "square" | "diamond" undefined Default dot shape
respectReducedMotion boolean true Pause animations when the OS has prefers-reduced-motion: reduce

useSpinner hook

Drive any element with the raw frame string for fully custom rendering.

import { useSpinner } from 'cli-loaders';

function MyComponent() {
  const frame = useSpinner('helix', 1.5);

  return (
    <h1 style={{ fontFamily: 'monospace', color: '#00ff99' }}>
      {frame}
    </h1>
  );
}
useSpinner(
  name: SpinnerName,
  speed?: number,           // default 1
  paused?: boolean,         // default false
  ignoreReducedMotion?: boolean  // default false
): string

useSpinnerFrames

Returns a sliding window of the last length frames — the primitive behind SpinnerTrail.

useSpinnerFrames(
  name: SpinnerName,
  length?: number,          // default 3
  speed?: number,
  paused?: boolean,
  ignoreReducedMotion?: boolean
): string[]

Available spinners

All 19 names are available as the SpinnerName union type.

import { spinnerNames } from 'cli-loaders';
// ['braille', 'braillewave', 'dna', ...]

Accessibility

  • The animated glyph is wrapped in aria-hidden="true" — it is invisible to screen readers.
  • A visually-hidden <span role="status" aria-live="polite"> announces the label prop (default: "Loading").
  • SpinnerButton sets aria-busy and aria-disabled on the button element automatically.
  • SpinnerOverlay sets aria-busy on the container and uses the native inert attribute to prevent keyboard/AT access to obscured content.
  • By default, all animations respect prefers-reduced-motion: reduce. Override per-component with ignoreReducedMotion or globally via <SpinnerProvider respectReducedMotion={false}>.


FAQ

Can I use this with Next.js? Yes. Works as both client and server components in the App Router.

How do I customize spinner animations? Use the useSpinner hook to drive any custom rendering. See useSpinner hook for examples.

Does this work with TypeScript? Full type support. All component props and the DotShape type are exported for type safety.

How do I change shapes (circles, squares, diamonds)? Use the shape prop: <Spinner shape="square" />. See Spinner props for details.

Does this support reduced motion preferences? Yes, by default. Animations pause when prefers-reduced-motion: reduce is set. Override with ignoreReducedMotion={true}.

What's the bundle size? ~15KB gzipped for the core library. SVG rendering mode adds ~1KB.


Common Patterns

Form submission with loading state

const [loading, setLoading] = useState(false);

async function handleSubmit() {
  setLoading(true);
  try {
    await api.post('/submit', data);
  } finally {
    setLoading(false);
  }
}

return (
  <SpinnerButton
    loading={loading}
    onClick={handleSubmit}
    spinnerProps={{ name: 'helix', color: '#00ff99' }}
  >
    {loading ? 'Submitting...' : 'Submit'}
  </SpinnerButton>
);

Full-screen loading overlay

const [fetching, setFetching] = useState(false);

return (
  <SpinnerOverlay
    active={fetching}
    name="orbit"
    size="2rem"
    backdrop="rgba(0,0,0,0.5)"
  >
    <Dashboard data={data} />
  </SpinnerOverlay>
);

Inline status message with shape customization

return (
  <SpinnerInline
    name="scan"
    shape="diamond"
    color="#7c3aed"
  >
    Syncing your data...
  </SpinnerInline>
);

Contributing

Bug reports and pull requests are welcome. Please open an issue before submitting large changes.

# Install dependencies
npm install

# Start the interactive demo
npm run dev

# Build the library
npm run build

License

MIT

About

Set of Braille unicode spinners as React decorator components.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors