Skip to content
txbrendan/ui

Ring Progress

beta

A small ring that fills toward a goal, with the percent inside. It turns green once the goal is met.

Preview

Texture

tone

neutralbranddangersuccesswarning

no tone

size

no value

beside words

3 of 4 forms returned

in a list

  • Ava Clarke9 of 12
  • Sam Okafor12 of 12
  • Mia Hughes4 of 10

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/ring-progress

Use it for

  • Progress toward a goal in a small space, like tasks done this week, a profile that is 75% done, or a target in a card.
  • Pass value as a percent, or value and max, like value={3} max={4}.
  • Pass aria-label to say what it measures, like 'Tasks'. Screen readers hear 'Tasks: 75%'.
  • Pass showValue={false} for a small ring beside its own words, like '3 of 4'.
  • Leave tone out, and the ring turns success once it is full. Pass a tone to keep one color.

Avoid it for

  • A task that runs, like an upload. Use Progress.
  • Parts of a whole, like a budget split. Use DonutChart.
  • A ranking or many values side by side. Use BreakdownList.

Props

  • value

    Required. How much is done, from 0 to max. Values outside the range are clamped.

    number

  • max

    The value of a full ring.

    number · default 100

  • tone

    Color of the filled part. Left out, the ring is neutral and turns success once it is full. Passed, it stays that tone.

    "neutral" | "brand" | "danger" | "success" | "warning" · default "neutral"

  • size

    36, 44, or 66px across at the default density. lg uses larger text.

    "sm" | "md" | "lg" · default "md"

  • showValue

    Shows the percent inside. The percent is read out either way.

    boolean · default true

  • aria-label

    What the ring measures, like 'Tasks'. Screen readers hear it before the percent.

    string

  • texture

    dither draws the filled part as dots, gradient fades it along the arc to full at its end, and grain speckles it, with a glow on dark pages. Hover fills gradient and grain in. Left out, the ring follows the Texture around it.

    "flat" | "dither" | "gradient" | "grain"

  • className

    Extra classes. Later classes win.

    string

Accessibility

  • Renders role="img", named by aria-label and the percent, like 'Tasks: 75%'. With no aria-label, the name is the percent.
  • The number inside is hidden from screen readers, since the name already says it. With showValue={false} the name still does.
  • The filled part uses the tone's color on the track. The track is quiet, so the percent carries the meaning, in text or in the name.
  • The fill sweeps to a new value over --motion-duration-slow. Reduced motion jumps straight to it.

Examples

A percent, named by its label

<RingProgress value={75} aria-label="Tasks" />

A count toward a target

<RingProgress value={3} max={4} aria-label="Sessions this week" />

Beside its own words

<span className="flex items-center gap-2">
  <RingProgress value={3} max={4} size="sm" showValue={false} aria-label="Forms returned" />
  3 of 4 forms returned
</span>

One color, even when full

<RingProgress value={100} tone="brand" aria-label="Ticket sales" />

A gradient ring that fills in on hover

<RingProgress value={68} tone="brand" texture="gradient" aria-label="CPU load" />