Toolbar

Buttons and toggles with one tab stop.

Example

Installation

pnpm dlx shadcn@latest add superzero11/uiset/toolbar

It lands in components/ui/toolbar.tsx, with UISet’s root (uiset). Wrap your app in <UISet> once, and every component takes its skin.

What UISet adds

The component is UI SDK’s own, with its parts, props and names, so everything below works as UI SDK documents it. UISet adds the look and checks it:

  • Styles in five skins, each in light and dark, from CSS variables you can override.
  • Text at 4.5:1 contrast and control edges and focus rings at 3:1, in every skin and theme, checked with axe in tests.
  • Haptic cues on phones, and sound cues when you set sound on <UISet>.
  • Examples that name every control, so agents can find it (see Agents below).

The API reference and accessibility notes below are UI SDK’s, from its docs for @ui-sdk/react (Apache-2.0). Toolbar on ui-sdk.dev has more usage examples. Your coding agent can read the same page at node_modules/@ui-sdk/react/docs/components/toolbar.md.

API Reference

Toolbar

  • orientation? (ToolbarOrientation): Which arrow keys move between items: left and right, or up and down.
  • loop? (boolean): Whether arrow keys wrap from the last item to the first.
  • disabled? (boolean): Stops every item from being used. Items stay focusable with aria-disabled, so the toolbar keeps its tab stop and stays discoverable.
  • controller? ({ focus: () => void; }): Focuses the toolbar from anywhere. Create one with createToolbarController().
  • debug? (boolean): Logs every state change with its reason to the console, in development builds.

ToolbarButton

  • disabled? (boolean): Stops the item from being used. It stays focusable with aria-disabled, so arrow keys still reach it and a tooltip can say why.
  • focusableWhenDisabled? (boolean): Whether a disabled item stays reachable with arrow keys. Turn it off to skip it, natively disabled.

ToolbarInput

  • disabled? (boolean): Stops the item from being used. It stays focusable with aria-disabled, so arrow keys still reach it and a tooltip can say why.
  • focusableWhenDisabled? (boolean): Whether a disabled item stays reachable with arrow keys. Turn it off to skip it, natively disabled.

ToolbarGroup

  • disabled? (boolean): Disables every item in the group. They stay focusable with aria-disabled.

ToolbarLink and ToolbarSeparator take no props of their own.

State

What render functions and state-based class names receive on ToolbarButton and ToolbarInput:

  • disabled (boolean): Whether the item is disabled, by itself, its group or the toolbar.
  • orientation (ToolbarOrientation): The toolbar's orientation option: "vertical", or "horizontal" for anything else.

On ToolbarLink:

  • disabled (boolean): Whether the link is disabled, by its group or the toolbar.
  • orientation (ToolbarOrientation): The toolbar's orientation option: "vertical", or "horizontal" for anything else.

On ToolbarSeparator:

  • orientation (ToolbarOrientation): Which way the line runs: across the toolbar, so vertical in a horizontal toolbar.

Controller

  • focus() (() => void): Moves focus to the item that holds the toolbar's tab stop: the one focused last, or the first.

Accessibility

The toolbar follows the WAI-ARIA toolbar pattern. The root has the toolbar role and aria-orientation, and separators have the separator role.

Keyboard

The toolbar has one tab stop, a roving focus. Tab enters on the first enabled item, then returns to the item focused last. The next Tab leaves the toolbar.

KeyAction
ArrowRight / ArrowLeftMove focus in a horizontal toolbar
ArrowDown / ArrowUpMove focus in a vertical toolbar
Home / EndFocus the first or last item, except in a field
Enter / SpaceActivate a button or toggle, or open its popup

Controls keep the keys they use: Down opens a menu, and Up and Down step a number input. Moving focus never presses a toggle. In right-to-left text, Left and Right swap.

Tested Behavior

Every line below is an automated test.

  • The toolbar is named and has a horizontal orientation.
  • Tab lands on the first item, and Tab again leaves the toolbar.
  • Shift+Tab from after the toolbar lands on its tab stop.
  • ArrowRight moves through every item, into toggle groups and out of fields.
  • ArrowRight on the last item wraps to the first, and ArrowLeft back.
  • Arrow keys never press a toggle, and Space and Enter do.
  • Tab comes back to the item focused last.
  • A toggle group in the toolbar is no tab stop of its own.
  • A disabled button stays reachable, says it's disabled and does nothing.
  • A button runs its action when pressed.
  • A menu trigger rendered through a button opens with ArrowDown, and arrows still move past it.
  • Choosing from a menu in the toolbar returns focus to its trigger.
  • A text field keeps Home and End for its caret.
  • Up and Down step a number field in the toolbar.
  • Separators run across the toolbar.
  • A group of buttons is named.
  • A link in the toolbar follows its href with Enter.
  • With loop off, arrows stop at the ends.
  • A disabled toolbar keeps one tab stop, and every item says it's disabled.

Vertical

  • ArrowDown and ArrowUp move between items, and Left and Right don't.
  • Separators run across a vertical toolbar.

Disabled groups

  • Toggles and toggle groups in a disabled group are disabled.
  • A link in a disabled group is disabled, and follows its href once enabled.
  • Enabling a group enables the toggles and buttons in it.

Runs in React, Vue and web components.

Agents

UI SDK’s agent bridge finds this component by its accessible name: give it a visible label or an aria-label, and an agent can read its state and call its actions without screenshots. Building with AI agents shows how to connect one.

This is agent.snapshot() for the example above, read live from this page. Change something in the example, then read it again: the values follow.

agent.snapshot()
[
  {
    "ref": "c8",
    "component": "toolbar",
    "actions": [
      "press"
    ],
    "label": "Formatting",
    "buttons": [
      "Undo edit",
      "Redo edit",
      "Insert link"
    ]
  }
]