Skip to content

Toolbar

The Toolbar component provides a canvas-style icon toolbar with support for orientations, active states, animated context switching, separators, fan-out sub-panels, and drag & drop onto surfaces, and headed drawers of labeled tiles.

Toolbar belongs to the Advanced React capability profile — a specialized, React-only surface with no Pixi dependency, despite its canvas-adjacent purpose.

Toolbar is not a default page action row. It is built for a canvas/tool-palette interaction — active tools, groups, slots, folders, and fan-out panels — not for an ordinary page’s list of commands. DataPage’s built-in action row renders ActionMenubar (from @cratis/components/Common), not Toolbar. Reach for ActionMenubar, or a product-owned action row, for flat page-level actions; reach for Toolbar only when the surface is genuinely a spatial tool palette. See Choosing a component: Actions and tool palettes.

Pass React icon nodes or product-owned SVGs for a dependency-free toolbar. Consumer-owned icon-font class strings remain accepted, but Components does not install an icon font or infer provider base classes; the product must load the matching stylesheet and pass the complete class string.

Import every Toolbar component from its subpath:

import { Toolbar, ToolbarButton } from '@cratis/components/Toolbar';
  • The root renders role='toolbar', aria-orientation, and an accessible name. The name falls back to the provider’s messages.toolbar.label, then Tools; pass aria-label or aria-labelledby to name each toolbar.
  • focusMode defaults to ToolbarFocusMode.Arrows: Up/Down move between tools in the default vertical orientation; a horizontal toolbar uses Left/Right (reversed in RTL). Home/End move to the first/last available tool in DOM order. Navigation skips disabled, hidden, and inert tools and stops at either end; it does not wrap. Each tool remains its own Tab stop, so Tab and Shift+Tab work as before.
  • Set focusMode={ToolbarFocusMode.SingleTabStop} for the WAI-ARIA toolbar pattern: the toolbar’s own tools (ToolbarButton, folder and fan-out triggers) share one Tab stop, which starts at the first available tool, follows the arrow keys, and returns to the last focused tool. When that tool is disabled, hidden, or removed, the Tab stop moves to the first available tool. A tool rendered outside the toolbar’s own markup, such as through a portal or inside a nested element with role="toolbar", keeps its own Tab stop. Tools in an open folder or fan-out panel belong to the same Tab stop, so you reach them with the arrow keys rather than Tab. Controls you place in the toolbar yourself, widgets such as inputs and sliders, a ToolbarButton with an explicit pt.root.tabIndex, and a folder or fan-out trigger with an explicit pt.trigger.tabIndex keep their own Tab stop; with a tab index of 0 or higher the arrow keys still reach them, while -1 removes the tool from both Tab and arrow-key navigation. Until the toolbar has chosen its active tool (for example, in server-rendered markup before hydration), every tool keeps its own Tab stop.
  • Set focusMode={ToolbarFocusMode.None} to keep native Tab behavior without toolbar key handling. Import ToolbarFocusMode from @cratis/components/Toolbar. SingleTabStop becomes the default in the next major release (#353).
  • Arrows and Home/End inside inputs, sliders, selects, and custom widgets retain their own behavior. Nested toolbars handle their own keys. Key handlers on a tool or pt.root can cancel navigation with preventDefault() or stopPropagation().
  • title is required on ToolbarButton and ToolbarFolder, and tooltip on ToolbarFanOutItem. That text becomes the button’s aria-label and its tooltip, which appears on hover and on keyboard focus.
  • Passing active sets aria-pressed to true or false as well as the visual state (data-active, data-selected); omitting active leaves it unset. See Active state.
  • Folder and fan-out triggers expose aria-expanded and aria-controls. Their panels close on Escape or a pointer press outside, return focus to their trigger after Escape, and are inert and aria-hidden while collapsed.
  • Toolbar, folder, fan-out, and context transitions are suppressed under prefers-reduced-motion: reduce.
ComponentDescription
ToolbarContainer that groups toolbar buttons into a pill-shaped bar
ToolbarButtonButton that renders non-empty text or otherwise an icon, with a hover tooltip
ToolbarSeparatorVisual divider that separates groups of buttons
ToolbarSectionSection within a toolbar that animates between named contexts
ToolbarContextNamed context (set of buttons) inside a ToolbarSection
ToolbarFanOutItemButton that slides out a horizontal sub-panel on click
ToolbarFolderButton that reveals a dynamically sized grid of buttons on click
ToolbarGroupLogical sub-group of toolbar items, rendered as its own pill with a gap between adjacent groups
ToolbarSlotProviderContext provider that enables the slot system — wrap the application root or a feature boundary
ToolbarSlotRenders nothing itself; injects its children into the named slot at the given order position
ToolbarLayoutNamed layout boundary that swaps default content for matching slot content

Deeply styled products should measure and select Toolbar structure through typed pt surfaces and data-cratis-part, not implementation class names.

ComponentTypept key → data-cratis-part
ToolbarToolbarPartsroot → root
ToolbarButtonToolbarButtonPartsroot → button; icon → icon; label → label
ToolbarGroupToolbarGroupPartsroot → toolbar-group; slot → toolbar-slot; incoming → toolbar-slot-incoming; outgoing → toolbar-slot-outgoing
ToolbarSeparatorToolbarSeparatorPartsroot → toolbar-separator
ToolbarLayoutToolbarLayoutPartsroot → toolbar-layout; slot → toolbar-slot; incoming → toolbar-slot-incoming; outgoing → toolbar-slot-outgoing
ToolbarSectionToolbarSectionPartsroot → toolbar-section; context → toolbar-context
ToolbarFolderToolbarFolderPartsroot → toolbar-folder; trigger → toolbar-folder-trigger; panel → toolbar-folder-panel; drawer: drawerHeader, drawerTitle, drawerClose, tile, tileIcon, tileLabel (see Headed drawer)
ToolbarFanOutItemToolbarFanOutPartsroot → fanout-root; trigger → fanout-trigger; panel → fanout-panel

The active tool, active context, and an open folder or fan-out also carry the canonical data-selected or data-open state. Contexts expose data-context-name and data-active; sections and slots expose data-transitioning. Folder and fan-out panels expose data-expanded and data-direction; fan-out also exposes data-settled, while folders expose data-mode. Collapsed/inactive panels and contexts are inert so hidden tools do not remain in keyboard navigation.