Components · Data display & feedback
Progress
A determinate completion bar — an 8px track with a primary fill. Use it when the amount of work is known; use Spinner for indeterminate operations.
Preview
Build with the skill
No package to install — hand this to your AI to generate or migrate the component in your stack.
Apply the Zetta "Progress" component to this project using the zetta-design-md skill.
WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `progress` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `progress` spec in this project's stack.
AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.
ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility. Anatomy & rules
[████████░░░░░░░░░░░░] 64% ← progress-bar (determinate)
[▓▓▓▓▓▓▓▓████░░░░░░░] ← progress-bar-indeterminate (sweeping animation)
⟳ ← progress-spinner
| State | Fill | Track |
|---|---|---|
| Default | {colors.primary} |
{colors.surface-muted} |
| Success | {colors.success} |
{colors.success-bg} |
| Danger / Error | {colors.danger} |
{colors.danger-bg} |
| Indeterminate | Animated shimmer | {colors.surface-muted} |
| Complete (100%) | {colors.success} |
{colors.success-bg} |
When to use
- File uploads, form submissions, multi-step processes (bar, determinate).
- Data fetching, background sync, initial page load (spinner or indeterminate bar).
When not to use
- Instant operations (< 300ms) — don't flash a spinner for imperceptible waits.
- Skeleton loading — prefer Skeleton for content placeholders.
Do
- Show determinate bar when progress percentage is known — it reduces perceived wait time.
- Show spinner only when the operation cannot be measured.
- Display a progress label when the bar value alone lacks context ("Uploading 3 of 10 files").
Don't
- Don't use both a spinner and a bar for the same operation.
- Don't animate the bar width with a sudden jump — always use
transition.
- Progress label: match the action verb — "Uploading…", "Processing…", "Generating…". Include quantity when known: "Uploading 3 of 10 files".
- Spinner label (visually hidden): short present-participle phrase: "Loading", "Saving", "Submitting". Never "Please wait…".
- Completion announcement: after operation ends, update the live region to a past-tense phrase: "Upload complete", "Saved".
Keyboard
Progress Indicator is non-interactive and has no keyboard behavior.
Accessibility
- Bar determinate:
role="progressbar"witharia-valuenow,aria-valuemin="0",aria-valuemax="100", andaria-label. - Bar indeterminate:
role="progressbar"— omitaria-valuenow. - Spinner:
role="status"with a visually hidden<span>containing the status label (e.g. "Loading…"). - After loading completes, update the live region to announce completion: "Upload complete".
Spec
Values and token references resolved straight from the Zetta spec.
progress-bar
- height
- 4px
- borderRadius
- {rounded.full}
- backgroundColor
- {colors.surface-muted}
- overflow
- hidden
progress-bar-fill
- height
- 100%
- borderRadius
- {rounded.full}
- backgroundColor
- {colors.primary}
- transition
- width {motion.duration-base} {motion.easing-default}
progress-bar-indeterminate
- backgroundColor
- {colors.primary}
- animation
- indeterminate {motion.duration-slow} {motion.easing-default} infinite
progress-bar-success
- backgroundColor
- {colors.success}
progress-bar-danger
- backgroundColor
- {colors.danger}
progress-bar-sm
- height
- 2px
progress-bar-lg
- height
- 8px
progress-spinner
- width
- 20px
- height
- 20px
- borderWidth
- 2px
- borderStyle
- solid
- borderColor
- {colors.surface-muted}
- borderTopColor
- {colors.primary}
- borderRadius
- {rounded.full}
- animation
- spin {motion.duration-slow} linear infinite
progress-spinner-sm
- width
- 14px
- height
- 14px
- borderWidth
- 1.5px
progress-spinner-lg
- width
- 32px
- height
- 32px
- borderWidth
- 3px
progress-spinner-fullscreen
- width
- 50px
- height
- 50px
- borderWidth
- 4px
- borderStyle
- solid
- borderColor
- {colors.surface-muted}
- borderTopColor
- {colors.primary}
- borderRadius
- {rounded.full}
- animation
- spin {motion.duration-slow} linear infinite
- position
- fixed
- top
- 50%
- left
- 50%
- transform
- translate(-50%, -50%)
- zIndex
- {elevation.z-index.modal}
progress-spinner-invert
- borderColor
- rgba(255,255,255,0.25)
- borderTopColor
- {colors.canvas}
progress-label
- fontFamily
- Geist
- fontSize
- 12px
- fontWeight
- 400
- textColor
- {colors.muted}
- marginTop
- {spacing.xs}
Known gaps
progress-spinner-invertarc color — currently hardcoded torgba(255,255,255,0.25)(track) and{colors.canvas}(arc). No semantic token covers this use case cleanly.- Fullscreen spinner blanket —
progress-spinner-fullscreenpositions the spinner but the blanket overlay behind it is product responsibility. A dedicatedfullscreen-loadingcomposite pattern is not yet specified. - Progress bar success/error visual state transition animation — not yet specified.