Progress indicators
components specs/components/progress-indicators.kmd
Visual feedback that an operation is in progress — linear (bar) or circular (spinner), determinate (known %) or indeterminate (unknown duration). Material parity (`/components/progress-indicators`).
Quando esta spec se aplica
Triggers primários
- Add a progress indicator
Todos os triggers
- Show a known-progress operation
- Show that work is happening without a known duration
- Replace generic spinner with branded indicator
Corpo da especificação
Spec — Progress indicators
Facet Visual of Koder Design. Material parity: https://m3.material.io/components/progress-indicators.
2 shapes × 2 modes
| Shape \ Mode | Determinate | Indeterminate |
|---|---|---|
| Linear | Bar fills 0 → 100% | Sliding band animates left → right (no fill) |
| Circular | Arc sweeps 0 → 360° | Arc rotates continuously |
Pick:
- Shape: linear when there's horizontal real estate (above content, banner-like); circular when space is constrained (button, dialog, icon-sized).
- Mode: determinate when % is known (file upload); indeterminate when not (network handshake, ML inference).
Anatomy (linear, determinate)
45% complete
████████░░░░░░░░░░░░░░░░░░░░░
←──────── 100% ────────────→
- Track height: 4 px (default) / 8 px (large)
- Track bg:
surface-container-highest(orprimary12%) - Active fill:
primarycolor - Corner radius: track height / 2 (pill shape)
- Stop point (Material 3): 4 px circle of
primarycolor at the end of the track to indicate "track end"
Anatomy (circular):
╭ ━━━╮
┃ ╲
┃ ● ← stroke head
╲ ╱
╲ ━╱
- Diameter: 24 px (small) / 32 px (default) / 48 px (large)
- Stroke width: 4 px (default), scales with size
- Track:
surface-container-highestring - Active arc:
primarycolor
R1 — Linear determinate
- Fill animates from current % to new % (motion-fast, ~150 ms)
- Don't reset to 0 between updates — smooth interpolation
- At 100%: brief settle (stop-point disappears as track ends)
- Backwards progress (resume after pause) allowed — animate back
R2 — Linear indeterminate
- Sliding band: 30-50% of track width, slides left → right continuously
- Cycle duration: 2 s
- Easing: cubic ease-in-out
- No stop point in indeterminate mode
R3 — Circular determinate
- Arc length proportional to %
- Starts at 12 o'clock; sweeps clockwise
- Stroke ends with rounded cap
R4 — Circular indeterminate
- Full circle rotates clockwise: 1.5 s per rotation
- Arc length pulses: 25% → 75% → 25% over 1 cycle
- Two pulses per rotation (Material 3 default; document if changed)
R5 — Buffer indicator (linear only)
For media / download with "buffered ahead of playhead":
████████████░░░░░░░░░░░░░░░░░░░
━━━━━━━━━━━━━━━━━░░░░░░░░░░░░░░
↑ ↑
playhead (filled)
buffered (lighter)
- Buffer color:
primary40% (lighter than active) - Buffer always ≥ playhead position
R6 — Indicator placement
| Placement | Use |
|---|---|
| Above content (full-width linear) | Page-level loading |
| In-button (small circular replaces text) | Submitting a form |
| Inline next to label (24 px circular) | Item-level operation (e.g., row syncing) |
| Centered in container (large circular) | Empty card / pane loading |
| In app bar bottom edge (linear) | Long page operations (refresh) |
R7 — Combine with action button
When a button triggers a long operation, replace its content with a small circular indicator (24 px):
Before: [ Submit ]
During: [ ◐ ] ← circular indeterminate, 24 px
After: [✓ Saved ]
- Button width stays the same (avoid layout shift)
- Disable the button during operation
- On success: brief check icon (1 s) before reverting to idle / new label
R8 — Multi-step / segmented (linear)
Linear can show discrete steps:
███ ███ ███ ░░░ ░░░ Step 3 of 5
step1 step2 step3 step4 step5
- 2 px gap between segments
- Each segment radius = track height / 2
- Filled segments use active color; unfilled use track color
Used for: onboarding stepper, multi-page form indicator.
R9 — Empty / not-started state
Don't show the indicator at 0% — looks broken. Either:
- Show indicator only after operation starts (delay 200-400 ms)
- Begin animation as soon as visible (indeterminate)
For determinate, if value is genuinely 0, render the track only (no fill) and show "0%" or "Not started" label nearby.
R10 — Completion state
Determinate at 100%:
- Brief settle (200 ms)
- Fade out indicator OR replace with success icon (✓)
- For background operations, fade out after 1-2 s
R11 — Accessibility
- Determinate linear / circular:
role="progressbar"+aria-valuenow/aria-valuemin/aria-valuemax+aria-labeldescribing what's loading - Indeterminate:
role="progressbar"+aria-busy="true"+ noaria-valuenow(omitted means indeterminate per ARIA) - For long operations, periodically announce progress to screen reader
(e.g., every 25%):
aria-live="polite"on container - Don't rely on color alone — pair with text label ("45% complete") for determinate
R12 — Animation
- Determinate update: motion-fast (~150 ms) interpolation
- Indeterminate linear cycle: 2 s
- Indeterminate circular cycle: 1.5 s
- All animations respect
prefers-reduced-motion:- Linear indeterminate: pulsing opacity 50% → 100% instead of sliding
- Circular indeterminate: no rotation; opacity pulse instead
R13 — States
Progress indicators don't have hover / pressed / focused states (they're decorative output, not interactive).
Disabled state IS valid — operation cancelled:
- Indicator fades to 38% opacity
- Active animation stops
- Track stays visible briefly before fading entirely
R14 — Density
| Density | Linear height | Circular default |
|---|---|---|
| Compact | 2 px | 20 px |
| Default | 4 px | 32 px |
| Comfortable | 8 px | 48 px |
R15 — Per-preset variation
| Preset | Linear | Circular |
|---|---|---|
material3 | Track + stop point at end | Stroke with rounded cap, 2 pulses/rotation |
material2 | No stop point; flat ends | Full rotation, 1 pulse |
ios_cupertino | Solid bar without stop | UIActivityIndicator gear-style |
gnome | GtkProgressBar style | Adwaita spinner (3 dots) |
windows_11 | Accent bar with subtle glow | Fluent spinner (dots circling) |
brutalist | Solid block, no easing (step changes only) | Square instead of circle, hard rotation |
terminal_classic | [██████░░░░] 60% ASCII | Spinner cycle ` |
R16 — Forbidden patterns
- ❌ Indeterminate when % is actually known (defeats user trust)
- ❌ Multiple progress indicators stacked on the same operation
- ❌ Indicator < 24 px for circular (unreadable)
- ❌ Animation that resets to 0 between % updates
- ❌ Showing indicator for < 200 ms (annoying flash; either delay it or do without)
- ❌ Operation that runs > 30 s with no progress feedback (always show indicator)
- ❌ Indicator without label / context (user can't tell WHAT is loading)
- ❌ Hiding the button label entirely on submit without a replacement (no feedback that work started)
Cross-link
themes/motion.kmd— easing for indeterminate cyclethemes/color-roles.kmd—primaryfor active,surface-container-highestfor trackinteraction/states.kmd— disabled overlaycomponents/buttons.kmd— in-button progress patternfoundations/elements.kmd— Output family
Referências
specs/foundations/elements.kmdspecs/themes/color-roles.kmdspecs/themes/motion.kmdspecs/interaction/states.kmd