blokhaus

FloatingToolbar

A floating format toolbar that appears on text selection.

FloatingToolbar renders a floating toolbar that appears above (or below) the user's text selection. It provides quick access to text formatting, links, colors, fonts, undo/redo, and text direction controls. The toolbar is rendered in a React Portal attached to document.body.

Import

import { FloatingToolbar } from "@blokhaus/core";

Props

PropTypeDefaultDescription
colorPaletteColorPaletteDEFAULT_COLOR_PALETTECustom color palette for text color and highlight color pickers.
fontFamiliesFontFamilyEntry[]DEFAULT_FONT_FAMILIESCustom font family options for the font picker.

Basic usage

Add FloatingToolbar as a child of EditorRoot:

app/editor/page.tsx
"use client";

import { EditorRoot, FloatingToolbar, InputRulePlugin } from "@blokhaus/core";

export default function EditorPage() {
  return (
    <EditorRoot
      namespace="my-editor"
      className="min-h-[400px] p-4 border rounded"
    >
      <FloatingToolbar />
      <InputRulePlugin />
    </EditorRoot>
  );
}

Toolbar buttons

The floating toolbar includes the following controls, organized into groups separated by visual dividers:

Undo / Redo

ButtonActionShortcut
UndoUndo the last changeCmd+Z (Mac) / Ctrl+Z
RedoRedo the last undone changeCmd+Shift+Z (Mac) / Ctrl+Shift+Z

Undo and Redo buttons are automatically disabled when there is nothing to undo or redo.

Text formatting

ButtonFormatShortcut
BoldboldCmd+B
ItalicitalicCmd+I
UnderlineunderlineCmd+U
StrikethroughstrikethroughCmd+Shift+S
Inline CodecodeCmd+E

Active formats are visually highlighted with the --blokhaus-accent color.

Opens the link input dialog. Active when the cursor is inside a link node.

ButtonActionShortcut
LinkOpen link inputCmd+K

Color picker

Opens an inline dropdown with two sections:

  • Text color -- 10 colors (Default, Gray, Brown, Orange, Yellow, Green, Blue, Purple, Pink, Red)
  • Background highlight -- 10 colors (same range)

Dispatches SET_TEXT_COLOR_COMMAND and SET_HIGHLIGHT_COLOR_COMMAND.

Font picker

Opens an inline dropdown listing available font families. Each font name is rendered in its own typeface for a visual preview. Dispatches SET_FONT_FAMILY_COMMAND.

Direction toggle

Toggles the current block's text direction between LTR and RTL. The icon changes to indicate the direction that will be applied on click.

Custom color palette

Pass a custom ColorPalette to override the default colors:

import { FloatingToolbar } from "@blokhaus/core";
import type { ColorPalette } from "@blokhaus/core";

const customPalette: ColorPalette = {
  text: [
    { label: "Default", value: "", swatch: "currentColor" },
    { label: "Brand", value: "var(--brand-color)", swatch: "#6366f1" },
    { label: "Muted", value: "var(--muted-color)", swatch: "#94a3b8" },
  ],
  highlight: [
    { label: "Default", value: "", swatch: "transparent" },
    { label: "Highlight", value: "var(--highlight-color)", swatch: "#fef08a" },
  ],
};

<FloatingToolbar colorPalette={customPalette} />;

Custom font families

Pass a custom FontFamilyEntry[] to override the default fonts:

import { FloatingToolbar } from "@blokhaus/core";
import type { FontFamilyEntry } from "@blokhaus/core";

const customFonts: FontFamilyEntry[] = [
  { label: "Default", value: "", preview: "system-ui, sans-serif" },
  { label: "Georgia", value: "Georgia, serif", preview: "Georgia, serif" },
  {
    label: "Courier",
    value: '"Courier New", monospace',
    preview: '"Courier New", monospace',
  },
];

<FloatingToolbar fontFamilies={customFonts} />;

Positioning behavior

The toolbar uses position: fixed and is positioned relative to the DOM selection's bounding rectangle:

  • Default: appears above the selection, horizontally centered
  • Near top of viewport: automatically repositions below the selection to stay visible

Position updates are throttled to one requestAnimationFrame per frame to avoid layout thrashing during rapid selection changes.

Visibility rules

The toolbar shows when:

  • A non-collapsed RangeSelection exists (text is selected)
  • The selection has a non-zero bounding rectangle

The toolbar hides when:

  • The selection is collapsed (no text selected)
  • A TableSelection is active (tables have their own context menu)
  • The user presses Escape

Keyboard accessibility

  • The toolbar does not trap focus. Focus remains in the editor at all times.
  • Pressing Escape closes the toolbar and returns focus to the editor.
  • All buttons include aria-label attributes and tooltips with keyboard shortcuts.
  • Active formatting states are communicated via aria-pressed.

Styling

The toolbar uses inline styles with CSS custom properties from the Blokhaus theme:

  • --blokhaus-popover-bg -- toolbar background
  • --blokhaus-popover-border -- toolbar border
  • --blokhaus-popover-shadow -- toolbar shadow
  • --blokhaus-accent -- active button background
  • --blokhaus-accent-foreground -- active button text/icon color
  • --blokhaus-icon-secondary -- inactive button icon color
  • --blokhaus-separator -- divider color between button groups

The toolbar includes a frosted glass effect via backdrop-filter: blur(20px) saturate(180%).

Notes

  • FloatingToolbar is a client component ('use client').
  • It renders into a React Portal on document.body, so it works correctly regardless of the editor's CSS overflow or positioning context.
  • On touch devices, consider also including MobileToolbar for a better mobile experience. The FloatingToolbar still works on touch devices but the MobileToolbar provides a more touch-friendly, fixed-position alternative.
  • The toolbar automatically closes sub-menus (color picker, font picker) when the toolbar itself hides.
  • The floating toolbar is hidden when a table selection is active, since TableActionMenu provides its own context menu for table operations.