# Toolbar

A row of grouped controls with a toolbar role and roving keyboard focus.

## Installation

```tsx
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarInput,
  ToolbarLink,
  ToolbarMenuTrigger,
  ToolbarSeparator,
  ToolbarToggle,
  ToolbarToggleGroup,
} from "@makeplane/propel/components/toolbar";
```
## Usage

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { Menu, MenuContent, MenuItem } from "@makeplane/propel/components/menu";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarMenuTrigger,
  ToolbarSeparator,
  ToolbarToggle,
  ToolbarToggleGroup,
} from "@makeplane/propel/components/toolbar";
import {
  AlignCenterOutline,
  AlignLeftOutline,
  AlignRightOutline,
  BoldOutline,
  CodeOutline,
  ImageOutline,
  ItalicOutline,
  LinkOutline,
  ListOutline,
  ToggleListOutline,
  NumberedListOutline,
  ChatOutline,
  QuoteOutline,
  StrikethroughOutline,
  TableEditorOutline,
  UnderlineOutline,
} from "@makeplane/propel/icons";

const TEXT_STYLES = ["Paragraph", "Heading 1", "Heading 2", "Heading 3"];
const FONTS = ["Sans", "Serif", "Mono"];

export default function BasicDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Formatting">
      <Menu>
        <ToolbarMenuTrigger aria-label="Text style" label="Text" />
        <MenuContent>
          {TEXT_STYLES.map((style) => (
            <MenuItem key={style} label={style} />
          ))}
        </MenuContent>
      </Menu>
      <Menu>
        <ToolbarMenuTrigger aria-label="Font" label="Aa" />
        <MenuContent>
          {FONTS.map((font) => (
            <MenuItem key={font} label={font} />
          ))}
        </MenuContent>
      </Menu>
      <ToolbarButton aria-label="Comment" icon={<Icon icon={ChatOutline} />} />
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={UnderlineOutline} />} />
        <ToolbarToggle aria-label="Strikethrough" icon={<Icon icon={StrikethroughOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarToggleGroup defaultValue={["left"]} aria-label="Text alignment">
        <ToolbarToggle
          value="left"
          aria-label="Align left"
          icon={<Icon icon={AlignLeftOutline} />}
        />
        <ToolbarToggle
          value="center"
          aria-label="Align center"
          icon={<Icon icon={AlignCenterOutline} />}
        />
        <ToolbarToggle
          value="right"
          aria-label="Align right"
          icon={<Icon icon={AlignRightOutline} />}
        />
      </ToolbarToggleGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Lists">
        <ToolbarToggle aria-label="Bullet list" icon={<Icon icon={ListOutline} />} />
        <ToolbarToggle aria-label="Numbered list" icon={<Icon icon={NumberedListOutline} />} />
        <ToolbarToggle aria-label="Checklist" icon={<Icon icon={ToggleListOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Blocks">
        <ToolbarToggle aria-label="Quote" icon={<Icon icon={QuoteOutline} />} />
        <ToolbarToggle aria-label="Code block" icon={<Icon icon={CodeOutline} />} />
        <ToolbarButton aria-label="Insert table" icon={<Icon icon={TableEditorOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={LinkOutline} />} />
      <ToolbarButton aria-label="Insert image" icon={<Icon icon={ImageOutline} />} />
    </Toolbar>
  );
}
```
## Examples

### Elevations

`raised` draws its own card (border and shadow) so the toolbar can float over content; `flat` draws no surface and sits flush inside an existing bar.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import type { ToolbarElevation } from "@makeplane/propel/components/toolbar";
import { BoldOutline, ItalicOutline, LinkOutline, UnderlineOutline } from "@makeplane/propel/icons";

function Formatting({ elevation }: { elevation: ToolbarElevation }) {
  return (
    <Toolbar elevation={elevation} size="sm" aria-label="Text formatting">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={UnderlineOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={LinkOutline} />} />
    </Toolbar>
  );
}

export default function ElevationsDemo() {
  return (
    <div className="flex flex-col gap-6">
      <Formatting elevation="raised" />
      <Formatting elevation="flat" />
    </div>
  );
}
```

### Sizes

`size` sets the control hit targets: `sm` packs them to 24px, `md` gives them 28px. Orthogonal to `elevation`. Figma's `density` maps here — `compact` → `sm`, `comfortable` → `md` (raised floaters use `sm`).

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import type { ToolbarSize } from "@makeplane/propel/components/toolbar";
import { BoldOutline, ItalicOutline, LinkOutline, UnderlineOutline } from "@makeplane/propel/icons";

function Formatting({ size }: { size: ToolbarSize }) {
  return (
    <Toolbar elevation="raised" size={size} aria-label="Text formatting">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
        <ToolbarToggle aria-label="Underline" icon={<Icon icon={UnderlineOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarButton aria-label="Insert link" icon={<Icon icon={LinkOutline} />} />
    </Toolbar>
  );
}

export default function SizesDemo() {
  return (
    <div className="flex flex-col gap-6">
      <Formatting size="sm" />
      <Formatting size="md" />
    </div>
  );
}
```

### With filter

`ToolbarInput` is an inline filter box that joins the roving tab order, its height tracking the toolbar's size like every other item.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarGroup,
  ToolbarInput,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import { BoldOutline, ItalicOutline } from "@makeplane/propel/icons";

export default function WithFilterDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Issue actions">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarInput aria-label="Filter issues" placeholder="Filter…" />
    </Toolbar>
  );
}
```

### With link

`ToolbarLink` is a toolbar item that navigates — an `<a>` sharing the same roving tab order and icon chrome as the buttons around it. Its required `icon` takes the bare glyph, so name it with `aria-label`.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarGroup,
  ToolbarLink,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import { BoldOutline, HistoryOutline, ItalicOutline } from "@makeplane/propel/icons";

export default function WithLinkDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Document actions">
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarLink
        href="#"
        onClick={(event) => event.preventDefault()}
        aria-label="View edit history"
        icon={<Icon icon={HistoryOutline} />}
      />
    </Toolbar>
  );
}
```

### With labels

`ToolbarButton`, `ToolbarToggle`, and `ToolbarLink` take an optional `label` after the icon (e.g. "Comment", "Link"): the square icon box widens to hug icon + text, with the size's gap and side padding. The visible `label` names the control; icon-only items still take `aria-label`.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import {
  Toolbar,
  ToolbarButton,
  ToolbarGroup,
  ToolbarLink,
  ToolbarSeparator,
  ToolbarToggle,
} from "@makeplane/propel/components/toolbar";
import {
  BoldOutline,
  HistoryOutline,
  ItalicOutline,
  LinkOutline,
  ChatOutline,
} from "@makeplane/propel/icons";

export default function WithLabelsDemo() {
  return (
    <Toolbar elevation="raised" size="sm" aria-label="Selection actions">
      <ToolbarButton label="Comment" icon={<Icon icon={ChatOutline} />} />
      <ToolbarSeparator />
      <ToolbarGroup aria-label="Text formatting">
        <ToolbarToggle aria-label="Bold" icon={<Icon icon={BoldOutline} />} />
        <ToolbarToggle aria-label="Italic" icon={<Icon icon={ItalicOutline} />} />
      </ToolbarGroup>
      <ToolbarSeparator />
      <ToolbarToggle label="Link" icon={<Icon icon={LinkOutline} />} />
      <ToolbarSeparator />
      <ToolbarLink
        href="#"
        onClick={(event) => event.preventDefault()}
        label="History"
        icon={<Icon icon={HistoryOutline} />}
      />
    </Toolbar>
  );
}
```

### With tooltips

Toolbar controls are usually icon-only, so pair each with a `Tooltip` naming it (plus a dimmed shortcut hint). A shared `TooltipProvider` keeps open and close timing consistent across the bar.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { Toolbar, ToolbarToggle } from "@makeplane/propel/components/toolbar";
import { Tooltip, TooltipProvider } from "@makeplane/propel/components/tooltip";
import { BoldOutline, ItalicOutline, UnderlineOutline } from "@makeplane/propel/icons";

export default function WithTooltipsDemo() {
  return (
    <TooltipProvider>
      <Toolbar elevation="raised" size="sm" aria-label="Text formatting">
        <Tooltip label="Bold" shortcut="⌘ B">
          <ToolbarToggle
            aria-label="Bold"
            aria-keyshortcuts="Meta+B"
            icon={<Icon icon={BoldOutline} />}
          />
        </Tooltip>
        <Tooltip label="Italic" shortcut="⌘ I">
          <ToolbarToggle
            aria-label="Italic"
            aria-keyshortcuts="Meta+I"
            icon={<Icon icon={ItalicOutline} />}
          />
        </Tooltip>
        <Tooltip label="Underline" shortcut="⌘ U">
          <ToolbarToggle
            aria-label="Underline"
            aria-keyshortcuts="Meta+U"
            icon={<Icon icon={UnderlineOutline} />}
          />
        </Tooltip>
      </Toolbar>
    </TooltipProvider>
  );
}
```
## API Reference

### Toolbar

The ready-made toolbar: grafts Base UI's `Toolbar.Root` behavior (the `toolbar` role + roving
focus, plus `disabled` / `loopFocus`) onto the styled horizontal `elements/toolbar` row and
shares its `size` with the controls inside via context (so each control packs to match).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` (required) | `"md" \| "sm"` | — | Control hit-target scale: `sm` (24px) or `md` (28px). Orthogonal to `elevation`. Shared with the controls inside via context. Figma's `density` maps here — `compact` → `sm`, `comfortable` → `md` (raised floaters use `sm`). |
| `elevation` (required) | `"raised" \| "flat"` | — | Whether the toolbar draws its own surface (`raised`) or sits flush (`flat`). |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Custom render for the underlying styled toolbar. |

### ToolbarGroup

Groups related controls: grafts Base UI's `Toolbar.Group` behavior (the `group` role + group
`disabled`) onto the styled `ToolbarGroup` cluster. The group shares the toolbar row's `gap-2`
per Figma, so it is a semantic grouping — pair it with `ToolbarSeparator`s to make it visible.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Custom render for the underlying styled toolbar group. |

### ToolbarSeparator

A thin vertical rule that visually divides one cluster of controls from the next: grafts Base
UI's `Toolbar.Separator` behavior (the `separator` role) onto the styled rule.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Custom render for the underlying styled toolbar separator. |

### ToolbarButton

A toolbar action button: grafts Base UI's `Toolbar.Button` (roving-focus item) behavior onto the
styled `ToolbarButton`, taking its `size` from the surrounding `Toolbar` via context.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"md" \| "sm"` | — | Size override; defaults to the surrounding `Toolbar`'s size. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Custom render for the underlying styled toolbar button. |
| `label` | `string` | — | Visible text after the icon (e.g. "Comment"); widens the square icon box to fit. |
| `aria-label` | `string` | — | Accessible name; omit to use the visible `label`. Accessible name for the icon button. |
| `icon` | `ReactNode` | — | Icon element rendered inside the toolbar button. |

### ToolbarToggle

A two-state toolbar button for formatting toggles like bold or italic. Grafts Base UI's
`Toolbar.Button` (roving-focus item) and `Toggle` (pressed state) behaviors onto the styled
`ToolbarButton`, and takes its `size` from the surrounding `Toolbar` via context.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"md" \| "sm"` | — | Size override; defaults to the surrounding `Toolbar`'s size. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Custom render for the underlying styled toolbar button. |
| `label` | `string` | — | Visible text after the icon (e.g. "Comment"); widens the square icon box to fit. |
| `aria-label` | `string` | — | Accessible name; omit to use the visible `label`. Accessible name for the icon toggle. |
| `icon` | `ReactNode` | — | Icon element rendered inside the toolbar toggle. |

### ToolbarToggleGroup

A cluster of `ToolbarToggle`s sharing one selection: grafts Base UI `Toolbar.Group` (toolbar
group semantics + group `disabled`) and `ToggleGroup` (value state) onto the styled toggle-group
container. Drive it with `value`/`defaultValue` + `onValueChange`. An `aria-label` is required.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `aria-label` (required) | `string` | — | Accessible name for the toggle cluster. |

### ToolbarLink

A toolbar item that navigates — Base UI's `Toolbar.Link` (`<a>` in the roving tab order) grafted
onto the toolbar item chrome. Pass an icon element, e.g. `<Icon icon={ExternalLink} />`. An
`aria-label` is required when the item is icon-only.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"md" \| "sm"` | — | Size override; defaults to the surrounding `Toolbar`'s size. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Custom render for the underlying styled toolbar link (e.g. a router `Link`). |
| `label` | `string` | — | Visible text after the icon (e.g. "History"); widens the square icon box to fit. |
| `aria-label` | `string` | — | Accessible name; omit to use the visible `label`. Accessible name for the icon link. |
| `icon` | `ReactNode` | — | Icon element rendered inside the toolbar link. |

### ToolbarInput

An inline text input in the toolbar's roving tab order (e.g. a filter box) — Base UI's
`Toolbar.Input` grafted onto the styled miniature field, its height tracking the toolbar's size.
An `aria-label` is required.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `aria-label` (required) | `string` | — | Accessible name for the field (placeholder is not a name). |
| `size` | `"md" \| "sm"` | — | Size override; defaults to the surrounding `Toolbar`'s size. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Custom render for the underlying styled toolbar input. |

### ToolbarMenuTrigger

The trigger that opens a `ToolbarMenu`: a label plus a disclosure chevron.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | — | Visible menu-trigger label. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, MenuTriggerState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
