Aksara UI

ProgressBar

Progress bars show how far a task has got, such as an upload or a course, or that it is running with no known length.

import { ProgressBar } from '@aksornproject/aksara-ui'

Basic progress bar

value is required. label is the first line and wraps when it is long. valueLabel sits on the right of the header, and description sits under the bar.

The bar fills its container with a 96px minimum. Set a width through className.

Disk usage
1.8 GB remaining
x
<ProgressBar
  value={64}
  label="Disk usage"
  valueLabel="3.2 GB / 5 GB"
  description="1.8 GB remaining"
  className="max-w-[360px]"
/>

Variants

variant sets what the fill colour means. accent is the default. positive, warning and negative carry a status, and neutral carries none.

Course progress
x
Upload complete
x
Storage
x
Absences
x
Survey responses
x
<div className="flex w-full max-w-[360px] flex-col gap-spacing-24">
  <ProgressBar value={40} label="Course progress" variant="accent" />
  <ProgressBar value={100} label="Upload complete" variant="positive" />
  <ProgressBar value={85} label="Storage" variant="warning" />
  <ProgressBar value={95} label="Absences" variant="negative" />
  <ProgressBar value={25} label="Survey responses" variant="neutral" />
</div>

Range and value text

min and max default to 0 and 100, and the value is clamped to them. Screen readers hear valueLabel as the value. Without it they hear the percentage.

Changes to value ease the fill over 200ms.

Marking essays
x
<div className="flex w-full max-w-[360px] flex-col items-start gap-spacing-16">
  <ProgressBar
    value={marked}
    max={TOTAL}
    label="Marking essays"
    valueLabel={`${marked} of ${TOTAL}`}
  />
  <Button
    size="sm"
    variant="secondary-accent"
    disabled={marked === TOTAL}
    onClick={() => setMarked((count) => count + 1)}
  >
    Mark next essay
  </Button>
</div>

Indeterminate

value={null} means the length is unknown. The fill slides across the track and stops under prefers-reduced-motion. NaN and Infinity also draw the indeterminate bar.

Importing class list
This can take a minute.
x
<ProgressBar
  value={null}
  label="Importing class list"
  description="This can take a minute."
  className="max-w-[360px]"
/>

Track only

With no label, valueLabel or description, the root holds only the track. It still needs a name, from aria-label or from aria-labelledby, here the row header.

Unit 1: Fractions
x
Unit 2: Decimals
x
<table className={`${typography('body-regular')} text-color-text-primary`}>
  <tbody>
    <tr>
      <th
        id="unit-1"
        scope="row"
        className="pr-spacing-16 text-left font-normal"
      >
        Unit 1: Fractions
      </th>
      <td>
        <ProgressBar
          value={80}
          aria-labelledby="unit-1"
          className="w-[120px]"
        />
      </td>
    </tr>
    <tr>
      <th
        id="unit-2"
        scope="row"
        className="pr-spacing-16 text-left font-normal"
      >
        Unit 2: Decimals
      </th>
      <td>
        <ProgressBar
          value={35}
          aria-labelledby="unit-2"
          className="w-[120px]"
        />
      </td>
    </tr>
  </tbody>
</table>

Accessibility

  • The root has role="progressbar" with aria-valuenow, aria-valuemin and aria-valuemax. An indeterminate bar has no aria-valuenow.
  • A bar needs exactly one name. Pass a visible label, or aria-label or aria-labelledby when nothing shows. TypeScript rejects a nameless bar and a label paired with another name.
  • valueLabel is hidden from screen readers and announced as aria-valuetext instead. Without it they hear the percentage, or "indeterminate progress".
  • description is linked with aria-describedby, after any ids you pass.

API

ProgressBar

Prop

Type

Edit on GitHub

On this page