building-glamorous-tuis
Installation
SKILL.md
Building Glamorous TUIs with Charmbracelet
Quick Router — Start Here
| I need to... | Use | Reference |
|---|---|---|
| Add prompts/spinners to a shell script | Gum (no Go) | Shell Scripts |
| Build a Go TUI | Bubble Tea + Lip Gloss | Go TUI |
| Build a production-grade Go TUI | Above + elite patterns | Production Architecture |
| Serve a TUI over SSH | Wish + Bubble Tea | Infrastructure |
| Record a terminal demo | VHS | Shell Scripts |
| Find a Bubbles component | list, table, viewport, spinner, progress... | Component Catalog |
| Get a copy-paste pattern | Layouts, forms, animation, testing | Quick Reference / Advanced Patterns |
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
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
| Want | Pattern | Code |
|---|---|---|
| Bar charts in list columns | Unicode Sparklines | ▇▅▂ using 8-level block characters |
| Color-by-intensity | Perceptual Heatmaps | gray → blue → purple → pink gradient |
| Dependency graph in terminal | ASCII Graph Renderer | Canvas + Manhattan routing (╭─╮│╰╯) |
| Age at a glance | Age Color Coding | Fresh=green, aging=yellow, stale=red |
| Borders that mean something | Semantic Borders | Red=blocked, green=ready, yellow=high-impact |
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)
- Handle
tea.WindowSizeMsg— resize all components - Handle
ctrl+c— cleanup, restore terminal state - Detect piped stdin/stdout — fall back to plain text
- Test on 80×24 minimum terminal
- Provide
--no-tui/NO_TUIescape hatch - Test with both light AND dark backgrounds
- Test with
NO_COLOR=1andTERM=dumb
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.Scanfis 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 |
Weekly Installs
38
Repository
dicklesworthsto…ta_skillGitHub Stars
147
First Seen
Jan 28, 2026
Security Audits
Installed on
gemini-cli36
codex36
kimi-cli35
amp35
github-copilot35
opencode35