Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Progress Indicator

A compact circular pie-fill progress indicator (0–100%) — a server-safe SVG glyph in circle or squircle shapes.

Status
stable
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-indicator

Usage

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

PropTypeDefaultDescription
aria-labelstringAccessible 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.
maxnumber100Upper bound of the scale — the fill is reported as value / max. The percentage announced to assistive tech is Math.round(value / max * 100).
segmentsnumberDash-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).
segmentsFillbooleanfalseStretch 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.
shapeProgressIndicatorShape'circle'Outline shape: a circular ring (circle) or a rounded square (squircle).
sizeProgressIndicatorSize'md'Size variant — mirrors the system scale and maps to the size-* tokens. xs (14px), sm (16px), md (20px), lg (24px).
valuenumber0Fill percentage between 0 and max. 0 renders an empty track, max renders a fully filled pie. Values are clamped into range.
variantProgressIndicatorVariant'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

AttributeValues
data-segments-fill""
data-shape"segments"
data-sizemirrors a prop or state value
data-slot"progress-indicator"
data-valuemirrors a prop or state value
data-variantmirrors a prop or state value

Accessibility

  • Renders role="progressbar" with aria-valuenow / aria-valuemin / aria-valuemax. The value is normalized to a 0–100 percentage regardless of max.
  • A default aria-label ("{percent}% complete") is always present — pass a more descriptive aria-label (e.g. "Step 3 of 5") when the surrounding context needs it.
  • The inner <svg> is aria-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 semantic text-* token you apply, so it inherits the theme's AA-compliant pairings.
AttributePurpose
role="progressbar"Exposes the element as a progress indicator to assistive tech
aria-valuenowCurrent value as a 0–100 percentage
aria-valuemin / aria-valuemaxBounds of the announced scale (0 and 100)
aria-labelAccessible name (defaults to "{percent}% complete")
ContractStates tested
Behaviourdefault, complete, empty, range, success
Accessibilitylabeled
Visualdefault, success, empty

Do / Don't

Do
Use ProgressIndicator for a compact, determinate percentage glyph — inline beside a label, metric, or list row.
Don't
Use it for an indefinite wait with no measurable value — use a Spinner there instead.

On this page