name: tui-glamorous
description: >-
Build terminal UIs with Charmbracelet (Bubble Tea, Lip Gloss, Gum). Use when:
Go TUI, shell prompts/spinners, "make CLI prettier", adaptive layouts, async
rendering, focus state machines, sparklines, heatmaps, kanban boards, SSH apps.
Building Glamorous TUIs with Charmbracelet
Quick Router — Start Here
Decision Guide
Is it a shell script?
├─ Yes → Use Gum
│ Need recording? → VHS
│ Need AI? → Mods
│
└─ No (Go application)
│
├─ Just styled output? → Lip Gloss only
├─ Simple prompts/forms? → Huh standalone
├─ Full interactive TUI? → Bubble Tea + Bubbles + Lip Gloss
│ │
│ └─ Production-grade? → Also add elite patterns:
│ (multi-view, data- two-phase async, immutable snapshots,
│ dense, must be adaptive layout, focus state machine,
│ fast & polished) semantic theming, pre-computed styles
│ → See Production Architecture reference
│
└─ Need SSH access? → Wish + Bubble Tea
Shell Scripts (No Go Required)
brew install gum # One-time install
# Input
NAME=$(gum input --placeholder "Your name")
# Selection
COLOR=$(gum choose "red" "green" "blue")
# Fuzzy filter from stdin
BRANCH=$(git branch | gum filter)
# Confirmation
gum confirm "Continue?" && echo "yes"
# Spinner
gum spin --title "Working..." -- long-command
# Styled output
gum style --border rounded --padding "1 2" "Hello"
Full Gum Reference →
VHS Recording →
Mods AI →
Go Applications
go get github.com/charmbracelet/bubbletea github.com/charmbracelet/lipgloss
Minimal TUI (Copy & Run)
package main
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
var highlight = lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Bold(true)
type model struct {
items []string
cursor int
}
func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "q", "ctrl+c":
return m, tea.Quit
case "up", "k":
if m.cursor > 0 { m.cursor-- }
case "down", "j":
if m.cursor < len(m.items)-1 { m.cursor++ }
case "enter":
fmt.Printf("Selected: %s\n", m.items[m.cursor])
return m, tea.Quit
}
}
return m, nil
}
func (m model) View() string {
s := ""
for i, item := range m.items {
if i == m.cursor {
s += highlight.Render("▸ "+item) + "\n"
} else {
s += " " + item + "\n"
}
}
return s + "\n(↑/↓ move, enter select, q quit)"
}
func main() {
m := model{items: []string{"Option A", "Option B", "Option C"}}
tea.NewProgram(m).Run()
}
Library Cheat Sheet
| Need |
Library |
Example |
| TUI framework |
bubbletea |
tea.NewProgram(model).Run() |
| Components |
bubbles |
list.New(), textinput.New() |
| Styling |
lipgloss |
style.Foreground(lipgloss.Color("212")) |
| Forms |
huh |
huh.NewInput().Title("Name").Run() |
| Markdown |
glamour |
glamour.Render(md, "dark") |
| Animation |
harmonica |
harmonica.NewSpring() |
Full Go TUI Guide →
All Bubbles Components →
Layout & Animation Patterns →
SSH Apps (Infrastructure)
s, _ := wish.NewServer(
wish.WithAddress(":2222"),
wish.WithHostKeyPath(".ssh/key"),
wish.WithMiddleware(
bubbletea.Middleware(handler),
logging.Middleware(),
),
)
s.ListenAndServe()
Connect: ssh localhost -p 2222
Full Infrastructure Guide →
Production TUI Architecture (Elite Patterns)
Beyond basic Bubble Tea: patterns that make TUIs feel fast, polished, and professional.
Each links to a full code example in Production Architecture.
My TUI is slow or janky
| Symptom |
Pattern |
Fix |
| UI blocks during computation |
Two-Phase Async |
Phase 1 instant, Phase 2 background goroutine |
| Render path holds mutex |
Immutable Snapshots |
Pre-build snapshot, atomic pointer swap |
| File changes cause stutter |
Background Worker |
Debounced watcher + coalescing |
| Thousands of allocs per frame |
Pre-Computed Styles |
Allocate delegate styles once at startup |
| O(n²) string concat in View() |
strings.Builder |
Pre-allocated Builder with Grow() |
| Glamour re-renders every frame |
Cached Markdown |
Cache by content hash, invalidate on width change |
| GC pauses during interaction |
Idle-Time GC |
Trigger GC during idle periods |
| Large dataset = high memory |
Object Pooling |
sync.Pool with pre-allocated slices |
| Rendering off-screen items |
Viewport Virtualization |
Only render visible rows |
My layout breaks on different terminals
| Symptom |
Pattern |
Fix |
| Hardcoded widths break |
Adaptive Layout |
3-4 responsive breakpoints (80/100/140/180 cols) |
| Colors wrong on light terminals |
Semantic Theming |
lipgloss.AdaptiveColor + WCAG AA contrast |
| Items have equal priority → list shuffles |
Deterministic Sorting |
Stable sort with tie-breaking secondary key |
| Sort mode not visible |
Dynamic Status Bar |
Left/right segments with gap-fill |
My TUI has multiple views and it's getting messy
| Symptom |
Pattern |
Fix |
| Key routing chaos |
Focus State Machine |
Explicit focus enum + modal priority layer |
| User gets lost in nested views |
Breadcrumb Navigation |
Home > Board > Priority path indicator |
| Overlay dismiss loses position |
Focus Restoration |
Save focus before overlay, restore on dismiss |
| Old async results overwrite new data |
Stale Message Detection |
Compare data hash before applying results |
| Multiple component updates per frame |
tea.Batch Accumulation |
Collect cmds in slice, return tea.Batch(cmds...) |
| Background goroutine panic kills TUI |
Error Recovery |
defer/recover wrapper for all goroutines |
I want to add data-rich visualizations
I want my TUI to feel polished and professional
| Want |
Pattern |
Key Idea |
Vim-style gg/G |
Vim Key Combos |
Track waitingForG state between keystrokes |
| Search without jank |
Debounced Search |
150ms timer, fire only when typing stops |
| Search across all fields at once |
Composite FilterValue |
Flatten all fields into one string |
| 4-line cards with metadata |
Rich Delegates |
Custom delegate with Height()=4 |
| Expand detail inline |
Inline Expansion |
Toggle with d, auto-collapse on j/k |
| Copy to clipboard |
Clipboard Integration |
y for ID, C for markdown + toast feedback |
? / ` / ; help |
Multi-Tier Help |
Quick ref + tutorial + persistent sidebar |
| Kanban with mode switching |
Kanban Swimlanes |
Pre-computed board states, O(1) switch |
| Collapsible tree with h/l |
Tree Navigation |
Flatten tree to visible list for j/k nav |
| Suspend TUI for vim edit |
Editor Dispatch |
tea.ExecProcess for terminal, background for GUI |
| Remember expand/collapse |
Persistent State |
Save to JSON, graceful degradation on corrupt |
| Tune via env vars |
Env Preferences |
NO_COLOR, theme, debounce, split ratio |
| Optional feature missing? |
Graceful Degradation |
Detect at startup, hide unavailable features |
Full Production Architecture Guide →
Pre-Flight Checklist (Every TUI)
For production TUIs, see the full checklist (16 must-have + 20 polish items).
When NOT to Use Charm
- Output is piped:
mytool | grep → plain text
- CI/CD: No terminal → use flags/env vars
- One simple prompt: Maybe
fmt.Scanf is fine
Escape hatch:
if !term.IsTerminal(os.Stdin.Fd()) || os.Getenv("NO_TUI") != "" {
runPlainMode()
return
}
All References
| I need... |
Read this |
| Copy-paste one-liners |
Quick Reference |
| Prompts to give Claude for TUI tasks |
Prompts |
| Gum / VHS / Mods / Freeze / Glow |
Shell Scripts |
| Bubble Tea architecture, debugging, anti-patterns |
Go TUI |
| Bubbles component APIs (list, table, viewport...) |
Component Catalog |
| Theming, layouts, animation, Huh forms, testing |
Advanced Patterns |
| Elite patterns: async, snapshots, focus machines, adaptive layout, sparklines, kanban, trees, caching |
Production Architecture |
| Wish SSH server, Soft Serve, teatest |
Infrastructure |