building-glamorous-tuis
SKILL.md
Building Glamorous TUIs with Charmbracelet
Quick Router
What are you building?
| Context | Solution | Reference |
|---|---|---|
| Shell/Bash script | Gum, VHS, Mods, Freeze | → Shell Scripts |
| Go CLI/TUI | Bubble Tea + Lip Gloss | → Go TUI |
| SSH-accessible app | Wish + Bubble Tea | → Infrastructure |
| Recording demos | VHS | → Shell Scripts |
| Testing TUIs | teatest | → Infrastructure |
For Shell Scripts
No Go required. Gum gives you all the UI primitives.
brew install gum
Essential Gum Commands
# Input
NAME=$(gum input --placeholder "Your name")
# Selection (single)
COLOR=$(gum choose "red" "green" "blue")
# Selection (multi)
ITEMS=$(gum choose --no-limit "a" "b" "c")
# 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"
# File picker
FILE=$(gum file .)
Quick Recipe: Git Commit
TYPE=$(gum choose "feat" "fix" "docs" "refactor")
MSG=$(gum input --placeholder "commit message")
gum confirm "Commit?" && git commit -m "$TYPE: $MSG"
VHS Quick Start
Record terminal → GIF:
brew install vhs
cat > demo.tape << 'EOF'
Output demo.gif
Set Theme "Catppuccin Mocha"
Type "echo hello"
Enter
Sleep 1s
EOF
vhs demo.tape
Other Shell Tools
| Tool | Install | One-liner |
|---|---|---|
| Mods | brew install mods |
git diff | mods "review this" |
| Glow | brew install glow |
glow README.md |
| Freeze | brew install freeze |
freeze code.go -o screenshot.png |
For Go Applications
Instant Start
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 (simple) | huh |
huh.NewInput().Title("Name").Run() |
| Markdown | glamour |
glamour.Render(md, "dark") |
| Animation | harmonica |
harmonica.NewSpring() |
Quick Patterns
Styled Output (no TUI):
style := lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Bold(true)
fmt.Println(style.Render("Hello!"))
Simple Form (no TUI):
var name string
huh.NewInput().Title("Your name?").Value(&name).Run()
Confirmation:
var ok bool
huh.NewConfirm().Title("Delete?").Value(&ok).Run()
Full Go TUI Reference → Component API → Advanced Patterns →
For Infrastructure
Wish: SSH Apps
Serve TUI over SSH:
s, _ := wish.NewServer(
wish.WithAddress(":2222"),
wish.WithHostKeyPath(".ssh/key"),
wish.WithMiddleware(
bubbletea.Middleware(handler),
logging.Middleware(),
),
)
s.ListenAndServe()
Connect: ssh localhost -p 2222
Other Infrastructure
| Tool | Purpose | Install |
|---|---|---|
| Soft Serve | Self-hosted Git | brew install soft-serve |
| Pop | Send email | brew install pop |
| Skate | Key-value store | brew install skate |
| Melt | SSH key backup | brew install melt |
| Wishlist | SSH gateway | go install github.com/charmbracelet/wishlist |
Testing TUIs
tm := teatest.NewTestModel(t, model)
tm.Send(tea.KeyMsg{Type: tea.KeyEnter})
tm.Type("hello")
teatest.WaitFor(t, tm, func(b []byte) bool {
return strings.Contains(string(b), "expected")
})
Full Infrastructure Reference →
Decision Guide
Is it a shell script?
├─ Yes → Use Gum
│ Need recording? → VHS
│ Need AI? → Mods
│
└─ No (Go application)
│
├─ Just need styled output?
│ └─ Lip Gloss only (no Bubble Tea)
│
├─ Need simple prompts/forms?
│ └─ Huh standalone
│
├─ Need full interactive TUI?
│ └─ Bubble Tea + Bubbles + Lip Gloss
│
└─ Need SSH access?
└─ Wish + Bubble Tea
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
| Reference | Contents |
|---|---|
| Shell Scripts | Gum, VHS, Mods, Freeze, Glow - complete |
| Go TUI | Bubble Tea patterns, debugging, anti-patterns |
| Infrastructure | Wish, Soft Serve, teatest, x/term |
| Component Catalog | All Bubbles components API |
| Advanced Patterns | Theming, layouts, production architecture |
Weekly Installs
9
Repository
dicklesworthstone/meta_skillFirst Seen
Jan 28, 2026
Security Audits
Installed on
codex9
gemini-cli8
opencode7
amp7
github-copilot7
kimi-cli7