Toolbar

.toolbar[role="toolbar"][data-ui="toolbar"]

Toolbar — a grouped row of controls acting on the current view: a text editor's formatting bar, a table's filter row, the actions along the top of a card.

Examples

1 — a toolbar, with the behaviour

View “1 — a toolbar, with the behaviour” as
<div class="toolbar" role="toolbar" aria-label="Text formatting" data-ui="toolbar">
  <div class="toolbar-group" role="group" aria-label="Style">
    <button class="btn btn-quiet btn-icon" type="button" aria-pressed="true" aria-label="Bold">
      <svg class="icon" aria-hidden="true"><use href="#icon-bold"></use></svg>
    </button>
    <button class="btn btn-quiet btn-icon" type="button" aria-pressed="false" aria-label="Italic">
      <svg class="icon" aria-hidden="true"><use href="#icon-italic"></use></svg>
    </button>
    <button class="btn btn-quiet btn-icon" type="button" aria-pressed="false" aria-label="Underline">
      <svg class="icon" aria-hidden="true"><use href="#icon-underline"></use></svg>
    </button>
  </div>

  <hr class="divider divider-vertical" aria-orientation="vertical" />

  <input class="input" type="search" aria-label="Find in document" placeholder="Find" />

  <span class="toolbar-spacer"></span>

  <button class="btn btn-quiet" type="button">Clear</button>
</div>

role="toolbar" promises one tab stop and arrow keys, so it ships with data-ui="toolbar" or not at all. The trap is the text field: arrow keys inside it must move the caret, not the toolbar.

2 — a row of buttons, without it

View “2 — a row of buttons, without it” as

<div class="toolbar">
  <button class="btn btn-quiet" type="button">Duplicate</button>
  <button class="btn btn-quiet" type="button">Archive</button>
  <hr class="divider divider-vertical" aria-orientation="vertical" />
  <button class="btn btn-quiet" type="button">Delete</button>
</div>

For three or four buttons this is usually the better choice.

role="toolbar" earns its keep when the bar is long enough that tabbing through it is a burden — below that it trades a familiar Tab for an arrow-key model the user has to discover.

Tokens 11

Level 2, declared on .toolbar itself. Set any of them on that selector to restyle this component without touching the skin.

Level 2 tokens declared by toolbar
TokenDefault
--toolbar-group-gapvar(--space-1)
--toolbar-separator-colorvar(--color-border)
--toolbar-separator-spacingvar(--space-1)
--toolbar-bgvar(--color-surface)
--toolbar-border-colorvar(--color-border-subtle)
--toolbar-border-widthvar(--border-width)
--toolbar-colorvar(--color-on-surface)
--toolbar-gapvar(--space-2)
--toolbar-padding-blockvar(--space-2)
--toolbar-padding-inlinevar(--space-2)
--toolbar-radiusvar(--radius-box)

Variants and states

Variants

  • .toolbar-plain

Inside it

  • .toolbar-group
  • .toolbar-scroll
  • .input
  • .select
  • .toolbar-spacer
  • .toolbar-vertical
  • .divider
  • .divider-vertical

State it reads

None.

Before you ship it

This one needs a script. Opt the markup in with data-ui="toolbar" and import import 'mostlycss/js/components/toolbar' — that specifier and not the package root, which is a bundle carrying a second copy of the registry, so a page holding both initialises every element twice. What you lose without the script is listed below — for some components that is only polish, for others it is the whole interaction.

What you have to do 1 requirements

  1. Pick one of the two markups above and copy all of it. Then: Name the bar in markup 1. aria-label on the role="toolbar" — "Text formatting", "Row actions". A toolbar with no name announces as "toolbar" and nothing else, which is useless on a page with two of them. Name every icon-only button, on the button and never on the <svg>. A toolbar is where icon-only controls cluster, so this is where the omission costs most: a bar of nine unnamed buttons is nine identical announcements. A toggle button carries aria-pressed, and nothing else. button.css already styles the pressed state from that attribute, so there is no class to add and no state to mirror. aria-pressed="false" must be present when the button is off — omitting it leaves a plain button rather than an unpressed toggle. Use aria-checked only inside a role="radiogroup", and .segmented is the component for that.

src/css/components/toolbar.css · npx mostlycss add toolbar