@agilek/cli-loaders

中文文档
1.1.0发布于 2026/03/22MITTypeScriptESM + CJS零依赖近 30 天下载 4.3万

Braille unicode spinners as React decorator components

原始使用文档
文档目录24 节
  1. Features
  2. Why cli-loaders?
  3. Install
  4. Quick start
  5. Components
  6. <Spinner>
  7. <SpinnerInline>
  8. <SpinnerText>
  9. <SpinnerBadge>
  10. <SpinnerTrail>
  11. <SpinnerButton>
  12. <SpinnerOverlay>
  13. <SpinnerProvider>
  14. useSpinner hook
  15. useSpinnerFrames
  16. Available spinners
  17. Accessibility
  18. FAQ
  19. Common Patterns
  20. Form submission with loading state
  21. Full-screen loading overlay
  22. Inline status message with shape customization
  23. Contributing
  24. License

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.

PropTypeDefaultDescription
nameSpinnerName"braille"Which spinner animation to use
colorstringcurrentColorCSS color value
sizestring | numberundefinedFont size of the glyph, e.g. "1.5rem" or 24
speednumber1Playback multiplier — 2 = twice as fast
pausedbooleanfalseFreeze the animation
ignoreReducedMotionbooleanfalseOverride prefers-reduced-motion and always animate
shape"circle" | "square" | "diamond"undefinedRender dots as SVG shapes instead of braille text
labelstring"Loading"Accessible label announced by screen readers
classNamestring—Applied to the outer <span>
styleCSSProperties—Inline styles for the outer <span>
refRef<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:

PropTypeDefaultDescription
gapstring | number"0.4em"Gap between spinner and children
childrenReactNode—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 ⠿
PropTypeDefaultDescription
textstringrequiredThe text to decorate
bookendbooleanfalsePlace a spinner on both sides
gapstring | 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" />
PropTypeDefaultDescription
labelstringrequiredBadge text (also used as the a11y label)
paddingYstring"0.4em"Vertical padding
paddingXstring"0.8em"Horizontal base padding
balanceRationumber0.3Extra right padding fraction for optical balance
borderRadiusstring | number"999px"Border radius
gapstring | 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} />
PropTypeDefaultDescription
trailLengthnumber4Number of ghost frames (including the current one)
minOpacitynumber0.1Opacity of the oldest ghost frame
reversebooleanfalseFlip 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>
PropTypeDefaultDescription
loadingbooleanfalseShow spinner and disable the button
spinnerPosition"left" | "right""left"Which side the spinner appears on
spinnerGapstring | number"0.45em"Gap between spinner and button label
spinnerPropsOmit<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>
PropTypeDefaultDescription
activebooleantrueShow the overlay
backdropstring"rgba(0,0,0,0.35)"CSS background for the backdrop
sizestring | number"2rem"Spinner glyph size
containerStyleCSSProperties—Style applied to the outer wrapper <div>
containerClassNamestring—Class applied to the outer wrapper <div>
childrenReactNode—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" />
PropTypeDefaultDescription
defaultNameSpinnerName"braille"Default spinner name
defaultColorstringundefinedDefault color
defaultSizestring | numberundefinedDefault glyph size
defaultSpeednumber1Default speed multiplier
defaultShape"circle" | "square" | "diamond"undefinedDefault dot shape
respectReducedMotionbooleantruePause 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

版本与发布记录

@agilek/cli-loaders 共发布 4 个版本,首次发布于 2026/03/21;过去 12 个月发布了 4 个版本。

版本发布日期距今
1.1.0latest2026/03/227 个月前
1.0.22026/03/217 个月前
1.0.12026/03/217 个月前
1.0.02026/03/217 个月前

关于 @agilek/cli-loaders 的常见问题

@agilek/cli-loaders 是什么?

Braille unicode spinners as React decorator components

如何安装 @agilek/cli-loaders?

在项目目录运行 npm install @agilek/cli-loaders,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=18.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@agilek/cli-loaders 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@agilek/cli-loaders 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@agilek/cli-loaders 可以商用吗?

@agilek/cli-loaders 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@agilek/cli-loaders 的下载量有多少?

根据 npm 官方下载统计,@agilek/cli-loaders 在 2026/10/05 – 2026/11/03 这 30 天内被下载 43,061 次,日均约 1,435 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@agilek/cli-loaders 还在维护吗?

最新版本 1.1.0 发布于 2026/03/22(7 个月前);至今共发布 4 个版本,其中过去 12 个月发布了 4 个;首次发布于 2026/03/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。