caliper uidocs

Selection toolbar

Free

Actions for the words you selected, where you selected them: the toolbar grows out of the selection and leaves with it; Alt+T reaches it from the keyboard.

Installs components/ui/selection-toolbar.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/selection-toolbar.json
Ostra wants intercompany in scope for the first quarter. The security review is the last item before signature, and João has asked for the order form to arrive before the board meets on the 24th.
Select text, then press Alt+T for actions.

Select some words in the paragraph.

When to use #

Use it for

  • Comment, highlight or link on documents and transcripts.
  • Quoting part of a message.

Skip it when

  • Rich text editing; use the editor's own toolbar.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/selection-toolbar.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { Bold, Highlighter, Link2, MessageSquarePlus } from "lucide-react"
import { useRef, useState } from "react"
import { SelectionToolbar } from "@/components/ui/selection-toolbar"

export function SelectionToolbarDemo() {
  const area = useRef<HTMLDivElement>(null);
  const [last, setLast] = useState("Select some words in the paragraph.");
  return (
    <div className="grid w-full max-w-md gap-3">
      <div ref={area} tabIndex={-1} className="rounded-[12px] p-4 text-sm leading-7 shadow-[inset_0_0_0_1px_var(--border)] outline-none">
        Ostra wants intercompany in scope for the first quarter. The security review is the last item before signature, and João has asked for the order form to arrive before the board meets on the 24th.
      </div>
      <SelectionToolbar
        container={area}
        label="Selected text"
        actions={[
          { id: "comment", label: "Comment", icon: <MessageSquarePlus />, onSelect: (text) => setLast(`Comment on “${text}”`) },
          { id: "highlight", label: "Highlight", icon: <Highlighter />, onSelect: (text) => setLast(`Highlighted “${text}”`) },
          { id: "bold", label: "Bold", icon: <Bold />, onSelect: (text) => setLast(`Bold “${text}”`) },
          { id: "link", label: "Link", icon: <Link2 />, onSelect: (text) => setLast(`Link “${text}”`) },
        ]}
      />
      <p className="text-xs text-muted-foreground" role="status">{last}</p>
    </div>
  );
}

API reference #

SelectionToolbar

component
PropTypeDefault
container *RefObject<HTMLElement | null>—
actions *readonly SelectionAction[]—
labelstring"Selection"
classNamestring—

Accessibility #

It exposes toolbar semantics, with aria-label and aria-hidden kept in step with state.

KeyBehaviour
EscapeCloses or cancels, then returns focus to where it came from.
→Moves to the next item or value.
←Moves to the previous item or value.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.