Selection toolbar
FreeActions 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.
pnpm dlx shadcn@latest add https://caliperui.site/r/selection-toolbar.jsonOstra 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.