Built-in shapes — declare the meaning, get the visual
Attach these types as properties; the formatter draws the right visual. The anti-pattern is
building bars/trees by hand (new string('█', n), indented dashes). Say what the data is — a
measurement, a breakdown, an alert — not how to draw it.
The shapes
[MarkoutSerializable(TitleProperty = nameof(Title))]
public class Dashboard
{
public string Title { get; set; } = "";
// Bar chart — comparative quantities.
[MarkoutSection(Name = "Timings"), MarkoutIgnoreInTable]
public List<Metric>? Timings { get; set; } // new Metric("Build", 4.2)
// Stacked/proportional bar.
[MarkoutSection(Name = "Severity"), MarkoutIgnoreInTable]
public List<Breakdown>? Severity { get; set; }
// new Breakdown("Issues", [new Slice("Critical", 3), new Slice("Low", 12)])
// Alert box. [MarkoutSkipDefault] hides it when unset.
[MarkoutIgnoreInTable, MarkoutSkipDefault]
public Callout Warning { get; set; } // new Callout(CalloutSeverity.Warning, "3 issues")
// Tree hierarchy. Children are a COLLECTION, never trailing args.
[MarkoutIgnoreInTable]
public List<TreeNode>? Deps { get; set; }
// new TreeNode("root", [new TreeNode("child")]) { Badge = "📁" }
// Term + explanation list.
[MarkoutSection(Name = "Glossary"), MarkoutIgnoreInTable]
public List<Description>? Glossary { get; set; } // new Description("API", "Application ...")
// Code block.
[MarkoutIgnoreInTable]
public CodeSection? Snippet { get; set; } // new CodeSection("csharp", "class Foo { }")
}
Critical guardrails
[MarkoutIgnoreInTable] on every shape list/property. Without it, List<Metric> etc. get
mistreated as a table of columns instead of rendering as the shape. This is the #1 shapes mistake.
- A single
Breakdown property (not a list) renders as ONE labeled proportional bar — use it for a
covered-vs-uncovered coverage bar rather than a List<Breakdown>:
[MarkoutIgnoreInTable] public Breakdown Coverage { get; set; } with
new Breakdown("Coverage", [new Slice("Covered", 82), new Slice("Uncovered", 18)]). Under a Unicode/
terminal formatter this shows the group label + █ bar, not per-slice table rows.
- Children go in a collection expression, never as trailing constructor arguments:
new TreeNode("root", [new TreeNode("leaf")]). Badge is an optional object-initializer property.
Callout is a value type — pair it with [MarkoutSkipDefault] so an unset callout disappears.
- Do not hand-draw bars/trees; if you're building glyphs by hand you're using the wrong tool.
Shape cheat-sheet
| Type |
Meaning |
Construct |
Metric |
one measured value (bar) |
new Metric("Build", 4.2) |
Breakdown + Slice |
proportional composition |
new Breakdown("By type", [new Slice("A", 3)]) |
Callout |
alert/severity box |
new Callout(CalloutSeverity.Warning, "…") |
TreeNode |
hierarchy |
new TreeNode("root", [children]) { Badge = "📁" } |
Description |
term + text |
new Description("API", "…") |
CodeSection |
fenced code block |
new CodeSection("csharp", "…") |
1---2name: built-in-shapes3description: Use when a report needs rich visual elements — bar charts, stacked/proportional bars, alert boxes, tree hierarchies, term/definition glossaries, or code blocks — instead of hand-drawn ASCII or manual Markdown. Markout ships these as data types (Metric, Breakdown/Slice, Callout, TreeNode, Description, CodeSection) you attach as model properties. Requires the base `markout` pattern. Don't decompile the assembly or web-search the API — the shape types are here.4---56# Built-in shapes — declare the meaning, get the visual78Attach these types as properties; the formatter draws the right visual. The anti-pattern is9building bars/trees by hand (`new string('█', n)`, indented dashes). Say *what the data is* — a10measurement, a breakdown, an alert — not *how to draw it*.1112## The shapes1314```csharp15[MarkoutSerializable(TitleProperty = nameof(Title))]16public class Dashboard17{18 public string Title { get; set; } = "";1920 // Bar chart — comparative quantities.21 [MarkoutSection(Name = "Timings"), MarkoutIgnoreInTable]22 public List<Metric>? Timings { get; set; } // new Metric("Build", 4.2)2324 // Stacked/proportional bar.25 [MarkoutSection(Name = "Severity"), MarkoutIgnoreInTable]26 public List<Breakdown>? Severity { get; set; }27 // new Breakdown("Issues", [new Slice("Critical", 3), new Slice("Low", 12)])2829 // Alert box. [MarkoutSkipDefault] hides it when unset.30 [MarkoutIgnoreInTable, MarkoutSkipDefault]31 public Callout Warning { get; set; } // new Callout(CalloutSeverity.Warning, "3 issues")3233 // Tree hierarchy. Children are a COLLECTION, never trailing args.34 [MarkoutIgnoreInTable]35 public List<TreeNode>? Deps { get; set; }36 // new TreeNode("root", [new TreeNode("child")]) { Badge = "📁" }3738 // Term + explanation list.39 [MarkoutSection(Name = "Glossary"), MarkoutIgnoreInTable]40 public List<Description>? Glossary { get; set; } // new Description("API", "Application ...")4142 // Code block.43 [MarkoutIgnoreInTable]44 public CodeSection? Snippet { get; set; } // new CodeSection("csharp", "class Foo { }")45}46```4748## Critical guardrails4950- **`[MarkoutIgnoreInTable]` on every shape list/property.** Without it, `List<Metric>` etc. get51 mistreated as a table of columns instead of rendering as the shape. This is the #1 shapes mistake.52- **A single `Breakdown` property (not a list) renders as ONE labeled proportional bar** — use it for a53 covered-vs-uncovered coverage bar rather than a `List<Breakdown>`:54 `[MarkoutIgnoreInTable] public Breakdown Coverage { get; set; }` with55 `new Breakdown("Coverage", [new Slice("Covered", 82), new Slice("Uncovered", 18)])`. Under a Unicode/56 terminal formatter this shows the group label + `█` bar, not per-slice table rows.57- **Children go in a collection expression**, never as trailing constructor arguments:58 `new TreeNode("root", [new TreeNode("leaf")])`. `Badge` is an optional object-initializer property.59- **`Callout` is a value type** — pair it with `[MarkoutSkipDefault]` so an unset callout disappears.60- Do not hand-draw bars/trees; if you're building glyphs by hand you're using the wrong tool.6162## Shape cheat-sheet6364| Type | Meaning | Construct |65|---|---|---|66| `Metric` | one measured value (bar) | `new Metric("Build", 4.2)` |67| `Breakdown` + `Slice` | proportional composition | `new Breakdown("By type", [new Slice("A", 3)])` |68| `Callout` | alert/severity box | `new Callout(CalloutSeverity.Warning, "…")` |69| `TreeNode` | hierarchy | `new TreeNode("root", [children]) { Badge = "📁" }` |70| `Description` | term + text | `new Description("API", "…")` |71| `CodeSection` | fenced code block | `new CodeSection("csharp", "…")` |