Toolbar
Container for grouping a set of controls — buttons, toggles, links, inputs, separators. Wraps `@base-ui/react/toolbar` for keyboard focus management. Four variants, three sizes, five rounded values, horizontal (with overflow wrap) + vertical orientation, LTR/RTL.
Playground
Installation
pnpm add @tessinaui/uiUsage
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarLink,
ToolbarInput,
ToolbarSeparator,
ToolbarSpacer,
} from "@tessinaui/ui";{/* Text editor toolbar — multi-select bold/italic, single-select align */}
function FormatToolbar() {
const [bold, setBold] = useState(true);
const [italic, setItalic] = useState(false);
const [align, setAlign] = useState("left");
return (
<Toolbar aria-label="Format text">
<ToolbarButton iconOnly leadingIcon={<Undo2 />} aria-label="Undo" />
<ToolbarSeparator />
<ToolbarButton iconOnly leadingIcon={<Bold />} aria-label="Bold"
aria-pressed={bold} pressed={bold} onClick={() => setBold((v) => !v)} />
<ToolbarButton iconOnly leadingIcon={<Italic />} aria-label="Italic"
aria-pressed={italic} pressed={italic} onClick={() => setItalic((v) => !v)} />
<ToolbarSeparator />
<ToolbarButton iconOnly leadingIcon={<AlignLeft />} aria-label="Align left"
aria-pressed={align === "left"} pressed={align === "left"} onClick={() => setAlign("left")} />
<ToolbarButton iconOnly leadingIcon={<AlignCenter />} aria-label="Align center"
aria-pressed={align === "center"} pressed={align === "center"} onClick={() => setAlign("center")} />
<ToolbarSpacer />
<ToolbarLink href="/help">Help</ToolbarLink>
</Toolbar>
);
}
{/* Data table action bar */}
<Toolbar variant="outline" aria-label="Table actions">
<ToolbarInput placeholder="Search…" aria-label="Search" />
<ToolbarSeparator />
<ToolbarButton leadingIcon={<Filter />} trailingIcon={<ChevronDown />}>Filter</ToolbarButton>
<ToolbarButton leadingIcon={<ArrowDownUp />} trailingIcon={<ChevronDown />}>Sort</ToolbarButton>
<ToolbarSpacer />
<ToolbarButton leadingIcon={<Plus />}>Add row</ToolbarButton>
</Toolbar>
{/* Vertical media player */}
<Toolbar variant="solid" rounded="full" orientation="vertical">
<ToolbarButton iconOnly leadingIcon={<SkipBack />} aria-label="Previous" />
<ToolbarButton iconOnly leadingIcon={<Play />} aria-label="Play" />
<ToolbarButton iconOnly leadingIcon={<SkipForward />} aria-label="Next" />
</Toolbar>Showcase
Composition
The toolbar is a focus-traversal container — arrow keys move between items, Tab moves to the next focusable region. Compose any focusable primitive inside.
For toggle-style behaviour (bold / italic / underline, alignment groups, pin / favorite), use ToolbarButton with the pressed prop and own the state externally. The pressed appearance uses a tone-aware overlay that reads on every toolbar surface (foreground tint on soft / outline / ghost, background tint on solid). Set aria-pressed for accessibility.
const [bold, setBold] = useState(false);
<ToolbarButton
iconOnly
leadingIcon={<Bold />}
aria-label="Bold"
aria-pressed={bold}
pressed={bold}
onClick={() => setBold((v) => !v)}
/>API Reference
Toolbar (root)
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" | Layout direction. Horizontal wraps items to the next row when there isn't enough width. |
variant | "solid" | "soft" | "outline" | "ghost" | "soft" | Surface visual style |
size | "sm" | "md" | "lg" | "md" | Affects padding + item heights. Padding is equal on all sides. |
rounded | "none" | "sm" | "md" | "lg" | "full" | "md" | Container border radius |
loopFocus | boolean | true | Wrap arrow-key focus at the ends |
disabled | boolean | false | Disable all items in the toolbar |
dir | "ltr" | "rtl" | inherited | Text direction |
aria-label | string | — | Recommended; describes the toolbar's purpose |
ToolbarButton
| Prop | Type | Default | Description |
|---|---|---|---|
iconOnly | boolean | false | Square aspect, no horizontal padding |
leadingIcon | ReactNode | — | Icon at the start of the button |
trailingIcon | ReactNode | — | Icon at the end of the button |
pressed | boolean | — | Render in pressed state. Use this for any toggle behaviour — multi-select (bold / italic) or single-select (alignment). Combine with aria-pressed. |
disabled | boolean | false | Disable this single item |
focusableWhenDisabled | boolean | true | Whether the item stays focusable when disabled (Base UI default) |
aria-label | string | — | Required for iconOnly buttons |
ToolbarLink
Same shape as ToolbarButton but renders an <a> element. Pass href and standard anchor attributes.
ToolbarInput
Text input integrated with toolbar focus traversal — arrow keys still navigate the toolbar even when this input is focused.
ToolbarSeparator
Auto-orientation-aware divider:
- Horizontal toolbar → renders as a vertical line (
w-px) - Vertical toolbar → renders as a horizontal line (
h-px) - Color adapts to surface (
bg-current/30on solid,bg-borderotherwise)
ToolbarGroup
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "none" | "contained" | "none" | none = ARIA group only; contained = subtle background tint to visually cluster items |
disabled | boolean | false | Disable all items in the group |
aria-label | string | — | Recommended for any non-trivial group |
ToolbarSpacer
Inserts a flex-1 filler to push following items to the trailing edge — handy for left-aligned tools and right-aligned actions.
Notes
- Variants are neutral —
solid(foreground surface),soft(secondary surface, default),outline(border on background),ghost(transparent). Items use a tone-aware hover overlay (foreground/8on light surfaces,background/15on solid) so hover state is always visible. - Pressed state on
ToolbarButtonworks for any toggle pattern. For a multi-select group (bold / italic / underline), drive each item'spressedfrom its own boolean. For a single-select group (alignment), comparepressed={align === "left"}and update on click. Always pair witharia-pressed. - Padding — equal on all sides (
p-1.5/p-2/p-2.5for sm / md / lg) and matched to the gap between items, so the first/last item has the same breathing room from the toolbar edges as it does from its neighbours. - Horizontal overflow — items wrap to the next row when the toolbar runs out of width (
flex-wrap). Vertical orientation always stacks each item on its own row. - Disabled items stay focusable by default (
focusableWhenDisabled=true) so users can still tab to them and learn what's disabled — this is the Base UI default and matches WAI-ARIA toolbar guidance. - RTL — the toolbar mirrors automatically via
dir="rtl". Arrow-key direction follows the writing direction (right-to-left arrow goes to the next item in RTL). - Accessibility — built on
@base-ui/react/toolbar. Rendersrole="toolbar"witharia-orientation. Always passaria-labelto describe the toolbar's purpose.
ToggleGroup
A pill container of toggle buttons that act like a radio group (single select) or a multi-select toolbar, built on Base UI's ToggleGroup primitive.
Navigation Menu
A full top-bar navigation pattern — logo, centre menu with mega-menu popups, and right-aligned action slots for language selector and auth CTAs. Ships two desktop trigger treatments (`pill`, `underline`), a `mode="mobile"` variant with a right-side drawer + submenu drill-in, richer mega-menu rows (tag + badge), and a theme-aware logo that swaps between light and dark sources. Built on @base-ui/react with 5 sizes, 5 rounded options, and LTR/RTL support.