# Toolbar

> A row of buttons, toggles, links, and menus that takes one Tab stop

Status: beta. Added in Components 2.16.0.

A row of buttons, toggles, links, and menus that takes one Tab stop.

## Import

```tsx
import { Toolbar } from '@oztix/roadie-components/toolbar'
```

## Examples

### Default

Each part renders a Roadie component you already know. `Toolbar.Button` is a `Button` at `emphasis='subtler'`, and the toolbar sits in a normal surface, a pill that hugs its items, so a row of buttons reads as one control.

```tsx
<Toolbar aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
  <Toolbar.Separator />
  <Toolbar.Button>Save draft</Toolbar.Button>
</Toolbar>
```

### Sizes

`size` sets the buttons and links the toolbar renders, using the `Button` sizes.

```tsx
{/* Small */}
<Toolbar size='sm' aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
{/* Medium */}
<Toolbar size='md' aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
{/* Large */}
<Toolbar size='lg' aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
```

### Emphasis

`emphasis` sets the surface behind the toolbar: `normal` by default, `subtle`, `raised` or `floating` for a toolbar over other content, and `subtler` for none, when the toolbar sits inside another surface such as a `Card`. The buttons stay at `subtler` on every surface.

```tsx
{/* Normal */}
<Toolbar aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
{/* Subtle */}
<Toolbar emphasis='subtle' aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
{/* Raised */}
<Toolbar emphasis='raised' aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
{/* Floating */}
<Toolbar emphasis='floating' aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
{/* Subtler */}
<Toolbar emphasis='subtler' aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
```

### States

A disabled `Toolbar.Button` stays in the arrow order, so a keyboard user still finds it. `disabled` on `Toolbar.Group`, `Toolbar.ToggleGroup`, or the toolbar disables every item inside it, and they stay in the arrow order too. A `Toolbar.Button` with an `href` takes its own `disabled`, which keeps it in the arrow order and stops the click, but a disabled group or toolbar leaves it active. Disable it too, or remove it, when it shouldn't be followed.

```tsx
{/* One button disabled */}
<Toolbar aria-label='History'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button disabled>Redo</Toolbar.Button>
</Toolbar>
{/* Disabled */}
<Toolbar aria-label='History' disabled>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
</Toolbar>
```

### Composition

#### Formatting toolbar

`Toolbar.Toggle` is a `Toggle` at the toolbar's size that turns a setting on and off. Toggles in a `Toolbar.ToggleGroup` share one value: give each a `value`, and one is pressed at a time, or several with `multiple`. A `Toolbar.Button` whose only child is an icon renders square, as an `IconButton`, so give it an `aria-label`. Pass `Toolbar.Button` to `Menu.Trigger` with `render` to open a menu.

```tsx
<Toolbar aria-label='Formatting'>
  <Toolbar.Group aria-label='Text style'>
    <Toolbar.Toggle aria-label='Bold'><TextBIcon weight='bold' className='size-4' /></Toolbar.Toggle>
    <Toolbar.Toggle aria-label='Italic'><TextItalicIcon weight='bold' className='size-4' /></Toolbar.Toggle>
  </Toolbar.Group>
  <Toolbar.Separator />
  <Toolbar.ToggleGroup aria-label='Alignment' defaultValue={['left']}>
    <Toolbar.Toggle value='left' aria-label='Align left'><TextAlignLeftIcon weight='bold' className='size-4' /></Toolbar.Toggle>
    <Toolbar.Toggle value='center' aria-label='Align centre'><TextAlignCenterIcon weight='bold' className='size-4' /></Toolbar.Toggle>
    <Toolbar.Toggle value='right' aria-label='Align right'><TextAlignRightIcon weight='bold' className='size-4' /></Toolbar.Toggle>
  </Toolbar.ToggleGroup>
  <Toolbar.Separator />
  <Menu>
    <Menu.Trigger render={<Toolbar.Button><PlusIcon weight='bold' className='size-4' />Insert<CaretDownIcon weight='bold' className='size-4' /></Toolbar.Button>} />
    <Menu.Content>
      <Menu.Item icon={<ImageIcon weight='bold' />}>Image</Menu.Item>
      <Menu.Item icon={<TableIcon weight='bold' />}>Table</Menu.Item>
      <Menu.Item icon={<LinkSimpleIcon weight='bold' />}>Link</Menu.Item>
    </Menu.Content>
  </Menu>
</Toolbar>
```

Every part takes the toolbar's `size`. Anything passed to `render` keeps its own props instead, so set `size` on it to match. With one value pressed at a time, a `Toolbar.ToggleGroup`'s fill slides from toggle to toggle, as in `ToggleGroup`; with `multiple`, each pressed toggle fills itself. Use it here rather than `ToggleGroup`, whose own track and smaller items wouldn't match the toolbar.

#### Links

Give `Toolbar.Button` an `href` and it renders a link, as `Button` does: an internal `href` routes through the Link given to `RoadieProvider`, and an external one opens in a new tab. It stays in the arrow order with the buttons.

```tsx
<Toolbar aria-label='Event'>
  <Toolbar.Button>Save draft</Toolbar.Button>
  <Toolbar.Button href='/events/feathered-anchor-sessions'>Preview</Toolbar.Button>
</Toolbar>
```

#### Vertical

With `orientation='vertical'` the items stack in a column as wide as the widest, and separators run across it.

```tsx
<Toolbar orientation='vertical' aria-label='Formatting'>
  <Toolbar.Toggle aria-label='Bold'><TextBIcon weight='bold' className='size-4' /></Toolbar.Toggle>
  <Toolbar.Toggle aria-label='Italic'><TextItalicIcon weight='bold' className='size-4' /></Toolbar.Toggle>
  <Toolbar.Toggle aria-label='Underline'><TextUnderlineIcon weight='bold' className='size-4' /></Toolbar.Toggle>
  <Toolbar.Separator />
  <Toolbar.Button aria-label='Undo'><ArrowCounterClockwiseIcon weight='bold' className='size-4' /></Toolbar.Button>
  <Toolbar.Button aria-label='Redo'><ArrowClockwiseIcon weight='bold' className='size-4' /></Toolbar.Button>
</Toolbar>
```

#### When space runs out

On a touch screen, or a screen narrower than `md`, the items stay on one row and scroll sideways, with the edges fading while there's more to see. Elsewhere they wrap onto new lines, and the pill becomes a rounded box. Arrow keys scroll the focused item into view either way.

```tsx
<Toolbar aria-label='Event'>
  <Toolbar.Button>Undo</Toolbar.Button>
  <Toolbar.Button>Redo</Toolbar.Button>
  <Toolbar.Separator />
  <Toolbar.Button>Duplicate</Toolbar.Button>
  <Toolbar.Button>Save draft</Toolbar.Button>
  <Toolbar.Button>Schedule</Toolbar.Button>
  <Toolbar.Button href='/events/feathered-anchor-sessions'>Preview</Toolbar.Button>
</Toolbar>
```

## Guidelines

- **Group related controls.** Put controls that work together, such as text styles, in a `Toolbar.Group` and separate the groups with `Toolbar.Separator`.
- **Drop the surface inside another one.** Inside a `Card`, a pane header, or a bar that already has a surface, use `emphasis='subtler'` so the toolbar doesn't draw a box within a box. Use `floating` for a toolbar over content, such as on an image.
- **Keep the toolbar short.** On a phone the row scrolls, so actions past the edge are out of sight until someone scrolls. Put the most used first, and rarely used actions in a `Menu`.
- **Size it with `className` or `style`.** Both go on the surface, the toolbar's outermost element. It's at most as wide as its container, so space it with the container's padding or `gap` rather than a margin, which pushes a full-width toolbar past the edge.
- **Let its container shrink.** On a phone the toolbar's row is as wide as all its items. A grid or flex item holding it, such as a form control in a `Field`, grows to fit unless it has `min-w-0`.
- **Use `Toolbar.Toggle` for an on and off state.** It reports whether it's pressed; a `Toolbar.Button` only runs an action. Put toggles that pick one of a set, such as alignment, in a `Toolbar.ToggleGroup`.
- **Wrap popup triggers, not the button.** Pass `Toolbar.Button` to a `Menu.Trigger`, `Popover.Trigger`, or `Tooltip.Trigger` with `render`, so it keeps the toolbar's look and arrow order.
- **Label every icon-only control,** and the toolbar itself.

## Accessibility

- Renders `role='toolbar'` with `aria-orientation`. Give it an `aria-label`, or point `aria-labelledby` at a visible heading.
- **Keyboard:** `Tab` moves into the toolbar and out again in one stop. Arrow keys move between items (`ArrowLeft` and `ArrowRight`, or `ArrowUp` and `ArrowDown` when vertical). Focus wraps from the last item to the first unless `loopFocus` is `false`.
- A disabled `Toolbar.Button` keeps focus, with `aria-disabled`, so a screen reader still announces it.
- `Toolbar.Separator` renders `role='separator'`, running across the toolbar.

## API reference

### Toolbar

```tsx
import { Toolbar } from '@oztix/roadie-components/toolbar'
```

- `disabled`: `boolean`.
- `rel`: `string`. Override the auto `rel='noopener noreferrer'` default on external hrefs.
- `orientation`: `"horizontal" | "vertical"`. Defaults to `horizontal`. The orientation of the toolbar.
- `loopFocus`: `boolean`. Defaults to `true`. If `true`, using keyboard navigation will wrap focus to the other end of the toolbar once the end is reached.
- `className`: `string`. Classes for the toolbar's surface, its outermost element.
- `size`: `"sm" | "md" | "lg"`. Defaults to `'md'`. The size of the buttons and links the toolbar renders. A `render` element keeps its own size.
- `emphasis`: `"normal" | "subtle" | "subtler" | "raised" | "floating"`. Defaults to `'normal'`. The surface behind the toolbar. `subtler` has none, for a toolbar inside another surface. The buttons stay at `subtler` on every one.

### Toolbar.Button

- `disabled`: `boolean`. Defaults to `false`. When `true` the item is disabled.
- `focusableWhenDisabled`: `boolean`. Defaults to `true`. When `true` the item remains focusable when disabled.
- `nativeButton`: `boolean`. Defaults to `true`. Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).
- `rel`: `string`. Override the auto `rel='noopener noreferrer'` default on external hrefs.
- `className`: `string | ((state: ToolbarButtonState) => string)`. CSS class applied to the element, or a function that returns a class based on the component's state.
- `href`: `string`. Pass a URL to render the button as a routed anchor instead of a `<button>`. Internal hrefs route through the configured `RoadieLinkProvider` (or fall back to plain `<a>`); external hrefs (`http(s)://`, `//…`) render as `<a target='_blank' rel='noopener noreferrer'>`; `mailto:` / `tel:` / `sms:` render as plain `<a>`. Pair with `external`, `target`, or `rel` to override the defaults.
- `external`: `boolean`. Force external-link treatment regardless of `href` shape. Useful for first-party URLs that should still open in a new tab, or for an `https://` URL that should route internally through the provider.
- `target`: `string`. Override the auto `target='_blank'` default on external hrefs.
- `download`: `string | boolean`. Download the `href` instead of navigating, optionally as this filename.

### Toolbar.Group

- `disabled`: `boolean`. Defaults to `false`. When `true` all toolbar items in the group are disabled.
- `rel`: `string`. Override the auto `rel='noopener noreferrer'` default on external hrefs.
- `className`: `string | ((state: ToolbarGroupState) => string)`. CSS class applied to the element, or a function that returns a class based on the component's state.

### Toolbar.Separator

- `orientation`: `"horizontal" | "vertical"`. The orientation of the separator. Defaults to the opposite of the toolbar's orientation, so a horizontal toolbar renders vertical separators.
- `rel`: `string`. Override the auto `rel='noopener noreferrer'` default on external hrefs.
- `className`: `string | ((state: SeparatorState) => string)`. CSS class applied to the element, or a function that returns a class based on the component's state.

### Toolbar.Toggle

A `Toggle` in the toolbar's arrow order, at its size. In a `Toolbar.ToggleGroup`, give it a `value`.

- `className`: `string | ((state: ToggleState) => string)`. CSS class applied to the element, or a function that returns a class based on the component's state.
- `disabled`: `boolean`. Defaults to `false`. Whether the component should ignore user interaction.
- `value`: `string`. A unique string that identifies the toggle when used inside a toggle group.
- `rel`: `string`. Override the auto `rel='noopener noreferrer'` default on external hrefs.
- `nativeButton`: `boolean`. Defaults to `true`. Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).
- `emphasis`: `"normal" | "subtle" | "subtler"`. Defaults to `subtler`. Emphasis at rest. Pressed steps up: `normal` and `subtle` to `strong`, `subtler` to a soft fill with a strong icon or label.
- `pressed`: `boolean`. Whether the toggle button is currently pressed. This is the controlled counterpart of `defaultPressed`.
- `defaultPressed`: `boolean`. Defaults to `false`. Whether the toggle button is currently pressed. This is the uncontrolled counterpart of `pressed`.
- `onPressedChange`: `((pressed: boolean, eventDetails: { reason: "none"; event: Event; cancel: () => void; allowPropagation: () => void; isCanceled: boolean; isPropagationAllowed: boolean; trigger: Element; }) => void)`. Callback fired when the pressed state is changed.
- `intent`: `"neutral" | "brand" | "brand-secondary" | "brand-blue" | "brand-orange" | "brand-purple" | "accent" | "danger" | "success" | "warning" | "info"`. Sets the colour palette. Inherits from the surrounding intent when unset.

### Toolbar.ToggleGroup

Toolbar toggles that share one value. With one pressed at a time, its fill slides between them, as in `ToggleGroup`; with `multiple`, each fills itself.

- `className`: `string | ((state: ToggleGroupState) => string)`. CSS class applied to the element, or a function that returns a class based on the component's state.
- `defaultValue`: `readonly string[]`. The pressed state of the toggle group represented by an array of the values of all pressed toggle buttons. This is the uncontrolled counterpart of `value`.
- `disabled`: `boolean`. Defaults to `false`. Whether the toggle group should ignore user interaction.
- `value`: `readonly string[]`. The pressed state of the toggle group represented by an array of the values of all pressed toggle buttons. This is the controlled counterpart of `defaultValue`.
- `rel`: `string`. Override the auto `rel='noopener noreferrer'` default on external hrefs.
- `onValueChange`: `((groupValue: string[], eventDetails: { reason: "none"; event: Event; cancel: () => void; allowPropagation: () => void; isCanceled: boolean; isPropagationAllowed: boolean; trigger: Element; }) => void)`. Callback fired when the pressed states of the toggle group changes.
- `multiple`: `boolean`. Defaults to `false`. When `false` only one item in the group can be pressed. If any item in the group becomes pressed, the others will become unpressed. When `true` multiple items can be pressed.

Docs updated 20 Oct 2026.
