Atelier

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.

Delivery boardTodo3REQ-1Write parserUI-2Review SVG outputDOC-4Theme examplesDoing2LAY-3Layout docsSVG-5Polish rendererDone2SCN-1Scene rendererTEST-2Parser corpus

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. Throws InvalidArgumentException on an empty label or a duplicate id.
  • card($columnId, $id, $label) - appends a card to an existing column. Throws InvalidArgumentException when the column is unknown or the card id is a duplicate.
  • build() - throws InvalidArgumentException when 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.

Delivery boardTodo3REQ-1Write parserUI-2Review SVG outputDOC-4Theme examplesDoing2LAY-3Layout docsSVG-5Polish rendererDone2SCN-1Scene rendererTEST-2Parser corpus

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.