A determinate ring or half ring drawn as an SVG stroke, with the value in the middle.

Last updated

62%
Show code
<CircularProgress value={62} label="Upload">62%</CircularProgress>

Add

npx @ava-ui/apogee add circular-progress

No Apogee dependencies. Zero JS.

Usage

---
import CircularProgress from '@/components/apogee/CircularProgress/CircularProgress.astro'
---
<CircularProgress value={62} label="Upload progress">62%</CircularProgress>

API

@composable — exposed to Content Editors in AVA. Any other native <div> attribute passes through. The default slot sits in the centre (at the bottom edge for shape="half").

Prop Type Default Description
value number 0 Current value.
min number 0 Lower bound.
max number 100 Upper bound.
label string — Accessible name (aria-label); say what is progressing.
size 'small' | 'medium' | 'large' | 'xlarge' 'medium' Ring 32 / 48 / 64 / 96px wide.
thickness 'thin' | 'regular' | 'thick' 'regular' Stroke width, relative to the ring.
color 'primary' | 'neutral' | 'critical' | 'positive' | 'warning' 'primary' Ring hue.
shape 'circle' | 'half' 'circle' A full ring or the top half (a gauge).
class string — Extra classes, appended last.

Variants

Every shape:

25%
100%
Show code
<CircularProgress value={25} label="Quarter">25%</CircularProgress>
<CircularProgress value={100} shape="half" label="Complete">100%</CircularProgress>

Every size:

40%
40%
40%
Show code
<CircularProgress value={40} size="small" />
<CircularProgress value={40} size="medium">40%</CircularProgress>
<CircularProgress value={40} size="large">40%</CircularProgress>
<CircularProgress value={40} size="xlarge">40%</CircularProgress>

Every thickness and every color:

Show code
<CircularProgress value={70} thickness="thin" />
<CircularProgress value={70} thickness="regular" />
<CircularProgress value={70} thickness="thick" />
<CircularProgress value={70} color="neutral" /> … critical, positive, warning

States

  • Determinate only — the value is always known; for an unknown wait use Spinner.
  • Animated — the arc tweens when value changes (--duration-slow), still under prefers-reduced-motion only through your own override; the stroke is a transition, not a loop.

Examples

A storage gauge:

8.2 / 10 GB

Show code
<CircularProgress value={8.2} max={10} shape="half" size="xlarge" color="warning" label="Storage used">
8.2 <small>/ 10 GB</small>
</CircularProgress>

The text inside is sized to the ring: it never grows past the inner edge and wraps when it must. Put a unit or a secondary part in <small>; any other size classes inside are overridden.

Accessibility

  • role="progressbar" with aria-valuemin, aria-valuemax and aria-valuenow; give it a label.
  • The slot text is visible to everyone; keep it the same number the attributes carry.
  • Native semantics, no extra ARIA needed.

Tokens

Token Used for
--background-neutral-faded Track
--background-{hue} Fill (through the stroke-{hue} utility)
--foreground-neutral Slot text
--duration-slow Arc transition

Do / Don’t

  • Do show the number in the slot; a ring alone is hard to read past “about half”.
  • Do use half for a gauge (a quota, a score), circle for progress.
  • Don’t use it for a step count; that is Steps or ProgressIndicator.
  • Don’t animate from 0 on every render; set the real value server-side.

Content

  • label names the thing measured (“Upload progress”, “Storage used”).
  • The slot shows the value in the user’s unit (percent, GB, points), short.