⚠️ Design system CSS not found. Check the path in cms/docs.config.js → designSystemPath, then re-run npm run docgen.

Progress

Show how far along something is

Website / Progress
Download .md file
Open .md in new tab
On this page
  • Basic usage
  • Status variants
  • Progress ring
  • Accessibility
  • Usage rules
  • CSS reference
  • Use in another product

Progress comes in two shapes sharing one palette: a bar built on the native <progress> element, and an SVG ring for app surfaces. Both indicate completion percentage and support the same status colour variants.


Basic usage

60%
<progress class="progress-bar" value="60" max="100">60%</progress>

The fallback text (60%) is shown in browsers that do not support <progress>.


Status variants

100% 40% 10%
<progress class="progress-bar progress-bar--success" value="100" max="100">100%</progress>
<progress class="progress-bar progress-bar--warning" value="40" max="100">40%</progress>
<progress class="progress-bar progress-bar--danger" value="10" max="100">10%</progress>

Progress ring

is the bar's job in a compact, centre-labelled circle — for app surfaces where a full-width bar has no width to fill: a completion figure beside a heading, a step counter in a sheet. Determinate only, like the bar.

60%
100%
10%
<div class="progress-ring" role="progressbar"
     aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
     aria-label="Profile completeness"
     style="--progress-ring-value: 60">
  <svg viewBox="0 0 48 48" fill="none" width="100%" height="100%" aria-hidden="true">
    <circle class="progress-ring-track" cx="24" cy="24" r="20" pathLength="100"/>
    <circle class="progress-ring-fill" cx="24" cy="24" r="20" pathLength="100"/>
  </svg>
  <span class="progress-ring-label" aria-hidden="true">60%</span>
</div>

The value is fed twice, and the two must move together: --progress-ring-value (a unitless 0–100 number) draws the arc, aria-valuenow announces it — the same pairing rule as and aria-pressed on the segmented control. From script:

ring.style.setProperty('--progress-ring-value', value);
ring.setAttribute('aria-valuenow', String(value));
label.textContent = value + '%';

The circles carry pathLength="100", so the dash arithmetic is percentage arithmetic — keep that attribute and the CSS never needs to know the radius. The canonical drawing is viewBox="0 0 48 48" with r="20"; rendered at --progress-ring-size the whole drawing scales together, so a bigger ring is a bigger drawing, not a thinner one.

Status variants use the same grammar as the bar: , , .

The centre label is optional; drop the <span> for a bare ring. Keep aria-hidden on it when present — aria-valuenow already announces the number, and without it a screen reader hears the value twice.


Accessibility

  • The native <progress> element is announced by screen readers automatically
  • Always include fallback text content inside the element
  • If the progress represents a named process, associate it with a label using aria-label or a visible <label>
  • The ring is a styled <div>, so its semantics are declared: role="progressbar", aria-valuenow, aria-valuemin, aria-valuemax, and an aria-label naming the process
  • Under reduced motion both shapes still move to the new value — they get there without the sweep
  • In forced-colors mode the ring declares its own pair (GrayText track, Highlight fill): the mode would flatten the token strokes, and author-declared system colour keywords are honoured where token values are not

Usage rules

Do:

  • Use status variants to indicate outcome (green for complete, red for critical)
  • Pair a status ring with visible text naming the outcome — the hue adds meaning it must never carry alone
  • Include fallback text inside the <progress> element
  • Update --progress-ring-value and aria-valuenow in the same place, always

Don't:

  • Don't use progress bars or rings for indeterminate loading, use a skeleton instead
  • Don't animate the value attribute with JS unless the operation is genuinely progressing
  • Don't rebuild the ring at another viewBox — resize it with --progress-ring-size

CSS reference

This section documents how the component is built. For usage, see the sections above.

Tokens

Token Default Purpose
Track (unfilled) colour, bar and ring
Fill (completed) colour, bar and ring
0px Bar corner radius
Bar height
Value-change travel, bar and ring — one knob times both shapes
48px Ring rendered diameter
4px Stroke width, in viewBox units — scales with the ring

Selectors

Selector Purpose
progress.progress-bar Base component: width, height, border-radius, appearance reset
progress.progress-bar::-webkit-progress-bar Track background (WebKit)
progress.progress-bar::-webkit-progress-value Fill colour (WebKit)
progress.progress-bar::-moz-progress-bar Fill colour (Firefox)
progress.progress-bar--success Success status variant, green fill
progress.progress-bar--warning Warning status variant, yellow fill
progress.progress-bar--danger Danger status variant, red fill
Ring container: size, centre-label grid, default
.progress-ring svg The drawing, rotated so progress runs from 12 o'clock
Track circle
Fill circle: dash arithmetic, rounded cap, sweep transition
Centre label
/ --warning / --danger Status variants, same grammar as the bar

Use in another product

The design system installs once per product:

npm install github:bydefaultstudio/design-system-dist#semver:^4.7.0

This component's styles ship in design-system.css. No JavaScript, nothing else to include.

On this page
  • Basic usage
  • Status variants
  • Progress ring
  • Accessibility
  • Usage rules
  • CSS reference
  • Use in another product
Previous Tag
Next Rating

Was this page helpful?

We use this feedback to improve our documentation.

Thanks for your feedback

Send feedback

© 2026 By Default