Skip to content
WANCRAFT

A.154Buttons & Controls

Formatting Toolbar

An ARIA toolbar with toggle buttons, an alignment radio group, a style menu, overflow into “More”, tooltips and scoped shortcuts.

NewReactMotionTailwindadvanced

Overview

Formatting Toolbar is the control strip of a text editor, built to the WAI-ARIA toolbar pattern. Bold, italic, underline and strikethrough are toggle buttons with aria-pressed, alignment is a radio group with a sliding indicator, and the text style is a menu button listing paragraph and heading levels at their real sizes. The whole strip is one tab stop with roving focus. When its container gets narrow, a ResizeObserver collapses trailing items into a “More” menu that keeps every option reachable as checkbox and radio items. Tooltips show the label and a platform-aware shortcut (⌘ on Apple devices, Ctrl elsewhere) on hover and keyboard focus, with neighbours opening instantly once one is showing. Shortcuts can be scoped to a target element such as your editor. The component only manages state, so it works with any editor or preview.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/formatting-toolbar.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/formatting-toolbar.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/formatting-toolbar.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { FormattingToolbar } from "@/components/ui/formatting-toolbar";
import * as React from "react";

export function FormattingToolbarExample() {
  const editorRef = React.useRef<HTMLParagraphElement>(null);

  return (
    <div className="space-y-3">
      <FormattingToolbar
        shortcutTarget={editorRef}
        onValueChange={(format) => console.log(format)}
      />
      <p ref={editorRef} tabIndex={0}>Focus here and press Ctrl+B</p>
    </div>
  );
}

03Props

PropTypeDefaultDescription
valueFormatState—Controlled state { bold, italic, underline, strike, align: "left" | "center" | "right", block: "p" | "h1" | "h2" | "h3" }.
defaultValuePartial<FormatState>—Initial state when uncontrolled.
onValueChange(value: FormatState) => void—Called with the next state on every change.
size"sm" | "md""md"28 or 32px buttons.
tooltipsbooleantrueLabel + shortcut tooltips on hover and keyboard focus.
tooltipDelaynumber500Hover delay before the first tooltip, in ms. Neighbours open instantly for 400ms after.
shortcutsbooleantrueMod+B / I / U, Mod+Shift+X / L / E / R and Mod+Alt+0–3, on the toolbar and shortcutTarget.
shortcutTargetRefObject<HTMLElement | null>—Element whose key presses also trigger the shortcuts (your editor or preview).
overflowbooleantrueCollapse trailing items into a “More” menu when the container is too narrow.
accentstring"#FF7448"Pressed tint, sliding indicator and focus rings.
labelstring"Text formatting"Accessible name of the toolbar.
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the full-width wrapper (className for alignment, etc.).

04Features

  • 01Toggle buttons, an alignment radio group with a sliding indicator, a text-style menu button and separators
  • 02One tab stop with roving focus across every control
  • 03ResizeObserver overflow: trailing items collapse into a “More” menu of checkbox and radio items
  • 04Tooltips with platform-aware shortcut hints, a delay group and Escape to dismiss
  • 05Keyboard shortcuts scoped to the toolbar and an optional target element; ⌘ on Apple, Ctrl elsewhere (SSR-safe)
  • 06Controlled or uncontrolled state; renders no editor, so it drives anything
motion · springmotion · layoutinput · clickinput · keyboardinput · hoverinput · focus

05Accessibility

  • role="toolbar" with an accessible name; Left / Right arrows move focus with wrapping, Home / End jump to the ends, and Tab leaves the toolbar in one step.
  • Bold, italic, underline and strikethrough are buttons with aria-pressed; alignment buttons are role="radio" with aria-checked inside a labelled role="radiogroup" and are checked with Space or Enter.
  • The text-style and More buttons are menu buttons (aria-haspopup, aria-expanded, aria-controls): ArrowDown, Enter or Space opens them, Arrow keys, Home / End and typeahead move through menuitemradio / menuitemcheckbox items, and Escape returns focus.
  • Shortcuts are exposed with aria-keyshortcuts and a spoken description (“Control B”); tooltips are aria-hidden visual duplicates of that information and close on Escape without moving focus.
  • Separators are role="separator"; with prefers-reduced-motion the indicator jumps and menus only fade.

06Limitations

  • The item set is fixed (text style, four marks, three alignments); fork the ITEMS list to change it.
  • It manages formatting state only; applying it to a rich-text editor's selection is up to your editor.