Skip to content
cennge-ui
GitHubv0.3.1, changelog

Spinner

A small turning ring that says something is at work, in the colour of the text around it.

  • TypecheckPassed
  • Unit testsRunning
  • End-to-end, WebKitRunning
  • BuildQueued

Deploying payload-cache to production

Releases

  1. The spinner takes the colour of its text: the deploy line is in the accent's text colour, and so is its ring.
  2. Beside words that say what is happening it is hidden from screen readers; alone, with a label, it is a status.
  3. With reduced motion it keeps turning, at 1.5 seconds a turn instead of one.
Live: try it with reduced motion on.

Field marks

Import
import { Spinner } from "cennge-ui";
Size
0.7 KB gzip for this import, its dependencies aside
Pattern
An <svg aria-hidden>; role="status" with an aria-label when you pass label
SSR
Rendered on the server in the Next.js check; hydrates without a warning
Sizes
14, 16 and 20 px: sm, md and lg

When to use#

A Spinner marks something that is at work right now and has no amount to show: a job running, a panel fetching, a deploy on its way. It is sized to sit in a line of text or a list row. When you can say how much is done, a progress bar says more.

Similar#

  • Progress shows how far a task has got; without a value its ring spins too, but it is a progressbar and comes in larger sizes.
  • Skeleton holds the shape of content that is loading, so the page does not move when it arrives.
  • Button has its own spinner: pass isLoading instead of putting a Spinner inside.

Sizes and colours#

By default the ring is currentColor, so it matches the text it sits in. color picks a role colour instead; default is the muted grey.

smmdlg
defaultprimarysecondarysuccesswarningdangerinfo
sizes.tsx
import { Spinner, type CenngeColor, type CenngeSize } from "cennge-ui";

const sizes: CenngeSize[] = ["sm", "md", "lg"];
const colors: CenngeColor[] = ["default", "primary", "secondary", "success", "warning", "danger", "info"];

export default function SpinnerSizes() {
  return (
    <div className="flex flex-col gap-6 text-sm text-muted">
      <div className="flex flex-wrap items-center gap-x-6 gap-y-3">
        {sizes.map((size) => (
          <span key={size} className="flex items-center gap-2 text-foreground">
            <Spinner size={size} />
            {size}
          </span>
        ))}
      </div>
      <div className="flex flex-wrap items-center gap-x-6 gap-y-3">
        {colors.map((color) => (
          <span key={color} className="flex items-center gap-2">
            <Spinner color={color} size="lg" />
            {color}
          </span>
        ))}
      </div>
    </div>
  );
}

With a label, or without#

Without label the spinner is hidden from screen readers, which suits a spinner next to words that already say what is going on. When it stands alone, label makes it a role="status" element named by that text.

Syncing 3 forks

label.tsx
import { Spinner } from "cennge-ui";

export default function SpinnerLabel() {
  return (
    <div className="flex w-full flex-wrap items-center gap-x-10 gap-y-6">
      {/* The text says what is happening: the spinner stays hidden from screen readers. */}
      <p className="flex items-center gap-2 text-sm text-muted">
        <Spinner />
        Syncing 3 forks
      </p>
      {/* Nothing else says it: the label makes it a status. */}
      <div className="grid h-28 w-full max-w-xs place-items-center rounded-large bg-surface-3">
        <Spinner size="lg" color="primary" label="Loading the changelog" />
      </div>
    </div>
  );
}

Props#

PropTypeDefault
size
"sm""md""lg"
"md"
color
"default""primary""secondary""success""warning""danger""info""current""current" inherits the text colour.
"current"
label
stringAnnounced to screen readers. Omit when the spinner sits inside a labelled element.
–