A token-colored circular progress meter with a readable center and accessible value.
Almost ready
Complete your workspace setup.
import { ProgressRing } from "@/components/ballmac/progress-ring"
export default function ProgressRingDemo() {
return (
<div className="flex w-full max-w-xs items-center gap-5 rounded-xl border border-border bg-card p-5">
<ProgressRing label="Onboarding complete" value={72} />
<div>
<p className="text-sm font-semibold">Almost ready</p>
<p className="text-muted-foreground mt-1 text-xs">
Complete your workspace setup.
</p>
</div>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/progress-ringCopy the source into your project.
components/ballmac/progress-ring.tsx// Ballmac UI: Progress Ring. https://ui.ballmac.com/components/progress-ring import * as React from "react" import { cn } from "@/lib/utils" type ProgressRingProps = React.ComponentProps<"div"> & { /** Current progress value. */ value: number /** Maximum value; defaults to 100. */ max?: number /** Accessible name for the progress meter. */ label: string /** Diameter in pixels. */ size?: number /** Center content, or the formatted percentage by default. */ children?: React.ReactNode } function ProgressRing({ className, value, max = 100, label, size = 96, children, ...props }: ProgressRingProps) { const safeMax = max > 0 ? max : 100 const bounded = Math.min( safeMax, Math.max(0, Number.isFinite(value) ? value : 0), ) const percent = Math.round((bounded / safeMax) * 100) const radius = 42 const circumference = 2 * Math.PI * radius return ( <div data-slot="progress-ring" role="progressbar" aria-label={label} aria-valuemin={0} aria-valuemax={safeMax} aria-valuenow={bounded} className={cn( "relative inline-grid shrink-0 place-items-center text-primary", className, )} style={{ width: size, height: size }} {...props} > <svg viewBox="0 0 100 100" aria-hidden="true" className="absolute inset-0 size-full -rotate-90" > <circle cx="50" cy="50" r={radius} fill="none" stroke="currentColor" strokeWidth="8" className="text-muted" /> <circle cx="50" cy="50" r={radius} fill="none" stroke="currentColor" strokeWidth="8" strokeLinecap="round" strokeDasharray={circumference} strokeDashoffset={circumference * (1 - percent / 100)} className="transition-[stroke-dashoffset] duration-200 motion-reduce:transition-none" /> </svg> <span data-slot="progress-ring-value" aria-hidden="true" className="text-foreground relative text-lg font-semibold tabular-nums" > {children ?? `${percent}%`} </span> </div> ) } export { ProgressRing, type ProgressRingProps }Update the import paths to match your project setup.
Usage
import { ProgressRing } from "@/components/ballmac/progress-ring"The full example is in the Code tab above.
Examples
States and variants
import { ProgressRing } from "@/components/ballmac/progress-ring"
export default function ProgressRingStates() {
return (
<div className="flex w-full max-w-xs items-center justify-around gap-4">
<ProgressRing label="Upload complete" value={100} size={72} />
<ProgressRing label="Sync progress" value={3} max={10} size={72} />
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
value*Current progress value. | number | — |
maxMaximum value; defaults to 100. | number | 100 |
label*Accessible name for the progress meter. | string | — |
sizeDiameter in pixels. | number | 96 |
childrenCenter content, or the formatted percentage by default. | React.ReactNode | — |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| None | Progressbar name and current value are announced |
Use with AI
A token-colored circular progress meter with a readable center and accessible value. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Progress Ring (@ballmac/progress-ring) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Show bounded progress in a small area
Not for
- Use Progress for a full-width track
Registry JSON: https://ui.ballmac.com/r/progress-ring.json
Credits
Free to use in personal and commercial projects.
- npm
- None
- Registry
- shadcn/utils
Pairs well with
Progress
A Radix progress bar with clamped values, optional percentage text, and a reduced-motion-safe pending state.
Activity Feed
A compact actor and action feed with timestamps and an empty state.
Audio Player
A podcast-style audio player: play and pause, skip back and forward, a scrubber with chapter ticks, a named current chapter and a speed control. Works with a real file or as a silent demo.
Avatar Circles
Overlapping avatars that spread on hover, lift individually with a name and role tooltip, show presence, fall back to initials and end in a +N count that can be a button.