Toolbar
A strip of controls that costs one tab stop, not twelve.
Installation
npx shadcn@latest add https://qure-ui.qure.ai/r/toolbar.jsonUsage
import {
Toolbar, ToolbarButton, ToolbarGroup, ToolbarSeparator,
} from '@/components/ui/toolbar'<Toolbar aria-label="Image tools">
<ToolbarButton aria-label="Pan" aria-pressed><MoveIcon /></ToolbarButton>
<ToolbarSeparator />
<ToolbarButton aria-label="Zoom"><ZoomInIcon /></ToolbarButton>
</Toolbar>The point of a toolbar is the roving tab stop. Twelve buttons in a row are twelve presses of Tab
to get past; inside a Toolbar they are one, and the arrow keys move between them. A radiologist
tabbing from the study list to the report should not travel through the window-level control on
the way.
Give it an aria-label. role="toolbar" with no name announces as "toolbar" and nothing else,
which is worse than no role at all.
Composition
<Toolbar> {/* role="toolbar", owns the roving focus */}
<ToolbarGroup> {/* a labelled cluster — related tools */}
<ToolbarButton> {/* one control; aria-pressed for a sticky tool */}
</ToolbarGroup>
<ToolbarSeparator /> {/* orientation is the toolbar's, flipped */}
<ToolbarLink> {/* an <a>, kept in the same roving ring */}
<ToolbarInput> {/* an <input>; arrows inside it move the caret */}
</Toolbar>ToolbarGroup is not decoration — give it an aria-label and the tools inside it are announced
as a set, which is how a screen-reader user learns that four buttons are four choices of one
thing rather than four separate actions.
Imaging tools
A sticky tool is aria-pressed, and the tinted look is styled off that attribute rather than a
class — the state a screen reader announces and the state an eye sees cannot drift apart.
Vertical
orientation="vertical" down the side of a viewport. Up and Down move between items, and the
separator flips to horizontal on its own. The disabled item stays focusable — Base UI's
focusableWhenDisabled defaults to true, which is right: a tool that is off for this series
is information, and skipping it silently hides that.
Floating over an image
variant="floating" swaps the border for a shadow, for a toolbar that sits over the image
rather than beside it.
With our Button
For a toolbar of labelled actions rather than icon tools, hand the rendering to Button:
<ToolbarButton variant="bare" render={<Button variant="tertiary" size="sm" />}>
Print
</ToolbarButton>variant="bare" drops the toolbar's own look so Button's wins; the roving focus stays with the
toolbar. Do not nest a Button inside a ToolbarButton — that is two buttons, and the inner
one is not in the roving ring.
ToolbarButton maps size to 32 and 40, the same numbers as Button's icon-sm and icon-md
— so a toolbar of icons lines up with a row of buttons beneath it without either being nudged.
API Reference
Prop
Type
On ToolbarButton:
Prop
Type
The rest is Base UI's Toolbar.