CSS-Only

Progress

Progress indicator using the native HTML progress element with semantic variants and sizes. Works by default, enhances with data attributes.

Example

70%
45%
85%
<progress class="omni-progress" value="70" max="100" aria-label="Loading progress: 70%">70%</progress>

<div data-variant="primary">
  <progress class="omni-progress" value="45" max="100" aria-label="Primary progress: 45%">45%</progress>
</div>

<div data-variant="success">
  <progress class="omni-progress" value="85" max="100" aria-label="Success progress: 85%">85%</progress>
</div>

Variants

Semantic Variants

Wrap the progress element in a container with data-variant to convey meaning through color:

Default (Primary) 60%
Secondary 60%
Success 60%
Warning 60%
Danger 60%
<div data-variant="secondary">
  <progress class="omni-progress" value="60" max="100" aria-label="Secondary progress: 60%">60%</progress>
</div>

<div data-variant="success">
  <progress class="omni-progress" value="60" max="100" aria-label="Success progress: 60%">60%</progress>
</div>

<div data-variant="warning">
  <progress class="omni-progress" value="60" max="100" aria-label="Warning progress: 60%">60%</progress>
</div>

<div data-variant="danger">
  <progress class="omni-progress" value="60" max="100" aria-label="Danger progress: 60%">60%</progress>
</div>

Sizes

Use data-size to adjust progress bar height:

Small (0.5rem)
50%
Medium (0.75rem - default)
50%
Large (1.25rem)
50%
<progress class="omni-progress" data-size="sm" value="50" max="100">50%</progress>
<progress class="omni-progress" value="50" max="100">50%</progress>
<progress class="omni-progress" data-size="lg" value="50" max="100">50%</progress>

Indeterminate State

Omit the value attribute for an indeterminate/loading state:

Indeterminate Default Loading...
Indeterminate Primary Processing...
Indeterminate Success Uploading...
<!-- No value attribute creates indeterminate state -->
<progress class="omni-progress" aria-label="Loading" aria-busy="true">Loading...</progress>

<div data-variant="primary">
  <progress class="omni-progress" aria-label="Processing" aria-busy="true">Processing...</progress>
</div>

States

Paused

Add the paused class for a pulsing paused state:

45%
<div data-variant="warning">
  <progress class="omni-progress paused" value="45" max="100" aria-label="Paused at 45%">45%</progress>
</div>

Error

Add the error class for error state styling:

25%
<progress class="omni-progress error" value="25" max="100" aria-label="Error at 25%">25%</progress>

API Reference

Attribute Values Default Description
value number Current progress value (omit for indeterminate)
max number 1 Maximum progress value
data-size sm, md, lg md Progress bar height
class="paused" Adds pulsing animation for paused state
class="error" Applies error state styling (danger color)

Variant System

Note: The progress element itself doesn't take data-variant. Instead, wrap it in a container with the variant attribute:

Container Attribute Values Default Description
data-variant primary, secondary, success, warning, danger primary Semantic color variant applied to progress bar

Accessibility

  • Uses semantic <progress> element with native browser accessibility
  • Always include aria-label describing the progress (e.g., "Upload progress: 70%")
  • For indeterminate states, add aria-busy="true" to indicate activity
  • Consider aria-live="polite" for dynamic progress updates
  • Color states (danger/warning/success) should include descriptive labels (e.g., "Upload progress: 30%, low")
  • Fallback text content between tags provides context for screen readers
  • Respects prefers-reduced-motion by disabling animations
  • Smooth transitions provide visual feedback for sighted users

Example with Full Accessibility

<!-- Determinate progress -->
<div data-variant="success">
  <progress
    class="omni-progress"
    value="75"
    max="100"
    aria-label="Upload progress: 75%, high"
  >75% complete</progress>
</div>

<!-- Indeterminate progress -->
<div data-variant="primary">
  <progress
    class="omni-progress"
    aria-label="Loading content"
    aria-busy="true"
    aria-live="polite"
  >Loading...</progress>
</div>