Dots
Three small dots that pulse in sequence — a quieter, more inline-friendly
loading indicator than Spinner. CSS-only.
When to use
- Inline next to a label that's loading: "Sending…".
- Inside a button when the action is async but the chrome shouldn't
imply a heavy operation (use
Spinnerfor that). - As a placeholder for a count or status that will resolve shortly.
Variants
| Variant | Class | Use for |
|---|---|---|
| Default | .dots |
Ink-regular dots, default size |
| Accent | .dots.-accent |
Accent-colored, for emphasis |
| Subtle | .dots.-subtle |
De-emphasized, for muted contexts |
| Inverse | .dots.-inverse |
On dark / accent surfaces |
| Large | .dots.-l |
Larger dots and gap |
Anatomy
<span class="dots" role="status" aria-label="Loading">
<span class="dots__item"></span>
<span class="dots__item"></span>
<span class="dots__item"></span>
</span>
<!-- Inside running text -->
<p>
Sending message
<span class="dots -subtle" role="status" aria-label="Loading">
<span class="dots__item"></span>
<span class="dots__item"></span>
<span class="dots__item"></span>
</span>
</p>
Tokens used
From semantic tier
--color-ink-regular— default dot color--color-ink-subtle— subtle variant--color-ink-inverse— inverse variant--color-accent— accent variant--space-inset-element-s— default size and gap--space-inset-element-m— large size and gap
Component-tier (defined inline)
--dots-size— overridable dot diameter--dots-gap— overridable gap between dots--dots-color— overridable color--dots-duration— overridable pulse cycle
Accessibility
Authors set role="status" and aria-label="Loading" on the outer
.dots element so assistive technology announces the loading state.
The three .dots__item children are presentational; they do not need
ARIA attributes. They render purely as visual rhythm.
The component honors prefers-reduced-motion: reduce by removing the
pulse animation and showing the dots at a constant opacity.
Edge cases
- Inline with text: the component uses
display: inline-flexandvertical-align: middle, so it sits on the text baseline cleanly. - Inside buttons: pair
.dots.-inversewith a primary button during async work. The button'saria-busy="true"carries the semantic meaning; the dots are decorative inside the button.
Do
- Use dots when the work is inline-scoped (a single label, a single cell in a table).
- Use spinner when the work is layout-scoped (a whole section, a modal body).
Don't
- Don't combine dots with a spinner in the same loading context — pick one indicator vocabulary per surface.
- Don't author more or fewer than three items. The component is a fixed-shape primitive.