<!-- Protocore Design System — ProgressBar -->
# ProgressBar

- **Category:** Feedback (`feedback`)
- **Slug:** `feedback/progress-bar`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { ProgressBar } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/feedback/progress-bar

> A thin track that fills to value/max, or sweeps indeterminately when value is omitted.

## When to use it

Use a **ProgressBar** when the work has a *measurable* extent — an upload, a migration, a chain sync, a multi-step import. The filled proportion is a promise that the task will end.

- Can't measure it, and the wait is short? Use a [Spinner](/feedback/spinner) — a determinate bar stuck at an arbitrary percent is worse than an honest spin.
- Standing in for content that's about to render? Use a [LoadingSkeleton](/feedback/loading-skeleton).
- Letting the user *set* a value rather than observe one? That's a [Slider](/inputs/slider), not a progress track.

Reserve the indeterminate sweep for truly open-ended work; prefer a real percentage whenever you can compute one.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `className` | `string` | no | — | — |
| `label` | `string` | no | — | Accessible label describing what is progressing. |
| `max` | `number` | no | `100` | Upper bound of `value`. |
| `style` | `CSSProperties` | no | — | — |
| `tone` | `enum` | no | — | Switches the fill from ink to a status color. |
| `value` | `number` | no | — | Current progress. Omit for an indeterminate sweep. |

## Examples

### Basics

Pass `value` (against a `max` of 100 by default) for a determinate fill. Always give it a `label` describing what is progressing.

```tsx
import { ProgressBar } from "@protocore/pds";

export default function Basics() {
  return <ProgressBar value={64} label="Syncing blocks" />;
}
```

### Tones

The default fill is ink. A `tone` recolors it to a status meaning — a green uptime bar, an amber quota, a red remaining-budget — the one place a track carries color.

```tsx
import { ProgressBar, VStack } from "@protocore/pds";

export default function Tones() {
  return (
    <VStack gap={4} style={{ width: 280 }}>
      <ProgressBar value={92} tone="success" label="Uptime" />
      <ProgressBar value={71} tone="warning" label="Disk usage" />
      <ProgressBar value={38} tone="danger" label="Quota remaining" />
    </VStack>
  );
}
```

## Do & don't

**Do**

- Use it for work with a measurable, finite extent.
- Always pass a `label` naming what is progressing.
- Use `tone` to encode a status reading (uptime, quota, budget).
- Show a real percentage whenever the total is knowable.

**Don't**

- Fake progress or park a determinate bar at an arbitrary percent.
- Use an indeterminate bar where a Spinner is simpler and just as honest.
- Confuse it with a Slider — ProgressBar is read-only.
- Rely on `tone` color alone to convey the reading; keep the label meaningful.

## Accessibility

**Notes**

- The root is `role="progressbar"` with `aria-valuemin`, `aria-valuemax`, and `aria-valuenow` (omitted while indeterminate).
- Provide `label` so `aria-label` names the task; without it the bar announces only a percentage.
- Value is clamped to the 0…max range, and a non-positive `max` falls back to 100, so ARIA values stay valid.
- The fill and indeterminate sweep respect `prefers-reduced-motion`.

## Related

`spinner`, `loading-skeleton`, `slider`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
