Progress
A linear progress bar: a range inside a track, driven by one value.
The root carries role="progressbar" with the ARIA value trio, every stateful part carries
loading | complete | indeterminate, and the runtime publishes the fraction as
--progress-percent so a design system can paint the fill from it. There is no model — the
value is display-only.
Import
import { Progress } from '@sigx/zero/progress';
Progress is a compound: Progress.Root, Progress.Label, Progress.Track,
Progress.Range, Progress.ValueText. It is also re-exported from the @sigx/zero root,
together with progressAnatomy and useProgressContext.
Usage
import { component } from 'sigx';
import { Progress } from '@sigx/zero/progress';
const Upload = component(({ signal }) => {
const state = signal({ done: 62 });
return () => (
<Progress.Root value={state.done} color="primary">
<Progress.Label>Uploading…</Progress.Label>
<Progress.Track>
<Progress.Range />
</Progress.Track>
<Progress.ValueText />
</Progress.Root>
);
});
value is a plain prop, not a model: nothing the user does to a progress bar changes it,
so the app writes state.done and the bar follows. Progress.ValueText with no children
renders the rounded percent (62%); give it children to render your own text.
Indeterminate
<Progress.Root value={null}>
<Progress.Label>Connecting…</Progress.Label>
<Progress.Track><Progress.Range /></Progress.Track>
</Progress.Root>
value={null} (or no value at all) is indeterminate: the root and range render
data-state="indeterminate", aria-valuenow is omitted, --progress-percent is not
published and ValueText renders nothing. The design system animates the range.
A custom range
<Progress.Root value={bytesSent} min={0} max={bytesTotal}>
<Progress.Label>Sending</Progress.Label>
<Progress.Track><Progress.Range /></Progress.Track>
<Progress.ValueText>{formatBytes(bytesSent)} of {formatBytes(bytesTotal)}</Progress.ValueText>
</Progress.Root>
min and max default to 0 and 100. The percent is (value - min) / (max - min),
clamped to 0–100; a degenerate range (max <= min) has nothing left to fill, so any present
value reads as 100 and complete.
Anatomy
| Part | Element | States | Flags | Notes |
|---|---|---|---|---|
root | div | loading | complete | indeterminate | — | role="progressbar", aria-valuemin / aria-valuemax / aria-valuenow, aria-labelledby. Carries the variant axes. Publishes --progress-percent. |
label | div | — | — | The accessible name; its id is the root's aria-labelledby. Inside root. |
track | div | — | — | The channel. Inside root. |
range | div | loading | complete | indeterminate | — | The fill; inline width follows the percent. Inside track. |
value-text | div | — | — | Defaults to the rounded percent. Inside root. |
Every part carries data-scope="progress" and data-part="<part>". The state is derived:
indeterminate when the value is null, complete at 100 percent, loading otherwise.
When the value is present the root's inline style publishes
--progress-percent: <n>%, and the range's inline width is set to the same percent — a
recipe that paints the fill as a background or a transform reads the custom property
instead of relying on the width. See The anatomy contract.
The root's aria-labelledby always points at the label's id; render a Progress.Label so
the reference resolves, or the bar has no accessible name.
Props
Progress.Root
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | null | The current value; null is indeterminate. |
min | number | 0 | The lower bound; rendered as aria-valuemin. |
max | number | 100 | The upper bound; rendered as aria-valuemax. |
color / size / variant / axes / mods | design-system vocabulary | — | The variant axes, rendered as data-* on root. |
class | string | — | Extra classes on the root element. |
aria-* / data-* / id / title | HTML attributes | — | Forwarded. An app aria-labelledby joins the Label's. role is refused — the root is the progressbar. |
Progress.Label, Progress.Track
class and the forwarded HTML attributes. Each renders its part around its children. The Label refuses id — the root points at it.
Progress.Range
class and the forwarded HTML attributes. Renders the range part; it takes no children.
Progress.ValueText
class and the forwarded HTML attributes. Renders its children, or the rounded percent when it has none and the value
is present.
In the shipped design systems
Both @sigx/zero-basic and @sigx/zero-daisyui wire color (the eight recommended roles)
and size (xs–xl) on progress, so <Progress.Root color="success" size="sm"> is styled
in both. Neither wires a variant or any mods on the scope; under a design system's
/register import the variant prop is therefore absent. See
Typed vocabulary.
A recipe animates the indeterminate range in a loop, and a looping animation has to
stop under prefers-reduced-motion: reduce — give the loop a literal duration and switch
it off in the media query, rather than a --duration-* token, which collapses to
near-zero under reduced motion and turns the sweep into a strobe. The track and range read
radius-selector, so the bar rounds like a selector control.
Related
Radial Progress is the same value model on a ring; Spinner is progress with no value at all; Skeleton holds the layout while content loads.
