Callouts
A callout is a framed aside that holds blocks: a note, a tip, a warning, a danger notice, or a
quotation. It is the core callout node, so every stesuraSchema() has it and no setup is needed.
| Kind | Looks like | Default label |
|---|---|---|
note | Blue fill, blue left rule, info icon | Note |
tip | Green fill, lightbulb icon | Tip |
warning | Amber fill, warning icon | Warning |
danger | Red fill, error icon | Danger |
quote | No fill, grey left rule, indented. No title bar unless it has a title | Quote |
kind is a free string: any lowercase identifier ([a-z][a-z0-9-]*, up to 32 characters) is
kept. Kinds outside the five above look like note until you style them.
A callout holds any block except tables (content: "block+", see
Table placement): paragraphs, headings, lists, code blocks, images,
equations, other callouts. Its optional title is one line of plain text shown in the title bar.
In the editor
- Insert tab → Callout: pick a kind. The selected blocks, or the block holding the cursor, are wrapped in the callout.
>at the start of a paragraph wraps it in a quote, as in markdown. Backspace right after undoes the conversion and restores the>.- Callout tab: appears while the cursor is in a callout. It has one toggle per kind, the title field (Enter or leaving the field applies it, Escape reverts) and Remove callout, which keeps the content.
- Right-click inside a callout: Callout type changes its kind, Remove callout keeps the
content. The menu’s key is
callout, socontextMenuDefaults={{ callout: false }}hides it and acontextMenuExtensionsentry with that key replaces it (see Customizing the Menus). - Enter on an empty last paragraph leaves the callout. On an empty paragraph in the middle, it splits the callout in two.
- Backspace at the start of the first block moves that block out of the callout.
- Arrow keys past the callout’s last line (or before its first) stop on a gap cursor beside it, even when nothing follows it; typing there starts a paragraph.
The title bar is not editable in place: change it from the Callout tab.
Commands
import { insertCallout, liftCallout, setCalloutAttrs } from "@stesura/core/commands";
import { findSelectedCallout } from "@stesura/core/helpers";| Command / helper | Signature | Behaviour |
|---|---|---|
insertCallout | (kind?) => Command | Wraps the selected blocks, or the cursor’s block, in a callout of kind (default note). Where they can’t be wrapped, inserts a callout holding a placeholder paragraph. The kind is sanitized. |
setCalloutAttrs | ({ kind?, title? }) => Command | Sets the kind and/or title of the callout around the selection (the innermost one when nested) or of a selected callout. title: "" or null removes the title. False outside a callout. |
liftCallout | Command | Removes the callout around the selection, keeping its blocks. |
findSelectedCallout | (selection) => { node, pos, depth } | null | The callout the selection is in, or the selected callout. |
CALLOUT_KINDS, toCalloutKind and toCalloutTitle (@stesura/core/helpers) list the
built-in kinds and sanitize untrusted values. The toolbar’s probes and selectors are exported as
calloutProbes and calloutSelectors from @stesura/editor-react, for custom buttons
(see Customizing the Toolbar).
Customizing
Colours
Each built-in kind reads three variables. Set them anywhere above the editor:
:root {
--stesura-callout-note-bg: #f5f3ff;
--stesura-callout-note-border: #7c3aed;
--stesura-callout-note-title: #6d28d9;
}| Variable | Default (note / tip / warning / danger / quote) |
|---|---|
--stesura-callout-<kind>-bg | #eff6ff / #f0fdf4 / #fffbeb / #fef2f2 / transparent |
--stesura-callout-<kind>-border | #2563eb / #16a34a / #d97706 / #dc2626 / #d1d5db |
--stesura-callout-<kind>-title | #1d4ed8 / #15803d / #b45309 / #b91c1c / #4b5563 |
The page is a sheet of paper, so there are no dark-mode variants. The same CSS styles the PDF export, which captures the editor.
Custom kinds
Create one with insertCallout("summary"), setCalloutAttrs({ kind: "summary" }) or
> [!SUMMARY] in markdown, then style it by its data-kind:
.ProseMirror [data-type="callout"][data-kind="summary"] {
--callout-border: #0d9488;
--callout-bg: #f0fdfa;
--callout-title: #0f766e;
}Any other property can go on that selector. Add room with padding or borders, never margins: pagination charges a callout’s padding and borders as its chrome and zeroes block margins, so a margin would put every page break after it off by that much.
The title bar
The bar shows the kind’s icon and the title, or the kind’s localized label when there is no
title. To draw your own, give a UI extension a calloutFrame:
import type { CalloutFrameProps, StesuraUIExtension } from "@stesura/editor-react";
const SummaryAwareFrame = ({ kind, title, label, icon }: CalloutFrameProps) => (
<>
{kind === "summary" ? <SummaryIcon /> : icon}
<span>{title ?? label}</span>
</>
);
const calloutStyle: StesuraUIExtension = { calloutFrame: SummaryAwareFrame };It renders inside the non-editable bar, which pagination measures as part of the callout’s box:
again, padding rather than margins. The last extension declaring calloutFrame wins.
Limits
- The Insert menu and the Callout tab’s toggles list the five built-in kinds only. A custom kind is created through your own UI, markdown or the commands.
- An untitled custom kind is labelled “Callout: summary” with the note icon, unless your
calloutFramesays otherwise. - DOCX export colours custom kinds like
note: it doesn’t read your CSS. The kind itself survives the round trip.
Pagination and PDF
A callout that fits on a page is never split: when it doesn’t fit in the space left, it moves to
the next page whole (the keepTogether spec key).
A callout taller than a page splits between its children; its frame is then drawn across the page
gap. Callouts inside table cells follow the cell’s own break rules.
Two callouts in a row are kept 8px apart (6pt, Normal’s paragraph spacing), collapsing with their neighbors’ spacing like any block.
Markdown
@stesura/markdown reads and writes callouts as blockquotes, in GitHub’s alert syntax with an
optional Obsidian-style title:
> [!WARNING] Back up first
> The migration rewrites every table.
> A plain blockquote is a quote.[!KIND]is case-insensitive and keeps kinds outside the built-ins.- A blockquote without a marker is a
quotecallout. An escaped\[!NOTE]stays text. - Block attrs (
{#id style=…}) stay on the blocks inside. The callout’s own id is not written. - A blockquote holding something a callout can’t (a table) is unwrapped, with a warning.
The AI tools exchange documents as this markdown, so the assistant reads and writes callouts with no extra setup.
DOCX
Word has no callout, so export picks the closest native shape and tags it for import:
- Quote, without a title: its paragraphs on Word’s built-in
Quotestyle (paragraphs with their own style keep it). On import, a run ofQuoteparagraphs becomes one quote callout, back onNormal. - Every other kind, and a titled quote: a one-cell table with the kind’s fill and left rule,
the title as a bold first paragraph, and
stesura:callout:<kind>in the table’s description (w:tblDescription). Import turns such a table back into the callout, reading a bold-only first paragraph as the title. Nested callouts become nested tables, and are restored even past the two levels other nested tables are flattened at.
A tracked insertion or deletion of a whole callout is exported on the table row and comes back on import. See Word Import & Export.
Track changes
Inserting, pasting or deleting a whole callout is a tracked block change, and edits inside it are
tracked like any text. Wrapping blocks in a callout (insertCallout on a selection) or removing
one (liftCallout) is applied untracked: the engine has no tracked representation for wrap
and unwrap yet.
Next steps
- Diagrams: Mermaid diagrams, the other documentation block
- Theming: the editor’s other variables
- Extending the Schema:
keepTogetheranddefaultSpacingfor your own wrappers