Box Drawing Characters: Building Text-Based UI with Unicode
- ○ 1. Ligatures in Web Typography: From fi to Modern OpenType Features
- ○ 2. Whitespace Characters in Web Development: Beyond the Space Bar
- ○ 3. CJK Web Typography: Chinese, Japanese, and Korean Text on the Web
- ● 4. Box Drawing Characters: Building Text-Based UI with Unicode
- ○ 5. Font Fallback and Tofu: Why Characters Display as Empty Boxes
Long before CSS existed, programmers were drawing boxes and tables with characters. The box drawing tradition stretches from CP437 (the DOS character set), through DEC VT100 terminals, to the Unicode standard that preserves and extends it today. These characters remain genuinely useful for terminal output, documentation formatting, code comments, ASCII art diagrams, and any context where you need structured visual layout using only text.
The Box Drawing Block (U+2500–U+257F)
Unicode's Box Drawing block contains 128 characters covering every combination of line direction, thickness, and double-line variant. The block is logically organized:
Light Single Lines
The four directional segments form the foundation. Combine them to build any box:
| Character | Code Point | Description |
|---|---|---|
| ─ | U+2500 | BOX DRAWINGS LIGHT HORIZONTAL |
| │ | U+2502 | BOX DRAWINGS LIGHT VERTICAL |
| ┌ | U+250C | BOX DRAWINGS LIGHT DOWN AND RIGHT |
| ┐ | U+2510 | BOX DRAWINGS LIGHT DOWN AND LEFT |
| └ | U+2514 | BOX DRAWINGS LIGHT UP AND RIGHT |
| ┘ | U+2518 | BOX DRAWINGS LIGHT UP AND LEFT |
| ├ | U+251C | BOX DRAWINGS LIGHT VERTICAL AND RIGHT |
| ┤ | U+2524 | BOX DRAWINGS LIGHT VERTICAL AND LEFT |
| ┬ | U+252C | BOX DRAWINGS LIGHT DOWN AND HORIZONTAL |
| ┴ | U+2534 | BOX DRAWINGS LIGHT UP AND HORIZONTAL |
| ┼ | U+253C | BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL |
A complete light-line box:
┌──────────────┐
│ Content │
│ goes here │
└──────────────┘
Heavy Single Lines
Heavy variants are the same structure but thicker:
| Character | Code Point | Description |
|---|---|---|
| ━ | U+2501 | BOX DRAWINGS HEAVY HORIZONTAL |
| ┃ | U+2503 | BOX DRAWINGS HEAVY VERTICAL |
| ┏ | U+250F | BOX DRAWINGS HEAVY DOWN AND RIGHT |
| ┓ | U+2513 | BOX DRAWINGS HEAVY DOWN AND LEFT |
| ┗ | U+2517 | BOX DRAWINGS HEAVY UP AND RIGHT |
| ┛ | U+251B | BOX DRAWINGS HEAVY UP AND LEFT |
| ┣ | U+2523 | BOX DRAWINGS HEAVY VERTICAL AND RIGHT |
| ┫ | U+252B | BOX DRAWINGS HEAVY VERTICAL AND LEFT |
| ┳ | U+2533 | BOX DRAWINGS HEAVY DOWN AND HORIZONTAL |
| ┻ | U+253B | BOX DRAWINGS HEAVY UP AND HORIZONTAL |
| ╋ | U+254B | BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL |
A heavy-line box (good for section headers):
┏━━━━━━━━━━━━━━┓
┃ Section ┃
┗━━━━━━━━━━━━━━┛
Double Lines
Double-line characters are the classic DOS/ANSI art style:
| Character | Code Point | Description |
|---|---|---|
| ═ | U+2550 | BOX DRAWINGS DOUBLE HORIZONTAL |
| ║ | U+2551 | BOX DRAWINGS DOUBLE VERTICAL |
| ╔ | U+2554 | BOX DRAWINGS DOUBLE DOWN AND RIGHT |
| ╗ | U+2557 | BOX DRAWINGS DOUBLE DOWN AND LEFT |
| ╚ | U+255A | BOX DRAWINGS DOUBLE UP AND RIGHT |
| ╝ | U+255D | BOX DRAWINGS DOUBLE UP AND LEFT |
| ╠ | U+2560 | BOX DRAWINGS DOUBLE VERTICAL AND RIGHT |
| ╣ | U+2563 | BOX DRAWINGS DOUBLE VERTICAL AND LEFT |
| ╦ | U+2566 | BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL |
| ╩ | U+2569 | BOX DRAWINGS DOUBLE UP AND HORIZONTAL |
| ╬ | U+256C | BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL |
A classic double-line dialog box:
╔══════════════╗
║ Warning ║
╠══════════════╣
║ Are you sure?║
╚══════════════╝
Mixed Light/Heavy and Mixed Single/Double
The block includes corners and junctions for mixed-weight lines, allowing hierarchical visual structure:
| Character | Code Point | Description |
|---|---|---|
| ╒ | U+2552 | BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE |
| ╓ | U+2553 | BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE |
| ╪ | U+256A | BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL DOUBLE |
| ╫ | U+256B | BOX DRAWINGS LIGHT VERTICAL DOUBLE AND HORIZONTAL SINGLE |
Mixed-weight tables use these for header/body dividers:
╒══════╤═══════╕
│ Name │ Score │
╞══════╪═══════╡
│ Alice│ 100 │
│ Bob │ 98 │
╘══════╧═══════╛
Dashed Lines
For lighter visual structures:
| Character | Code Point | Description |
|---|---|---|
| ╌ | U+254C | BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL |
| ╍ | U+254D | BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL |
| ╎ | U+254E | BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL |
| ╏ | U+254F | BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL |
| ┄ | U+2504 | BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL |
| ┅ | U+2505 | BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL |
| ┈ | U+2508 | BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL |
| ┉ | U+2509 | BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL |
The Block Elements Block (U+2580–U+259F)
Block elements are eight-directional partial-fill characters that enable pixel-art-style shading and progress bars using only text:
| Character | Code Point | Description | Fill |
|---|---|---|---|
| ▀ | U+2580 | UPPER HALF BLOCK | Top 50% |
| ▄ | U+2584 | LOWER HALF BLOCK | Bottom 50% |
| █ | U+2588 | FULL BLOCK | 100% |
| ░ | U+2591 | LIGHT SHADE | ~25% |
| ▒ | U+2592 | MEDIUM SHADE | ~50% |
| ▓ | U+2593 | DARK SHADE | ~75% |
| ▌ | U+258C | LEFT HALF BLOCK | Left 50% |
| ▐ | U+2590 | RIGHT HALF BLOCK | Right 50% |
| ▉ | U+2589 | LEFT SEVEN EIGHTHS BLOCK | 87.5% |
| ▊ | U+258A | LEFT THREE QUARTERS BLOCK | 75% |
| ▋ | U+258B | LEFT FIVE EIGHTHS BLOCK | 62.5% |
| ▍ | U+258D | LEFT THREE EIGHTHS BLOCK | 37.5% |
| ▎ | U+258E | LEFT ONE QUARTER BLOCK | 25% |
| ▏ | U+258F | LEFT ONE EIGHTH BLOCK | 12.5% |
These enable smooth progress bars and bar charts:
Progress: [████████░░░░░░░░] 50%
CPU: [▓▓▓▓▓▓▓▓▒▒▒▒░░░░] 62%
Memory: [█████████████░░░] 81%
Practical Examples
File Tree / Directory Listing
The most common non-table use of box drawing characters is a file tree. Most CLI tools (like tree on Linux/macOS) use this pattern:
project/
├── src/
│ ├── components/
│ │ ├── Button.tsx
│ │ └── Input.tsx
│ ├── pages/
│ │ ├── index.tsx
│ │ └── about.tsx
│ └── utils/
│ └── helpers.ts
├── public/
│ └── favicon.ico
├── package.json
└── README.md
The characters used are ├ (U+251C), └ (U+2514), │ (U+2502), and ─ (U+2500). This pattern is ubiquitous in documentation and README files.
Data Tables in Plain Text
┌─────────────┬──────────┬──────────┐
│ Feature │ Free │ Pro │
├─────────────┼──────────┼──────────┤
│ Projects │ 3 │ Unlimited│
│ Storage │ 5 GB │ 100 GB │
│ API calls │ 1,000/mo │ 50,000/mo│
│ Support │ Community│ Priority │
└─────────────┴──────────┴──────────┘
Flowcharts and State Diagrams in Code Comments
┌─────────┐ ┌──────────┐ ┌─────────┐
│ Request │────▶│ Validate │────▶│ Process │
└─────────┘ └──────────┘ └─────────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ Error │ │ Success │
└─────────┘ └─────────┘
(Note: ▶ is U+25B6 BLACK RIGHT-POINTING TRIANGLE, from the Geometric Shapes block U+25A0–U+25FF)
CLI Tool Output
Server Status
─────────────────────────────────────
● nginx active (running)
● postgresql active (running)
✗ redis inactive (dead)
Disk Usage
┌──────────────────────┬──────┬──────┐
│ Mount │ Used │ Free │
├──────────────────────┼──────┼──────┤
│ / │ 45% │ 127G │
│ /var │ 62% │ 38G │
└──────────────────────┴──────┴──────┘
CSS Requirements: Monospace is Non-Negotiable
Box drawing characters only align correctly with a monospace font. Each character must occupy exactly the same horizontal width. In a proportional font, the ─ horizontal line may be slightly narrower or wider than the space occupied by a letter, creating gaps or overlaps.
/* Box drawing requires monospace */
.box-drawing,
pre,
code {
font-family:
"Cascadia Code",
"Fira Code",
"JetBrains Mono",
"Consolas",
"Menlo",
"Monaco",
"Courier New",
monospace;
}
Line Height Matters
Box drawing characters are designed to connect seamlessly when line-height is exactly 1. Any additional line spacing creates vertical gaps between horizontal runs:
.box-art {
font-family: monospace;
line-height: 1; /* Critical: no gap between lines */
letter-spacing: 0; /* Prevent horizontal gaps */
white-space: pre; /* Preserve all spacing */
}
Color Styling
Box drawing characters respond to color like any text character, enabling colored terminal-style output:
.terminal {
background: #1e1e1e;
color: #cccccc;
font-family: monospace;
line-height: 1;
padding: 1rem;
}
.terminal .header-box {
color: #4ec9b0; /* Teal for headers */
}
.terminal .error-box {
color: #f44747; /* Red for errors */
}
.terminal .success-box {
color: #4caf50; /* Green for success */
}
Block Elements as Progress Bars
Block element characters work well for inline progress indicators styled as monospace text:
.progress-bar {
font-family: monospace;
font-size: 1rem;
letter-spacing: -0.05em; /* Tighten the blocks slightly */
}
.progress-filled {
color: #4caf50;
}
.progress-empty {
color: #555555;
}
<span class="progress-bar">
<span class="progress-filled">████████</span><span class="progress-empty">░░░░░░░░</span>
</span>
50%
Terminal Emulator Rendering Differences
One significant challenge with box drawing characters is that different terminal emulators render them differently — gaps between characters, slightly off-metric widths, and font substitution all affect alignment. Web rendering is more predictable because you fully control the font stack, but these concerns still apply to <pre> blocks that might be copy-pasted into terminals.
Safe practices for cross-context box drawing:
- Stick to the light single-line characters (U+2500–U+253C) — these have the most consistent rendering across platforms.
- Avoid mixing light and heavy lines unless you have tested in all target environments.
- Double-line characters (U+2550–U+256C) are well-supported but look slightly different across fonts.
- The dashed variants (triple dash, quadruple dash) are less reliably rendered in some legacy terminal fonts.
For web contexts specifically, fonts with excellent box drawing support include: Cascadia Code, Fira Code, JetBrains Mono, Inconsolata, and Iosevka. The system fallbacks (Consolas on Windows, Menlo/Monaco on macOS) also render box drawing correctly.
Using Box Drawing in HTML
When embedding box drawing art in HTML, use a <pre> element to preserve whitespace, and encode any characters that have HTML significance:
<pre class="box-drawing">
┌──────────────────┐
│ Unicode Symbol │
│ Reference │
└──────────────────┘
</pre>
You can also use box drawing characters directly in regular text within <code> spans when describing terminal output or CLI interfaces. No special HTML entities are needed — box drawing characters are standard Unicode and can be used directly in UTF-8 HTML.
For generated output (dynamic tables, tree views), you can construct box drawing strings in JavaScript:
const BOX = {
topLeft: '┌', topRight: '┐',
bottomLeft: '└', bottomRight: '┘',
horizontal: '─', vertical: '│',
teeRight: '├', teeLeft: '┤',
teeDown: '┬', teeUp: '┴',
cross: '┼'
};
function drawBox(lines, width) {
const pad = (s) => s.padEnd(width);
const top = BOX.topLeft + BOX.horizontal.repeat(width + 2) + BOX.topRight;
const bottom = BOX.bottomLeft + BOX.horizontal.repeat(width + 2) + BOX.bottomRight;
const rows = lines.map(l => `${BOX.vertical} ${pad(l)} ${BOX.vertical}`);
return [top, ...rows, bottom].join('\n');
}
Next in Series: Part 5 closes the series by explaining why characters sometimes render as empty boxes (tofu), how font fallback works across operating systems, and how to build font stacks that handle the full range of Unicode gracefully. Font Fallback and Tofu: Why Characters Display as Empty Boxes