Toolbar
Action bars for grouping tools, controls, and contextual actions.
Playground
<Toolbar
aria-label="Formatting toolbar"
>
<Button variant="ghost" size="sm" class="font-bold">B</Button>
<Button variant="ghost" size="sm" class="italic">I</Button>
<Button variant="ghost" size="sm" class="underline">U</Button>
<Separator orientation="vertical" size="sm" />
<Button variant="ghost" size="sm">⇤</Button>
<Button variant="ghost" size="sm">≡</Button>
<Button variant="ghost" size="sm">⇥</Button>
<Separator orientation="vertical" size="sm" />
<Button variant="ghost" size="sm">🔗</Button>
<Button variant="ghost" size="sm">📷</Button>
</Toolbar>01 Examples
Rich Text Editor
Click the buttons above to toggle formatting. The text alignment and style update live.
<Toolbar aria-label="Text formatting" gap="xs" padding="sm">
<Button
variant={bold ? 'filled' : 'ghost'}
intent={bold ? 'primary' : 'neutral'}
size="sm"
class="min-w-8 justify-center"
aria-label="Bold"
aria-pressed={bold}
onclick={() => (bold = !bold)}><BoldIcon size={16} /></Button
>
<Button
variant={italic ? 'filled' : 'ghost'}
intent={italic ? 'primary' : 'neutral'}
size="sm"
class="min-w-8 justify-center"
aria-label="Italic"
aria-pressed={italic}
onclick={() => (italic = !italic)}><ItalicIcon size={16} /></Button
>
<Button
variant={underline ? 'filled' : 'ghost'}
intent={underline ? 'primary' : 'neutral'}
size="sm"
class="min-w-8 justify-center"
aria-label="Underline"
aria-pressed={underline}
onclick={() => (underline = !underline)}><UnderlineIcon size={16} /></Button
>
<Button variant="ghost" size="sm" class="min-w-8 justify-center" aria-label="Strikethrough"
><StrikethroughIcon size={16} /></Button
>
<Separator orientation="vertical" size="sm" />
<Button
variant={alignment === 'left' ? 'filled' : 'ghost'}
intent={alignment === 'left' ? 'primary' : 'neutral'}
size="sm"
class="min-w-8 justify-center"
aria-label="Align left"
aria-pressed={alignment === 'left'}
onclick={() => (alignment = 'left')}><AlignLeftIcon size={16} /></Button
>
<Button
variant={alignment === 'center' ? 'filled' : 'ghost'}
intent={alignment === 'center' ? 'primary' : 'neutral'}
size="sm"
class="min-w-8 justify-center"
aria-label="Align centre"
aria-pressed={alignment === 'center'}
onclick={() => (alignment = 'center')}><AlignCenterIcon size={16} /></Button
>
<Button
variant={alignment === 'right' ? 'filled' : 'ghost'}
intent={alignment === 'right' ? 'primary' : 'neutral'}
size="sm"
class="min-w-8 justify-center"
aria-label="Align right"
aria-pressed={alignment === 'right'}
onclick={() => (alignment = 'right')}><AlignRightIcon size={16} /></Button
>
<Separator orientation="vertical" size="sm" />
<Button variant="ghost" size="sm" class="min-w-8 justify-center" aria-label="Insert link"
><LinkIcon size={16} /></Button
>
<Button variant="ghost" size="sm" class="min-w-8 justify-center" aria-label="Insert image"
><ImageIcon size={16} /></Button
>
</Toolbar>
<div
class="bg-surface-base border-border-subtle rounded-contain w-full border p-4 text-sm transition-all"
style="text-align: {alignment};"
>
<p
class={[
'text-text-secondary',
bold && 'font-bold',
italic && 'italic',
underline && 'underline'
]}
>
Click the buttons above to toggle formatting. The text alignment and style update live.
</p>
</div>Vertical Tools Palette
Active tool: select
<Toolbar aria-label="Drawing tools" orientation="vertical" variant="elevated" gap="xs">
{#each [{ id: 'select', icon: MoveIcon, label: 'Select' }, { id: 'pen', icon: EditIcon, label: 'Pen' }, { id: 'brush', icon: HighlighterIcon, label: 'Brush' }, { id: 'undo', icon: UndoIcon, label: 'Undo' }, { id: 'shape', icon: SquareIcon, label: 'Shape' }, { id: 'text', icon: TypeIcon, label: 'Text' }] as tool (tool.id)}
<Button
variant={activeTool === tool.id ? 'filled' : 'ghost'}
intent={activeTool === tool.id ? 'primary' : 'neutral'}
size="sm"
class="min-w-9 justify-center"
onclick={() => (activeTool = tool.id)}
aria-label={tool.label}
aria-pressed={activeTool === tool.id}
>
<tool.icon size={16} />
</Button>
{/each}
</Toolbar>
<div class="bg-surface-base border-border-subtle flex-1 rounded-xl border p-6">
<p class="text-text-tertiary text-center text-sm">
Active tool: <Badge size="sm" intent="primary">{activeTool}</Badge>
</p>
</div>Media Controls
<Toolbar aria-label="Media player" variant="elevated" gap="sm" padding="md">
<Button variant="ghost" size="sm" class="justify-center" aria-label="Previous track"
><SkipBackIcon size={16} /></Button
>
<Button
variant="filled"
intent="primary"
size="sm"
class="min-w-9 justify-center"
aria-label="Play"><PlayIcon size={16} /></Button
>
<Button variant="ghost" size="sm" class="justify-center" aria-label="Next track"
><SkipForwardIcon size={16} /></Button
>
<Separator orientation="vertical" size="sm" />
<Button variant="ghost" size="sm" class="justify-center" aria-label="Shuffle"
><ShuffleIcon size={16} /></Button
>
<Button variant="ghost" size="sm" class="justify-center" aria-label="Repeat"
><RepeatIcon size={16} /></Button
>
<Separator orientation="vertical" size="sm" />
<span class="text-text-tertiary px-2 text-xs tabular-nums">2:34 / 4:12</span>
</Toolbar>02 Customization
Developer Toolbar
<Toolbar
aria-label="Developer actions"
variant="ghost"
slotClasses={{
base: 'bg-linear-to-r from-neutral-900 to-neutral-800 border border-neutral-700/50 shadow-xl shadow-black/20 rounded-xl px-3 py-2'
}}
gap="sm"
>
<Button
unstyled
class="flex items-center gap-1.5 rounded-lg bg-emerald-500/15 px-3 py-1.5 text-xs font-semibold text-emerald-400 transition-colors hover:bg-emerald-500/25"
><PlayIcon size={14} /> Run</Button
>
<Button
unstyled
class="flex items-center gap-1.5 rounded-lg bg-amber-500/15 px-3 py-1.5 text-xs font-semibold text-amber-400 transition-colors hover:bg-amber-500/25"
><PauseIcon size={14} /> Debug</Button
>
<Separator orientation="vertical" size="sm" class="!border-neutral-600" />
<Button
unstyled
class="flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium text-neutral-400 transition-colors hover:bg-neutral-700/50 hover:text-neutral-200"
><SettingsIcon size={14} /> Settings</Button
>
<Button
unstyled
class="flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium text-neutral-400 transition-colors hover:bg-neutral-700/50 hover:text-neutral-200"
><PackageIcon size={14} /> Build</Button
>
</Toolbar>Floating Glass Toolbar
<Toolbar
aria-label="Quick actions"
unstyled
class="flex items-center gap-2 rounded-2xl border border-white/20 bg-white/10 px-4 py-2.5 shadow-2xl backdrop-blur-xl"
>
<Button
unstyled
class="flex items-center gap-1.5 rounded-xl px-3 py-1.5 text-sm font-medium text-white/90 transition-all hover:bg-white/15"
><SparklesIcon size={15} /> New</Button
>
<Button
unstyled
class="flex items-center gap-1.5 rounded-xl px-3 py-1.5 text-sm font-medium text-white/90 transition-all hover:bg-white/15"
><FolderOpenIcon size={15} /> Open</Button
>
<div class="mx-1 h-5 w-px bg-white/20"></div>
<Button
unstyled
class="flex items-center gap-1.5 rounded-xl bg-white/20 px-3 py-1.5 text-sm font-semibold text-white shadow-lg shadow-black/10 backdrop-blur-sm transition-all hover:bg-white/30"
><SaveIcon size={15} /> Save</Button
>
</Toolbar>Pill Toolbar
<Toolbar
aria-label="Navigation"
variant="outlined"
slotClasses={{ base: 'rounded-full px-1.5 py-1 border-border-default' }}
gap="xs"
>
<Button variant="filled" intent="primary" size="sm" class="rounded-full">Dashboard</Button>
<Button variant="ghost" size="sm" class="rounded-full">Projects</Button>
<Button variant="ghost" size="sm" class="rounded-full">Teams</Button>
<Button variant="ghost" size="sm" class="rounded-full">Settings</Button>
</Toolbar>A dock or pill-nav chrome used on several screens belongs in a BlocksProvider preset (presets.Toolbar), applied via preset — see Customization.
03 Accessibility
Built-in ARIA
Renders with role="toolbar". aria-label is required to identify the toolbar's
purpose. aria-orientation is set automatically from
the orientation prop.
Keyboard
Tab moves focus into and out of the toolbar. Individual items inside the toolbar follow their own keyboard semantics (e.g. Buttons activate via Enter / Space).
Best Practices
Use descriptive aria-label values that communicate
the toolbar's function (e.g. "Text formatting", "File actions"). Group related controls
using Separator for visual clarity. Avoid nesting toolbars
inside each other.
04 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
aria-label required | string | — | Accessible label describing the toolbar's purpose (e.g. "Formatting toolbar"). Required. | |
children required | Snippet | — | Toolbar content — typically Buttons, Toggles, Separators, or ButtonGroups. | |
class | string | — | Additional CSS class applied to the root element. | |
gap variant | lgmdsmxl +1 more | sm | Controls the gap behavior and appearance of the Toolbar component. Available options: lg, md, sm, and 2 more. | |
orientation variant | horizontalvertical | horizontal | Controls the orientation behavior and appearance of the Toolbar component. Available options: horizontal, vertical. | |
padding variant | lgmdsmxl +1 more | sm | Controls the padding behavior and appearance of the Toolbar component. Available options: lg, md, sm, and 2 more. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ Toolbar: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent
and make the custom look reusable across the project. | |
slotClasses | Partial<Record<ToolbarSlots, string>> | — | Per-slot class overrides merged with (or replacing when unstyled) the default classes.
Slots: base | |
tier | InteractiveTier | 'modify' | Semantic radius tier propagated to tier-aware children (Buttons,
Badges, Inputs, …). The Toolbar's own surface stays r-structure
regardless of this prop.
Default modify — toolbars typically hold compact, icon-only actions
where pill-shaped buttons read as tags. Set to commit for marketing
or hero toolbars with full CTAs. | |
unstyled | boolean | — | Remove all default tv() classes. Combine with slotClasses to restyle from scratch. | |
variant variant | elevatedghostoutlinedquiet | quiet | Controls the visual style and presentation of the Toolbar. Determines the component's visual treatment. Available options: elevated, ghost, outlined, quiet. | |
...HTMLAttributes<HTMLDivElement> inherited | HTMLAttributes | — | HTML attributes (excluding: 'children') | |
...ToolbarVariants variant | VariantProps | — | Styling variants from ToolbarVariants |
05 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
ToolbarProps | interface | props | 0 | Props interface for Toolbar component | |
ToolbarVariants | type | variant | 1 | — | |
ToolbarSlots | type | variant | 0 | Slot names derived from the tv() config — single source of truth for slotClasses. | |
InteractiveTier | type | helper | 1 | Semantic radius tier for interactive surfaces (3-tier system).
- commit → r-human (CTA, identity, status declarations)
- modify → r-interactive (fields, navigation, secondary actions)
Container components (Card, Alert, Toolbar surface, …) live in a third
tier contain (r-structure) which is **not** part of this propagation
context — those surfaces are always r-structure by design and have no
tier-flip use case. |
06 Installation
Import
import { Toolbar, Button, Separator } from '@urbicon-ui/blocks';