Skip to content

0048. Blocks that are needed but not common go in blocknote_extras, and the callout is the first

Status: Accepted

Date: 2026-11-03

Context

The base module provides BlockNote's fourteen default blocks and a table of contents, and blocknote_media provides the media block (0006). On 2026-11-02 the maintainer decided that blocks a site needs but not every site uses go in a submodule of their own, and that a callout comes first. Older sites mark a callout with a class on a paragraph, such as highlighted, which the converter reports as class_not_mapped unless a site maps it to a quote or a background color, the nearest a document holds today.

A block's client library is attached only in the formats that enable the block (0005), which ExtensionTest checks, so a block a format does not enable costs that format nothing.

The test module already has a block of type callout (tests/modules/blocknote_test/src/Plugin/BlockNoteBlock/Callout.php). It is the worked example of the plugin contract in docs/extending.md section 10; calloutMismatch shares its library to prove the version refusal, and bun run extension:oot builds its client half from a package of its own and compares the result with the committed build. It has the tones info, warning and danger and a title, renders an aside with its children after it, and draws a tone select and a title field inside the block, which 0011 records as reached with Tab only from a block that cannot be nested; its title has no block menu entry.

BlockNote's own alert examples at v0.57.0 are the models upstream offers. 06-custom-schema/05-alert-block-full-ux holds one line of inline content in one of four tones, warning, error, info and success, each with a Material Design icon the author changes by clicking it. 13-alert-block-with-title-and-body makes that line the alert's title and its child blocks its body: renderFrame draws one box around both, and its experimental_keyboard settings keep the body inside, so Enter in the title starts the first child block and Enter in an empty last child leaves the alert.

An aside is a complementary landmark when it is scoped to body or main rather than to sectioning content (HTML-AAM), so in a theme that does not put the body field inside an article or a section, a page gains one landmark per callout. role="note" marks a section whose content is parenthetic or ancillary to the main content (WAI-ARIA 1.2). A heading inside a box would enter the document's table of contents from inside it (0043).

The editor's icons are Remix Icon glyphs from the tag 0033 pins, held as documented .svg assets in a directory of each bundle's own.

Decision

Blocks a site needs but not every site uses go in blocknote_extras, in modules/blocknote_extras. Each is its own plugin with its own client bundle and CSS, enabled per format. The callout is its only block for now. Its documentation is a section of its own in the README, and its HTML is in docs/rendering.md.

A callout, callout, holds inline content as its first paragraph, and the blocks nested under it as children, rendered inside the box: lists, more paragraphs, an image. It has an optional title, plain text, as a prop. It holds no heading at any depth: the editor refuses to turn a block inside a callout into a heading, and the save refuses one, with a message worded by 0023's rule. Amended 2026-11-03. On the page, a heading at any depth below a callout, which only a document written without validation holds, renders as a paragraph with the same text and no id, and is left out of the outline and the table of contents, through BlockNoteBlockRenderedChildrenInterface, which the renderer asks while it lists the headings and before it builds the block; a review found it rendered as a heading inside the box, outline entry included. The editor's refusal is spoken and also shown above the editor until the next edit, through the runtime's showRefusal(), since a refused ## or paste otherwise gave an author who does not hear the announcement no sign of why nothing happened.

A callout has one of five tones, its tone prop: note, the default and neutral, then info, success, warning and danger. The format's settings for the block choose which are offered, all of them by default, as a format chooses its colors. A tone the format no longer offers is enforced as the format's other policy values are (0010): the editor replaces it with the first tone the format offers, in the order above, and names the change in the stripped-content notice, and the save refuses it. On the page a stored tone the format no longer offers renders as note. Amended 2026-11-03. A callout with no tone renders in the first tone the format offers, since BlockNote leaves a prop at its default out of the document and the editor's default is that tone; only a stored tone the format no longer offers renders as note.

Each tone has one fixed icon, a Remix Icon glyph, rendered as inline SVG with aria-hidden="true", focusable="false" and fill="currentColor", so the icon needs no stylesheet and follows forced colors; the hidden tone name carries the meaning. The glyphs are unmodified files vendored in the submodule, held as js/icons/remix/ holds the editor's (0033), and the editor draws the same ones. The author does not choose the icon. A theme may hide it with CSS or replace it in the template.

On the page a callout is a note, neither a landmark nor a heading:

<div class="blocknote-block blocknote-block--callout blocknote-callout--warning" role="note">…</div>

Inside it come, in order: the tone's name, visually hidden and followed by a colon, such as Warning:, so that the tone is heard; the icon's svg; the title, when there is one, in a <p class="blocknote-callout__title">; then the content and the children. A theme may override the template, as for every block.

An author inserts a callout with the slash menu's Callout, and sets its tone and title from the formatting toolbar and the Alt+F10 block menu, a Tone select and a Title field, as the media block's functions are set (0019). The block menu holds no text field (0014), so its Title entry opens a form, as the media block's Alternative text entry does (0036).

The converter does not recognize callouts. A site maps its own class with one line, such as ClassMapping::block('p', 'highlighted', 'callout', ['tone' => 'info']), which the documentation shows, and the converter carries the mapped element's inline content and children into the callout. The callout's conversion helper turns a heading that is the callout's first child into its title when the title is empty, and any other heading into a paragraph, and lists each in the report. Amended 2026-11-03. "The callout's first child" is read as the first thing in the callout: a heading becomes the title only when the callout has no text of its own before it, because below that text it would be shown above it; after text it becomes a paragraph like any other.

The test module's blocks are renamed sample_callout and sample_callout_mismatch, and test what they test today. The type callout is the extras block's.

Alternatives considered

Adding the blocks to the base module. Rejected. A submodule keeps the base module lean, and a site enables only what it uses.

One line of text, as BlockNote's example 05 holds. Rejected. Real callouts often hold a list or two paragraphs, and children cost the renderer nothing extra, as a quote's do.

A heading for the title, or headings inside. Rejected. Either would enter the document's outline and its table of contents from inside a box.

An aside, as the test module's block renders. Rejected. It adds a landmark per callout wherever the body is not inside sectioning content.

An icon the author chooses, or the Material Design icons of BlockNote's example. Rejected. A picker is a feature nobody asked for, and a second icon set would be a second question for the Licensing Working Group (0033).

An icon drawn by the stylesheet. Rejected. A theme that turns the optional stylesheet off would lose it, and an inline SVG filled with currentColor follows forced colors with no rule of its own.

Recognizing callout classes in the converter. Rejected. There is no common callout markup across editors, and guessing from classes such as alert, callout, note or highlighted would convert things that are not callouts on some sites.

Keeping callout for the test module's block and naming the extras block otherwise. Rejected. The test block exists to prove the extension contract, not to be a callout.

Consequences

Records 0003, 0005, 0011, 0014 and 0023, and docs/extending.md section 10, call the test module's block "the callout". From this record on that block is sample_callout, and the records are not rewritten. Its theme hook, template and library follow its type, and bun run extension:oot compares the renamed build. The names Callout tone and Callout title and the Tab limit in 0011 are the test block's. The extras callout's tone and title are reached through the toolbar and the block menu, so its title has the keyboard route the test block's never had.

The submodule is a third tree of bundles: its dist/ joins the directories bun run verify rebuilds and the dist-reproducible job compares, its js/__tests__ is export-ignored as the media submodule's is, and its icon directory joins those scripts/generate-third-party-notices.ts lists.

The tone icons are glyphs the module does not hold yet, five new files from the same Remix Icon tag, documented as 0033 requires, and the first such glyphs a public page carries. Inline SVG holds no style attribute, so 0004's one exception stays the only one.

The tone's hidden name uses core's visually-hidden class, so system/base is attached wherever a callout renders, as it is for a new-tab link's hidden text (docs/rendering.md section 4).

A converted callout keeps no heading as a heading: its first becomes the title where there is none, every other one a paragraph, and the report lists each, so a heading the old page showed inside a box is not lost and does not block the save.

A format that offers fewer tones still renders every stored callout, a dropped tone as note, so a page never loses one.

References

  • BlockNote v0.57.0: examples/06-custom-schema/05-alert-block-full-ux, examples/06-custom-schema/13-alert-block-with-title-and-body.
  • HTML Accessibility API Mappings, aside; WAI-ARIA 1.2, note.
  • This module: tests/modules/blocknote_test/src/Plugin/BlockNoteBlock/Callout.php, tests/modules/blocknote_test/src/Plugin/BlockNoteBlock/CalloutMismatch.php, tests/modules/blocknote_test/js/callout.tsx, tests/modules/blocknote_test/templates/blocknote-block--callout.html.twig, tests/src/FunctionalJavascript/ExtensionTest.php, scripts/verify-extension-build.ts, scripts/generate-third-party-notices.ts, src/Convert/ClassMapping.php, .gitattributes.
  • docs/extending.md sections 10 and 14, docs/rendering.md sections 2, 3 and 4.
  • 0003, 0004, 0005, 0006, 0010, 0011, 0014, 0016, 0019, 0023, 0033, 0036, 0043.