Kanban Diagrams
A kanban board is a set of ordered columns, each holding an ordered stack of cards laid out left to right in source order.
Overview
The model lives in Atelier\Diagram\Kanban\: KanbanDiagram, KanbanColumn (id, label, ordered cards), and KanbanCard (id, label). All model classes are immutable.
Reach for it to show work items moving across the stages of a workflow: a backlog split into todo, doing, and done, or a review pipeline. Layout preserves source order and never sorts, balances, or applies WIP limits. Use a flowchart instead when you need to show transitions between stages rather than the items parked in each one.
Format
Kanban round-trips through a kanban subset. Indentation is strict: 4 spaces for the title and columns, 8 spaces for cards.
kanban
title Delivery board
todo [Todo]
REQ-1 [Write parser]
UI-2 [Review SVG output]
doing [Doing]
LAY-3 [Layout docs]
done [Done]
SCN-1 [Scene renderer]
Ids match [A-Za-z_][A-Za-z0-9_-]*, so issue-like ids such as REQ-1 are accepted. Labels are the bracket text and cannot contain ]. Tabs, other indentation widths, WIP limits, assignees, tags, and markdown labels are rejected, never skipped. Full grammar: Mermaid support.
Builder API
KanbanDiagramBuilder (or Diagram::kanban(), which returns it) is the one way to build the model:
use Atelier\Diagram\Diagram;
$diagram = Diagram::kanban()
->title('Delivery board')
->column('todo', 'Todo') // declare a column
->card('todo', 'REQ-1', 'Write parser') // add a card to a column
->card('todo', 'UI-2', 'Review SVG output')
->column('doing', 'Doing')
->card('doing', 'LAY-3', 'Layout docs')
->column('done', 'Done')
->card('done', 'SCN-1', 'Scene renderer')
->build();
Diagram::of($diagram)->saveSvg('kanban.svg');
title($text)- sets a title (not part of the Mermaid grammar; dropped on serialization).column($id, $label)- declares a column. ThrowsInvalidArgumentExceptionon an empty label or a duplicate id.card($columnId, $id, $label)- appends a card to an existing column. ThrowsInvalidArgumentExceptionwhen the column is unknown or the card id is a duplicate.build()- throwsInvalidArgumentExceptionwhen no column was declared.
Options
| Setting | Default | Effect |
|---|---|---|
title(string) |
none | heading above the board; builder-only, dropped on serialization |
Theme::spacingUnit |
per preset | lane width, gaps, header height, card padding |
Theme::fontSize |
per preset | card and header text size |
- Order: layout is source-order based and deterministic. Columns run left to right, cards top to bottom, in declaration order. There is no sorting or balancing.
- Card wrapping: card labels wrap into multiple text lines when they exceed the card content width; column width is fixed.
Layout\Kanban\KanbanLayoutEngine renders columns as fixed-width lanes with a tinted header showing the label and card count, then stacks cards with a stable gap.
Themes
Kanban uses accentColors per column: each column takes the next accent (cycled by index) as its header tint, so a larger palette gives more distinct columns. Lanes and cards use nodeFillColor and nodeStrokeColor, labels use textColor, and card ids and column counts use mutedTextColor.
All presets apply (Theme::default(), dark(), blueprint(), mono(), neutral()); mono() collapses the per-column accents to one tone. See Theming.
Theme::blueprint() on the same columns. Column width comes from the layout engine, not the theme, so the board keeps its proportions.
Parse
Header keyword: kanban.
$diagram = Diagram::fromMermaid($source); // throws ParseException
$result = Diagram::tryFromMermaid($source); // non-throwing ParseResult
toMermaid() / toMarkdown() serialize a built model back; titles are dropped because the grammar has no place for them. Full grammar and round-trip rules: Mermaid support.
Debug
On a rejected source, inspect ParseResult::getDiagnostic() (a ParserDiagnostic with a message, a stable code, and a source span) or catch ParseException (getLineNumber(), getSourceLine()). Render a code frame with ParserDiagnosticFormatter:
$result = Diagram::tryFromMermaid($source);
if ($result->isFailure()) {
echo \Atelier\Diagram\Parser\Support\ParserDiagnosticFormatter::format($result->getDiagnostic());
}
See parser diagnostics.