Text Diagram Drawing
Draw professional, pixel-perfect text-based architecture diagrams using monospace characters. Every character must align correctly in a monospace font.
Critical Rules (NEVER violate)
- Calculate widths BEFORE drawing. Never freehand a diagram. Always compute column widths, box widths, and total widths first, then render.
- Every line in a box must be the SAME character length. Count characters. Verify.
- Use ONLY ASCII-safe monospace characters for borders:
|,-,+for basic; or Unicode box-drawing (see charset below). Never mix styles in one diagram. - Pad content with spaces to fill the full inner width of every box.
- Vertical connectors (
|or│) must be at the EXACT same column position on every line between two boxes. - After drawing, mentally re-count character positions of the first line, last line, and any connector lines to verify alignment.
- One box-drawing style per diagram. Don't mix
+--+with┌──┐.
Character Sets
Style A: Pure ASCII (safest, works everywhere)
Corners: +
Horiz: -
Vert: |
Arrow: -> or -->
Down: v or V
Cross: +
T-junc: +
Style B: Unicode Light Box Drawing (cleaner look)
Corners: ┌ ┐ └ ┘
Horiz: ─
Vert: │
T-junc: ├ ┤ ┬ ┴
Cross: ┼
Arrow: → ← ↑ ↓
Triangle: ▼ ▲ ► ◄
Style C: Unicode Rounded (friendly look)
Corners: ╭ ╮ ╰ ╯
Horiz: ─
Vert: │
(rest same as Style B)
Style D: Double-line (emphasis/outer containers)
Corners: ╔ ╗ ╚ ╝
Horiz: ═
Vert: ║
T-junc: ╠ ╣ ╦ ╩
Cross: ╬
Style E: Dashed borders (secondary containers)
Vert: ¦ or : or ╎
Horiz: - - - (dash-space-dash)
Width Constraints
- Default max width: 80 characters (standard terminal). Use 120 for wide diagrams.
- If a diagram exceeds the limit, reduce padding, abbreviate labels, or split into multiple diagrams.
- Never use tab characters. Always spaces.
- All Unicode box-drawing chars (U+2500-U+257F) are exactly 1 column wide. Safe to use.
- Avoid: CJK chars (2-wide), emoji (variable width), heavy/dashed variants if cross-platform rendering matters.
Drawing Algorithm
ALWAYS follow these steps in order:
Step 1: Plan the Layout
- List all boxes, their nesting depth, and content lines
- Determine layout direction (top-down, left-right, or mixed)
- Identify connectors between boxes
Step 2: Calculate Dimensions (CRITICAL)
For each box:
inner_width = max(len(line) for line in content_lines) + 2*padding
outer_width = inner_width + 2 (for left/right borders)
For side-by-side boxes:
total_width = sum(outer_widths) + gaps_between_boxes
For container boxes:
container_inner_width = max(total_width_of_children, len(title)) + 2*padding
container_outer_width = container_inner_width + 2
Step 3: Render Top-Down
- Draw each line as a fixed-width string
- Use
.ljust()/ space-padding to ensure every line in a box is the same width - For nested boxes: indent children by container's left border + padding
- Symmetric padding rule: if you indent a child row with 2 spaces on the LEFT after the container's
│, you must reserve 2 spaces on the RIGHT before the closing│. The most common alignment bug is leaving only 1 space on the right.
Step 4: Verify Alignment
- Check: first border line length == last border line length
- Check: all content lines length == border line length
- Check: vertical connector column is consistent across all rows
- Check: side-by-side boxes have matching heights (pad shorter ones with empty lines)
Layout Patterns
Pattern: Container with Title
┌──────────────────────────────┐
│ Container Title │
│ │
│ ┌────────┐ ┌────────┐ │
│ │ Box A │ │ Box B │ │
│ └────────┘ └────────┘ │
│ │
└──────────────────────────────┘
Rule: outer width = 32. Every row (border, title, empty, child rows, bottom) is exactly 32 chars. Right padding before the closing │ mirrors the left padding (2 spaces) plus any extra slack.
Pattern: Vertical Flow with Connector
┌──────────┐
│ Box A │
└────┬─────┘
│
▼
┌──────────┐
│ Box B │
└──────────┘
Rule: The │ and ▼ must be at the same column as ┬. Calculate: left_border + (inner_width / 2) + 1.
Pattern: Horizontal Flow Inside Container
┌──────────────────────────────────────┐
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ Step 1 │ → │ Step 2 │ │
│ └─────────┘ └─────────┘ │
│ │
└──────────────────────────────────────┘
Rule: outer width = 40. Arrow → sits at the vertical midpoint of the boxes, between them. Right side keeps ≥2 spaces before the closing │ to mirror left padding.
Pattern: Side-by-Side Boxes (Equal Height)
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Title A │ │ Title B │ │ Title C │
│ (desc) │ │ (desc) │ │ (desc) │
└──────────┘ └──────────┘ └──────────┘
Rule: All boxes MUST have the same number of lines. Pad shorter boxes with blank content lines.
Pattern: Centered Connector Between Sections
┌─────────────────────────┐
│ Section A │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Section B │
└─────────────────────────┘
Rule: Both sections should be the same width. Connector at width / 2.
Common Mistakes to Avoid
| Mistake | Fix |
|---|---|
| Lines in a box have different lengths | Pad ALL lines to max_content_width + 2*padding |
| Right border 1 char short on child rows | Mirror left padding: if left has 2 spaces, right needs 2 spaces too |
| Vertical connector off by 1 | Calculate center: border_start + 1 + floor(inner_width / 2) |
| Nested box wider than parent | Calculate parent width from children FIRST |
| Side-by-side boxes different heights | Count lines, pad shorter box with empty │{spaces}│ lines |
| Right border not aligned | Use fixed-width strings, never rely on "looks right" |
| Mixed border styles | Pick ONE style at the start, use it throughout |
| Arrow not vertically centered on box | Arrow row = top_border_row + 1 + floor(content_lines / 2) |
| Forgot to account for border chars | outer_width = inner_width + 2 (left │ + right │) |
Rendering Checklist (Run mentally after drawing)
- Every box: first border line length == last border line length
- Every box: all content lines == border line length
- Container boxes: wider than all children combined + gaps + padding
- Side-by-side boxes: all same height (line count)
- Vertical connectors: same column on every row
- No trailing spaces inconsistency
- Nested boxes indented consistently
- Arrows aligned to box midpoints
- Right padding mirrors left padding before the closing border
Examples
See references/examples.md for complete worked examples with dimension calculations, and references/advanced-patterns.md for nested architectures, fan-out, and annotated connectors.