Skip to content

Architecture decision records

This directory records the decisions that shape the BlockNote module: what was decided, why, what else was considered, and what the decision costs. Each record is written to be read on its own, by somebody who has never seen the design notes that preceded it. If you want to know why the module looks the way it does, read these before reading the code.

Format

Every record is one Markdown file with these sections, in this order.

Section What it holds
Title # NNNN. Short imperative statement of the decision
Status Starts with one of Proposed, Accepted, Superseded by NNNN, Rejected, optionally followed by what closed or amended it. A record one part of which a later record replaces reads Accepted; <the part> is superseded by NNNN (date). A proposal that depends on an unfinished experiment says what closes it.
Date The date the status last changed, ISO 8601.
Context The forces: what the module needs, what the upstream library and Drupal core actually do, what was measured. Facts only, with enough detail that the decision can be re-argued.
Decision What was chosen, stated as a rule the code must follow.
Alternatives considered Each option that was genuinely on the table and the reason it lost.
Consequences What follows, including the costs and the work the decision creates. Honest, not promotional.
References Upstream source, core source, issue links, other records.

A record that waited on an experiment keeps the sections that stated the question and recorded the result, between Decision and Alternatives considered.

Prose is not hard wrapped: one paragraph per line.

A record may cite a file the release tarball leaves out: .gitattributes export-ignores docs/backlog/, CONTRIBUTING.md, recipes/ and every __tests__ directory (js/__tests__/, js/src/__tests__/, modules/blocknote_media/js/__tests__/ and tests/modules/blocknote_test/js/__tests__/), so in a release those paths dangle, and the repository holds the file they name. A record may also cite a draft in docs/drafts/, which only the maintainer's checkout holds: the folder is in .gitignore.

Numbering

Files are named NNNN-short-slug.md, numbered from 0001 in the order the decisions were taken, never reused. The number is the record's permanent identity, so it is safe to cite from code comments, issues and commit messages.

Superseding

A record is never deleted and never rewritten to say something different. When a decision changes, write a new record that states the new decision and explains what changed, and edit only the Status line of the old one to read Superseded by NNNN, with the date. The old record stays in place so that the reasoning behind code that still exists remains readable, and so that a decision is not quietly reversed twice.

Small corrections of fact inside a record are allowed when the decision itself does not change. A record whose status is Proposed may be moved to Accepted or Rejected by editing the status and date, which is how the records that wait on an experiment are closed. A correction that changes what a paragraph claims is introduced in place with Amended YYYY-MM-DD. before the new text, so that the original reasoning stays readable.

Index

Record Decision Status
0001 One document per field item, stored in a versioned envelope Accepted
0002 Text formats are the editor profile and the permission set Accepted
0003 TypeScript and Vite, a committed IIFE bundle, one guarded runtime Accepted
0004 Public output is render arrays and our own semantic HTML Accepted
0005 One plugin type describes every block, default blocks included Accepted
0006 Media is a submodule, and a media block stores a UUID and a view mode Accepted; view mode enforcement is superseded by 0015, the media theme variable's registry alter by 0034, alt text not being stored and the preview's trust boundary by 0036
0007 An uploaded file is stored as file:<uuid> and resolved when the page is built Accepted
0008 An entity link is custom inline content, not a link mark Accepted; the placement in blocknote_linkit and the linkit search are superseded in part by 0028, the substitution prop's placement and its permitted list by 0029
0009 A document has an identity, a repository, an updater and two post-commit events Accepted; the change origin's three cases are superseded by 0039
0010 The editor enforces the format's link and color policy as the author edits Accepted; the pasted block and cell color posture is superseded by 0024
0011 The editor is named and described by its field, and every function has a keyboard route Accepted; the media block's own controls are superseded by 0019
0012 A read-only widget renders the document instead of disabling the editor Accepted
0013 The editor draws the box of a form control on its editable element Accepted; the disabled state is superseded by 0020
0014 A block declares its caption and its toolbar and block menu items at client registration Accepted
0015 Permitted media view modes are curation, strict where the author chooses and lenient where the page renders Accepted
0016 A setting lives where it is enforced Accepted
0017 The filter's settings govern alignment, heading levels and toggles, code block languages, and merged cells Accepted
0018 The media block stores an alignment and no width Accepted; the alignment's values and default are superseded by 0044
0019 The media block draws no box, and its functions live in the formatting toolbar and the block menu Accepted; the toolbar's item set for a selection is superseded by 0025
0020 A disabled editor draws the disabled box of the theme's inputs Accepted
0021 A caption refuses every block conversion rule and shortcut Accepted
0022 Escape leaves the editor, and Tab then continues in the page Accepted
0023 Refusal messages speak to the author, name what was refused in their own words, and say what they can do Accepted
0024 A forbidden block or cell color is clamped as the author edits, and a stripped document is written back after a refused save Accepted
0025 The formatting toolbar is keyed on the selection kind for a caption block Accepted
0026 A click into a link places the caret, and an upstream editing behavior changed for Drupal authors is an editor toggle, on by default Accepted
0027 One plugin type describes every custom inline content type, on the same terms as a block Accepted
0028 The base module links to content by uuid and resolves it by core's link target rule Accepted; the rejected substitution-prop alternative is superseded by 0029
0029 A content link may carry a substitution the format permits, resolved by a decorated resolver Accepted
0030 A text format may hold one paragraph, and says which element it renders as Accepted; the "Summary or trimmed" formatter and the trimmed formatter left for later are superseded by 0045
0031 Subscript and superscript join the fixed style vocabulary Accepted; the server's posture on a run carrying both is superseded by 0037
0032 A link's new-tab choice is a boolean style on the link's own runs, permitted per format Accepted
0033 Editor icons are BlockNote's own, held as documented icon assets Accepted
0034 A block or inline content type declares its template's theme variables on its attribute Accepted
0035 Documents that do not render in full are counted by cron in state and reported on the status report Accepted
0036 A media block may override an image item's alternative text, stored as the text and a decorative flag Accepted
0037 The editor and the server agree on the marks one run may carry: a save refuses what the editor's schema excludes, and the editor loads such a run without code, or without subscript Accepted
0038 Formatted text converts to a document through a PHP walker whose rules are tagged services Accepted
0039 Stored text converts in place into a document field beside it Accepted
0040 Markup the converter cannot convert is kept in a block no plugin provides Accepted
0041 Converted references to files and content resolve through two more kinds of rule Accepted
0042 Media is found before it is created, and created when its document is written Accepted
0043 A heading carries an id made from its words, and a table of contents is a block the author places Accepted
0044 A media block floats left or right, and breaks text by default Accepted
0045 A trimmed formatter, with a summary field shown in its place Accepted
0046 A format lists styles per block type, and a block carries one as classes on its outer element Accepted
0047 A media gallery is a block whose children are media blocks, shown in one view mode Accepted
0048 Blocks that are needed but not common go in blocknote_extras, and the callout is the first Accepted