Skip to main content

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.

SemanticRich MarkupANSI CodeHex (reference)Usage
Success[bold green]\033[1;32m#00ff00Completed actions, passing tests, healthy
Error[bold red]\033[1;31m#ff0000Failures, crashes, critical problems
Warning[bold yellow]\033[1;33m#ffff00Non-fatal issues, deprecations
Info[cyan]\033[0;36m#00ffffIdentifiers, names, URLs, ports
Accent[bold blue]\033[1;34m#5f87ffBrand accent, group headers
Dim[dim]\033[2mSecondary text, timestamps, paths
Header[bold]\033[1mSection headers, command names
Muted[dim italic]\033[2;3mHints, defaults, placeholders
Reset[/]\033[0mClose all styles

Unicode Characters

Standard symbols used throughout CLI output. Each has a plain ASCII fallback for environments without Unicode support.

PurposeCharacterUnicodeFallback
SuccessU+2713[OK]
FailureU+2717[FAIL]
WarningU+26A0[WARN]
Arrow (right)U+2192->
Arrow (pointer)U+25B8>
BulletU+2022*
SeparatorU+2502|
EllipsisU+2026...
Running/spinner◐◓◑◒U+25D0–D3-|/-
Progress filledU+2501#
Progress emptyU+257A-
Progress cap (L)U+2578[
Progress cap (R)U+257A]
Section ruleU+2500-

Typography Rules

Consistent styling ensures that users can scan output quickly and identify key elements at a glance.

ElementStyleExample
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

ConditionUse
Total work units knownProgress bar
Total unknown / single operationSpinner
Multiple parallel operationsMulti-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]
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 TypeLibraryStyling
Confirmationrich.prompt.Confirm[bold]Question?[/] [dim][y/N][/]
Text inputrich.prompt.Prompt[bold]Label[/]: with default shown in [dim italic]
Selectionrich.prompt.PromptNumber-indexed list, selected item in [bold cyan]
Multi-selectCustom (checkbox list)[dim][ ][/] unchecked, [bold green][✓][/] checked
Passwordrich.prompt.Promptpassword=True — input masked

Status Indicators

Inline labels used in dashboards, tables, and log output.

StatusDisplayUsage
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() or console.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_COLOR env var is set (to any value), disable all color and styling
  • If FORCE_COLOR env 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-color flag is a global option on the gl root group