Skip to content

Components

Spinner

Add it to an element with aria-busy="true". Spinnners are always indeterminate.

html
<div aria-busy="true"></div>

Sizes

The spinner's size is set to 1em, which means it will adjust to its current font size.

h2

h4

Paragraph

SpanLink
html
<h2 aria-busy="true">h2</h2>
<h4 aria-busy="true">h4</h4>
<p aria-busy="true">Paragraph</p>
<span aria-busy="true">Span</span>

Buttons

Simply add aria-busy="true" to a <button>.

html
<!-- Text buttons -->
<button aria-busy="true" class="button">Text</button>
<button aria-busy="true" disabled class="button outlined">Outlined</button>
<button aria-busy="true" class="button filled">Filled</button>

<!-- Icon buttons -->
<button aria-busy="true" class="button">
  <span class="sr-only">Text</span>
</button>
<button aria-busy="true" disabled class="button outlined">
  <span class="sr-only">Outlined</span>
</button>
<button aria-busy="true" class="button filled">
  <span class="sr-only">Filled</span>
</button>

Linear progress

Check out the documentation for the progress bar.

When not to use aria-busy="true"

There are a few exceptions where aria-busy="true" won't render a spinner. Either because it doesn't make sense or because there are other reasons you would like to use that aria attribute.

1. Because it's blocked by another use case

In conjunction with the <progress> element aria-busy="true" is used on the section that is being updated. Rendering a spinner here would result in a spinner and a progress bar which doesn't make sense.

See progress accessibility section for more.

2. Because it doesn't make sense

  • <input>
  • <select>
  • <textarea>
  • <html>
  • <progress>

Installation

css
@layer components.base {
  [aria-busy="true"]:not(
      input,
      select,
      textarea,
      html,
      progress,
      [aria-describedby]
    ) {
    position: relative;

    &:before {
      animation: spin 0.7s linear infinite;
      border-color: transparent currentColor currentColor;
      border-radius: 50%;
      border-style: solid;
      border-width: 3px;
      content: "";
      display: inline-block;
      block-size: 1em;
      opacity: 0.5;
      vertical-align: -0.14em;
      inline-size: 1em;
    }

    &:not(button.button):not(:empty) {
      &:before {
        margin-inline-end: 0.5em;
      }
    }
  }

  @keyframes spin {
    to {
      transform: rotate(1turn);
    }
  }
}