Components
Progress
See also: Spinner.
Indeterminate
<div class="ui-progress"> <progress aria-busy="true"></progress></div>Determinate
<div class="ui-progress"> <progress value="10" max="100"></progress></div>Variants
Use the modifier classes .ui-filled, .ui-default, or
.ui-tonal on the wrapper <div> to swap the
progress bar track surface for better contrast on different backgrounds.
<div class="ui-progress ui-default"> <progress value="25" max="100"></progress></div><div class="ui-progress ui-filled"> <progress value="50" max="100"></progress></div><div class="ui-progress ui-tonal"> <progress value="75" max="100"></progress></div>Accessibility
If the <progress> element is describing the loading progress
of a section of a page:
- use
aria-describedbyto point to the status -
set
aria-busy="true"on the section that is being updated, removing it when loading is finished.
Source: MDN
Content here is loading.
<div aria-busy="true" aria-describedby="progress-bar"> Content here is loading.</div><div class="ui-progress"> <progress aria-label="Content loading…" id="progress-bar"></progress></div>API
| Type | Modifiers | Default | Description |
|---|---|---|---|
| Progress | <div
class="ui-progress"><progress>…</progress></div> | - | Wrapper div with the native HTML progress element inside. |
| Indeterminate | No value attribute | - | Display an indeterminate loading state. |
| Variant | .ui-filled, .ui-default, .ui-tonal | - | Modifiers on the wrapper div for different background surfaces. |
Installation
@layer components.root { :where(.ui-progress) { --_accent-color: var(--primary); --_bg-color: var(--surface-tonal);
background-color: var(--_bg-color); block-size: var(--size-1); border-radius: var(--border-radius, 0.25rem); display: inline-block; inline-size: 100%; overflow: hidden; position: relative; vertical-align: baseline;
/* Color */ &.ui-filled { --_bg-color: var(--surface-filled); }
&.ui-default { --_bg-color: var(--surface-default); }
&.ui-tonal { --_bg-color: var(--surface-tonal); }
& > progress { appearance: none; background: none; block-size: 100%; border: 0; display: block; inline-size: 100%;
&::-webkit-progress-bar { background: none; border-radius: var(--border-radius, 0.25rem); }
&[value]::-webkit-progress-value { background-color: var(--_accent-color);
@media (prefers-reduced-motion: no-preference) { transition: inline-size 0.2s var(--ease-out-4, cubic-bezier(0, 0, 0.1, 1)); } }
&::-moz-progress-bar { background-color: var(--_accent-color); } } }
@media (prefers-reduced-motion: no-preference) { :where(.ui-progress):has(> progress:indeterminate) { &::after { animation: indeterminate 2s linear infinite; background-color: var(--_accent-color); content: ""; inset: 0 auto 0 0; position: absolute; will-change: inset-inline-start, inset-inline-end; }
& > progress { &::-webkit-progress-value { background-color: transparent; }
&::-moz-progress-bar { background-color: transparent; } } }
[dir="rtl"] { :where(.ui-progress):has(> progress:indeterminate) { &::after { animation-direction: reverse; } } } }
@keyframes indeterminate { 0% { inset-inline-start: -200%; inset-inline-end: 100%; }
60% { inset-inline-start: 107%; inset-inline-end: -8%; }
100% { inset-inline-start: 107%; inset-inline-end: -8%; } }}