Toolbars
Toolbars provide a surface for frequently used actions, typically floating or docked at the screen edge.
Toolbars are a versatile alternative to traditional App Bars in Material Design 3 Expressive. They can be docked to the bottom of the screen or float above content, providing context-aware actions in a compact, modern format.
Introduction
MD3 Expressive Toolbars are designed for high-density action surfaces. Unlike standard App Bars, which are usually pinned to the top, Toolbars are often placed at the bottom or sides of the screen to optimize thumb reachability on mobile and provide a more focused workspace on desktop. They support dynamic expansion states, toggleable action building blocks, and spatial XR elevations.
Anatomy
- Container: The background surface, available in pill (standard), large rounded, or spatial glassmorphic shapes.
- Leading Content: Actions shown only when the toolbar is expanded.
- Main Content: Primary actions that remain visible in both collapsed and expanded states.
- Trailing Content: Secondary actions or settings shown when expanded.
- FAB integration: Floating toolbars can be paired with a Floating Action Button (FAB).
Variants
Bottom Docked Toolbar
A full-width toolbar fixed at the bottom of the screen. It is the modern replacement for the traditional Bottom App Bar, supporting leading, centered, and trailing content slots.
<BottomDockedToolbar
startContent={<IconButton aria-label="Menu"><Icon name="menu" /></IconButton>}
endContent={<IconButton aria-label="More"><Icon name="more_vert" /></IconButton>}
>
<div className="flex gap-2">
<IconButton aria-label="Search"><Icon name="search" /></IconButton>
<IconButton aria-label="Edit"><Icon name="edit" /></IconButton>
</div>
</BottomDockedToolbar>
Floating Toolbar (Horizontal & Vertical)
Floating toolbars are independent surfaces that hover over content. They are highly expressive, supporting a collapsed "pill" state and an expanded state that reveals more actions.
<HorizontalFloatingToolbar expanded={expanded}>
<IconButton aria-label="Action"><Icon name="bolt" /></IconButton>
</HorizontalFloatingToolbar>
Floating Toolbar with FAB
A specialized variant that docks a Floating Action Button next to a floating toolbar. This creates a cohesive "action hub" that manages both the primary page action and secondary utility actions.
<HorizontalFloatingToolbarWithFab
expanded={expanded}
floatingActionButton={<FAB icon={<Icon name="add" />} aria-label="Add" />}
>
<IconButton aria-label="Share"><Icon name="share" /></IconButton>
</HorizontalFloatingToolbarWithFab>
Spatial & XR Elevations (Glassmorphism)
Toolbars support 6 elevation and color variants, including spatial XR translucent surfaces with backdrop blur (backdrop-blur-md):
standard: Low-emphasis surface container.vibrant: High-emphasis primary container.surface-high: Elevated surface container high.surface-highest: Elevated surface container highest.tertiary: Tertiary container colors.xr: Semi-transparent glassmorphic surface with backdrop blur for spatial/XR experiences.
<HorizontalFloatingToolbar variant="xr" expanded={true}>
<ToolbarToggleButton selected icon={<Icon name="view_in_ar" />}>
Spatial View
</ToolbarToggleButton>
<ToolbarIconButton aria-label="Rotate"><Icon name="3d_rotation" /></ToolbarIconButton>
</HorizontalFloatingToolbar>
Flexibility & Slots
When configuring a toolbar, think of it as a container with slots. Slots can be populated by icon buttons, toggle buttons, images, text fields, or any custom component.
Toggle Actions & Buttons
Toolbars provide dedicated building blocks for toggleable actions (ToolbarToggleButton & ToolbarIconButton with selected state) and slot delegation (asChild):
import {
ToolbarIconButton,
ToolbarToggleButton,
ToolbarDivider,
HorizontalFloatingToolbar,
} from "@bug-on/m3-expressive";
<HorizontalFloatingToolbar expanded={true}>
{/* Toggle Icon Button */}
<ToolbarIconButton
aria-label="Format Bold"
selected={isBold}
onClick={() => setIsBold(!isBold)}
>
<Icon name="format_bold" />
</ToolbarIconButton>
<ToolbarDivider />
{/* Toggle Button with Text & Icon */}
<ToolbarToggleButton
selected={activeTab === 'edit'}
onClick={() => setActiveTab('edit')}
icon={<Icon name="edit" />}
emphasis="tonal"
>
Edit
</ToolbarToggleButton>
{/* Slot Delegation via asChild */}
<ToolbarIconButton aria-label="Home" asChild>
<a href="#home"><Icon name="home" /></a>
</ToolbarIconButton>
</HorizontalFloatingToolbar>
Emphasis Hierarchy & Dividers
Use ToolbarIconButton to add emphasis hierarchy and ToolbarDivider to group related actions:
import {
ToolbarIconButton,
ToolbarDivider,
HorizontalFloatingToolbar,
} from "@bug-on/m3-expressive";
// Single filled (high-emphasis) action alongside standard siblings
<HorizontalFloatingToolbar expanded={true}>
<ToolbarIconButton aria-label="Bold"><BoldIcon /></ToolbarIconButton>
<ToolbarIconButton aria-label="Italic"><ItalicIcon /></ToolbarIconButton>
<ToolbarDivider />
{/* One filled wide button draws the eye — avoid emphasising more than one */}
<ToolbarIconButton emphasis="filled" width="wide" aria-label="Add">
<AddIcon />
</ToolbarIconButton>
</HorizontalFloatingToolbar>
Video-Like Toolbar Integration
Toolbars seamlessly host complex child components. By nesting a ButtonGroup with variant="connected" and fullWidth inside a HorizontalFloatingToolbar along with startContent and endContent, you can create highly sophisticated media or navigation surfaces:
<HorizontalFloatingToolbar
colors={colors}
expanded={expanded}
itemGap={6}
contentPadding="px-2!"
startContent={<ToolbarIconButton aria-label="Volume"><VolumeIcon /></ToolbarIconButton>}
endContent={<ToolbarIconButton aria-label="More"><MoreIcon /></ToolbarIconButton>}
>
<ButtonGroup variant="connected" fullWidth className="h-full">
<Button selected>Chapter 1</Button>
<Button>Chapter 2</Button>
</ButtonGroup>
</HorizontalFloatingToolbar>
Features
Scroll Behavior
Toolbars can be configured to respond to scrolling.
- Floating Toolbars: Use the
useFloatingToolbarScrollBehaviorhook to automatically collapse or hide. - Docked Toolbars: Use the
hideOnScrollprop to slide off-screen.
Floating Toolbar Scroll
Docked Toolbar Scroll
Accessibility
- Roles: All toolbars apply
role="toolbar"to ensure correct screen reader behavior. - Labels: Every interactive element within a toolbar must have an
aria-labelor visible text. - Keyboard Navigation: Toolbars support standard Tab navigation; focus remains visible through the MD3 focus ring.
- Reduced Motion: All animations (expansion, translation, color shifts) respect the
prefers-reduced-motionsetting.
API Reference
BottomDockedToolbar
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'standard' | 'vibrant' | 'surface-high' | 'surface-highest' | 'tertiary' | 'xr' | 'standard' | The color and surface elevation configuration. |
hideOnScroll | boolean | false | Whether to hide the toolbar when scrolling down. |
startContent | ReactNode | — | Content at the left/start. |
endContent | ReactNode | — | Content at the right/end. |
children | ReactNode | — | Centered content. |
paddingX | number | 16 | Horizontal padding in px. |
justify | 'between' | 'center' | 'end-weighted' | 'between' | Content distribution layout. |
shape | 'none' | 'large' | 'full' | 'none' | Container shape. Use large for modern web feel. |
scrollContainerRef | RefObject | — | Reference to the scrollable viewport. |
Floating Toolbars (Horizontal/Vertical)
| Prop | Type | Default | Description |
|---|---|---|---|
expanded | boolean | — | Controls visibility of leading/trailing content. |
variant | 'standard' | 'vibrant' | 'surface-high' | 'surface-highest' | 'tertiary' | 'xr' | 'standard' | Surface elevation & color configuration. Use xr for glassmorphic backdrop. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout direction. |
shape | 'full' | 'large' | 'full' | 'full' for pill, 'large' for rounded rectangle. |
contentPadding | string | object | — | Custom padding for the internal container. |
startContent | ReactNode | — | Content revealed when expanded (start/top). |
endContent | ReactNode | — | Content revealed when expanded (end/bottom). |
children | ReactNode | — | Main content, always visible. |
scrollBehavior | object | — | Result from useFloatingToolbarScrollBehavior. |
itemGap | number | 4 | Gap (px) between items. |
childrenAlignment | 'start' | 'center' | 'end' | 'center' | Justify-content of the center slot. |
itemClassName | string | — | Custom CSS class applied to each child item. |
colors | object | standard | Custom color configuration override. |
disableScrollTranslation | boolean | false | Disable automatic scroll-based movement. |
disableLayoutAnimation | boolean | false | Disable framer-motion layout transitions. |
ToolbarToggleButton
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | false | Active toggle state (applies aria-pressed). |
emphasis | 'standard' | 'tonal' | 'filled' | 'standard' | Visual emphasis style. |
icon | ReactNode | — | Optional leading icon. |
children | ReactNode | — | Button text label or content. |
asChild | boolean | false | Renders as the child element (Radix Slot). |
ToolbarIconButton
Inherits all IconButton props:
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | — | Active toggle state (applies aria-pressed). |
emphasis | 'standard' | 'tonal' | 'filled' | 'standard' | Visual color style. Use filled for the single highest-priority action. |
width | 'narrow' | 'default' | 'wide' | 'default' | Slot width ratio (MD3SlotWidth): 40 / 48 / 64px. Height always 48px (touch target). |
asChild | boolean | false | Renders as the child element (e.g., Next.js Link) while keeping toolbar button styles. |
aria-label | string | — | Required accessible label. |