All Taste components
Data display
Progress
How far along something is, as labelled bars or rings, plus a spinner when the end is unknown.
bar · warning bar · thin bar · ring · small ring · spinner
Storage6.2 of 10 GB
Invoices this month92 of 100
8 left on the Starter plan.
Profile setup3 of 5 steps
72%Collected45%UploadingSyncing
import { cn } from "@/lib/utils";
export function ProgressBar({ label, value, max = 100, detail, tone = "primary" }:
{ label: string; value: number; max?: number; detail?: string; tone?: "primary" | "warning" | "danger" }) {
const pct = Math.min(100, Math.round((value / max) * 100));
return (
<div className="grid gap-1.5">
<div className="flex justify-between gap-3 text-[13px]">
<span className="font-medium">{label}</span>
<span className="tabular-nums text-fg-muted">{detail ?? pct + "%"}</span>
</div>
<div role="progressbar" aria-label={label} aria-valuenow={value} aria-valuemin={0} aria-valuemax={max}
className="h-2 overflow-hidden rounded-full bg-surface-2">
<div
className={cn("h-full rounded-full transition-[width] duration-500", { primary: "bg-primary", warning: "bg-warning", danger: "bg-danger" }[tone])}
style={{ width: pct + "%" }}
/>
</div>
</div>
);
}
export function ProgressRing({ value, size = 72, stroke = 7, label }: { value: number; size?: number; stroke?: number; label: string }) {
const r = (size - stroke) / 2;
return (
<span role="progressbar" aria-label={label} aria-valuenow={value} aria-valuemin={0} aria-valuemax={100}
className="relative inline-grid place-items-center">
<svg width={size} height={size} className="-rotate-90" aria-hidden>
<circle cx={size / 2} cy={size / 2} r={r} fill="none" strokeWidth={stroke} className="stroke-surface-2" />
<circle cx={size / 2} cy={size / 2} r={r} fill="none" strokeWidth={stroke} strokeLinecap="round"
pathLength={100} strokeDasharray={value + " 100"} className="stroke-primary transition-[stroke-dasharray] duration-500" />
</svg>
<span className="absolute text-[15px] font-semibold tabular-nums tracking-tight">{value}%</span>
</span>
);
}
export function Spinner({ size = 20, label = "Loading" }: { size?: number; label?: string }) {
return (
<svg role="status" aria-label={label} width={size} height={size} viewBox="0 0 24 24" className="animate-spin">
<circle cx="12" cy="12" r="10" fill="none" strokeWidth="3" className="stroke-surface-2" />
<circle cx="12" cy="12" r="10" fill="none" strokeWidth="3" strokeLinecap="round" pathLength={100} strokeDasharray="28 100" className="stroke-primary" />
</svg>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.