M3 Expressive's indicator for a short wait. One element, CSS-only, no JavaScript plugin. The shape morphs through a circle and four cookie shapes while the whole thing rotates.
<span class="loading-indicator" role="status" aria-label="Loading"></span>
The element is empty, so it reports nothing on its own.
role="status" announces the wait and
aria-label gives it something to announce — not
role="progressbar", which promises an
aria-valuenow an indeterminate indicator does not have.
Contained
contained puts the indicator on a
secondary-container circle and switches it to
on-secondary-container. Use it over an image, a
coloured surface, or anything else the plain indicator would get
lost against.
<span class="loading-indicator contained" role="status" aria-label="Loading"></span>
Or a progress indicator?
This supersedes the indeterminate circular
progress indicator.
Reach for the loading indicator whenever you would have written
<span class="progress circular"> with no value.
.progress keeps everything else — both linear bars, and
every case where you know the percentage.
| Situation | Use |
|---|---|
| Under 200 ms | Nothing — show the result |
| Up to about 5 s, percentage unknown | .loading-indicator |
| Longer, percentage known | .progress, determinate |
| Longer, percentage unknown | .progress, indeterminate linear |
Tokens
Override any of these to retheme one indicator. The colours are role tokens, never raw hex.
| Token | Default | What it sets |
|---|---|---|
--md-comp-loading-indicator-active-indicator-size | 38px | The morphing shape. |
--md-comp-loading-indicator-container-size | 48px | The box it sits in — the visible circle when contained. |
--md-comp-loading-indicator-active-indicator-color | primary | The shape's fill. |
--md-comp-loading-indicator-container-color | transparent | The container fill. |
--md-comp-loading-indicator-duration | 3000ms | One full morph-and-spin cycle. |
<span class="loading-indicator"
style="--md-comp-loading-indicator-active-indicator-color: var(--md-sys-color-error)"
role="status" aria-label="Loading"></span>
prefers-reduced-motion: reduce stops both animations
and leaves the circle.