All Taste components
Loaders
Progress ring
Circular progress that draws its arc up to the value, for storage, goals or task completion.
3 tones · staggered draw · centre value
72%
45%
90%
import { cn } from "@/lib/utils";
const tones = { primary: "stroke-primary", info: "stroke-info", success: "stroke-success" } as const;
interface ProgressRingProps {
value: number;
label: string;
tone?: keyof typeof tones;
/** Stagger several rings: 0, 1, 2… */
index?: number;
}
export function ProgressRing({ value, label, tone = "primary", index = 0 }: ProgressRingProps) {
return (
<figure className="grid justify-items-center gap-2 text-[12.5px] text-fg-muted">
<div role="progressbar" aria-valuenow={value} aria-valuemin={0} aria-valuemax={100} aria-label={label} className="relative grid size-[76px] place-items-center">
<svg viewBox="0 0 40 40" aria-hidden className="absolute inset-0 size-full -rotate-90">
<circle cx="20" cy="20" r="16" pathLength={1} fill="none" strokeWidth={3.5} className="stroke-surface-2" />
<circle
cx="20" cy="20" r="16" pathLength={1} fill="none" strokeWidth={3.5} strokeLinecap="round"
strokeDasharray={1} strokeDashoffset={1 - value / 100}
className={cn(tones[tone], "animate-draw motion-reduce:animate-none")}
style={{ animationDelay: index * 120 + "ms" }}
/>
</svg>
<b className="text-base font-medium tabular-nums text-fg">{value}%</b>
</div>
<figcaption>{label}</figcaption>
</figure>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.