Progress Indicator
A compact circular pie-fill progress indicator (0–100%) — a server-safe SVG glyph in circle or squircle shapes.
- Status
- Since
0.1.0- Accessibility pattern
- ARIA progressbar
Last updated
Install
Add Progress Indicator from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/progress-indicatorUsage
import { ProgressIndicator } from "@/components/ui/progress-indicator";
<ProgressIndicator value={60} />;The ProgressIndicator is the radial counterpart to the linear Progress bar. Reach for it when a tiny determinate percentage glyph fits better than a full-width bar — inline next to a label, in a dense list row, or beside a metric.
It is server-safe — pure SVG driven entirely by the value prop (no hooks, no 'use client', no DOM measurement). The arc is drawn with stroke-dasharray, so it renders identically on the server and the client.
Examples
Values
value is clamped into [0, max] (default max is 100) and the fill grows clockwise from 12 o'clock.
Shapes
shape controls the outline: a true circle ring or a rounded-square squircle.
Sizes
xs (14px), sm (16px), md (20px), and lg (24px) — mapped to the size-* token scale.
Variants
Use variant="inline-value" when the percentage should be readable beside a compact indicator, or
variant="contained-value" for a larger circular indicator with the percentage centered inside and
progress drawn on the border ring only.
Colors
The SVG inherits currentColor, so the track and fill follow the root's text color (text-primary by default). Recolor it with any semantic text-* token via className — never a hardcoded hex.
Custom scale
Pass max to track progress on an arbitrary scale — the announced aria-valuenow is normalized to a percentage (3 / 5 reports as 60):
<ProgressIndicator value={3} max={5} aria-label="Step 3 of 5" />Motion
A value change sweeps the fill wedge instead of jumping to the new position — the fill's
stroke-dasharray transitions on duration-base/ease-standard, matching the linear
Progress bar's transition-[width].
Shape × value matrix
Both shape outlines across the full fill range — the squircle's pie wedge reads against its rounded-square frame just as the circle's does against its ring.
Segments mode
Pass segments={n} to render a dash row instead of the radial glyph — round(value/max × n)
bars filled. Use it for step counts (checklists, setup progress), not smooth percentages. Add
segmentsFill when the row should span its container rather than sit inline as a compact meter;
that is the form OnboardingChecklist composes.
Playground
Adjust the glyph's value, size, and shape — the pie wedge sweeps to each new value — then copy the generated JSX.
<ProgressIndicator value={50} />API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | — | Accessible label announced by assistive tech. Defaults to a percentage
string (e.g. "60% complete"). The element always exposes
role="progressbar" with aria-valuenow / aria-valuemin /
aria-valuemax, so a custom label is optional. |
max | number | 100 | Upper bound of the scale — the fill is reported as value / max. The
percentage announced to assistive tech is Math.round(value / max * 100). |
segments | number | — | Dash-segment mode (Wave 2 — the checklist/steps progress voice): render a
row of segments bars instead of the radial glyph, with
round(value / max × segments) of them filled in currentColor and the
rest on the track opacity. Use for step counts ("2 of 6 steps"), not for
smooth percentages — the radial glyph stays the default. Takes precedence
over shape when set; minimum 1 (a one-step scale is a single full bar, never a
radial glyph). |
segmentsFill | boolean | false | Stretch the segment bars to share the available inline width instead of taking the fixed
per-size bar width. For a segmented bar that spans a card (the onboarding-checklist voice)
rather than a compact inline meter. Only meaningful with segments. |
shape | ProgressIndicatorShape | 'circle' | Outline shape: a circular ring (circle) or a rounded square (squircle). |
size | ProgressIndicatorSize | 'md' | Size variant — mirrors the system scale and maps to the size-* tokens.
xs (14px), sm (16px), md (20px), lg (24px). |
value | number | 0 | Fill percentage between 0 and max. 0 renders an empty track, max
renders a fully filled pie. Values are clamped into range. |
variant | ProgressIndicatorVariant | 'default' | Display style. default renders only the compact pie-fill glyph,
inline-value adds the percentage beside the glyph, and
contained-value renders a larger bordered circle with the percentage
centered inside and progress drawn on the ring only. |
Data attributes and CSS variables on ProgressIndicator
| Attribute | Values |
|---|---|
data-segments-fill | "" |
data-shape | "segments" |
data-size | mirrors a prop or state value |
data-slot | "progress-indicator" |
data-value | mirrors a prop or state value |
data-variant | mirrors a prop or state value |
Accessibility
- Renders
role="progressbar"witharia-valuenow/aria-valuemin/aria-valuemax. The value is normalized to a0–100percentage regardless ofmax. - A default
aria-label("{percent}% complete") is always present — pass a more descriptivearia-label(e.g."Step 3 of 5") when the surrounding context needs it. - The inner
<svg>isaria-hidden— the accessible state lives on the root element, so screen readers announce one progressbar, not a decorative graphic. - Color is token-driven (
currentColor); contrast follows whichever semantictext-*token you apply, so it inherits the theme's AA-compliant pairings.
| Attribute | Purpose |
|---|---|
role="progressbar" | Exposes the element as a progress indicator to assistive tech |
aria-valuenow | Current value as a 0–100 percentage |
aria-valuemin / aria-valuemax | Bounds of the announced scale (0 and 100) |
aria-label | Accessible name (defaults to "{percent}% complete") |
| Contract | States tested |
|---|---|
| Behaviour | default, complete, empty, range, success |
| Accessibility | labeled |
| Visual | default, success, empty |