Skip to content

<u-progress> ​

<u-progress> lets you indicatate the amount of completed work, typically displayed as a progress bar.

Quick intro:

  • Use the max attribute to change the amount of total work to be done
  • Use the value attribute to change the amount of completed work
  • Remove the value attribute to change to indeterminate/unknown amount of work
  • MDN Web Docs: <progress> (HTMLProgressElement)

Example ​

Install ​

bash
npm add -S @u-elements/u-progress
bash
pnpm add -S @u-elements/u-progress
bash
yarn add @u-elements/u-progress
bash
bun add -S @u-elements/u-progress
html
<script type="module" src="https://unpkg.com/@u-elements/u-progress@latest/dist/u-progress.js"></script>

Attributes and props ​

<u-progress> ​

  • Attributes: all global HTML attributes such as id, class, data-
    • max can contain amount of total work to be done. Must be a valid number greater than 0. Defaults to 1.
    • value can contain amount of completed work. If present, value must be a valid number between 0 and max. If there is no value attribute, the progress bar is indeterminate; indicating work is being done, but withouth knowing of how long it will take.
  • DOM interface: HTMLProgressElement
    • HTMLProgressElement.max returns number reflecting max attribute
    • HTMLProgressElement.position returns number between 0 and 1 reflecting the calculation value / max
    • HTMLProgressElement.value returns number reflecting value value attribute
    • HTMLProgressElement.labels returns NodeListOf<HTMLLabelElement>

Using Preact?

Setting the value property to null does not remove the value attribute in Preact. Remove through a ref instead: ref={(el) => value === null && el?.removeAttribute("value")}

Styling ​

Styling the value bar ​

Currently, there are no standardized pseudo-element selectors specifically designated for <progress> elements (all existing selectors include a ::-vendor- prefix). Consequently, <u-progress> simply renders its value bar using a ::before pseudo-element:

Styling the indeterminate state ​

Replicating the native CSS pseudo-class :indeterminate is not possible. Instead, use the selector :not([value]), which is functionally identical, and compatible with both <u-progress> and native <progress>. Example:

css
.my-progress:not([value]) {
  /* Your indeterminate styling here */
}

Labeling ​

In most cases you should provide an accessible label when using <u-progress>. While you can use the standard ARIA labelling attributes aria-labelledby or aria-label, when using <u-progress> you can alternatively use the <label> element.

Server side rendering ​

You can server side render <u-progress> by using Declarative Shadow DOM. Styling and markup needed is exported as UHTMLProgressShadowRoot. Example:

TS
import { UHTMLProgressShadowRoot } from '@u-elements/u-progress';

const renderToStaticMarkup = (value?: number, max?: number) =>
 `<u-progress
   ${value ? ` value="${value}"` : ''}
   ${max ? ` max="${max}"` : ''}
 >
   ${UHTMLProgressShadowRoot}
 </u-progress>`

Hydration: Attributes are dynamically added in the browser, so the client DOM will differ from the server markup. Use suppressHydrationWarning in React, data-allow-mismatch="attribute" in Vue, or similar for your framework on these elements, to avoid hydration warnings.

Accessibility ​

Screen reader <progress> <u-progress>
VoiceOver (Mac) + Chrome ✅ ✅
VoiceOver (Mac) + Edge ✅ ✅
VoiceOver (Mac) + Firefox ✅ ✅
VoiceOver (Mac) + Safari ✅ ✅
VoiceOver (iOS) + Chrome ❌ Does not announce value ✅
VoiceOver (iOS) + Safari ❌ Announces value, but not related max ✅
Jaws (PC) + Chrome ❌ Announces value, but not related max ✅
Jaws (PC) + Edge ❌ Announces value, but not related max ✅
Jaws (PC) + Firefox ✅ ✅
NVDA (PC) + Chrome ❌ Announces value, but not related max ✅
NVDA (PC) + Edge ❌ Announces value, but not related max ✅
NVDA (PC) + Firefox ✅ ✅
Narrator (PC) + Chrome ✅ ✅
Narrator (PC) + Edge ✅ ✅
Narrator (PC) + Firefox ✅ ✅
TalkBack (Android) + Chrome ❌ Announces value, but not related max ✅
TalkBack (Android) + Firefox ❌ Announces value, but not related max ✅
TalkBack (Android) + Samsung Internet ❌ Announces value, but not related max ✅

Specifications ​

Changelog ​

  • 1.1.1: Not writing redundant aria-valuenow, aria-valuemin and aria-valuemax when iOS does not properly support role="progress"
  • 1.1.0: Framework compatibility:
    • Setting value or max properties to null or undefined removes the attribute, like frameworks expect for a native element (i.e. value={undefined} for indeterminate)
    • Better handling of aria-labelledby and aria-label written by the consumer
    • aria-labelledby takes precedence over aria-label, then <label>, like the accessible name computation
    • aria-labelledby is replaced by a reference to the element itself instead of removed, so removing it later falls back to aria-label or <label>
    • Label is re-synced after framework-rendering, as <label for> or aria-labelledby references may be available async (i.e. Angular and Qwik)
    • id is observed, so a <label for> matching an id written after connect is found
    • Framework typings for Qwik 1, Qwik 2 and Preact 11 uses HTMLElementTagNameMap
  • 1.0.1: Use attachInternals when supported by the current browser and speed up attribute assignment
  • 1.0.0: Plays nice with strict CSP, and renders attributes in consistent order
  • 0.0.7: Uses role="progress" also in Firefox who now has sufficient support
  • 0.0.6: Enable declarative shadow root support and export UHTMLProgressShadowRoot for easier server side rendering

Released under the MIT License