FairGarden Design

Toolbar

A compact row of tools acting on one object or region: a list's count and actions, formatting controls, a context bar or the controls under a figure.

import {
  Toolbar,
  ToolbarButton,
  ToolbarCount,
  ToolbarGroup,
  ToolbarMore,
  ToolbarSeparator,
} from '@fairgarden-private/design/components/Toolbar'

A Base UI Toolbar: one Tab stop enters it, arrow keys move within it, and it remembers the last focused item. ToolbarButton is a text Button by default, so its hover, press and focus come from the Button; disabled buttons stay focusable so their names can be read. Add a Toggle or a Menu trigger through ToolbarItem's render, and a Link with ToolbarLink. Groups sit --size-px-1 apart inside and divide with short vertical rules. The row takes the band's ground and has no face of its own. Avoid saturated fields, and never float a toolbar or stack two sticky bars.

List and figure toolbars

List and figure toolbars

list (the default) puts the mono count first and its actions at the end. figure spreads its groups under the figure. A toolbar reflows on its own width and never scrolls or wraps: below 768 px a text Button with an icon shows only its glyph, its label kept as the accessible name and shown in a tooltip, and lowPriority items leave the row for ToolbarMore, whose menu lists them again. From 768 px labels and low-priority items return and "More" goes. The root is an inline-size container named toolbar. Drag a frame's corner to watch the reflow.

list

54 items · Sorted by recently accessed

figure

Zoom 100% · No action yet.

ToolbarKinds.tsx
'use client'

import * as React from 'react'
import {
  Menu,
  MenuItem,
  MenuPopup,
  MenuRadioGroup,
  MenuRadioItem,
  MenuTrigger,
} from '@fairgarden-private/design/components/Menu'
import {
  Toolbar,
  ToolbarBottomRule,
  ToolbarButton,
  ToolbarCount,
  ToolbarGroup,
  ToolbarItem,
  ToolbarMore,
  ToolbarSeparator,
} from '@fairgarden-private/design/components/Toolbar'
import styles from './kinds.module.css'

const sorts = {
  recent: 'Recently Accessed',
  name: 'Name',
  size: 'Size',
} as const

type Sort = keyof typeof sorts

export function ToolbarKinds() {
  const [sort, setSort] = React.useState<Sort>('recent')
  const [zoom, setZoom] = React.useState(100)
  const [figure, setFigure] = React.useState(3)
  const [last, setLast] = React.useState('No action yet.')
  const step = (by: number) => () => setFigure((value) => ((value - 1 + by + 12) % 12) + 1)

  return (
    <div className={styles.stack}>
      <p className={styles.name}>list</p>
      <div className={styles.frame}>
        <Toolbar
          aria-label="Survey plots"
          caption={`54 items · Sorted by ${sorts[sort].toLowerCase()}`}
        >
          <ToolbarCount>Items (54)</ToolbarCount>
          <ToolbarButton icon="add" onClick={() => setLast('Added a plot.')}>
            Add Plot
          </ToolbarButton>
          <ToolbarButton icon="download" lowPriority onClick={() => setLast('Exported the list.')}>
            Export List
          </ToolbarButton>
          <Menu>
            <ToolbarItem
              render={
                <MenuTrigger variant="text" size="sm" icon="expand_more" iconPosition="end">
                  {sorts[sort]}
                </MenuTrigger>
              }
            />
            <MenuPopup align="end">
              <MenuRadioGroup value={sort} onValueChange={(value: Sort) => setSort(value)}>
                {(Object.keys(sorts) as Sort[]).map((value) => (
                  <MenuRadioItem key={value} value={value}>
                    {sorts[value]}
                  </MenuRadioItem>
                ))}
              </MenuRadioGroup>
            </MenuPopup>
          </Menu>
          <ToolbarMore>
            <MenuItem icon="download" onClick={() => setLast('Exported the list.')}>
              Export list
            </MenuItem>
          </ToolbarMore>
          <ToolbarBottomRule variant="hairline" />
        </Toolbar>
      </div>

      <p className={styles.name}>figure</p>
      <div className={styles.frame}>
        <Toolbar kind="figure" aria-label="Figure controls">
          <ToolbarGroup>
            <ToolbarButton
              iconOnly
              icon="zoom_out"
              onClick={() => setZoom((value) => Math.max(50, value - 25))}
            >
              Zoom Out
            </ToolbarButton>
            <ToolbarButton
              iconOnly
              icon="zoom_in"
              onClick={() => setZoom((value) => Math.min(200, value + 25))}
            >
              Zoom In
            </ToolbarButton>
            <ToolbarButton iconOnly icon="recenter" onClick={() => setZoom(100)}>
              Recenter
            </ToolbarButton>
            <ToolbarSeparator />
            <ToolbarButton icon="download" onClick={() => setLast('Downloaded the figure.')}>
              Download Figure
            </ToolbarButton>
          </ToolbarGroup>
          <ToolbarGroup>
            <ToolbarButton iconOnly icon="chevron_left" onClick={step(-1)}>
              Previous Figure
            </ToolbarButton>
            <ToolbarCount>{figure} of 12</ToolbarCount>
            <ToolbarButton iconOnly icon="chevron_right" onClick={step(1)}>
              Next Figure
            </ToolbarButton>
          </ToolbarGroup>
        </Toolbar>
      </div>

      <p className={styles.status} aria-live="polite">
        Zoom {zoom}% · {last}
      </p>
    </div>
  )
}

format is a start-aligned row of icon toggles and buttons in groups. context is the docked bar: pass docked for the top edge's one sticky bar, opaque on the band's ground with a rule on the content side; swap it with the header, never stack the two.

On grounds

On page grounds and a deep field

Items keep their own roles: labels in the text ink, glyphs in the glyph role, separators and the bottom rule in the rule role.

paper

tide

forest

ToolbarGrounds.tsx
'use client'

import * as React from 'react'
import { Ground } from '@fairgarden-private/design/components/Ground'
import {
  Toolbar,
  ToolbarBottomRule,
  ToolbarButton,
  ToolbarCount,
  ToolbarGroup,
  ToolbarSeparator,
} from '@fairgarden-private/design/components/Toolbar'
import {
  isFieldPreset,
  isPageGroundPreset,
  presets,
  type GroundPreset,
} from '@fairgarden-private/design/utils/scope'
import styles from './grounds.module.css'

/** The first preset that passes `test`, read from the preset table, never named here. */
function firstPreset(test: (preset: GroundPreset) => boolean): GroundPreset | undefined {
  return (Object.keys(presets) as GroundPreset[]).find(test)
}

/** A sample surface: a field preset as an inset field, a page ground as a face. */
function Sample({
  preset,
  className,
  children,
}: {
  preset: GroundPreset
  className: string
  children: React.ReactNode
}) {
  if (isFieldPreset(preset)) {
    return (
      <Ground kind="field" preset={preset} className={className}>
        {children}
      </Ground>
    )
  }
  if (isPageGroundPreset(preset)) {
    return (
      <Ground kind="face" preset={preset} className={className}>
        {children}
      </Ground>
    )
  }
  return null
}

/** A light page ground, a pastel and a deep field; toolbars avoid saturated fields. */
const grounds = [
  firstPreset((preset) => presets[preset].tone === 'light-base'),
  firstPreset((preset) => presets[preset].tone === 'tinted'),
  firstPreset((preset) => isFieldPreset(preset) && presets[preset].mode === 'always-dark'),
].filter((preset): preset is GroundPreset => preset != null)

export function ToolbarGrounds() {
  return (
    <div className={styles.stack}>
      {grounds.map((preset) => (
        <Sample key={preset} preset={preset} className={styles.face}>
          <p className={styles.name}>{preset}</p>
          <Toolbar aria-label={`Sightings on ${preset}`}>
            <ToolbarCount>Sightings (12)</ToolbarCount>
            <ToolbarGroup>
              <ToolbarButton icon="add">Log Sighting</ToolbarButton>
              <ToolbarSeparator />
              <ToolbarButton iconOnly icon="download">
                Download
              </ToolbarButton>
            </ToolbarGroup>
            <ToolbarBottomRule />
          </Toolbar>
        </Sample>
      ))}
    </div>
  )
}

In print the toolbar is hidden. Pass caption to print the state it carries as one line in its place, such as "54 items · Sorted by name".

API Reference

Toolbar

A Base UI Toolbar: one Tab stop enters it and arrow keys move within it. Compose ToolbarGroup, ToolbarSeparator, ToolbarButton, ToolbarLink, ToolbarCount and ToolbarMore; a Toggle or a Menu trigger joins through Base UI’s render, e.g. <ToolbarItem render={<Toggle />} />. The row takes the band’s ground and has no face of its own. Avoid saturated grounds; on a photo, use media buttons.

PropTypeDescription
caption
React.ReactNode | undefined

The state the toolbar carries, printed as one type-caption line in its place (“54 items · Sorted by recently accessed”); the toolbar itself never prints.

docked
boolean | null | undefined
kind
| 'list'
| 'format'
| 'context'
| 'figure'
| null
| undefined
primary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined
secondary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

ToolbarButton

A Button (§9.2) in the toolbar’s roving focus. Defaults: variant="text", size sm (md when iconOnly). A text Button with an icon is icon-only below 768 px of the toolbar’s width, its label kept as the accessible name and shown in a Tooltip; from 768 px the label returns. An iconOnly Button always carries the Tooltip. Disabled buttons stay focusable so their name can be read.

PropTypeDescription
butted
'start' | 'end' | null | undefined

Butts the button against an adjacent field on its start or end edge, as in the butted submit. Default: none.

icon
| 'menu'
| 'search'
| 'circle'
| 'arrow_forward'
| 'arrow_upward'
| 'expand_more'
| 'close'
| 'remove'
| 'add'
| 'check'
| 'chevron_right'
| 'chevron_left'
| 'more_horiz'
| 'play_arrow'
| 'pause'
| 'download'
| 'zoom_in'
| 'zoom_out'
| 'recenter'
| 'help'
| 'mail'
| undefined

One optional functional glyph (§6.10), inline tier, FILL 0.

iconOnly
boolean | null | undefined

true hides the label and shows only icon. Default false.

iconPosition
'start' | 'end' | undefined

Which side of the label the glyph sits on (a text Button’s chevron trails).

lowPriority
boolean | null | undefined

Moves the item into ToolbarMore below 768 px of the toolbar’s width; list it there too, as a MenuItem. Default false.

onMedia
false | boolean | null | undefined

Only with iconOnly.

onPress
PressCallback | undefined

Called on click, after onClick.

primary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale, from the primary roster: the outline edge, labels and focus ring. Never defaulted; omitted, it inherits the scope [D133].

secondary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale: the solid fill and the text glyph. Omitted, solid falls back to the scope’s action scale.

size
'sm' | 'md' | 'lg' | 'xl' | null | undefined

Fixed height: sm 32 px (hit area extended to 44), md 40 px (default), lg 48 px, xl 56 px for the page’s single transactional action.

variant
'text' | 'solid' | 'outline' | null | undefined

outline (default) is the outline twin; solid is the page’s primary action, filled with the scope’s action scale; text is a text button whose glyph trails (§9.2).

children
React.ReactNode | undefined

The label: verb plus object, authored in title case [D160].

ToolbarGroup and ToolbarSeparator

Groups related items, --size-px-1 apart (Base UI Toolbar.Group). disabled disables the whole group.

A vertical --border-size-1 rule in --role-rule, --size-px-4 tall with --size-px-2 each side, announced to assistive technology. It takes the axis opposite the toolbar’s and hides beside a pressed segment.

A roving-focus slot for a control that is not a ToolbarButton: a Toggle (§9.4) or a Menu trigger (§9.7), passed as render. Disabled items stay focusable by default.

A Link (§9.3) in the toolbar’s roving focus (Base UI Toolbar.Link). Default kind="nav": no rest underline, Link’s hover and ring.

PropTypeDescription
external
boolean | null | undefined

Adds the arrow-open mark and “(external site)” for assistive technology. Default false.

index
boolean | null | undefined

Adds the screen-only visited ✓ used in long indexes, such as reference and archive lists. Default false [D174, D175].

kind
| 'nav'
| 'title'
| 'inline'
| 'standalone'
| 'backref'
| 'noteref'
| null
| undefined

Which build: inline (default) underlines in running text; standalone is the caps module link with a trailing ›; nav has no rest underline and marks the current page; title stretches over a card or list item; noteref and backref are the note call and return (§9.3).

list
boolean | null | undefined

With kind="nav": a list link (nav-panel, footer, drawer and breadcrumb lists), whose hover is --role-link-hover color only, plus the --ds-stroke-1-5 --role-accent underline where that ink is --primary12. Without it, nav is bare navigation text (bar and utility items, page numbers, toolbar links), whose hover is the --border-size-2 --role-accent underline (§9.3) [D181]. Default false.

muted
boolean | null | undefined

Rests in --role-muted instead of --primary12, as a breadcrumb’s ancestors. Hover takes --role-link-hover only (the underline is added where that ink is --primary12); with kind="nav" it replaces the bar item’s bare-text underline (§9.3, §9.8) [D181]. Default false.

primary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale: the link text and focus ring. Never defaulted; omitted, it inherits the scope [D133].

secondary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale: the accent underline. Never defaulted; omitted, it inherits the scope.

className
string | undefined

Extra class names, added after the module’s own.

ToolbarCount, ToolbarMore and ToolbarBottomRule

The list’s count in mono type-data, --primary12, following LTA’s parentheses: “Items (54)".

The “…” overflow: an icon-only more_horiz text Button named “More” that opens a Menu. It shows only below 768 px of the toolbar’s width, while the lowPriority items are hidden, so list those items here.

PropTypeDescription
label
string | undefined

The trigger’s accessible name and tooltip. Default “More”.

primary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale inside the popup’s white scope. Omitted, the white preset’s default: the popup never takes the trigger’s scales [D133].

secondary
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale inside the popup. It drives nothing but a destructive item’s fallback.

container
| HTMLElement
| ShadowRoot
| React.RefObject<HTMLElement | ShadowRoot | null>
| null
| undefined

The element the portal renders into. Default: document.body.

align
Align | undefined

Alignment to the trigger. Default start.

alignOffset
number | OffsetFunction | undefined

Offset along the alignment axis in px.

side
Side | undefined

Side of the trigger. Default bottom for a menu, the end side for a submenu.

sideOffset
number | OffsetFunction | undefined

Distance from the trigger in px. Default 8 (--size-px-2).

collisionPadding
Padding | undefined

Clearance from the viewport edge in px before the popup shifts or flips. Default 16.

children
React.ReactNode | undefined

MenuItems mirroring the toolbar’s lowPriority items, lowest priority last.

keepMounted
boolean | undefined

Keeps the portal mounted while closed.

The optional --border-size-1 rule along the toolbar’s bottom edge; place it last. Decorative.

PropTypeDescription
variant
'rule' | 'hairline' | null | undefined

rule (default) in --role-rule, governing a list or table; hairline in --role-hairline, where whitespace also separates.

Additional types

toolbar
toolbarBottomRule

The optional bottom rule (§9.11): rule governs a list or table; hairline where whitespace also separates.

type toolbarBottomRule = toolbarBottomRule
ToolbarBottomRuleProps

Props for ToolbarBottomRule: span props plus the rule’s role.

type ToolbarBottomRuleProps = Omit<
  React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>,
  'children'
> &
  VariantProps<__type> & { variant?: 'rule' | 'hairline' | null }
ToolbarButtonProps

Props for ToolbarButton: Button props plus the overflow priority.

type ToolbarButtonProps = (
  | {
      iconOnly?: false | null;
      onMedia?: false | null;
      size?: 'sm' | 'md' | 'lg' | 'xl' | null;
      icon?:
        | 'menu'
        | 'search'
        | 'circle'
        | 'arrow_forward'
        | 'arrow_upward'
        | 'expand_more'
        | 'close'
        | 'remove'
        | 'add'
        | 'check'
        | 'chevron_right'
        | 'chevron_left'
        | 'more_horiz'
        | 'play_arrow'
        | 'pause'
        | 'download'
        | 'zoom_in'
        | 'zoom_out'
        | 'recenter'
        | 'help'
        | 'mail';
      children?: React.ReactNode;
    }
  | {
      iconOnly: true;
      onMedia?: boolean | null;
      size?: 'sm' | 'md' | 'lg' | null;
      icon:
        | 'menu'
        | 'search'
        | 'circle'
        | 'arrow_forward'
        | 'arrow_upward'
        | 'expand_more'
        | 'close'
        | 'remove'
        | 'add'
        | 'check'
        | 'chevron_right'
        | 'chevron_left'
        | 'more_horiz'
        | 'play_arrow'
        | 'pause'
        | 'download'
        | 'zoom_in'
        | 'zoom_out'
        | 'recenter'
        | 'help'
        | 'mail';
      children: React.ReactNode;
    }
) & {
  /**
   * `outline` (default) is the outline twin; `solid` is the page's primary
   * action, filled with the scope's action scale; `text` is a text button
   * whose glyph trails (§9.2).
   */
  variant?: 'text' | 'solid' | 'outline' | null;
  /**
   * Butts the button against an adjacent field on its `start` or `end`
   * edge, as in the butted submit. Default: none.
   */
  butted?: 'start' | 'end' | null;
  /**
   * Primary Radix scale, from the primary roster: the outline edge, labels
   * and focus ring. Never defaulted; omitted, it inherits the scope [D133].
   */
  primary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  /**
   * Secondary Radix scale: the `solid` fill and the `text` glyph. Omitted,
   * `solid` falls back to the scope's action scale.
   */
  secondary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | 'amber'
    | 'blue'
    | 'cyan'
    | 'grass'
    | 'green'
    | 'jade'
    | 'lime'
    | 'mint'
    | 'orange'
    | 'sky'
    | 'teal'
    | 'yellow'
    | null;
  /** Which side of the label the glyph sits on (a `text` Button's chevron trails). */
  iconPosition?: 'start' | 'end';
  /**
   * Called on click, after `onClick`.
   * @deprecated Use `onClick`. Kept as an alias from the react-aria Button.
   */
  onPress?: PressCallback;
  /**
   * Moves the item into ToolbarMore below 768 px of the toolbar's width;
   * list it there too, as a MenuItem. Default `false`.
   */
  lowPriority?: boolean | null;
}
ToolbarCountProps

Props for ToolbarCount: span props.

type ToolbarCountProps = React.ComponentPropsWithRef<'span'>
ToolbarGroupProps

Props for ToolbarGroup: Base UI Toolbar.Group props.

type ToolbarGroupProps = ToolbarGroup.ToolbarGroupProps
ToolbarItemProps

Props for ToolbarItem: Base UI Toolbar.Button props; pass the control as render.

type ToolbarItemProps = ToolbarButton.ToolbarButtonProps
ToolbarLinkProps

Props for ToolbarLink: Link props.

type ToolbarLinkProps = {
  /**
   * Which build: `inline` (default) underlines in running text; `standalone`
   * is the caps module link with a trailing ›; `nav` has no rest underline
   * and marks the current page; `title` stretches over a card or list item;
   * `noteref` and `backref` are the note call and return (§9.3).
   */
  kind?: 'nav' | 'title' | 'inline' | 'standalone' | 'backref' | 'noteref' | null;
  /**
   * Adds the screen-only visited ✓ used in long indexes, such as reference
   * and archive lists. Default `false` [D174, D175].
   */
  index?: boolean | null;
  /**
   * Adds the arrow-open mark and "(external site)" for assistive technology.
   * Default `false`.
   */
  external?: boolean | null;
  /**
   * With `kind="nav"`: a list link (nav-panel, footer, drawer and breadcrumb
   * lists), whose hover is `--role-link-hover` color only, plus the
   * `--ds-stroke-1-5` `--role-accent` underline where that ink is
   * `--primary12`. Without it, `nav` is bare navigation text (bar and
   * utility items, page numbers, toolbar links), whose hover is the
   * `--border-size-2` `--role-accent` underline (§9.3) [D181]. Default
   * `false`.
   */
  list?: boolean | null;
  /**
   * Rests in `--role-muted` instead of `--primary12`, as a breadcrumb's
   * ancestors. Hover takes `--role-link-hover` only (the underline is added
   * where that ink is `--primary12`); with `kind="nav"` it replaces the bar
   * item's bare-text underline (§9.3, §9.8) [D181]. Default `false`.
   */
  muted?: boolean | null;
  /**
   * Primary Radix scale: the link text and focus ring. Never defaulted;
   * omitted, it inherits the scope [D133].
   */
  primary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  /**
   * Secondary Radix scale: the accent underline. Never defaulted; omitted,
   * it inherits the scope.
   */
  secondary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | 'amber'
    | 'blue'
    | 'cyan'
    | 'grass'
    | 'green'
    | 'jade'
    | 'lime'
    | 'mint'
    | 'orange'
    | 'sky'
    | 'teal'
    | 'yellow'
    | null;
  /** Extra class names, added after the module's own. */
  className?: string;
}
ToolbarMoreProps

Props for ToolbarMore: the overflow menu’s items and popup options.

type ToolbarMoreProps = {
  /**
   * Primary Radix scale inside the popup's `white` scope. Omitted, the
   * white preset's default: the popup never takes the trigger's scales [D133].
   */
  primary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  /** Secondary Radix scale inside the popup. It drives nothing but a destructive item's fallback. */
  secondary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | 'amber'
    | 'blue'
    | 'cyan'
    | 'grass'
    | 'green'
    | 'jade'
    | 'lime'
    | 'mint'
    | 'orange'
    | 'sky'
    | 'teal'
    | 'yellow'
    | null;
  /** Side of the trigger. Default `bottom` for a menu, the end side for a submenu. */
  side?: Side;
  /** Alignment to the trigger. Default `start`. */
  align?: Align;
  /** Distance from the trigger in px. Default 8 (`--size-px-2`). */
  sideOffset?: number | OffsetFunction;
  /** Offset along the alignment axis in px. */
  alignOffset?: number | OffsetFunction;
  /** Clearance from the viewport edge in px before the popup shifts or flips. Default 16. */
  collisionPadding?: Padding;
  /** The element the portal renders into. Default: `document.body`. */
  container?: HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null;
  /** Keeps the portal mounted while closed. */
  keepMounted?: boolean;
  /** MenuItems mirroring the toolbar's `lowPriority` items, lowest priority last. */
  children?: React.ReactNode;
  /** The trigger's accessible name and tooltip. Default "More". */
  label?: string;
}
ToolbarProps

Props for Toolbar: Base UI Toolbar.Root props plus the kind, docked and color axes.

type ToolbarProps = ToolbarRootProps &
  ToolbarVariants & {
    kind?: 'list' | 'format' | 'context' | 'figure' | null;
    docked?: boolean | null;
    primary?:
      | 'olive'
      | 'sage'
      | 'slate'
      | 'sand'
      | 'gray'
      | 'mauve'
      | 'brown'
      | 'bronze'
      | 'gold'
      | 'red'
      | 'ruby'
      | 'crimson'
      | 'tomato'
      | 'pink'
      | 'plum'
      | 'indigo'
      | 'iris'
      | 'violet'
      | 'purple'
      | null;
    secondary?:
      | 'olive'
      | 'sage'
      | 'slate'
      | 'sand'
      | 'gray'
      | 'mauve'
      | 'brown'
      | 'bronze'
      | 'gold'
      | 'red'
      | 'ruby'
      | 'crimson'
      | 'tomato'
      | 'pink'
      | 'plum'
      | 'indigo'
      | 'iris'
      | 'violet'
      | 'purple'
      | 'amber'
      | 'blue'
      | 'cyan'
      | 'grass'
      | 'green'
      | 'jade'
      | 'lime'
      | 'mint'
      | 'orange'
      | 'sky'
      | 'teal'
      | 'yellow'
      | null;
    caption?: React.ReactNode;
  }
ToolbarSeparatorProps

Props for ToolbarSeparator: Base UI Toolbar.Separator props.

type ToolbarSeparatorProps = ToolbarSeparator.ToolbarSeparatorProps
ToolbarVariants
type ToolbarVariants = {
  primary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  secondary?:
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'ruby'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | 'amber'
    | 'blue'
    | 'cyan'
    | 'grass'
    | 'green'
    | 'jade'
    | 'lime'
    | 'mint'
    | 'orange'
    | 'sky'
    | 'teal'
    | 'yellow'
    | null;
  kind?: 'list' | 'format' | 'context' | 'figure' | null;
  docked?: boolean | null;
}

Specification: DESIGN-SYSTEM.md §9.11 (toolbar), §9.12 (separators) and §5.10.2 (reflow).