Tabs & Toolbars

Toolbar

Available

Groups a set of buttons so you Tab in once and navigate among them with arrow keys.

What Is a Toolbar?

A toolbar is a UI element that groups related action buttons together in a single row. Common examples include "Bold / Italic / Underline" in a text editor, a toolbox in an image editor, and playback controls in a media player.

Simply lining up buttons is easy, but the accessibility key is"making the entire toolbar a single group = one tab stop."

Why Does Accessibility Matter?

The mechanism that achieves this "one tab stop per group" behavior isroving tabindex (a technique that keeps only one item focusable at a time).

Live Demo (Recommended Implementation)

The toolbar below follows the APG specification. Try navigating it with the keyboard along with the input fields before and after it.

Accessible Toolbar

Try it: Tab into the toolbar (one press to enter) → ← → to move between buttons → Home / End to jump to edges → Enter / Space to toggle → Tab again to exit to the next element.

Tip

Buttons that toggle on/off (like Bold) should have aria-pressed. Screen readers will announce something like "Bold, toggle button, pressed," conveying the pressed state.

Keyboard Interaction

KeyActionPriority
TabEnter / leave the toolbar (the entire toolbar is a single tab stop)Required
/ Move focus to the previous / next buttonRequired
Home / EndMove to the first / last buttonRecommended
Enter / SpaceActivate / toggle the focused buttonRequired

Note

When you Tab out of a toolbar and then return, focus goes back to thelast focused button (because roving tabindex moves thetabindex="0" position). This is the behavior recommended by the APG.

Required WAI-ARIA Roles & Properties

TargetAttribute / RoleMeaning
Toolbar containerrole="toolbar"Conveys that it is a collection of action buttons.
Toolbar containeraria-label (or aria-labelledby)Provides a name for the toolbar, such as "Text Formatting".
Each buttontabindex="0 | -1"Only the focused button gets 0; all others get -1 (roving tabindex).
Toggle buttonaria-pressed="true | false"Conveys the on/off state of a toggle button.
Separatorrole="separator" + aria-orientationVisually and semantically separates groups of buttons (optional).

Recommended Pattern (Good)

Good / Recommended

Wrap with role="toolbar", and use roving tabindex so thatonly one button has tabindex="0". Arrow keys move focus between buttons.

Markup:

<div role="toolbar" aria-label="Text formatting">
  <!-- Only the first button has tabindex="0"; the rest are -1 (roving tabindex) -->
  <button type="button" aria-pressed="false" tabindex="0">Bold</button>
  <button type="button" aria-pressed="false" tabindex="-1">Italic</button>
  <button type="button" aria-pressed="false" tabindex="-1">Underline</button>
</div>

Roving tabindex and arrow key script:

document.querySelectorAll('[role="toolbar"]').forEach((bar) => {
  const items = Array.from(bar.querySelectorAll('button'));

  function focusItem(i) {
    items.forEach((b, n) => (b.tabIndex = n === i ? 0 : -1)); // Only one is 0
    items[i].focus();
  }

  items.forEach((btn, i) => {
    // Toggle buttons flip aria-pressed
    btn.addEventListener('click', () => {
      btn.setAttribute('aria-pressed',
        String(btn.getAttribute('aria-pressed') !== 'true'));
    });
    btn.addEventListener('keydown', (e) => {
      let next = null;
      if (e.key === 'ArrowRight') next = (i + 1) % items.length;
      else if (e.key === 'ArrowLeft') next = (i - 1 + items.length) % items.length;
      else if (e.key === 'Home') next = 0;
      else if (e.key === 'End') next = items.length - 1;
      if (next !== null) { e.preventDefault(); focusItem(next); }
    });
  });
});

Anti-Pattern (Bad)

The toolbar below has every button as a separate tab stop with no arrow key support. Furthermore, "Left align" is a <div> button that can't even receive focus.

Broken toolbar with separate tab stops + div buttons
Left align
Center

Try it: Each Tab press advances one button at a time (the more tools, the worse it gets), and ← → does nothing. 'Left align' is skipped by Tab and can't be activated with the keyboard.

<!-- ❌ Anti-pattern -->
<div class="toolbar">
  <!-- Every button is a separate tab stop. No arrow key navigation; users must press Tab repeatedly -->
  <button>Bold</button>
  <button>Italic</button>
  <button>Underline</button>
  <!-- A div button can't even receive focus -->
  <div class="btn" onclick="align('left')">Left align</div>
</div>

Bad / Avoid

Problems with this implementation:

  • One button = one tab stop — The more tools there are, the more Tab presses are needed. Not APG-compliant.
  • No arrow key navigation — Neither roving tabindex nor role="toolbar" is present.
  • Div buttons are not focusable — A div without tabindex or role cannot be reached by keyboard.
  • No group semantics — Without role="toolbar" / aria-label, it sounds like a random list of buttons.

Implementation Checklist


Source (English):Toolbar Pattern — W3C APG(opens in a new tab)