--- title: Toolbar description: Toolbars group formatting or view controls that affect a shared context, such as rich text editors, chart controls, or data tables. category: Actions --- import { meta as ToolbarMeta, Default } from '@/content/stories/toolbar'; import Canvas from '@/components/component-docs/canvas'; import Demo from '@/components/component-docs/demo'; export const config = { dependencies: { components: [Canvas, Demo], scripts: [ '../../components/component-docs/demo.script.tsx', '../../components/component-docs/canvas.script.tsx', '../../components/component-docs/controls.script.tsx', ], }, }; # Toolbar

Toolbars group formatting or view controls that affect a shared context, such as rich text editors, chart controls, or data tables.

## Try it ## Usage Place toggle `RuiButton` children inside `RuiToolbar`. Use `RuiSeparator` to divide related control categories. Enable `exclusiveToggles` when only one toggle can be active. ```tsx import { RuiToolbar } from '@ecopages/radiant-ui/toolbar'; import { RuiButton } from '@ecopages/radiant-ui/button'; import { RuiSeparator } from '@ecopages/radiant-ui/separator'; ``` ## Custom markup `` coordinates any light-DOM tree with focusable controls. The `RuiToolbar` helper stamps `[role="toolbar"]`; it is not required. ```tsx import '@ecopages/radiant-ui/toolbar';
; ``` Toggle buttons need `data-toggle` and `aria-pressed`. The host updates `aria-pressed` on click. ## Exclusive toggles

Use `exclusiveToggles` for view modes where only one option (list vs grid) can be active at a time.

## Theming Toolbar surfaces map to **semantic surface roles**, never Tailwind palette steps: | Part | CSS roles | | --- | --- | | Bar (`rui-toolbar`) | `surface`, `border`, `rounded-container` | | Pressed toggle (`rui-button--ghost` / `--outline[aria-pressed='true']`) | `background`, `on-background`, `shadow-sm` | Geometry shares control tokens (`--size-control-*`, `--space-control-*`) with `RuiButton`. Override roles at the theme layer, not in component CSS. ## API `RuiToolbar` is a custom element (``) rendering a `role="toolbar"` container. Place `RuiButton` toggles or other focusable controls as children. ### Attributes | Attribute | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `''` | Accessible name for the toolbar region. | | `exclusive-toggles` | `boolean` | `false` | Only one toggle button stays pressed at a time. | ### Light-DOM contract | Target | Required | Host writes | Author owns | | --- | --- | --- | --- | | `button`, `a[href]`, `input`, `select`, `textarea`, `[tabindex]` | yes | roving `tabIndex` | control markup, `disabled` | | `button[data-toggle][aria-pressed]` | for toggles | `aria-pressed` | initial `aria-pressed`, `data-toggle` | Nested hosts: none. ### View helpers | Component | Target stamped | Notes | | --- | --- | --- | | `RuiToolbar` | `[role="toolbar"]` with `data-ref="root"` | Place focusable controls as children. | ### CSS classes Public BEM classes on the composed surface (documented via `@cssclass`): | Class | Description | | --- | --- | | `.rui-toolbar` | Toolbar surface (`role="toolbar"`). | Pressed-toggle styling applies to `rui-button--ghost` / `rui-button--outline` inside the toolbar (see Button). Toggles expose their state via `aria-pressed`. `RuiSeparator` is a generic helper. In a toolbar, use `orientation="vertical"` to divide categories without adding a focus stop. ### Theme roles | Part | CSS variables consumed | | --- | --- | | Bar | `--surface`, `--border` | | Pressed toggle | `--background`, `--on-background`, `--shadow-sm` | | Focus ring | `--focus-ring` | ## Accessibility - Toolbar renders with `role="toolbar"` and an accessible name from `label`. - Toggle buttons expose `aria-pressed` for their on/off state. - Arrow keys move between toolbar controls when focus is inside the toolbar; separators are skipped.