Build Assets
UI Component

Progress Bar

Linear indicator of completion for determinate and indeterminate processes.

Platform: bothTags: progress, loading, completion, bar, indicator

Purpose

Communicates progress toward a known or unknown completion point. Determinate: percentage known (file upload, multi-step completion). Indeterminate: duration unknown (background processing). Both reduce perceived wait time vs. no feedback.

Anatomy

  • Track: full-width grey bar, 6–8px height, rounded ends
  • Fill: accent-color bar growing left to right
  • Optional label: percentage or "Step 2 of 5" above or beside
  • Indeterminate variant: animated shimmer traversing the track

States

  • Determinate: fill width = % complete, transitions smoothly
  • Indeterminate: animated sweep from left to right, loops
  • Complete: 100% fill, optional color change to success green
  • Error: fill color changes to red, stops at point of failure

Tokens

spacing

height: 6pxborderRadius: 3pxlabelGap: 6px

colors

track: var(--color-bg-secondary)fill: var(--color-accent)complete: var(--color-success)error: var(--color-error)

Do

  • Use determinate when you can calculate percentage — users prefer knowing how far
  • Animate fill transitions — abrupt jumps feel broken
  • Show percentage label for long operations (uploads, exports)
  • Change to success state on completion — positive reinforcement

Don't

  • Use indeterminate when you actually know the percentage
  • Show progress bars for operations under 2 seconds — use a spinner
  • Reset a progress bar to 0% and restart — users experience this as failure
  • Use multiple progress bars on the same view without clear labels

Accessibility

  • role="progressbar" with aria-valuenow, aria-valuemin="0", aria-valuemax="100"
  • aria-valuetext="Uploading: 45% complete" for screen readers
  • Indeterminate: aria-valuenow omitted, aria-busy="true" on parent
  • Completion: announce via aria-live="polite"

Source Topic

ux psychology

Doherty Threshold

Why 400ms is the magic number