# Diagram kinds and settings

> Page: https://docs.plotwork.dev/reference/language/kinds. Index of every page: https://docs.plotwork.dev/llms.txt

The twenty kinds of diagram with an example of each, and the settings a diagram accepts: tones, directions, themes, sequence frames, and schema column flags.

## TL;DR

| Question | Answer |
|---|---|
| What starts a diagram? | A header line naming the kind and a quoted title: `flow "Title"`, `sequence "Title"`, `schema "Title"`, `git "Title"`, `gantt "Title"`, `class "Title"`, `state "Title"`, `timeline "Title"`, `journey "Title"`, `mindmap "Title"`, `treemap "Title"`, `pie "Title"`, `quadrant "Title"`, `layers "Title"`, `c4 "Title"`, `matrix "Title"`, `sankey "Title"`, `xychart "Title"`, `tree "Title"`, `swimlane "Title"`. |
| Who picks colors? | The theme. Authors choose a type or a tone (neutral, blue, violet, aqua, orange, green, amber, red), never a color value. |
| Which directions can a flow take? | `auto`, `right`, `down`, `left`, `up`, written `direction=<value>` on the header. |
| Where is each kind explained in full? | Flow: [Flow node types](https://docs.plotwork.dev/reference/language/flow-nodes.md), [Groups and overlays](https://docs.plotwork.dev/reference/language/groups.md), and [Arrows and edge labels](https://docs.plotwork.dev/reference/language/edges.md). Sequence and schema: [Writing diagrams](https://docs.plotwork.dev/writing-diagrams.md). |

## Diagram kinds

| Kind | Header line |
|---|---|
| `flow` | `flow "Title"` |
| `sequence` | `sequence "Title"` |
| `schema` | `schema "Title"` |
| `git` | `git "Title"` |
| `gantt` | `gantt "Title"` |
| `class` | `class "Title"` |
| `state` | `state "Title"` |
| `timeline` | `timeline "Title"` |
| `journey` | `journey "Title"` |
| `mindmap` | `mindmap "Title"` |
| `treemap` | `treemap "Title"` |
| `pie` | `pie "Title"` |
| `quadrant` | `quadrant "Title"` |
| `layers` | `layers "Title"` |
| `c4` | `c4 "Title"` |
| `matrix` | `matrix "Title"` |
| `sankey` | `sankey "Title"` |
| `xychart` | `xychart "Title"` |
| `tree` | `tree "Title"` |
| `swimlane` | `swimlane "Title"` |

## Examples

### C4: Internet banking system

**Diagram source**

```diagram
c4 "Internet banking system" view=container desc="The containers of the internet banking system and the people and systems around it."

person customer "Personal banking customer" desc="A customer of the bank."
system mainframe "Mainframe banking system" external=on desc="Stores all the core banking data."
system email "Email system" external=on desc="The bank's email system."

boundary bank "Internet banking system" kind=system {
  container web "Web application" tech="Spring MVC" desc="Delivers the static content and the single-page app."
  container spa "Single-page app" tech="Angular" desc="Provides the banking features in the browser."
  container mobile "Mobile app" tech="Xamarin" desc="Provides a limited set of banking features."
  container api "API application" tech="Spring Boot" desc="Provides the banking features as a JSON API."
  db store "Database" tech="Oracle" desc="Stores accounts, credentials, and audit logs."
}

customer -> web : "Visits bigbank.com" tech="HTTPS"
customer -> spa : "Views balances and pays"
customer -> mobile : "Views balances and pays"
web -> spa : "Delivers"
spa -> api : "Makes API calls to" tech="JSON/HTTPS"
mobile -> api : "Makes API calls to" tech="JSON/HTTPS"
api -> store : "Reads and writes" tech="JDBC"
api -> mainframe : "Makes API calls to" tech="XML/HTTPS"
api --> email : "Sends email using" tech="SMTP"
email --> customer : "Sends emails to"
```

*Picture: Internet banking system: container view, 8 elements, 1 boundary, 10 relationships. The containers of the internet banking system and the people and systems around it.*

**Text description**

**Elements:**

- Personal banking customer, person (A customer of the bank.)
- Mainframe banking system, external system (Stores all the core banking data.)
- Email system, external system (The bank's email system.)
- Boundary: Internet banking system, software system
  - Web application, container, Spring MVC (Delivers the static content and the single-page app.)
  - Single-page app, container, Angular (Provides the banking features in the browser.)
  - Mobile app, container, Xamarin (Provides a limited set of banking features.)
  - API application, container, Spring Boot (Provides the banking features as a JSON API.)
  - Database, database, Oracle (Stores accounts, credentials, and audit logs.)

**Relationships:**

- Personal banking customer to Web application: Visits bigbank.com, HTTPS
- Personal banking customer to Single-page app: Views balances and pays
- Personal banking customer to Mobile app: Views balances and pays
- Web application to Single-page app: Delivers
- Single-page app to API application: Makes API calls to, JSON/HTTPS
- Mobile app to API application: Makes API calls to, JSON/HTTPS
- API application to Database: Reads and writes, JDBC
- API application to Mainframe banking system: Makes API calls to, XML/HTTPS
- API application to Email system (dashed): Sends email using, SMTP
- Email system to Personal banking customer (dashed): Sends emails to

### Class: Notification service

**Diagram source**

```diagram
class "Notification service" desc="A notifier queues messages and delivers each one through a channel. Channels are interchangeable, so a new one needs no change to the notifier."

class Notifier stereotype="service" {
  "-queue: Queue<Message>"
  "+send(m: Message): void"
  "+retry(): int"
}

class Message {
  "+id: uuid"
  "+body: String"
  "+priority: Priority"
}

class Priority type=enum {
  "LOW"
  "NORMAL"
  "URGENT"
}

class Template "Template<T>" type=abstract {
  "+render(data: T): String" abstract
  "+named(n: String): Template" static
}

package channels "Channels" {
  class Channel type=interface {
    "+deliver(m: Message): Ack"
  }
  class Email "EmailChannel" {
    "-host: String"
    "+deliver(m: Message): Ack"
  }
  class Sms "SmsChannel" {
    "-gateway: String"
    "+deliver(m: Message): Ack"
  }
}

Notifier "1" owns "*" Message : "queues"
Notifier "1" has "1..*" Channel : "delivers through"
Notifier --> Template : "renders with"
Message --> Priority
Email implements Channel
Sms implements Channel

note on Message : "Kept until every channel has confirmed delivery."
```

*Picture: Notification service: 7 classes, 1 package, 6 relations. A notifier queues messages and delivers each one through a channel. Channels are interchangeable, so a new one needs no change to the notifier.*

**Text description**

**Classes:**

- Notifier: class, service
  - private queue: Queue<Message>
  - public send(m: Message): void
  - public retry(): int
- Message: class
  - public id: uuid
  - public body: String
  - public priority: Priority
- Priority: enum
  - LOW
  - NORMAL
  - URGENT
- Template<T>: abstract class
  - public render(data: T): String (abstract)
  - public named(n: String): Template (static)
- Package Channels:
  - Channel: interface
    - public deliver(m: Message): Ack
  - EmailChannel: class
    - private host: String
    - public deliver(m: Message): Ack
  - SmsChannel: class
    - private gateway: String
    - public deliver(m: Message): Ack

**Relations:**

- Notifier (1) is composed of Message (*): queues
- Notifier (1) aggregates Channel (1..*): delivers through
- Notifier depends on Template<T>: renders with
- Message depends on Priority
- EmailChannel implements Channel
- SmsChannel implements Channel

**Notes:**

- Note on Message: Kept until every channel has confirmed delivery.

### Flow: Login request

**Diagram source**

```diagram
flow "Login request" desc="A user signs in through an API that checks credentials in a database and keeps the session in a cache."

node user  person   "User"
node api   service  "Auth API"  sub="Validates credentials"

group data "Data" {
  node db     database "PostgreSQL"  sub="Password hashes"
  node cache  cache    "Redis"       sub="Sessions"
}

user -> api   : "signs in"       role=identity
api  -> db    : "looks up user"  role=data
db   --> api  : "row"
api  -> cache : "stores session" role=data
```

*Picture: Login request: 4 nodes, 4 connections. A user signs in through an API that checks credentials in a database and keeps the session in a cache.*

**Text description**

**Nodes:**

- User, person
- Auth API (Validates credentials), service
- Data tier: Data
  - PostgreSQL (Password hashes), database
  - Redis (Sessions), cache

**Connections:**

- User to Auth API (identity line): signs in
- Auth API to PostgreSQL (stored data line): looks up user
- PostgreSQL to Auth API (dashed): row
- Auth API to Redis (stored data line): stores session

### Gantt: Product launch plan

**Diagram source**

```diagram
gantt "Product launch plan" start=2026-01-05 unit=week today=2026-02-11 weekends=skip holidays=2026-04-03 desc="A fourteen week plan from research to launch, with design and build running side by side."

section "Discover" {
  task research "User research"  start=2026-01-05  duration=2w  status=done
  task brief    "Product brief"  after=research    duration=1w  status=done
}

section "Design" tone=violet {
  task ux  "UX flows"       after=brief  duration=2w  status=done
  task ui  "Visual design"  after=ux     duration=3w  status=active
  task ds  "Design system"  after=brief  duration=4w  status=active
}

section "Build" tone=aqua {
  task api     "API and data model"  after=brief   duration=5w  status=active
  task web     "Web app"             after=ui,api  duration=4w  status=critical
  task mobile  "Mobile app"          after=ui,api  duration=4w
  milestone beta "Beta ready"        after=web
}

section "Launch" tone=orange {
  task docs  "Help center"        after=ui          duration=3w
  task qa    "Testing and fixes"  after=web,mobile  duration=2w  status=critical
  milestone ga "Public launch"    after=qa          status=critical
}
```

*Picture: Product launch plan: 10 tasks and 2 milestones in 4 sections, from 2026-01-05 to 2026-04-14. A fourteen week plan from research to launch, with design and build running side by side.*

**Text description**

**Section Discover (2026-01-05 to 2026-01-23):**

- User research: 2026-01-05 to 2026-01-16, 12 days, done
- Product brief: 2026-01-19 to 2026-01-23, 5 days, done, after User research

**Section Design (2026-01-26 to 2026-02-27):**

- UX flows: 2026-01-26 to 2026-02-06, 12 days, done, after Product brief
- Visual design: 2026-02-09 to 2026-02-27, 19 days, active, after UX flows
- Design system: 2026-01-26 to 2026-02-20, 26 days, active, after Product brief

**Section Build (2026-01-26 to 2026-03-30):**

- API and data model: 2026-01-26 to 2026-02-27, 33 days, active, after Product brief
- Web app: 2026-03-02 to 2026-03-27, 26 days, critical, after Visual design and API and data model
- Mobile app: 2026-03-02 to 2026-03-27, 26 days, after Visual design and API and data model
- Beta ready: milestone on 2026-03-30, after Web app

**Section Launch (2026-03-02 to 2026-04-14):**

- Help center: 2026-03-02 to 2026-03-20, 19 days, after Visual design
- Testing and fixes: 2026-03-30 to 2026-04-13, 15 days, critical, after Web app and Mobile app
- Public launch: milestone on 2026-04-14, critical, after Testing and fixes

**Today is 2026-02-11.**

**Weekends are shaded and not counted in durations.**

**Days off: 2026-04-03.**

### Git: Release and hotfix

**Diagram source**

```diagram
git "Release and hotfix" desc="A feature is merged into develop, develop is released as v1.0, and a hotfix is tagged and picked back."

commit "Initial commit"
branch develop order=2
commit "Set up CI"
branch "feature/search" from=develop
commit "Add search box"
commit "Index the docs"
checkout develop
merge "feature/search" "Add search"
checkout main
merge develop "Release 1.0" tag="v1.0" type=highlight
branch hotfix order=1
commit "Fix null check" id=fix
checkout main
merge hotfix tag="v1.0.1"
checkout develop
cherry-pick fix
commit "Revert flaky test" type=reverse
```

*Picture: Release and hotfix: 10 commits on 4 branches, 3 merges, 1 cherry-pick, 2 tags. A feature is merged into develop, develop is released as v1.0, and a hotfix is tagged and picked back.*

**Text description**

**Branches, in lane order:**

- main is the default branch
- hotfix was made from main at "Release 1.0"
- develop was made from main at "Initial commit"
- feature/search was made from develop at "Set up CI"

**History, oldest first:**

**1. On main: commit "Initial commit"**

- develop was made from main at "Initial commit"

**2. On develop: commit "Set up CI"**

- feature/search was made from develop at "Set up CI"

**3. On feature/search: commit "Add search box"**

**4. On feature/search: commit "Index the docs"**

**5. On develop: merge of feature/search into develop ("Add search")**

**6. On main: merge of develop into main ("Release 1.0"), highlighted, tagged v1.0**

- hotfix was made from main at "Release 1.0"

**7. On hotfix: commit "Fix null check", id fix**

**8. On main: merge of hotfix into main, tagged v1.0.1**

**9. On develop: cherry-pick of fix ("Fix null check")**

**10. On develop: commit "Revert flaky test", a revert**

### Journey: Checkout experience

**Diagram source**

```diagram
journey "Checkout experience" desc="How a shopper feels from finding a product to holding the parcel, and who is involved at each step."

actor "Customer" tone=blue
actor "Support agent" tone=violet
actor "Bank"
actor "Courier"

section "Browse" {
  task "Find a product" score=5 actors="Customer"
  task "Compare prices" score=3 actors="Customer, Support agent" sub="Asks about delivery"
}

section "Pay" {
  task "Enter card details" score=2 actors="Customer"
  task "Confirm with the bank" score=1 actors="Customer, Bank"
  task "Get a receipt" score=4 actors="Customer"
}

section "Deliver" {
  task "Track the parcel" score=4 actors="Customer, Courier"
  task "Receive it" score=5 actors="Customer, Courier"
}
```

*Picture: Checkout experience: 7 tasks in 3 stages, average satisfaction 3.4 of 5. How a shopper feels from finding a product to holding the parcel, and who is involved at each step.*

**Text description**

**Satisfaction runs from 1 (frustrated) to 5 (delighted).**

- Browse: 2 tasks, average 4.0
  1. Find a product: 5 of 5 (delighted), with Customer
  2. Compare prices: 3 of 5 (neutral), with Customer and Support agent. Asks about delivery
- Pay: 3 tasks, average 2.3
  3. Enter card details: 2 of 5 (unhappy), with Customer
  4. Confirm with the bank: 1 of 5 (frustrated), with Customer and Bank
  5. Get a receipt: 4 of 5 (happy), with Customer
- Deliver: 2 tasks, average 4.5
  6. Track the parcel: 4 of 5 (happy), with Customer and Courier
  7. Receive it: 5 of 5 (delighted), with Customer and Courier

**Actors: Customer, Support agent, Bank, and Courier.**

**Lowest: "Confirm with the bank" at 1. Highest: "Find a product" and "Receive it" at 5.**

### Layers: Online banking platform

**Diagram source**

```diagram
layers "Online banking platform" desc="A customer reaches accounts, payments and cards through one gateway. Each layer calls the layer below it, and security and monitoring cut across all of them."

layer "Experience" tone=blue icon=lucide:monitor {
  block web "Web app" sub="React" icon=simple:react
  block mobile "Mobile app" sub="Swift and Kotlin" icon=lucide:smartphone
  block gateway "API gateway" sub="Routing and limits" icon=lucide:route
}

layer "Domain services" tone=violet icon=lucide:boxes {
  block accounts "Accounts" icon=lucide:wallet
  block payments "Payments" icon=lucide:credit-card
  block ledger "Ledger" icon=lucide:book-open
}

layer "Data" tone=aqua icon=lucide:database {
  block sql "PostgreSQL" sub="Accounts and ledger" icon=simple:postgresql span=2
  block cache "Redis" sub="Sessions" icon=simple:redis
  block events "Kafka" sub="Payment events" icon=simple:apachekafka
}

layer "Infrastructure" tone=neutral icon=lucide:server {
  block k8s "Kubernetes" sub="Three regions" icon=simple:kubernetes
  block net "Private network" sub="Subnets and routing" icon=lucide:cloud
}

aside "Cross-cutting" icon=lucide:shield {
  block iam "Identity" icon=lucide:key-round
  block audit "Audit log" icon=lucide:scroll-text
  block watch "Monitoring" icon=lucide:activity
}

gateway -> accounts : "REST"
gateway -> payments : "REST"
accounts -> sql : "SQL"
ledger <-> sql : "SQL"
payments ..> events : "payment events"
sql --> k8s : "runs on"
```

*Picture: Online banking platform: 4 layers, 14 blocks, 1 aside, 6 links. A customer reaches accounts, payments and cards through one gateway. Each layer calls the layer below it, and security and monitoring cut across all of them.*

**Text description**
- Layer Experience (1 of 4 from the top): Web app (React), Mobile app (Swift and Kotlin), API gateway (Routing and limits).
- Layer Domain services (2 of 4 from the top): Accounts, Payments, Ledger.
- Layer Data (3 of 4 from the top): PostgreSQL (Accounts and ledger), Redis (Sessions), Kafka (Payment events).
- Layer Infrastructure (4 of 4 from the top): Kubernetes (Three regions), Private network (Subnets and routing).
- Aside Cross-cutting, beside the layers: Identity, Audit log, Monitoring.
- Link: API gateway to Accounts: REST.
- Link: API gateway to Payments: REST.
- Link: Accounts to PostgreSQL: SQL.
- Link: Ledger and PostgreSQL, both ways: SQL.
- Link: Payments to Kafka: payment events, asynchronous.
- Link: PostgreSQL to Kubernetes: runs on, optional or indirect.

### Matrix: Message broker options

**Diagram source**

```diagram
matrix "Message broker options" desc="Four message brokers compared on five criteria. Kafka is the pick for the event pipeline because it keeps order at the highest throughput."

columns "Throughput" "Ordering" "Ease of ops" "Managed offer" "Typical cost"

row "Kafka" icon=simple:apachekafka sub="Log-based streaming" : 5, yes, 2, partial, "Medium"
row "RabbitMQ" icon=simple:rabbitmq sub="Classic queues" : 3, yes, 3, partial, "Low"
row "Amazon SQS" icon=aws:sqs sub="Managed queues" : 4, no, 5, yes, "Pay per use"
row "NATS" icon=simple:nats sub="Light messaging" : 4, partial, 4, no, "Low"

recommend "Kafka"
legend partial="Limited"
```

*Picture: Message broker options: 4 rows compared on 5 columns. Recommended: Kafka. Four message brokers compared on five criteria. Kafka is the pick for the event pipeline because it keeps order at the highest throughput.*

**Text description**

**Columns: Throughput, Ordering, Ease of ops, Managed offer, Typical cost.**

- Kafka (Log-based streaming, recommended): Throughput: 5 of 5; Ordering: yes; Ease of ops: 2 of 5; Managed offer: Limited; Typical cost: Medium.
- RabbitMQ (Classic queues): Throughput: 3 of 5; Ordering: yes; Ease of ops: 3 of 5; Managed offer: Limited; Typical cost: Low.
- Amazon SQS (Managed queues): Throughput: 4 of 5; Ordering: no; Ease of ops: 5 of 5; Managed offer: yes; Typical cost: Pay per use.
- NATS (Light messaging): Throughput: 4 of 5; Ordering: Limited; Ease of ops: 4 of 5; Managed offer: no; Typical cost: Low.

### Mindmap: Product launch

**Diagram source**

```diagram
mindmap "Product launch" desc="What the team settles before the product ships: who it is for, what it is, where to say so, and what could go wrong."

root "Product launch" icon=lucide:rocket

topic "Audience" icon=lucide:users {
  topic "Maintainers"
  topic "Platform teams"
  topic "Technical writers"
  topic "Agency designers"
}

topic "Product" icon=lucide:package {
  topic "Text language"
  topic "Layout engine"
  topic "Light and dark themes"
  topic "MCP server and API"
}

topic "Channels" icon=lucide:megaphone {
  topic "Docs and guides"
  topic "Launch post"
  topic "Forums"
  topic "Newsletters"
}

topic "Plan" icon=lucide:calendar-clock {
  topic "Soft launch"
  topic "Public beta"
  topic "Week 12 review"
}

topic "Risks" icon=lucide:triangle-alert tone=red shape=hexagon {
  topic "Slow first render"
  topic "Unclear pricing"
  topic "Thin documentation"
}
```

*Picture: Product launch: 24 topics, 5 branches, 2 levels. What the team settles before the product ships: who it is for, what it is, where to say so, and what could go wrong.*

**Text description**

**Central topic: Product launch**

**Branches:**

- Audience
  - Maintainers
  - Platform teams
  - Technical writers
  - Agency designers
- Product
  - Text language
  - Layout engine
  - Light and dark themes
  - MCP server and API
- Channels
  - Docs and guides
  - Launch post
  - Forums
  - Newsletters
- Plan
  - Soft launch
  - Public beta
  - Week 12 review
- Risks
  - Slow first render
  - Unclear pricing
  - Thin documentation

### Pie: Where visits come from

**Diagram source**

```diagram
pie "Where visits come from" total="Visits in March" desc="The share of one month's visits to the docs by the channel that sent them: search brings the most, then people who typed the address."

slice "Search"    18420  icon=lucide:search
slice "Direct"    11960  icon=lucide:globe
slice "Social"     6310  icon=lucide:share-2
slice "Email"      4180  icon=lucide:mail
slice "Referral"   2140  icon=lucide:link
slice "Paid"       1490  icon=lucide:megaphone
```

*Picture: Where visits come from: 6 slices of 44,500 in all, the largest Search at 42%. The share of one month's visits to the docs by the channel that sent them: search brings the most, then people who typed the address.*

**Text description**

**Slices, largest first:**

- Search: 18,420 (42%)
- Direct: 11,960 (27%)
- Social: 6,310 (14%)
- Email: 4,180 (9%)
- Referral: 2,140 (5%)
- Paid: 1,490 (3%)

### Quadrant: What to build next

**Diagram source**

```diagram
quadrant "What to build next" highlight=top-left desc="Fourteen roadmap ideas placed by the effort they take and the impact they have on customers. The quick wins in the top left are the ones to start with."

x "Effort" low="Low effort" high="High effort"
y "Impact" low="Low impact" high="High impact"

zone top-left "Quick wins" tone=green icon=lucide:zap
zone top-right "Big bets" tone=blue icon=lucide:rocket
zone bottom-left "Fill-ins"
zone bottom-right "Time sinks" tone=red icon=lucide:hourglass

point "Saved filters" x=0.1 y=0.88 icon=lucide:funnel
point "Dark mode" x=0.26 y=0.7
point "Keyboard shortcuts" x=0.36 y=0.56
point "Email digest" x=0.4 y=0.86 icon=lucide:mail

point "Mobile app" x=0.88 y=0.9 icon=lucide:smartphone
point "Public API" x=0.68 y=0.78 icon=lucide:plug
point "SSO and SCIM" x=0.6 y=0.62 icon=lucide:key-round
point "Offline mode" x=0.84 y=0.62

point "Release notes page" x=0.2 y=0.24
point "Footer links" x=0.4 y=0.38
point "Favicon refresh" x=0.1 y=0.1

point "Rewrite in Rust" x=0.8 y=0.14
point "Custom themes" x=0.66 y=0.4
point "Plugin marketplace" x=0.58 y=0.22
```

*Picture: What to build next: 14 points placed by Effort and Impact. Fourteen roadmap ideas placed by the effort they take and the impact they have on customers. The quick wins in the top left are the ones to start with.*

**Text description**

**Horizontal axis: Effort, from Low effort to High effort.**

**Vertical axis: Impact, from Low impact to High impact.**

**Quick wins (top left, highlighted):**

- Saved filters: Effort 0.1, Impact 0.88
- Dark mode: Effort 0.26, Impact 0.7
- Keyboard shortcuts: Effort 0.36, Impact 0.56
- Email digest: Effort 0.4, Impact 0.86

**Big bets (top right):**

- Mobile app: Effort 0.88, Impact 0.9
- Public API: Effort 0.68, Impact 0.78
- SSO and SCIM: Effort 0.6, Impact 0.62
- Offline mode: Effort 0.84, Impact 0.62

**Fill-ins (bottom left):**

- Release notes page: Effort 0.2, Impact 0.24
- Footer links: Effort 0.4, Impact 0.38
- Favicon refresh: Effort 0.1, Impact 0.1

**Time sinks (bottom right):**

- Rewrite in Rust: Effort 0.8, Impact 0.14
- Custom themes: Effort 0.66, Impact 0.4
- Plugin marketplace: Effort 0.58, Impact 0.22

### Sankey: Request traffic through the platform

**Diagram source**

```diagram
sankey "Request traffic through the platform" unit="req/s" desc="How 12,000 requests per second move from the edge through the API gateway and three services to the data stores, with blocked and failed requests branching off."

node "Edge" icon=lucide:globe
node "API gateway" icon=lucide:server
node "Auth" icon=lucide:key-round
node "Catalog" icon=lucide:book-open
node "Orders" icon=lucide:package
node "Redis cache" icon=simple:redis tone=aqua
node "PostgreSQL" icon=simple:postgresql
node "Search index" icon=lucide:search
node "Blocked" icon=lucide:shield-x tone=red
node "Errors" icon=lucide:circle-alert tone=red

"Edge" -> "API gateway" : 10800
"Edge" -> "Blocked" : 1200

"API gateway" -> "Auth" : 3000
"API gateway" -> "Catalog" : 5200
"API gateway" -> "Orders" : 2200
"API gateway" -> "Errors" : 400

"Auth" -> "Redis cache" : 2600
"Auth" -> "PostgreSQL" : 400
"Catalog" -> "Redis cache" : 3600
"Catalog" -> "PostgreSQL" : 1200
"Catalog" -> "Search index" : 400
"Orders" -> "PostgreSQL" : 1900
"Orders" -> "Errors" : 300
```

*Picture: Request traffic through the platform: 10 nodes and 13 flows in 4 columns, total 12,000 req/s. How 12,000 requests per second move from the edge through the API gateway and three services to the data stores, with blocked and failed requests branching off.*

**Text description**
- Edge to API gateway: 10,800 req/s, 90% of what leaves Edge
- Edge to Blocked: 1,200 req/s, 10% of what leaves Edge
- API gateway to Catalog: 5,200 req/s, 48% of what leaves API gateway
- API gateway to Auth: 3,000 req/s, 28% of what leaves API gateway
- API gateway to Orders: 2,200 req/s, 20% of what leaves API gateway
- API gateway to Errors: 400 req/s, 4% of what leaves API gateway
- Auth to Redis cache: 2,600 req/s, 87% of what leaves Auth
- Auth to PostgreSQL: 400 req/s, 13% of what leaves Auth
- Catalog to Redis cache: 3,600 req/s, 69% of what leaves Catalog
- Catalog to PostgreSQL: 1,200 req/s, 23% of what leaves Catalog
- Catalog to Search index: 400 req/s, 8% of what leaves Catalog
- Orders to PostgreSQL: 1,900 req/s, 86% of what leaves Orders
- Orders to Errors: 300 req/s, 14% of what leaves Orders

### Schema: Users and sessions

**Diagram source**

```diagram
schema "Users and sessions" desc="Each session belongs to one user."

table users icon=lucide:users {
  id          uuid          pk
  email       text          unique
  created_at  timestamptz
}

table sessions icon=lucide:key-round {
  id          uuid          pk
  user_id     uuid          fk
  expires_at  timestamptz   nullable
}

sessions.user_id -> users.id : "belongs to"
```

*Picture: Users and sessions: 2 tables, 1 relation. Each session belongs to one user.*

**Text description**

**Tables:**

- users
  - id uuid (primary key)
  - email text (unique)
  - created_at timestamptz
- sessions
  - id uuid (primary key)
  - user_id uuid (foreign key)
  - expires_at timestamptz (nullable)

**Relations:**

- sessions.user_id references users.id (many to one): belongs to

### Sequence: Token refresh

**Diagram source**

```diagram
sequence "Token refresh" desc="A client refreshes an expired access token, or is told to sign in again."

actor app  client   "App"
actor idp  identity "Auth0" sub="Identity provider"

app -> idp : "Refresh token"
note over idp : "Checks the refresh token has not been revoked"

alt "token valid" {
  idp --> app : "New access token"
} else "token revoked" {
  idp --> app : "401 sign in again"
}
```

*Picture: Token refresh: 2 participants. A client refreshes an expired access token, or is told to sign in again.*

**Text description**

**Participants:**

- App, client
- Auth0 (Identity provider), identity

**Steps:**

- 1. App to Auth0: Refresh token
- Note over Auth0: Checks the refresh token has not been revoked
- alt: token valid
  - 2. Auth0 to App (dashed): New access token
- else: token revoked
  - 3. Auth0 to App (dashed): 401 sign in again

### State: Order lifecycle

**Diagram source**

```diagram
state "Order lifecycle" desc="An order is paid, checked against stock, packed and delivered, or cancelled."

state pending "Pending" sub="Waiting for payment" tone=amber icon=status:pending
choice stock "In stock?"
state fulfilment "Fulfilment" icon=lucide:package {
  start -> picking
  state picking "Picking" tone=blue icon=lucide:clipboard-list
  state packed "Packed" tone=blue icon=lucide:package-check
  picking -> packed : "items scanned"
  packed -> end
}
state delivered "Delivered" tone=green icon=lucide:map-pin
state cancelled "Cancelled" sub="Money refunded" tone=red icon=status:error

start -> pending
pending -> stock : "pay [card valid] / charge card"
pending -> cancelled : "timeout [after 24 hours]"
stock -> fulfilment : "[in stock] / reserve items"
stock -> cancelled : "[sold out] / refund"
fulfilment -> delivered : "arrived"
delivered -> end
cancelled -> end

note cancelled : "Refunds stay open for 30 days"
```

*Picture: Order lifecycle: 6 states (1 composite), 11 transitions. An order is paid, checked against stock, packed and delivered, or cancelled.*

**Text description**

**States:**

- Start
- Pending (Waiting for payment), state
- In stock?, choice
- Fulfilment, composite state
  - Start of Fulfilment
  - Picking, state
  - Packed, state
  - End of Fulfilment
- Delivered, state
- Cancelled (Money refunded), state
- End

**Transitions:**

- Start of Fulfilment to Picking
- Picking to Packed on items scanned
- Packed to End of Fulfilment
- Start to Pending
- Pending to In stock? on pay, if card valid, then charge card
- Pending to Cancelled on timeout, if after 24 hours
- In stock? to Fulfilment, if in stock, then reserve items
- In stock? to Cancelled, if sold out, then refund
- Fulfilment to Delivered on arrived
- Delivered to End
- Cancelled to End

**Notes:**

- On Cancelled: Refunds stay open for 30 days

### Swimlane: Order fulfilment

**Diagram source**

```diagram
swimlane "Order fulfilment" desc="A customer's order is checked against stock, paid for, packed and shipped, or cancelled."

lane customer "Customer" icon=lucide:user {
  start placed "Order placed" icon=lucide:receipt
  end cancelled "Order cancelled" tone=red icon=status:error
  step card "Update card" sub="Enters a new card" icon=lucide:credit-card
  end delivered "Delivered" tone=green icon=status:success
}
lane web "Web app" icon=lucide:globe {
  step check "Check stock" icon=lucide:boxes
  decision stock "In stock?"
  step error "Show error" sub="Payment declined" tone=amber icon=lucide:triangle-alert
}
lane pay "Payments" icon=lucide:wallet {
  step charge "Charge card" icon=lucide:credit-card
}
lane warehouse "Warehouse" icon=lucide:warehouse {
  step pack "Pack order" icon=lucide:package-check
  step ship "Ship order" icon=lucide:send
}

placed -> check
check -> stock
stock -> charge : "[in stock]"
stock -> cancelled : "[sold out]"
charge -> pack : "[paid]"
charge -> error : "[declined]"
error -> card
card -> charge : "retry"
pack -> ship
ship -> delivered : "arrived"
```

*Picture: Order fulfilment: 4 lanes, 10 steps, 10 transitions. A customer's order is checked against stock, paid for, packed and shipped, or cancelled.*

**Text description**

**Lanes:**

- Customer
  - Order placed, start
  - Order cancelled, end
  - Update card (Enters a new card), step
  - Delivered, end
- Web app
  - Check stock, step
  - In stock?, decision
  - Show error (Payment declined), step
- Payments
  - Charge card, step
- Warehouse
  - Pack order, step
  - Ship order, step

**Transitions:**

- Order placed to Check stock
- Check stock to In stock?
- In stock? to Charge card, if in stock
- In stock? to Order cancelled, if sold out
- Charge card to Pack order, if paid
- Charge card to Show error, if declined
- Show error to Update card
- Update card to Charge card on retry
- Pack order to Ship order
- Ship order to Delivered on arrived

### Timeline: Orbit product history

**Diagram source**

```diagram
timeline "Orbit product history" desc="From a weekend prototype to a platform that teams run their work on."

section "Prototype" {
  at "2019" : "Weekend hack" sub="Two founders and one laptop" icon=lucide:laptop, "First paying customer" icon=lucide:badge-check
  at "2020" : "Public beta" sub="1,200 sign-ups in a week" icon=lucide:rocket
}

section "Growth" {
  period "2021" {
    event "Seed round" sub="Led by two angels" icon=lucide:wallet
    event "Team of ten" icon=lucide:users
  }
  at "2022" : "Series A", "Berlin office opens" icon=lucide:map-pin
}

section "Platform" {
  at "2023" : "Public API" sub="REST and webhooks" icon=lucide:code
  at "2024" : "10,000 teams" tone=green icon=lucide:crown, "SOC 2 report" icon=lucide:shield-check
}
```

*Picture: Orbit product history: 6 periods and 10 events in 3 sections. From a weekend prototype to a platform that teams run their work on.*

**Text description**

**Periods, in the order drawn:**

**Section Prototype:**

- 2019: Weekend hack (Two founders and one laptop); First paying customer
- 2020: Public beta (1,200 sign-ups in a week)

**Section Growth:**

- 2021: Seed round (Led by two angels); Team of ten
- 2022: Series A; Berlin office opens

**Section Platform:**

- 2023: Public API (REST and webhooks)
- 2024: 10,000 teams; SOC 2 report

### Tree: storefront

**Diagram source**

```diagram
tree "storefront" desc="The layout of a small repository: services, libraries, and the file a change touches."

dir "services" note="Deployable services" icon=lucide:layers {
  dir "api" note="REST API, a container service" icon=lucide:server {
    file "service.toml" note="Service config and bindings"
    file "index.ts" note="Hono routes" change=modified size="4 KB"
  }
  dir "docs" note="Documentation site" icon=lucide:book-open
  dir "site" note="Homepage" icon=lucide:globe
}

dir "libs" note="Shared code" icon=lucide:package {
  dir "engine" note="Parser, layout, renderer" {
    dir "src" {
      dir "kinds" note="One folder for each diagram kind"
      file "index.ts" note="Public API"
    }
    file "manifest.json"
  }
  dir "ui" note="Design system"
}

dir "scripts" note="Doc generators"
file "README.md" note="Start here" mark=on
file "tsconfig.json"
file "pnpm-lock.yaml" size="412 KB"
omitted 9 "files"
```

*Picture: storefront: file tree of 17 entries (10 folders, 7 files), 4 levels, 1 changed, 9 more not drawn. The layout of a small repository: services, libraries, and the file a change touches.*

**Text description**
- services/ (folder, 3 entries): Deployable services
  - services/api/ (folder, 2 entries): REST API, a container service
    - services/api/service.toml: Service config and bindings
    - services/api/index.ts (modified, 4 KB): Hono routes
  - services/docs/ (folder): Documentation site
  - services/site/ (folder): Homepage
- libs/ (folder, 2 entries): Shared code
  - libs/engine/ (folder, 2 entries): Parser, layout, renderer
    - libs/engine/src/ (folder, 2 entries)
      - libs/engine/src/kinds/ (folder): One folder for each diagram kind
      - libs/engine/src/index.ts: Public API
    - libs/engine/manifest.json
  - libs/ui/ (folder): Design system
- scripts/ (folder): Doc generators
- README.md (emphasised): Start here
- tsconfig.json
- pnpm-lock.yaml (412 KB)
- and 9 more files

### Treemap: Monthly cloud spend

**Diagram source**

```diagram
treemap "Monthly cloud spend" unit="$" better=down desc="Where the monthly cloud bill goes, by service, with the change since last month. A rise in cost is drawn red."

group "Compute" icon=lucide:cpu {
  item "EC2" 18400 icon=aws:ec2 delta="8%"
  item "Fargate" 6800 icon=aws:fargate delta="21%"
  item "Lambda" 4200 icon=aws:lambda delta="-12%"
  item "EKS" 3100 icon=aws:eks
}

group "Data" icon=lucide:database {
  item "RDS" 9600 icon=aws:rds delta="4%"
  item "S3" 5200 icon=aws:s3 delta="-3%"
  item "DynamoDB" 3900 icon=aws:dynamodb
  item "OpenSearch" 3400 icon=aws:opensearch
  item "ElastiCache" 2800
}

group "Network" icon=lucide:globe {
  item "Data transfer" 4800 delta="15%"
  item "NAT gateway" 3300 icon=aws:nat-gateway
  item "CloudFront" 2200 icon=aws:cloudfront delta="-6%"
  item "Load balancers" 1900 icon=aws:elb
}

group "Operations" icon=lucide:activity {
  item "CloudWatch" 4600 icon=aws:cloudwatch
  item "Support plan" 3200
  group "Security" icon=lucide:shield {
    item "GuardDuty" 2400 icon=aws:guardduty
    item "WAF" 1900 icon=aws:waf
  }
}
```

*Picture: Monthly cloud spend: 17 items in 5 groups, total $81,700. Where the monthly cloud bill goes, by service, with the change since last month. A rise in cost is drawn red.*

**Text description**
- Group Compute: $32,500, 40% of the total
  - EC2: $18,400, 23% of the total, up 8%
  - Fargate: $6,800, 8% of the total, up 21%
  - Lambda: $4,200, 5% of the total, down 12%
  - EKS: $3,100, 4% of the total
- Group Data: $24,900, 30% of the total
  - RDS: $9,600, 12% of the total, up 4%
  - S3: $5,200, 6% of the total, down 3%
  - DynamoDB: $3,900, 5% of the total
  - OpenSearch: $3,400, 4% of the total
  - ElastiCache: $2,800, 3% of the total
- Group Network: $12,200, 15% of the total
  - Data transfer: $4,800, 6% of the total, up 15%
  - NAT gateway: $3,300, 4% of the total
  - CloudFront: $2,200, 3% of the total, down 6%
  - Load balancers: $1,900, 2% of the total
- Group Operations: $12,100, 15% of the total
  - CloudWatch: $4,600, 6% of the total
  - Group Security: $4,300, 5% of the total
    - GuardDuty: $2,400, 3% of the total
    - WAF: $1,900, 2% of the total
  - Support plan: $3,200, 4% of the total

### Xychart: API latency against the SLO

**Diagram source**

```diagram
xychart "API latency against the SLO" desc="Weekly p50 and p95 latency of the checkout API against its 400 ms objective: p95 crossed the line after release 2.3 and came back under it after the cache fix."

x "Week" ["W1" "W2" "W3" "W4" "W5" "W6" "W7" "W8"]
y "Latency" unit="ms" min=0 max=500

bar "p95" [310 330 345 420 455 440 350 335]
line "p50" [120 125 128 160 170 165 135 130]

threshold "SLO 400 ms" 400 tone=red

note "Release 2.3" at="W4"
note "Cache fix" at="W7"
```

*Picture: API latency against the SLO: 2 series (bar and line) over 8 places on the Week axis, on a scale of 0 ms to 500 ms. Weekly p50 and p95 latency of the checkout API against its 400 ms objective: p95 crossed the line after release 2.3 and came back under it after the cache fix.*

**Text description**

**X axis, Week: W1, W2, W3, W4, W5, W6, W7, W8.**

**Y axis, Latency: 0 ms to 500 ms.**

**Drawn with the places along the bottom.**

**Series:**

- p95 (bar): W1 310 ms, W2 330 ms, W3 345 ms, W4 420 ms, W5 455 ms, W6 440 ms, W7 350 ms, W8 335 ms
- p50 (line): W1 120 ms, W2 125 ms, W3 128 ms, W4 160 ms, W5 170 ms, W6 165 ms, W7 135 ms, W8 130 ms

**Thresholds:**

- SLO 400 ms: 400 ms
- Crossed above SLO 400 ms: p95 in W4, W5, and W6

**Notes:**

- W4: Release 2.3
- W7: Cache fix

## Statements by kind

### Git graph

Branches as lanes, with the commits, merges, and tags along them, for a repository history. The header line is `git "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `git "<title>" direction=<auto\|right\|down> main=<name> desc="<summary>"` | The header, and the first line. Every option is optional. direction=auto, the default, draws one lane per row with time running left to right when that fits a column of the page, and one lane per column with time running down the page when it does not. direction=right and direction=down fix it. main= names the default branch, which is main unless you say otherwise. | `git "Release flow" direction=right main=trunk desc="A feature branch merges into the trunk and the release is tagged."` |
| `commit "<message>" id=<id> tag="<name>" type=<normal\|highlight\|reverse>` | A commit on the current branch. Statements are written in time order: the order written is the order in time. Every part after the word is optional, and a commit with no message is a bare dot. id= names the commit for cherry-pick and from=; an id is drawn only on a commit that a cherry-pick copies. tag= puts a tag on the commit; repeat it or list names with commas, up to 3. type=highlight draws a milestone with a halo, and type=reverse a ring with a cross for a revert. Valid types: normal, highlight, reverse. | `commit "Add search" id=c2 tag="v0.2" type=highlight` |
| `branch <name> from=<branch or id> order=<n> tone=<tone>` | Makes a branch at the newest commit of the current branch, or of from=, and switches to it. A name with a slash goes in double quotes: branch "feature/search". from= takes a branch name or a commit id, so a hotfix can start from an old release. order= moves the lane: the lowest order is the first lane, a branch with no order takes the number of branches made before it, and order=1 is the second lane. tone= sets the lane color. A branch needs a commit to start from, so write a commit first. | `branch "feature/search" from=develop order=2` |
| `checkout <branch>` | Switches the current branch, so the commits that follow go on it. switch is another spelling. The branch must already exist. | `checkout develop` |
| `merge <branch> "<message>" id=<id> tag="<name>" type=<type>` | A merge commit on the current branch that brings in the newest commit of another branch, drawn with a ring in the color of the merged branch and a line from that branch. The message and the options are optional. A branch cannot be merged into itself, and merging one that is already part of the current branch adds nothing, which draws warning W300. | `merge develop "Release 1.0" tag="v1.0" type=highlight` |
| `cherry-pick <id> "<message>" tag="<name>"` | A copy of one commit, made on the current branch and drawn with a dashed ring in the color of the lane it came from. The commit is named by its id=, so give the source commit an id. With no message the copy keeps the message of the commit it copies. A commit the current branch already has cannot be picked. | `cherry-pick c3 tag="v1.0.1"` |
| `desc "<summary>"` | The plain-language summary, as a statement of its own, for people who prefer it to desc= on the header. | `desc "Hotfix 1.0.1 is picked back onto main."` |

### Gantt chart

Tasks and milestones on a time axis, in sections and with dependencies, for a project schedule. The header line is `gantt "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `gantt "Title" start=<date> end=<date> unit=<day\|week\|month\|quarter\|year> today=<date> weekends=<show\|shade\|skip> holidays=<dates> desc="<text>"` | The header. Every option is optional. start= and end= can only widen the axis, never cut a task. unit= is the step of the axis (the default follows the span of the plan). today= draws a marker on that day, and there is none without it. weekends=shade tints Saturdays and Sundays, and weekends=skip also leaves them out when counting durations in days and weeks. holidays= is a comma-separated list of days off that are shaded and not counted. Dates are YYYY-MM-DD. | `gantt "Launch plan" start=2026-01-05 unit=week today=2026-01-26 weekends=skip holidays=2026-04-03` |
| `section "<Name>" tone=<tone> {` | Opens a band of rows with its name in a tab on the left. Put tasks and milestones inside and close it with } on its own line. Sections do not nest. tone= colors the band and the bars inside it. | `section "Design" tone=violet {` |
| `task <id> "<Label>" start=<date> \| after=<ids> duration=<n>d\|<n>w\|<n>mo \| end=<date> status=<status> tone=<tone>` | A bar on the axis. Say when it starts with start= (a day) or after= (one or more ids, a comma-separated list: it starts the day after the last of them ends), and how long it lasts with duration= (days, weeks, or calendar months) or end= (its last day). status= is planned (the default), active, done, or critical, and critical can be added to another: status=critical,done. The label goes inside the bar when it fits and beside it when it does not. | `task ui "UI design" after=ux duration=2w status=active` |
| `milestone <id> "<Label>" at=<date> \| after=<ids> status=<status> tone=<tone>` | A point in time drawn as a diamond: at= a day, or after= tasks, where the last of them ends. A milestone has no duration and no end. status= is planned, done, or critical. | `milestone beta "Beta" at=2026-03-02` |
| `desc "<text>"` | The plain-language description of the chart, for assistive technology; the same as desc= on the header. | `desc "A twelve week plan from research to launch."` |

### Class diagram

Classes with their members and the relations between them, for object models. The header line is `class "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `class <id> ["Label"] [type=<type>] [stereotype="<word>"] [tone=<tone>] [{]` | Declares a class, shown as a box. The label is the name in the header and may hold type parameters, such as "Repository<T>". type is class (a concrete class. the default), abstract (a class that cannot be created itself, only extended), interface (a contract of methods that other classes implement), enum (a fixed set of named values; each member line is one value). Without a { the class has no members; with one, list them on the lines that follow and close with } on its own line. | `class Order "Order" type=class stereotype="entity" {` |
| `"<mark><name>[: <type>]" [static] [abstract]` | A member of the open class, one per line, the whole text in double quotes. The mark says who sees it: + public, - private, # protected, ~ package. A member with a parenthesis is a method and the rest are attributes; attributes are drawn first. After the colon comes the type. static draws it underlined and abstract in italics. On an enum, each member is one value. | `"+place(order: Order): void" abstract` |
| `package <id> ["Label"] [tone=<tone>] {` | A frame around classes, with its name in a corner tab. Packages nest up to 3 deep. Close with } on its own line. | `package billing "Billing" tone=violet {` |
| `<from> ["<count>"] <relation> ["<count>"] <to> [: "label"]` | Relates two classes. The relation is one of: extends inheritance, implements realization, owns composition, has aggregation, -> association, <-> bidirectional, --> dependency, -- link. A count is the cardinality at that end, up to 12 characters, such as "1", "0..1" or "*". Write inheritance and implementation from the child to the parent, and composition and aggregation from the whole to the part. | `Customer "1" -> "*" Order : "places"` |
| `note on <class> : "text"` | A note card joined to a class by a dotted line. Write note : "text" for a note that stands alone. | `note on Order : "Totals are in cents."` |
| `desc "text"` | The plain-language description of the diagram, for readers who cannot see it. The header option desc="<text>" does the same. direction=down\|right\|up\|left on the header sets the reading direction; down, the default, puts parents above their children. | `desc "How an order reaches a customer."` |

### State

States and the transitions between them, with composite states, choices, and parallel regions, for lifecycles and state machines. The header line is `state "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `state <id> "Label" sub="<description>" tone=<tone> icon=<set:name>` | Adds one state: a rounded card with its name. sub= is the line or lines under the name, and a \n starts a new line. icon= puts a chip with that icon before the name, and a card with no icon has no chip. A tone other than neutral tints the card, and green, amber, or red also draw every transition that arrives at the state in a softer line of that tone, so the way to a good outcome and the way to a bad one part by color. The other tones never color a line. The ids start and end are taken by the markers. | `state paid "Paid" sub="Money received" tone=green` |
| `state <id> "Label" {` | Opens a composite state: a state that holds other states, drawn as a frame with its name, and its icon when it has one, in a corner tab. A frame inside a frame is shaded a step stronger. Close it with } on its own line. Inside, start and end are the markers of this state alone, and a transition may enter or leave the whole state by its id. | `state shipping "Shipping" {` |
| `from -> to : "event [guard] / action"` | Adds a transition. The label is optional, and each of its parts is optional too: the event that triggers it, a guard in square brackets that must hold, and an action after a slash that runs. The picture sets them apart: the event is plain text, the guard is a tag with a branch glyph, and the action follows a bolt glyph, so the brackets and the slash are not drawn. Only -> draws a transition. start and end are the markers of the scope the line is in: start only begins a transition and end only finishes one. | `pending -> paid : "payment received [card valid] / charge card"` |
| `start -> <id>` | The start marker of the scope: a filled dot where the lifecycle begins, at the top level of the diagram or inside a composite state. It needs no declaration. | `start -> pending` |
| `<id> -> end` | The end marker of the scope: a ring with a dot, where the lifecycle finishes. It needs no declaration, and several transitions may lead to it. | `delivered -> end` |
| `choice <id> "Question?" tone=<tone> icon=<set:name>` | A pill where one path splits into several, each taken on a condition: a tinted pill with a branch icon in a round chip and the question beside it, a sibling of the state cards. icon= swaps the icon and icon=none leaves the chip out. The question is optional: with none the choice is a small circle holding only the icon, and its branches carry the conditions as guards. Its default tone is amber. | `choice stock "In stock?"` |
| `fork <id>` | A bar where one path splits into parallel paths. It grows long enough to hold the transitions that meet it. | `fork split` |
| `join <id>` | A bar where parallel paths merge into one. | `join merge` |
| `history <id> deep=on` | A circle marked H inside a composite state: entering it returns to the state that was active last. deep=on remembers the whole depth of the nesting and draws H*. | `history last deep=on` |
| `region "Name" {` | Opens a region inside a composite state: one of the parts that run side by side. A composite state with regions holds only regions, and each region has its own start and end markers. A dashed line separates the regions. The name is optional. | `region "Reader" {` |
| `note <id> : "text" side=<left\|right\|top\|bottom>` | Attaches a note to a state, a choice, or a composite state. A note sits across the reading direction, so a transition never meets it: left and right become top and bottom in a diagram that reads sideways, and the other way round in one that reads up or down. A note on a composite state sits inside its frame. With no side, it goes below or to the right. | `note paid : "Refunds are allowed for 30 days" side=right` |
| `desc "<text>"` | A plain-language description of the diagram for the text description, as desc= on the header does. | `desc "How an order moves from payment to delivery."` |

### Timeline

Dated events on a rail, in the order written and grouped in sections, for a history or a roadmap. The header line is `timeline "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `timeline "Title" direction=right\|down side=both\|one desc="<text>"` | The header. direction=right (the default) draws a horizontal rail read left to right, direction=down a vertical one read top to bottom. side=both (the default) alternates the cards between the two sides of the rail, side=one puts them all on the same side: below the rail, or to its right. Periods are drawn in the order written, never sorted. | `timeline "Product history" direction=down side=one` |
| `section "Name" tone=<tone> icon=<ref> {` | Opens an era: a tinted frame, with its name in a corner tab, around the periods written before the closing } on its own line. A section takes the next tone of blue, violet, aqua, orange, green, amber unless tone= names another. Sections do not nest. A period written outside every section sits on the rail with no frame. | `section "Early days" tone=violet {` |
| `at "<period>" tone=<tone> : "<event>" sub="<text>" icon=<ref> tone=<tone>, "<event>"` | A period and its events on one line. The period is a milestone on the rail and each event is a card hung from it, the first nearest the rail. Events are separated by commas, and each may take sub= (a quieter second line), icon=, and tone=. A period is a label, not a date: "2019", "Q3 2019", "Spring", and "v2.0" all work. At least one event is expected; a period with none draws a milestone alone and a warning. | `at "2019" : "Founded", "First customer" sub="A design studio in Lyon" icon=lucide:flag` |
| `period "<period>" tone=<tone> {` | The same as at, with one event on each line, for a period whose events carry sub= and icon=. Write event lines inside it and close it with } on its own line. | `period "2020" tone=green {` |
| `event "<event>" sub="<text>" icon=<ref> tone=<tone>` | One card of the period block it is written in. Only a period block holds events; outside one the statement is error E381. | `event "Seed round" sub="Led by two angels" icon=lucide:wallet` |
| `desc "<text>"` | One or two sentences describing the timeline, for the text description. The same as desc= on the header. | `desc "How the product grew from a studio to a platform."` |

### User journey

The steps a person takes and how they feel at each, as a satisfaction curve over stages of tasks and the actors involved. The header line is `journey "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `journey "Title" desc="<text>" marks=faces\|dots legend=on\|off` | The header. marks=faces (the default) draws a face from sad to happy on the curve, and marks=dots draws the score as a number in a dot. legend=off hides the legend of the scale and the actors. A journey may hold at most 100 tasks, 25 stages, and 24 actors, and a task may name at most 8 actors; past that it is refused with E140. | `journey "Checkout experience" desc="How a shopper feels from search to payment" marks=faces` |
| `section "Stage name" tone=<tone> {` | A stage of the journey: a named run of tasks, drawn as a frame with a tab, one after another from left to right. It needs at least one task. tone= tints the frame and is optional. Tasks written outside any section are drawn with no frame. | `section "Browse" {` |
| `task "Name" score=<1-5> actors="<Actor>, <Actor>" sub="<second line>"` | A step the person takes, with how they feel at it: score 1 is frustrated, 3 is neutral, and 5 is delighted. The score is a whole number and the only required option. actors= lists who is involved, separated by commas, each as a chip of initials; sub= adds a second line under the name. Tasks run left to right in the order written, and a smooth curve joins their scores. | `task "Find a product" score=5 actors="Customer"` |
| `actor "Name" tone=<tone>` | Declares an actor so its color is chosen, and so a task that names someone else is an error (a typo). It is optional: with no actor statement the actors are the names that the tasks use, in the order they appear. Without tone= an actor takes the next of blue, violet, neutral, aqua, orange, amber, green, red. Two spellings that differ only in case or spacing are one actor. | `actor "Customer" tone=blue` |
| `desc "text"` | One or two sentences describing the journey, for the text description. The same as desc= on the header. | `desc "A shopper finds a product, pays, and waits for the parcel."` |

### Mind map

A root idea and the branches that grow from it, drawn round the root or as a tidy tree, for brainstorms and outlines. The header line is `mindmap "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `mindmap "<title>" layout=<layout> desc="<text>"` | The header; title, layout=, and desc= are optional, and with no title the root names the picture. layout= is auto, radial, right, or down: auto draws radial when the map has two branches or more and fits a page column, and right otherwise; radial puts the root in the middle with its branches on both sides; right and down are tidy trees. | `mindmap "Product strategy" layout=radial` |
| `root "<label>" icon=<set:name> tone=<tone> shape=<shape>` | The central idea, written once, directly under the header. Every other topic branches from it. It is a circle in the ink colour unless it says otherwise. | `root "Product strategy" icon=lucide:target` |
| `topic "<label>" icon=<set:name> tone=<tone> shape=<shape>` | A branch of the root when written at the left margin, and of the topic whose block it sits in otherwise. Branches take blue, orange, aqua, violet, amber, green, and red in turn and keep the tone down the branch; tone= overrides it. shape= is pill, card, circle, or hexagon, and pill is the default. Topics have no ids. | `topic "Growth" tone=blue icon=lucide:users` |
| `topic "<label>" {` | A topic with branches of its own: end the line with {, write its topics on the lines after, and close with } on its own line. Blocks nest to 10 levels. Indentation means nothing. | `topic "Retention" {` |
| `}` | Closes the nearest block that is still open, on a line of its own. | `}` |

### Treemap

Nested areas sized by value, grouped by branch, for where size or cost sits in a hierarchy. The header line is `treemap "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `treemap "Title" unit="<unit>" sort=<desc\|written> values=<on\|off> better=<up\|down> desc="<text>"` | The header. Every option is optional. unit= says what the values count and is drawn after each value (unit="MB", unit="%"; a currency symbol such as "$" leads). sort=desc (the default) draws the largest tile first, which packs best; sort=written keeps the order written. values=off hides the values on the tiles. better= says which way of a change is good news and so drawn green: better=up (the default) for sizes and coverage, better=down for cost and errors. | `treemap "Cloud spend" unit="$" better=down` |
| `group "<Name>" tone=<tone> icon=<icon> {` | Opens a frame sized by what it holds, with its name, icon, and total in a corner tab. Put items and groups inside and close it with } on its own line. Groups nest up to 4 deep. Each top-level branch takes the next tone of a rotation, and what is inside keeps it; tone= changes it. The theme chooses every color. | `group "Compute" tone=blue icon=lucide:server {` |
| `item "<Name>" <value> tone=<tone> icon=<icon> delta=<change>` | A tile whose area is its value. The value is a number above zero, written right after the name with no quotes and no unit. delta= draws a chip with an arrow and the size of a change since the last period, in the unit of the values or in percent: delta=5 for a rise, delta="-5" for a fall (a sign needs quotes), delta="-12%" for a percent. A tile too small for its label shows no label; the full list is in the text description. | `item "EC2" 12400 icon=lucide:server delta="8%"` |
| `desc "<text>"` | The plain-language description of the treemap, for assistive technology; the same as desc= on the header. | `desc "Where the monthly cloud bill goes, by service."` |

### Share chart

Parts of a whole drawn as a donut with a legend, for a few shares that add up to one total. The header line is `pie "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `pie "<title>" unit=<text> total=<text> sort=<order> values=<on\|off> fold=<n> labels=<place> desc="<text>"` | The header; every option is optional. unit= is written after each number ("%", "ms", "visits") or before it for a currency sign ("$"). total= is the caption under the total in the hole of the ring (the default is Total). sort= is desc or written: desc draws the largest first, clockwise from the top, with Other last. values=off prints the share and not the number. fold=5 draws at most 5 slices and merges the smallest into one Other. labels= is outside or legend: outside names each slice beside the ring on a leader line, and legend names them all in a legend under it. | `pie "Where visits come from" unit=" visits" total="Visits" fold=5` |
| `slice "<name>" <number> tone=<tone> icon=<set:name>` | One share of the whole, written as its name and then its number: zero or more, plain digits with an optional fraction, no sign, comma, or percent sign. The ring scales the numbers to a full turn, so they need not add up to 100. Slices take blue, orange, aqua, violet, amber, green, red, and neutral in turn; tone= chooses one, and a slice named Other is grey. icon= puts an icon in the slice's label. Names are unique, in any case, and slices have no ids. | `slice "Search" 28 tone=aqua icon=lucide:search` |
| `desc "<text>"` | The plain-language description of the chart, for assistive technology; the same as desc= on the header. | `desc "Where the visits of one month came from."` |

### Quadrant chart

Items placed on two axes and read by quadrant, for priorities such as impact against effort. The header line is `quadrant "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `quadrant "Title" highlight=<zone> desc="<text>"` | The header. Both options are optional. highlight= names the zone to act on (top-right, top-left, bottom-left, or bottom-right) and gives it a stronger wash and an outline. | `quadrant "Where to invest" highlight=top-left` |
| `x "<Title>" low="<text>" high="<text>"` | The horizontal axis: low= is the word at the left end (the value 0) and high= the word at the right end (the value 1). With a title and no ends they read Low and High. Write at least one of the three. | `x "Effort" low="Easy" high="Hard"` |
| `y "<Title>" low="<text>" high="<text>"` | The vertical axis, the same way: low= is the bottom (0) and high= the top (1). | `y "Impact" low="Small" high="Large"` |
| `zone <position> "<Name>" tone=<tone> icon=<icon>` | Names one of the four zones in a tab in its corner. Position is top-right, top-left, bottom-left, or bottom-right. tone= washes the zone, and its points take that tone unless they choose their own. | `zone top-left "Quick wins" tone=green icon=lucide:zap` |
| `point "<Label>" x=<0 to 1> y=<0 to 1> tone=<tone> icon=<icon>` | An item. x runs across and y runs up, both from 0 to 1 (write 0.8, not 80). 0.5 is the dashed line and belongs to the high side. icon= draws a chip and no icon draws a dot. Labels are unique. | `point "Public API" x=0.7 y=0.8 icon=lucide:plug` |
| `desc "<text>"` | The plain-language description of the chart, for assistive technology; the same as desc= on the header. | `desc "Where each idea falls on effort and impact."` |

### Layered architecture

Stacked layers of blocks with the links between them, for layered and tiered architectures. The header line is `layers "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `layers "Title" direction=<down\|up> counts=<on\|off> desc="<text>"` | The header. Every option is optional. direction=down (the default) puts the first layer on top; direction=up puts it at the bottom, for a stack built on its foundation. counts=on shows how many blocks each layer holds in its tab. | `layers "Online banking" direction=down` |
| `layer "<Name>" tone=<tone> icon=<icon> {` | Opens a wide frame with its name in a corner tab. Write blocks inside and close it with } on its own line. Layers stack in the order written, up to 12. Each takes the next tone of a rotation unless tone= sets one, and its blocks keep the tone. The blocks share one row and wrap to more rows when they cannot keep their words whole. The theme chooses every color. | `layer "Domain" tone=violet icon=lucide:boxes {` |
| `block <id> "<Label>" sub="<note>" icon=<icon> tone=<tone> span=<1-6>` | A card in a layer or an aside. The id is what links name. sub= is a short note under the label, such as the technology. span= makes the card as wide as that many columns of its layer, so a database can be wider than a service. At most 80 blocks in all. | `block pay "Payments" sub="Go" icon=lucide:credit-card span=2` |
| `aside "<Name>" tone=<tone> icon=<icon> {` | A tall band beside the stack for what cuts across every layer, such as security or monitoring, with its own blocks stacked in it. Up to 2 asides. Blocks in an aside take no links, because the aside applies to every layer. | `aside "Security" icon=lucide:shield {` |
| `<from> -> <to> : "<label>"` | A link between two blocks of the layers. Arrows: -> a call, --> a reply or an optional link (dashed), <-> two ways, ..> asynchronous (dotted), -- no direction. The line leaves and enters the top and bottom of a block and never crosses a block. Keep the few links that show how the layers depend on each other. | `web -> pay : "REST"` |
| `desc "<text>"` | The plain-language description of the diagram, for assistive technology; the same as desc= on the header. | `desc "Each layer calls the layer below it."` |

### C4 diagram

People, systems, containers, and components with their relationships and boundaries, in the C4 model of system context, container, component, deployment, and dynamic views. The header line is `c4 "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `person\|system\|container\|component\|db <id> "Name" desc="<what it does>" tech="<technology>" external=on icon=<set:name> tone=<tone>` | Adds one element: a card with its name, a caption that says what it is ([Person], [Software System], [Container: Spring Boot]), and its description. person is a user, system a software system, container an application or a data store that runs on its own, component a part of a container, and db a database. tech= is the technology and shows in the caption. external=on marks a person or a system outside the scope of the picture: a plain card with a dashed outline, while everything inside the scope is tinted. Each type has its own icon and tone, and icon= and tone= replace them. | `container api "API Application" tech="Spring Boot" desc="Serves the banking data as JSON."` |
| `boundary <id> "Name" kind=system\|container\|enterprise\|deployment\|group tech="<technology>" {` | Opens a boundary: a frame with its name and a caption such as [Software System] in a corner tab, around the elements written inside it. Boundaries nest, up to four deep, and close with } on a line of its own. A deployment boundary is a solid frame with a server icon, and tech= is its technology, such as Ubuntu 22.04. | `boundary bank "Internet Banking System" kind=system {` |
| `from -> to : "What it does" tech="<protocol>"` | Adds a relationship between two elements. The label and tech= are both optional; the technology is a tag at the end of the label. -> is a call, --> an asynchronous or optional one (dashed), ..> an event (dotted), and <-> or <--> go both ways. In a view=dynamic diagram the lines are numbered in the order written. | `spa -> api : "Makes API calls to" tech="JSON/HTTPS"` |
| `desc "<text>"` | A plain-language description of the diagram for the text description, as desc= on the header does. | `desc "How the customer reaches the banking system."` |

### Matrix

Rows against columns with meaningful cells, for options comparisons, decision matrices, RACI charts, and risk heat maps. The header line is `matrix "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `matrix "Title" style=<style> total=<on\|off> desc="<text>"` | The header. style= is compare (the default: options against criteria), score (a decision matrix, total on), raci (who is Responsible, Accountable, Consulted, Informed), or risk (a likelihood against impact grid). total=on adds a weighted total column. | `matrix "Message brokers" style=score` |
| `columns "<Name>" weight=<1 to 9> icon=<icon> "<Name>" "<Name>"` | The columns of compare, score, and raci: criteria, or roles in a RACI chart. Each quoted name may be followed by its own weight= (how much it counts in the total, default 1) and icon=. | `columns "Throughput" weight=3 icon=lucide:gauge "Ordering" "Cost"` |
| `row "<Name>" icon=<icon> tone=<tone> sub="<text>" : <cell>, <cell>` | A row with one cell for each column after the colon, separated by commas. A cell is yes, no, partial, na, a score from 0 to 5 (drawn as dots), or "quoted text". In style=raci a cell is R, A, C, I, a pair such as "A R", or na. | `row "Kafka" icon=simple:apachekafka sub="Log-based" : 5, yes, "Medium"` |
| `recommend "<Row name>"` | Marks one row as the pick, with a tone wash, a stronger outline, and a Recommended tab. It is never chosen from the totals for you. | `recommend "Kafka"` |
| `legend yes="<text>" partial="<text>" no="<text>" na="<text>"` | compare and score only: says what each mark means, drawn under the table as the same discs with their words. Only the marks named are shown, in the order written. Lint warns when a partial cell has no legend saying what partial means. | `legend yes="Allowed" partial="Own records only" no="Denied"` |
| `item "<Label>" likelihood=<1 to 5> impact=<1 to 5> icon=<icon>` | style=risk only: a risk placed on the 5 by 5 grid. The cell takes a band from likelihood times impact: low, moderate, high, or critical. | `item "Data loss" likelihood=2 impact=5 icon=lucide:database` |
| `levels <likelihood\|impact> "<L1>" "<L2>" "<L3>" "<L4>" "<L5>" title="<text>"` | style=risk only: names the five levels of an axis, lowest first, and optionally the axis. Without it the levels run Rare to Almost certain and Negligible to Severe. | `levels likelihood "Rare" "Unlikely" "Possible" "Likely" "Certain"` |
| `desc "<text>"` | The plain-language description of the matrix, for assistive technology; the same as desc= on the header. | `desc "Four brokers scored on five criteria."` |

### Sankey diagram

Flows between stages drawn as ribbons as thick as the amount, for where traffic, data, or cost goes. The header line is `sankey "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `sankey "<title>" unit="<unit>" values=<on\|off> sort=<desc\|written> align=<justify\|left> desc="<text>"` | The header; every option is optional. unit= says what the amounts count and is written after each amount (unit="req/s"), or before it for a currency sign (unit="$"). values=off hides the amounts. sort= is desc or written: it orders the first column and settles ties. align= is justify or left: justify puts every node with nothing leaving it in the last column, left leaves it beside its source. | `sankey "Request traffic" unit="req/s"` |
| `"<From>" -> "<To>" : <amount> tone=<tone>` | One flow, written as the two node names, an arrow, a colon, and the amount: plain digits with an optional fraction, above zero, no sign, comma, or unit. A node exists once a flow names it, so a node has no id and its name is its identity. The ribbon is as thick as the amount and takes the tone of the node it leaves. The same pair is written once. A flow back to an earlier node is an error, because a sankey only goes forward. Nodes take blue, orange, aqua, violet, amber, green, and neutral in turn, in the order they first appear. | `"Edge" -> "API gateway" : 12000` |
| `node "<Name>" icon=<set:name> tone=<tone>` | Gives a node an icon and a tone. Write it before or after the flows that name the node, once. A node line for a name no flow uses is an error. The theme chooses every color. | `node "API gateway" icon=lucide:server tone=blue` |
| `desc "<text>"` | The plain-language description of the diagram, for assistive technology; the same as desc= on the header. | `desc "How requests move from the edge to the data stores."` |

### XY chart

Bars, lines, and areas over a category or number axis, with reference lines, for metrics, capacity, and trends. The header line is `xychart "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `xychart "<title>" direction=<vertical\|horizontal> stack=<on\|off> values=<on\|off> desc="<text>"` | The header; every option is optional. horizontal puts the places down the side, which suits a ranking. stack=on stacks the bar series. values=off leaves the numbers off the bars. The title is in a corner tab. | `xychart "Latency by week" stack=off` |
| `x "<title>" ["<place>" "<place>"]  or  x "<title>" <from> to <to>` | The horizontal axis, once: named places in square brackets, or a number range that every series is spread evenly over. Each series then has one value for each place. Write a negative number as -5, and text with + ( < # or a bare / in quotes. | `x "Week" ["W1" "W2" "W3" "W4"]` |
| `y "<title>" unit=<text> min=<number> max=<number>` | The value axis, once; every part is optional. Ticks are on round numbers. Bars and areas start at zero unless min= says otherwise (lint warns). A word unit such as "ms" goes in the title, a symbol such as "$" or "%" on the numbers. | `y "Latency" unit="ms" min=0 max=800` |
| `bar\|line\|area "<name>" [<number> <number>] tone=<tone> icon=<set:name>` | One series: its name, then one plain number for each place in square brackets. area adds a wash under its line. Series take blue, orange, aqua, violet, amber, green, red, and neutral in turn; tone= chooses one. Up to 4 series are named beside their last points, more in a legend. | `line "p95" [310 360 345 420]` |
| `threshold "<label>" <number> tone=<tone> breach=<above\|below\|none>` | A labelled reference line, red unless tone= says otherwise. A bar or point on the wrong side of it (above by default) is drawn in its tone. The axis grows to hold it. | `threshold "SLO 400 ms" 400 tone=red` |
| `note "<text>" at="<place>"` | A callout above the plot with a dotted line down to a place on the x axis (an x value on a number axis). Not on a horizontal chart. | `note "Release 2.3" at="W3"` |
| `desc "<text>"` | The plain-language description, for assistive technology; the same as desc= on the header. | `desc "How the latency of the API moved over eight weeks."` |

### Tree

A hierarchy drawn as a file tree, an org chart, or an outline, for repository layouts, teams, ownership, and taxonomies. The header line is `tree "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `tree "Title" style=<files\|org\|outline> direction=<down\|right> counts=<on\|off> desc="<text>"` | The header. style=files (the default) draws a repository layout: a rounded frame with the title in its corner tab, one row for each entry with an icon chip, the name, a quiet column of notes, and thin guide lines from a folder to its entries. style=org draws cards in a tidy tree, for teams, ownership, and service dependencies: direction=down (the default) or direction=right says which way it grows, and a wide chart lists leaves under their parent. style=outline draws a compact bulleted list for a taxonomy. counts=on adds how many entries each folder holds. Only org reads direction. | `tree "plotwork-app" style=files` |
| `dir "<Name>" note="<text>" icon=<icon> tone=<tone> change=<added\|modified\|removed> size="<size>" mark=<on\|off> {` | A folder, a team, or a group. Write its entries inside and close with } on its own line; with no { it is an empty or collapsed folder. Entries nest up to 8 levels. note= is the quiet text after the name, or the second line of a card. change= washes the row green, amber, or red with a chip, for a diff. size="12 KB" is a pill at the right of the row. mark=on emphasises the entry. The folder icon is the default; tone= tints it. | `dir "src" note="source code" icon=lucide:folder {` |
| `file "<name>" note="<text>" icon=<icon> tone=<tone> change=<added\|modified\|removed> size="<size>" mark=<on\|off>` | A file, which holds nothing. Its icon is chosen from the name: TypeScript, JavaScript, JSON, Markdown, CSS, HTML, YAML, TOML, lock files, images, shell, Python, Go, Rust, Java, SQL, Dockerfile, and .gitignore each get one, and any other name gets a plain file icon. icon= overrides it. A name with a dot or a slash needs its quotes, as every name does. | `file "main.ts" note="entry point" change=modified size="2 KB"` |
| `node "<Name>" note="<text>" icon=<icon> tone=<tone> change=<added\|modified\|removed> size="<size>" mark=<on\|off> {` | A person, a team, a service, or a topic, for org and outline trees. It has no id, because a tree has no cross references. Write its reports or dependencies inside { }, or leave the braces off for a leaf. | `node "Platform" note="12 engineers" icon=lucide:users {` |
| `omitted <count> "<noun>"` | A quiet entry that says how many more exist and are not drawn: omitted 14 "files" draws "and 14 more files". Use it to keep a large folder short. | `omitted 14 "files"` |
| `desc "<text>"` | The plain-language description of the tree, for assistive technology; the same as desc= on the header. | `desc "The layout of the repository."` |

### Swimlane

A process whose steps sit in lanes, one for each actor or system, with decisions and hand-offs between the lanes, for business and integration flows. The header line is `swimlane "Title"`.

| Statement | What it does | Example |
|---|---|---|
| `swimlane "Title" direction=<auto\|right\|down> numbered=on start=<n> desc="<text>"` | The header. right draws the lanes as bands with time running left to right, down draws them as columns with time running down, and auto (the default) keeps the bands unless they would be wider than a Markdown column. numbered=on draws a numbered badge on each transition in the order written, and start= sets the first number. | `swimlane "Order fulfilment" direction=right numbered=on` |
| `lane <id> "Name" tone=<tone> icon=<set:name> {` | Opens a lane: the actor or system that performs the steps written inside it, drawn as a band or a column with its icon and name in a rail at its start. Lanes stack in the order written. Close it with } on its own line. Every step belongs to a lane. | `lane customer "Customer" icon=lucide:user {` |
| `step <id> "Label" sub="<detail>" tone=<tone> icon=<set:name>` | A step, drawn as a card in its lane. sub= is the line under the name, and a \n starts a new line. A tone other than neutral tints the card, and green, amber, or red also draw every transition that arrives at it in a softer line of that tone. | `step charge "Charge card" sub="Payments service" icon=lucide:credit-card` |
| `decision <id> "Question?" tone=<tone> icon=<set:name>` | A decision, drawn as a tinted pill with a branch icon: one way in, and a transition out for each answer, each labelled with the condition that takes it. The default tone is amber. | `decision stock "In stock?"` |
| `start <id> "Label" tone=<tone> icon=<set:name>` | Where the process begins, drawn as a small pill. | `start received "Order received"` |
| `end <id> "Label" tone=<tone> icon=<set:name>` | Where the process finishes, drawn as a small pill. A process may have several ends, such as delivered and cancelled, and a green, amber, or red one colors the line that reaches it. | `end delivered "Delivered" tone=green icon=status:success` |
| `from -> to : "event [guard] / action" step=<n>` | A transition, which may cross lanes freely and may return to an earlier step. The label and each part of it are optional: the event, a guard in square brackets that must hold, and an action after a slash. --> draws a dashed line, for a reply or an asynchronous hand-off, and ..> a dotted one, for an event. step= sets or skips the badge of a numbered diagram. | `stock -> charge : "[in stock]"` |
| `desc "<text>"` | A plain-language description of the process for the text description, as desc= on the header does. | `desc "How an order moves from the shop to the customer."` |

## Tones and settings

| Setting | Values |
|---|---|
| Tones | `neutral`, `blue`, `violet`, `aqua`, `orange`, `green`, `amber`, `red` |
| Flow directions (`direction=`) | `auto`, `right`, `down`, `left`, `up` |
| Themes | `light`, `dark` |
| Sequence frames | `alt`, `opt`, `loop`, `par` |
| Schema column flags | `pk`, `fk`, `unique`, `nullable` |

## See also

| Doc | Why |
|---|---|
| [Language reference](https://docs.plotwork.dev/reference/language.md) | Every page of the reference and what each covers |
| [Writing diagrams](https://docs.plotwork.dev/writing-diagrams.md) | The language by example, kind by kind |
| [Flow node types](https://docs.plotwork.dev/reference/language/flow-nodes.md) | The types a flow node can take |
| [Diagnostic codes](https://docs.plotwork.dev/reference/language/diagnostics.md) | What every error and warning means |
