A bar that shows how far a task has got, such as an export, with the words that name the task and the share done, such as "Exporting the report" at "45%", or, while how far is not known, that the task is under way.
When to use
Use Progress for a task the person waits on that ends, such as an export or a download.
| Use instead | When |
|---|---|
| Meter | The bar measures a value within a range, such as the share of vehicles reporting live, rather than a task |
| DataStatePanel | A data view's data has not arrived: its calm loading message takes the view's place |
Kongens Nytorv
Metro M1, M2 and M3.
Props
| Name | Type | Default | Description |
|---|---|---|---|
label* | ReactNode | — | The task, in a few words, such as "Exporting the report": shown above the track, and the progress bar's accessible name. Required |
value* | number | null | — | Required. How far the task has got, between min and max; one outside them is shown at the nearest end. null, or a number that is not finite, while how far is not known: Progress then sweeps a segment along the track and says "In progress" (CR-5) |
locale | LocalesArgument | The PerronProvider's locale | The locale Intl.NumberFormat formats the figure in: "45%" in English, "45 %" in Danish, Swedish and Norwegian. Left out, the nearest PerronProvider's |
inProgressLabel | string | The locale's | Overrides "In progress", heard in place of the figure while the value is indeterminate |
className | string | — | Joined after pn-Progress on the root |
style | CSSProperties | — | Reaches the root |
ref | Ref<HTMLDivElement> | — | Reaches the root element |
slotProps | { label?: ProgressLabelProps | undefined; value?: ProgressValueProps | undefined; track?: ProgressTrackProps | undefined; indicator?: ProgressIndicatorProps | undefined; } | — | Props for each part: label, value, track, indicator. |
label (required)ReactNode, default —- The task, in a few words, such as "Exporting the report": shown above the track, and the progress bar's accessible name. Required
value (required)number | null, default —- Required. How far the task has got, between
minandmax; one outside them is shown at the nearest end.null, or a number that is not finite, while how far is not known: Progress then sweeps a segment along the track and says "In progress" (CR-5) localeLocalesArgument, default The PerronProvider's locale- The locale
Intl.NumberFormatformats the figure in: "45%" in English, "45 %" in Danish, Swedish and Norwegian. Left out, the nearest PerronProvider's inProgressLabelstring, default The locale's- Overrides "In progress", heard in place of the figure while the value is indeterminate
classNamestring, default —- Joined after
pn-Progresson the root styleCSSProperties, default —- Reaches the root
refRef<HTMLDivElement>, default —- Reaches the root element
slotProps{ label?: ProgressLabelProps | undefined; value?: ProgressValueProps | undefined; track?: ProgressTrackProps | undefined; indicator?: ProgressIndicatorProps | undefined; }, default —- Props for each part:
label,value,track,indicator.
Use it
import { Progress } from '@mylder/perron'Ask your agent
get_reference({ library: "base-ui", component: "Progress" })Keyboard and ARIA
Progress has no keys of its own and is never focusable: a progressbar is not a control.
| Key | Does | From |
|---|---|---|
| None | Progress is not focusable or operable | Perron |
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
root | progressbar, from Base UI | aria-labelledby, the label part's id, so the caller's label. Base UI's Progress.Label registers its id on the client only, so server-rendered markup has no aria-labelledby until hydration | aria-valuenow, aria-valuemin, aria-valuemax, and aria-valuetext: the figure, "45%"; while indeterminate no aria-valuenow, and aria-valuetext "In progress" (CR-5) |
label | presentation, from Base UI | Not named; its text is the root's name | None |
value | Hidden: aria-hidden="true", from Base UI | Not read; aria-valuetext says it | None |
track, indicator | None (generic div) | Not named; they hold no text | None |
Base UI's "x" span | presentation, visually hidden | Chromium exposes it as the text "x" inside the progress bar, as with Meter; Progress keeps Base UI's root (CR-1) | None |
What axe won't catch, from the contract, each with how this component meets it:
- Everything the eye is told, the ear is told, for every shape each prop accepts: the eye gets the label, the figure and the fill's length; the ear the label as the name and the figure as
aria-valuetext, which Base UI keeps the same text as the shown figure, or a fuller one fromgetAriaValueText. While indeterminate the eye gets the sweeping segment and no figure, the ear "In progress" in place of Base UI's English "indeterminate progress" (CR-5); under reduced motion the resting segment and the words carry it. - Every landmark and named region has an accessible name: none here; the root is a
progressbar, named by its label, not a landmark or region. - Every default label can be overridden:
inProgressLabel. - Every shape the type accepts renders, in every variant:
labelis aReactNode, and a string, a number, an element and a list each render in every status;valuerenders0,min,max, a value outside the range (clamped), a fraction,null,NaNandInfinity(the last three indeterminate), whichProgress.test.tsxrenders each of. - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: none here; the sweep moves one element, and nothing collapses. The figure isaria-hiddenbecausearia-valuetextsays it, not for animation. - Native semantics first: no native element says "a task's progress" with Perron's look and Base UI's API (
<progress>draws its own bar), so Base UI'srole="progressbar"with itsaria-value*attributes, and no ARIA of Perron's beyond thearia-valuetextof an indeterminate value. - Focus moves in reading order and returns on close: Progress takes no focus and opens nothing.
- Every target is at least
size.target-minsquare: no target; Progress is not pressed. - Loops stop when the system asks for less motion: the indeterminate sweep, the one loop, stops inside
@media (prefers-reduced-motion: reduce)in a rule for.pn-Progress-indicator[data-indeterminate]that sets itsanimation(CR-6); the fill's change is a transition overduration.meter, which the tokens shorten to 0.01 ms.
Owned strings
Every word Progress shows comes from the caller, through label, or from Base UI's formatting of the number, but for the one it announces while how far is not known.
| Key | English | Override | Seen or heard where |
|---|---|---|---|
inProgress | In progress | inProgressLabel | The root's aria-valuetext while the value is indeterminate, heard after the label (CR-5) |
The word heard while indeterminate is a new key inProgress, "In progress", rather than the existing loading ("Loading") or unknown ("Unknown"): a progress bar tracks exports and downloads as well as loads, so "Loading" would say more than the screen knows, and "Unknown" would tell the ear the task's state is unknown when only its share is; after the label it reads "Exporting the report, In progress". Agents write its Danish, Swedish and Norwegian in the locale files, and its JSDoc in packages/perron/src/locale/en.ts names Progress.
The figure's digits and percent sign carry no letter, so expectOwnedStrings passes them in every locale; Base UI's "x" it already skips, and Base UI's English "indeterminate progress" and "indeterminate" never reach the markup (CR-5).
Do and don't
For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.
| Rule | Level | Do | Don't | Checked by |
|---|---|---|---|---|
| DD-1 | must | Use Progress for a task the person waits on that ends, such as an export or a download | Use it for a measure within a range, such as the share of vehicles reporting live: that is a Meter, a measurement rather than a task | review |
| DD-2 | must | Pass null while how far is not known, so Progress sweeps and says "In progress" | Pass 0 or a guess: an empty fill at "0%" claims a figure the task never gave | review |
| DD-3 | must | Name the task in a few words in label: "Exporting the report", "Loading the network" | Leave the name to the layout: a progress bar with no name tells the ear a number and nothing about the task | review |
| DD-4 | must | Announce the start and the end of the task, such as "Report ready", in the screen's own polite live region | Make a Progress a live region: a figure that counts up every moment floods the screen reader | review |
| DD-5 | must | Replace the bar with the screen's own message when the task fails, and say what happened | Leave a bar at its last figure, or tint it, when the task fails: the eye reads it as still running, and colour tells no screen reader | review |
| DD-6 | must | Give getAriaValueText the fuller words when the figure shows them, such as "3 of 12 files" through slotProps.value.children | Change the shown figure and leave the ear on the percentage: the eye and the ear then hear different numbers | review |
| DD-7 | should | Use DataStatePanel's loading status for a view whose data has not arrived | Put an indeterminate Progress in place of a data view's content: DataStatePanel's calm "Loading" message is the library's answer for that | review |