Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build terminal user interfaces with Go and Bubbletea framework. Use for creating TUI apps with the Elm architecture, dual-pane layouts, accordion modes, mouse/keyboard handling, Lipgloss styling, and reusable components. Includes production-ready templates, effects library, and battle-tested layout patterns from real projects.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 95% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 35% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 56% | 0% |
Production-ready skill for building beautiful terminal user interfaces with Go, Bubbletea, and Lipgloss.
Use this skill when:
CRITICAL: Before implementing ANY layout, consult references/golden-rules.md for the 4 Golden Rules. These rules prevent the most common and frustrating TUI layout bugs.
Full details and examples in references/golden-rules.md.
This project includes a production-ready template system. When this skill is bundled with a new project (via new_project.sh), use the existing template structure as the starting point.
All new projects follow this architecture:
your-app/
├── main.go # Entry point (minimal, ~21 lines)
├── types.go # Type definitions, structs, enums
├── model.go # Model initialization & layout calculation
├── update.go # Message dispatcher
├── update_keyboard.go # Keyboard handling
├── update_mouse.go # Mouse handling
├── view.go # View rendering & layouts
├── styles.go # Lipgloss style definitions
├── config.go # Configuration management
└── .claude/skills/bubbletea/ # This skill (bundled)main.go minimal (entry point only, ~21 lines)types.go (structs, enums, constants)See references/components.md for the complete catalog of reusable components:
Beautiful physics-based animations available in the template:
See references/effects.md for usage examples and integration patterns.
When implementing layouts, follow this sequence:
gofunc (m model) calculateLayout() (int, int) { contentWidth := m.width contentHeight := m.height // Subtract UI elements if m.config.UI.ShowTitle { contentHeight -= 3 // title bar (3 lines) } if m.config.UI.ShowStatus { contentHeight -= 1 // status bar } // CRITICAL: Account for panel borders contentHeight -= 2 // top + bottom borders return contentWidth, contentHeight }
go// Calculate weights based on focus/accordion mode leftWeight, rightWeight := 1, 1 if m.accordionMode && m.focusedPanel == "left" { leftWeight = 2 // Focused panel gets 2x weight } // Calculate actual widths from weights totalWeight := leftWeight + rightWeight leftWidth := (availableWidth * leftWeight) / totalWeight rightWidth := availableWidth - leftWidth
go// Calculate max text width to prevent wrapping maxTextWidth := panelWidth - 4 // -2 borders, -2 padding // Truncate ALL text before rendering title = truncateString(title, maxTextWidth) subtitle = truncateString(subtitle, maxTextWidth) func truncateString(s string, maxLen int) string { if len(s) <= maxLen { return s } return s[:maxLen-1] + "…" }
Always check layout mode before processing mouse events:
gofunc (m model) handleLeftClick(msg tea.MouseMsg) (tea.Model, tea.Cmd) { if m.shouldUseVerticalStack() { // Vertical stack mode: use Y coordinates topHeight, _ := m.calculateVerticalStackLayout() relY := msg.Y - contentStartY if relY < topHeight { m.focusedPanel = "left" // Top panel } else { m.focusedPanel = "right" // Bottom panel } } else { // Side-by-side mode: use X coordinates leftWidth, _ := m.calculateDualPaneLayout() if msg.X < leftWidth { m.focusedPanel = "left" } else { m.focusedPanel = "right" } } return m, nil }
See references/troubleshooting.md for detailed solutions to common issues:
go// BAD: Can cause misalignment panelStyle := lipgloss.NewStyle(). Border(border). Height(height) // Don't do this!
go// GOOD: Fill content lines to exact height for len(lines) < innerHeight { lines = append(lines, "") } panelStyle := lipgloss.NewStyle().Border(border)
When panels don't align or render incorrectly:
See references/troubleshooting.md for the complete debugging decision tree.
All projects support YAML configuration with hot-reload:
yamltheme: "dark" keybindings: "default" layout: type: "dual_pane" split_ratio: 0.5 accordion_mode: true ui: show_title: true show_status: true mouse_enabled: true show_icons: true
Configuration files are loaded from:
~/.config/your-app/config.yaml (user config)./config.yaml (local override)Required:
github.com/charmbracelet/bubbletea
github.com/charmbracelet/lipgloss
github.com/charmbracelet/bubbles
gopkg.in/yaml.v3Optional (uncomment in go.mod as needed):
github.com/charmbracelet/glamour # Markdown rendering
github.com/charmbracelet/huh # Forms
github.com/alecthomas/chroma/v2 # Syntax highlighting
github.com/evertras/bubble-table # Interactive tables
github.com/koki-develop/go-fzf # Fuzzy finderAll reference files are loaded progressively as needed:
Follow these patterns and you'll avoid 90% of TUI layout bugs.
Other measured skills in the registry, with their headline benchmark lift.