Visual Design Language
Every CLI tool in the GospeLib monorepo follows a shared visual language to deliver a consistent, polished developer experience. This page is the definitive reference for colors, symbols, typography, progress indicators, tables, prompts, status labels, banners, spacing, and accessibility.
All Python CLI tools use Rich for terminal rendering. Bash scripts use the equivalent ANSI escape codes.
Color Palette
Colors are specified as Rich markup tags. Bash equivalents use ANSI escape codes.
| Semantic | Rich Markup | ANSI Code | Hex (reference) | Usage |
|---|---|---|---|---|
| Success | [bold green] | \033[1;32m | #00ff00 | Completed actions, passing tests, healthy |
| Error | [bold red] | \033[1;31m | #ff0000 | Failures, crashes, critical problems |
| Warning | [bold yellow] | \033[1;33m | #ffff00 | Non-fatal issues, deprecations |
| Info | [cyan] | \033[0;36m | #00ffff | Identifiers, names, URLs, ports |
| Accent | [bold blue] | \033[1;34m | #5f87ff | Brand accent, group headers |
| Dim | [dim] | \033[2m | — | Secondary text, timestamps, paths |
| Header | [bold] | \033[1m | — | Section headers, command names |
| Muted | [dim italic] | \033[2;3m | — | Hints, defaults, placeholders |
| Reset | [/] | \033[0m | — | Close all styles |
Unicode Characters
Standard symbols used throughout CLI output. Each has a plain ASCII fallback for environments without Unicode support.
| Purpose | Character | Unicode | Fallback |
|---|---|---|---|
| Success | ✓ | U+2713 | [OK] |
| Failure | ✗ | U+2717 | [FAIL] |
| Warning | ⚠ | U+26A0 | [WARN] |
| Arrow (right) | → | U+2192 | -> |
| Arrow (pointer) | ▸ | U+25B8 | > |
| Bullet | • | U+2022 | * |
| Separator | │ | U+2502 | | |
| Ellipsis | … | U+2026 | ... |
| Running/spinner | ◐◓◑◒ | U+25D0–D3 | -|/- |
| Progress filled | ━ | U+2501 | # |
| Progress empty | ╺ | U+257A | - |
| Progress cap (L) | ╸ | U+2578 | [ |
| Progress cap (R) | ╺ | U+257A | ] |
| Section rule | ─ | U+2500 | - |
Typography Rules
Consistent styling ensures that users can scan output quickly and identify key elements at a glance.
| Element | Style | Example |
|---|---|---|
| Group/command names | [bold] | dev, start |
| Flag names | [cyan] | --service, -v |
| Flag values/defaults | [dim italic] | gateway |
| File paths | [dim] | services/content/src/ |
| URLs/ports | [cyan] | http://localhost:8100 |
| Counts/numbers | [bold] | 12 tests passed |
| Timing/durations | [dim] | (3.2s) |
| Environment variables | [bold cyan] | GOSPELIB_CONTENT_DB_URL |
Progress Indicators
Spinners
Use Rich's built-in Spinner with the dots style for operations with unknown duration:
from rich.console import Console
from rich.spinner import Spinner
from rich.live import Live
console = Console()
with Live(Spinner("dots", text="Starting services..."), console=console):
# work
pass
When to use: starting containers, downloading files, waiting for health checks — any operation where the total duration is unknown.
Progress Bars
Use Rich's Progress for operations with a known total:
from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn
with Progress(
TextColumn("[bold blue]{task.description}"),
BarColumn(bar_width=40),
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
TimeRemainingColumn(),
) as progress:
task = progress.add_task("Downloading...", total=100)
for i in range(100):
progress.update(task, advance=1)
When to use: file downloads, batch processing, test suite execution.
Choosing Between Them
| Condition | Use |
|---|---|
| Total work units known | Progress bar |
| Total unknown / single operation | Spinner |
| Multiple parallel operations | Multi-bar |
| Very fast (<500ms) | No indicator |
Tables
All tabular output uses rich.table.Table (Python) or equivalent formatted output.
Rules:
- Header row: bold, no cell borders
- Column alignment: strings left, numbers right, status center
- Maximum width: respect terminal width (
console.width). Truncate overflowing content with… - Style by column type:
- Name/identifier columns:
[cyan] - Status columns: colored by value (green/red/yellow)
- Numeric columns: plain
- Path columns:
[dim]
- Name/identifier columns:
table = Table(title="Service Health", show_header=True, header_style="bold")
table.add_column("Service", style="cyan", min_width=12)
table.add_column("Port", justify="right")
table.add_column("Status", justify="center")
table.add_column("Uptime", style="dim", justify="right")
table.add_row("Gateway", "8080", "[bold green]✓ healthy[/]", "2h 15m")
table.add_row("Content", "8100", "[bold red]✗ down[/]", "—")
console.print(table)
Prompts
| Prompt Type | Library | Styling |
|---|---|---|
| Confirmation | rich.prompt.Confirm | [bold]Question?[/] [dim][y/N][/] |
| Text input | rich.prompt.Prompt | [bold]Label[/]: with default shown in [dim italic] |
| Selection | rich.prompt.Prompt | Number-indexed list, selected item in [bold cyan] |
| Multi-select | Custom (checkbox list) | [dim][ ][/] unchecked, [bold green][✓][/] checked |
| Password | rich.prompt.Prompt | password=True — input masked |
Status Indicators
Inline labels used in dashboards, tables, and log output.
| Status | Display | Usage |
|---|---|---|
| Running | [bold blue]● running[/] | Service/container is active |
| Done | [bold green]✓ done[/] | Action completed successfully |
| Failed | [bold red]✗ failed[/] | Action failed |
| Skipped | [dim]○ skipped[/] | Action not applicable / skipped |
| Pending | [yellow]◌ pending[/] | Action queued / waiting |
| Degraded | [bold yellow]⚠ degraded[/] | Partially working |
Banners
Welcome Banner
Displayed by the gl root command (no arguments):
from rich.panel import Panel
console.print(Panel.fit(
"[bold blue]GospeLib CLI[/] [dim]v0.1.0[/]",
border_style="blue",
))
Error Banner
For fatal errors that terminate a command:
console.print(Panel(
f"[bold red]Error:[/] {message}\n\n[dim]{hint}[/]",
title="[bold red]✗ Command Failed[/]",
border_style="red",
))
Warning Banner
For non-fatal warnings that deserve visual emphasis:
console.print(Panel(
f"[bold yellow]Warning:[/] {message}",
border_style="yellow",
))
Spacing and Layout
- One blank line between logical sections of output
- Indent nested content with 2 spaces (Rich default)
- Section dividers:
console.rule("[bold]Section Title[/]")for Rich;echo ""for Bash - No trailing blank line at end of output
- Right-align timing information on the same line as a status message where possible
Terminal Width Adaptation
- Read terminal width via
os.get_terminal_size()orconsole.width - Tables must not exceed terminal width — use
Table(width=console.width)or truncation - Long values (paths, URLs) must be truncated with
…if they would cause wrapping - Minimum supported width: 60 columns. Below this, switch to a compact vertical layout
NO_COLOR / FORCE_COLOR Support
All tools comply with the NO_COLOR standard:
- If
NO_COLORenv var is set (to any value), disable all color and styling - If
FORCE_COLORenv var is set, force color even when output is piped - Rich handles this automatically via
Console(force_terminal=...) - Bash scripts must check:
[[ -n "${NO_COLOR:-}" ]] && RED="" && GREEN="" && ... - The
--no-colorflag is a global option on theglroot group