MD3
Expressive
MATERIAL DESIGN 3 EXPRESSIVE

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.

Loading demo...

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.

Loading demo...
<BottomDockedToolbar 
  startContent={<ToolbarIconButton aria-label="Menu" width="narrow"><Icon name="menu" /></ToolbarIconButton>}
  endContent={
    <div className="flex gap-2 items-center">
      <ToolbarIconButton aria-label="More" emphasis="tonal"><Icon name="more_vert" /></ToolbarIconButton>
    </div>
  }
>
  <div className="flex gap-2">
    <ToolbarIconButton aria-label="Search"><Icon name="search" /></ToolbarIconButton>
    <ToolbarIconButton aria-label="Edit"><Icon name="edit" /></ToolbarIconButton>
  </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.

Loading demo...
Loading demo...
<HorizontalFloatingToolbar expanded={expanded}>
  <ToolbarIconButton aria-label="Action"><Icon name="bolt" /></ToolbarIconButton>
</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.

Loading demo...
Loading demo...
<HorizontalFloatingToolbarWithFab
  expanded={expanded}
  floatingActionButton={<FAB icon={<Icon name="add" />} aria-label="Add" />}
>
  <ToolbarIconButton aria-label="Share"><Icon name="share" /></ToolbarIconButton>
</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.
Loading demo...
<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>

Custom Background & Glassmorphic Blur

Toolbars natively support Tailwind background utilities (e.g. bg-surface-container/60, bg-white/30, bg-primary/20, bg-black/40) and flexible backdrop blur classes (backdrop-blur-sm, backdrop-blur-md, backdrop-blur-xl, backdrop-blur-2xl).

When a custom bg-* class is provided via className, the toolbar automatically yields to the utility class without requiring !important overrides. This makes it effortless to craft custom frosted glass surfaces, subtle brand tints, or transparent HUD interfaces.

Loading demo...
// Translucent surface with ultra glass backdrop blur
<HorizontalFloatingToolbar 
  expanded={true} 
  className="bg-surface-container/60 backdrop-blur-xl border border-white/20 shadow-xl"
  startContent={<ToolbarIconButton aria-label="Home"><Icon name="home" /></ToolbarIconButton>}
>
  <ToolbarToggleButton selected icon={<Icon name="format_bold" />}>
    Bold
  </ToolbarToggleButton>
  <ToolbarIconButton aria-label="Palette"><Icon name="palette" /></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):

Loading demo...

[!NOTE] MD3 Floating Toolbar Shape Guideline:
"Don’t use square filled icon buttons in floating toolbars. Avoid using square icon buttons in floating toolbars. Their square shape conflicts with the fully-rounded shape of the floating toolbar container. Square buttons can be used in the docked toolbar."
For this reason, ToolbarIconButton sets morphRadius={false} by default to maintain an uncompromised fully-rounded circular shape when clicked or selected in floating toolbars. In docked toolbars, you can explicitly opt into morphing via morphRadius={true}.

import { HorizontalFloatingToolbar, ToolbarDivider, ToolbarIconButton, ToolbarToggleButton } from "@bug-on/m3-expressive/navigation";

<HorizontalFloatingToolbar expanded={true}>
  {/* Toggle Icon Button — retains fully-rounded shape */}
  <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

Loading demo...

Use ToolbarIconButton to add emphasis hierarchy and ToolbarDivider to group related actions:

import { HorizontalFloatingToolbar, ToolbarDivider, ToolbarIconButton } from "@bug-on/m3-expressive/navigation";

// Single filled (high-emphasis) action alongside standard siblings
<HorizontalFloatingToolbar expanded={true}>
  <ToolbarIconButton aria-label="Bold"><Icon name="format_bold" /></ToolbarIconButton>
  <ToolbarIconButton aria-label="Italic"><Icon name="format_italic" /></ToolbarIconButton>
  <ToolbarDivider />
  {/* One filled wide button draws the eye — avoid emphasising more than one */}
  <ToolbarIconButton emphasis="filled" width="wide" aria-label="Add">
    <Icon name="add" />
  </ToolbarIconButton>
</HorizontalFloatingToolbar>

Video-Like Toolbar Integration

Toolbars host expressive child components. By combining ToolbarIconButton with ToolbarToggleButton inside a HorizontalFloatingToolbar along with startContent and endContent, you can create rich media or gallery navigation surfaces:

<HorizontalFloatingToolbar
  colors={colors}
  expanded={expanded}
  itemGap={4}
  className="max-w-full"
  startContent={
    <ToolbarIconButton aria-label="Volume" width="narrow">
      <Icon name="volume_up" />
    </ToolbarIconButton>
  }
  endContent={
    <ToolbarIconButton aria-label="More options" emphasis="tonal">
      <Icon name="more_vert" />
    </ToolbarIconButton>
  }
>
  {NAV_ITEMS.map((item) => (
    <ToolbarToggleButton
      key={item.id}
      icon={<Icon name={item.icon} fill={selectedNav === item.id ? 1 : 0} />}
      selected={selectedNav === item.id}
      onClick={() => setSelectedNav(item.id)}
      emphasis={selectedNav === item.id ? "tonal" : "standard"}
    >
      {item.label}
    </ToolbarToggleButton>
  ))}
</HorizontalFloatingToolbar>

Features

Scroll Behavior

Toolbars can be configured to respond to scrolling.

  • Floating Toolbars: Use the useFloatingToolbarScrollBehavior hook to automatically collapse or hide.
  • Docked Toolbars: Use the hideOnScroll prop to slide off-screen.

Floating Toolbar Scroll

Loading demo...
import {
  HorizontalFloatingToolbarWithFab,
  ToolbarIconButton,
  useFloatingToolbarScrollBehavior,
} from "@bug-on/m3-expressive/navigation";
import { useRef } from "react";

export function ScrollDemo() {
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const scrollBehavior = useFloatingToolbarScrollBehavior({
    scrollContainerRef,
  });

  return (
    <div className="relative h-96 flex flex-col">
      <div className="flex-1 overflow-y-auto" ref={scrollContainerRef}>
        {/* Scrollable list items */}
      </div>

      <div className="absolute bottom-4 left-0 right-0 flex justify-center pointer-events-none">
        <HorizontalFloatingToolbarWithFab
          expanded={scrollBehavior.isExpanded}
          scrollBehavior={scrollBehavior}
          floatingActionButton={<FAB icon={<Icon name="add" />} aria-label="Add" />}
          startContent={
            <ToolbarIconButton aria-label="Undo" width="narrow">
              <Icon name="undo" />
            </ToolbarIconButton>
          }
        >
          <ToolbarIconButton aria-label="Bold"><Icon name="format_bold" /></ToolbarIconButton>
          <ToolbarIconButton aria-label="Italic"><Icon name="format_italic" /></ToolbarIconButton>
        </HorizontalFloatingToolbarWithFab>
      </div>
    </div>
  );
}

[!WARNING] No DOM onScroll binding required: The useFloatingToolbarScrollBehavior hook relies on Framer Motion's useScroll internally. Do NOT bind onScroll={scrollBehavior.onScroll} to your scroll container element — simply pass scrollContainerRef to the hook options.

Docked Toolbar Scroll

Loading demo...
import { BottomDockedToolbar, ToolbarIconButton } from "@bug-on/m3-expressive/navigation";
import { useRef } from "react";

export function DockedScrollDemo() {
  const scrollContainerRef = useRef<HTMLDivElement>(null);

  return (
    <div className="relative h-96 flex flex-col">
      <div className="flex-1 overflow-y-auto" ref={scrollContainerRef}>
        {/* Scrollable content */}
      </div>

      <BottomDockedToolbar
        className="absolute"
        hideOnScroll
        scrollContainerRef={scrollContainerRef}
        startContent={
          <ToolbarIconButton aria-label="Menu" width="narrow">
            <Icon name="menu" />
          </ToolbarIconButton>
        }
      >
        <ToolbarIconButton aria-label="Home" selected>
          <Icon name="home" />
        </ToolbarIconButton>
      </BottomDockedToolbar>
    </div>
  );
}

Color Configuration

Toolbars support two ways to specify colors:

  1. variant prop (Shorthand): Choose from predefined semantic MD3 themes: "standard", "vibrant", "surface-high", "surface-highest", "tertiary", or "xr".
  2. colors prop (Custom Object): Pass an explicit FloatingToolbarColors object, or use pre-configured presets:
    • standardFloatingToolbarColors
    • vibrantFloatingToolbarColors
    • surfaceContainerHighFloatingToolbarColors
    • surfaceContainerHighestFloatingToolbarColors
    • tertiaryContainerFloatingToolbarColors
    • xrFloatingToolbarColors

Accessibility

  • Roles: All toolbars apply role="toolbar" to ensure correct screen reader behavior.
  • Labels: Every interactive element within a toolbar must have an aria-label or 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-motion setting.

API Reference

BottomDockedToolbar

PropTypeDefaultDescription
variant'standard' | 'vibrant' | 'surface-high' | 'surface-highest' | 'tertiary' | 'xr''standard'The color and surface elevation configuration.
colorsFloatingToolbarColors—Custom color configuration object overriding variant.
hideOnScrollbooleanfalseWhether to hide the toolbar when scrolling down.
scrollContainerRefRefObject<HTMLElement | null>—Reference to the scrollable viewport (uses window scroll if omitted).
startContentReactNode—Content at the left/start.
endContentReactNode—Content at the right/end.
childrenReactNode—Centered content.
paddingXnumber16Horizontal padding in px.
justify'between' | 'center' | 'end-weighted''between'Content distribution layout.
shape'none' | 'large' | 'full''none'Container shape ('large' recommended for desktop / large screens).
classNamestring—Custom CSS class applied to the toolbar container. Supports Tailwind bg-* and backdrop-blur-* utilities.
styleCSSProperties—Inline style overrides.
aria-labelstring—Accessible label for the toolbar landmark.

Floating Toolbars (HorizontalFloatingToolbar / VerticalFloatingToolbar)

PropTypeDefaultDescription
expandedboolean—Required. Controls visibility of startContent and endContent.
variant'standard' | 'vibrant' | 'surface-high' | 'surface-highest' | 'tertiary' | 'xr''standard'Surface elevation & color configuration. Use 'xr' for glassmorphic backdrop.
colorsFloatingToolbarColors—Custom color configuration object overriding variant.
shape'full' | 'large''full''full' for pill, 'large' for rounded rectangle.
contentPaddingCSSProperties | string—Custom padding for the internal container.
startContentReactNode—Content revealed when expanded (start / top).
endContentReactNode—Content revealed when expanded (end / bottom).
childrenReactNode—Required. Main content, always visible.
scrollBehaviorFloatingToolbarScrollBehavior—Scroll controller returned by useFloatingToolbarScrollBehavior.
itemGapnumber4Gap (px) between items in the center children slot.
childrenAlignment'start' | 'center' | 'end''center'Alignment (justify-content) of the center children slot.
itemClassNamestring—Custom CSS class applied to each child item in the toolbar slots.
disableScrollTranslationbooleanfalseDisable automatic scroll-based movement.
disableLayoutAnimationbooleanfalseDisable motion layout transitions.
classNamestring—Custom CSS class applied to the toolbar container. Supports Tailwind bg-* and backdrop-blur-* utilities without collision.
styleCSSProperties—Inline style overrides.
aria-labelstring—Accessible label for the toolbar landmark.

Floating Toolbars With FAB (HorizontalFloatingToolbarWithFab / VerticalFloatingToolbarWithFab)

Inherits all FloatingToolbarProps plus:

PropTypeDefaultDescription
floatingActionButtonReactNode—Required. FAB element to dock next to the toolbar.
fabPosition'start' | 'end' | 'top' | 'bottom''end' (h) / 'bottom' (v)Placement of the docked FAB.
fabSize'sm' | 'md''sm'Size token: 'sm' (56×56dp) or 'md' (80×80dp).
containerClassNamestring—Custom CSS class applied to the outer wrapper containing both the FAB and the toolbar. Use className to style the toolbar itself.
animationDurationnumber0.3Animation duration override in seconds.

useFloatingToolbarScrollBehavior

Hook to orchestrate scroll-based collapsing and expansion:

const scrollBehavior = useFloatingToolbarScrollBehavior(options);

Options (UseFloatingToolbarScrollBehaviorOptions)

OptionTypeDefaultDescription
exitDirection'top' | 'bottom' | 'start' | 'end''bottom'Direction the toolbar moves when collapsing.
collapseThresholdnumber10Scroll distance (px) downward to trigger collapse.
expandThresholdnumber10Scroll distance (px) upward to trigger expansion.
scrollContainerRefRefObject<HTMLElement | null>—Target scrollable container (uses window scroll if omitted).

Return Value (FloatingToolbarScrollBehavior)

PropertyTypeDescription
offsetnumberCurrent fractional offset (0 = fully visible, -1 = hidden).
isExpandedbooleanWhether toolbar leading/trailing slots are currently visible.
setExpanded(expanded: boolean) => voidProgrammatic control of the expanded state.
exitDirection'top' | 'bottom' | 'start' | 'end'Active exit direction.
onScroll(event: UIEvent<HTMLElement>) => voidLegacy no-op. Kept for backwards compatibility; Framer Motion handles events automatically.

ToolbarIconButton

Inherits all IconButton props:

PropTypeDefaultDescription
childrenReactNode—Required. The icon element to display.
aria-labelstring—Required accessible label.
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 is always 48px touch target.
shape'round' | 'square''round'Container shape. Use 'round' for floating toolbars (per MD3 spec).
morphRadiusboolean | IconButtonMorphRadiusConfigfalseControls border-radius morphing. Defaults to false per MD3 Floating Toolbar guideline (preserves fully-rounded circular shape). Set to true for docked toolbars.
selectedboolean—Active toggle state (applies aria-pressed).
selectedIconReactNode—Optional active icon shown when selected={true}.
asChildbooleanfalseRenders as the child element (e.g., Next.js Link) while keeping toolbar button styles.

ToolbarToggleButton

PropTypeDefaultDescription
selectedbooleanfalseActive toggle state (applies aria-pressed).
emphasis'standard' | 'tonal' | 'filled''standard'Visual emphasis style ('standard', 'tonal', 'filled').
iconReactNode—Optional leading icon.
childrenReactNode—Button text label or content.
ripplebooleantrueEnable MD3 state layer ripple effect on click.
asChildbooleanfalseRenders as the child element (Radix Slot).
pressExpandbooleantrue(Deprecated) Kept for backwards compatibility. Spring border-radius morphing is used instead.
pressExpandRationumber0.06(Deprecated) Kept for backwards compatibility.

ToolbarDivider

A decorative separator that visually organizes groups of actions:

PropTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Orientation of the toolbar. Renders a vertical line in horizontal toolbars, and horizontal in vertical. Automatically detected from context if omitted.
classNamestring—Additional CSS class names.

FloatingToolbarColors

Interface for custom toolbar color overrides:

FieldTypeDescription
toolbarContainerColorstringBackground color of the toolbar surface container.
toolbarContentColorstringColor of the content (icons, text) inside the toolbar.
fabContainerColorstringBackground color of the FAB container (for FAB variants).
fabContentColorstringColor of the content inside the FAB.