The docs
Every surface in this project draws the same sheet from the same small file: the editor where a canvas is argued into shape, the command line that keeps it honest, the fence that draws it in your docs, the server that reads it to an agent. These pages cover each one end to end — install, the day-to-day, and the edges where it stops. SPEC.md is the letter of the law underneath.
One canvas, edited in place. There is no form beside a preview and no Save ahead of an export: the sheet on the projector during the workshop is the file in the repo after it. Blur commits a field, Esc reverts it, and everything else materializes on approach.
Sheet · JSON · Markdown are tabs over the same document, not three documents. The Sheet is where editing happens. The JSON view shows the exact bytes an export would write, and is editable with an explicit Apply — one commit, one undo step, validated by the same parser as import, so a pasted canvas never replaces a good one halfway. The Markdown view is read-only, and only ever an export.
No account, no server. The canvas autosaves to localStorage on every commit, but that slot is a safety net, not storage — the durable form is the Canvas file you export. The chrome keeps one score, unexported changes: has this canvas left the browser in a form that can come back? Exporting or importing a Canvas file or HTML artifact clears it; PNG, SVG and Markdown never do, because none of them could bring the canvas back. Anything that would replace a canvas still carrying unexported changes — an import, an example, a blank sheet — asks first. That is the app's only dialog.
Every accepted change — a field committed on blur, one add, one removal, one reorder — is one undo step. ⌘Z undoes, ⇧⌘Z redoes; mid-edit, ⌘Z reverts the field first. Importing or replacing the canvas is a session boundary, not an edit: it clears history.
The whole sheet is operable from the keyboard — every add, removal, pick and reorder. ⌘/ (Ctrl+/ on Windows and Linux) opens the Reference: the shortcut list, and the ddd-crew's own material on the method. Everything else the editor teaches in place — picker descriptions, the placeholder questions in an empty section, the footer legend.
Four invented domains ship in the Examples menu, from every section filled to mid-workshop with the open questions still winning. The same files are committed under examples/ and re-import as-is.
Everything on this page reads or writes one format:
<name>.bcc.json, a small, flat JSON file meant to be committed
beside the code it describes. It is deliberately boring — one key order, one indent,
one trailing newline — because boring is what diffs well.
version is currently 2; an older file is migrated up on
read, in the editor and everywhere else, because everything reads through the one
parser.bcc fmt restores them for a file
edited by hand. Honest diffs are the point: a canvas that churns bytes it does
not mean cannot live next to code..bcc.json. That is what
bcc ls globs on and what the editor's Import… accepts; a canvas
saved as shipping.json is invisible to both. The directory and the
rest of the name are yours.The full schema, shape rules and migration story are in SPEC.md §3, with a complete reference example.
Five ways out of the browser; two of them come back. The split is the one that
matters, because the editor's only dirty state is a canvas that has not left in a
form that can return — and the editor's export and bcc render call the
same function, so the files are byte-identical whichever wrote them.
comes back
.bcc.json — Canvas file.bcc.html — HTML artifactone way out
.bcc.svg — image<img> at it.
bcc check re-renders committed images and compares bytes, so a
stale picture fails a check instead of being believed..bcc.png — image.bcc.md — Markdownbcc treats canvases the way a toolchain treats source: list them, check
them, format them, build artifacts from them. It runs in plain Node, straight off
this repo — nothing is published, and there is nothing to install:
$ alias bcc='npx --yes github:mitchellvanw/bc-canvas-editor'
The first call clones and installs; later ones come out of npm's cache and start in
about a second. npx resolves main at the moment it runs —
pin a commit (…bc-canvas-editor#<sha>) if you need
reproducibility.
$ bcc ls # what canvases are here, what each is for, how full each one is $ bcc check # do they all still read, and are the images beside them current $ bcc fmt # canonical bytes, in place $ bcc render orders.bcc.json # the HTML artifact, beside the canvas $ bcc render --svg orders.bcc.json
check and fmt are what make a canvas behave like source
code rather than an attachment. check reads every canvas through the
parser the editor's Import… uses — a canvas that passes here opens there — and exits
1 if anything does not check out, stale images included. fmt rewrites a
canvas in its canonical bytes; fmt --check names what would change and
writes nothing, for CI. render writes the .bcc.html
artifact, or a .bcc.svg with --svg —
--out <file> redirects a single render, and only
render --svg ever needs a browser, and only to measure a height that
--height <pixels> can supply instead.
Every command takes --root <directory>: where bcc
looks, and the furthest it goes. It defaults to the working directory, and no
path — symlinks resolved first — ever reaches outside it. A canvas is any
*.bcc.json, or the canvas embedded in a *.bcc.html
artifact, found by walking the root and skipping hidden directories,
node_modules, dist and build. A directory the
walk cannot open stops that branch and nothing else; bcc ls names it at
the end rather than losing every canvas already found.
A fence is how documentation stops lying: point it at the canvas, and the sheet is drawn where the fence stands every time the file is built or previewed. The canvas stays the single source, and the picture can no longer fall behind it.
```bcc
../canvases/order-fulfillment.bcc.json
```what the fence draws
One path, resolved relative to the markdown file holding it. Nothing else goes in the fence — no JSON, no options. Everywhere the fence is not drawn, the path is what a reader sees, which is why it holds a pointer rather than a canvas.
Two adapters draw it — the remark plugin when a site builds, the VS Code extension while you write — over one shared contract, with the same renderer inlined into both: a fence means the same thing on both, and the sheet it draws cannot drift from the one the editor exports. A fence that cannot be drawn leaves a visible placeholder saying why, never a blank, and the build or preview carries on.
One plugin covers every site generator built on
unified. Two lines for Astro; two paragraphs for
Docusaurus, because two of its choices fight raw HTML and inline styles. Install
this repo — there is no registry package; #<sha> pins a commit:
$ npm i github:mitchellvanw/bc-canvas-editor
import remarkBcc from 'bc-canvas-editor/remark';
export default defineConfig({ markdown: { remarkPlugins: [remarkBcc] } });Docusaurus compiles both .md and .mdx through MDX, which
fails the build on a raw HTML node unless rehype-raw is in the
pipeline. And it renders through React, whose server pass escapes the text inside a
<style> element — an inlined stylesheet arrives mangled and the
sheet draws in Times. So the CSS comes from a file instead:
// inside the docs/blog preset options
remarkPlugins: [[remarkBcc, { css: 'imported' }]],
rehypePlugins: [[rehypeRaw, { passThrough: ['mdxjsEsm', 'mdxFlowExpression',
'mdxJsxFlowElement', 'mdxJsxTextElement', 'mdxTextExpression'] }]]@import 'bc-canvas-editor/sheet.css';Two rules. Raw HTML has to survive the pipeline — remark-rehype and
rehype-stringify both take allowDangerousHtml: true — and
the sheet's CSS has to reach the page one of two ways:
css |
what it does | when |
|---|---|---|
'inline' (default) |
a <style> in the page, once, ahead of the first fence |
one or two pages; nothing to configure |
'imported' |
nothing — you import bc-canvas-editor/sheet.css |
React-rendered sites, and fences on many pages: the fonts are ~190 KB and a stylesheet is fetched once |
The sheet brings its own fonts, its own reset and its own design tokens, all under
one .bcc-canvas wrapper — it neither picks up your site's styles nor
pushes anything onto the page around it. When a fence cannot be drawn, the
placeholder lands in the page and the plugin puts a warning on the VFile;
escalating is your site's call, through its own fail-on-warn. root is
the other option the plugin takes: paths never resolve outside it, and it defaults
to the directory the build runs in.
The extension puts the drawn sheet in VS Code's built-in markdown preview, live: edit the canvas, and every preview holding a fence to it redraws — including a fence pointing at a file you have not written yet, which heals the moment you write it.
There is no marketplace listing. Build a .vsix from a checkout of the
repo and install it by hand:
$ cd vscode && npx --yes @vscode/vsce package --no-dependencies $ code --install-extension bc-canvas-fence-0.0.1.vsix
A fence that cannot be drawn gets the same visible placeholder as everywhere else; the full detail, which names paths on your machine, goes to an output channel instead — BC Canvas: Show fence log in the command palette. A problem is reported once, not once per keystroke.
bcc fence there stays a code block.../ does not.The server's whole job is getting a canvas into a conversation, as prose. It never
writes — changing files is bcc's job — and that split is the design:
reading is a server, writing is a command line, and the plugin is what knows the
choreography. stdio only; nothing leaves the machine.
The server ships inside the bc-canvas plugin, together with three skills and a reviewer agent. There is nothing to build — the plugin carries the server ready to run, and Node is its only requirement:
/plugin marketplace add mitchellvanw/bc-canvas-editor /plugin install bc-canvas@bc-canvas-editor
The plugin does not carry bcc; set up the
npx alias in the project you are working in.
bcc_read_canvas — one canvas as prose: the sheet in
words, a third to a half shorter than the file. It reads a
.bcc.json or the canvas embedded in a .bcc.html, and
brings an older file up to date on the way through.bcc_explain — what a section is for, in the
ddd-crew's own questions, with the vocabulary it draws on and a row to calibrate
against.bcc://canvas/<path>. Attach one from the host's own UI to give
a conversation a context to work against.The facilitation layer, findable by typing / and the name:
In Claude Code the plugin is the whole setup: the server's root defaults to the
project the session started in, and bcc runs over Bash like any other
command. Claude Desktop has no shell, so it has no bcc — Desktop gets
reading and explaining, and the skills cannot write there. Desktop also starts
servers at the filesystem root, so --root is required — the plugin's
own server entry leaves it out and is refused at launch rather than walking your
disk. Connect the server yourself, naming the directory your canvases live under:
{
"mcpServers": {
"bc-canvas": {
"command": "node",
"args": [
"/path/to/bc-canvas-editor/mcp/dist/server.js",
"--root", "/path/to/your-project"
]
}
}
}--root is the only directory the server reads — a path that resolves
outside it is refused, symlinks followed out of it included — and it is fixed for
the life of the config, whichever project you happen to be standing in.
There is no conflict check — no mtime, no revision hash. Canvases are committed files, so git is already the conflict detector and git is already the undo; anything here would be a second, weaker history. The rest — protocol revision, development setup, what the resource listing leaves out and why — is in mcp/README.md.
attribution
The Bounded Context Canvas is by the ddd-crew, licensed CC BY 4.0. These docs just explain the paper.