Skilllibrary bubbletea-go
install
source · Clone the upstream repo
git clone https://github.com/merceralex397-collab/skilllibrary
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/10-cli-systems-and-ops/bubbletea-go" ~/.claude/skills/merceralex397-collab-skilllibrary-bubbletea-go && rm -rf "$T"
manifest:
10-cli-systems-and-ops/bubbletea-go/SKILL.mdsource content
Purpose
Build interactive terminal UIs in Go using Bubble Tea's Elm-architecture (Model / Update / View) with Lip Gloss styling.
When to use this skill
- building an interactive TUI application in Go with Bubble Tea
- composing multiple Bubble Tea components (lists, text inputs, tables)
- adding Lip Gloss styles, borders, or layout to a TUI
- converting a Cobra CLI to have interactive selection or forms
Do not use this skill when
- building a TUI in Python or Rust — prefer
tui-development - only need CLI flag/arg parsing — prefer
cobra-go - building a non-interactive CLI tool — prefer
cli-development-go
Procedure
- Define the Model struct — store all UI state: cursor position, items, selected values, dimensions, error state.
- Implement
— return initial commands (e.g.,Init()
, data fetch commands).tea.EnterAltScreen - Implement
— handleUpdate(msg)
,tea.KeyMsg
, and customtea.WindowSizeMsg
types. Returntea.Msg
.(model, cmd) - Implement
— render the entire screen as a string using Lip Gloss. Never do I/O in View.View() - Compose sub-models — embed child models (e.g.,
,list.Model
). Delegate messages and render in parent View.textinput.Model - Add Lip Gloss styling — define styles as package-level vars. Use
,lipgloss.NewStyle().Foreground()
,.Border()
..Padding() - Handle window resize — listen for
, store width/height, pass to child models.tea.WindowSizeMsg - Test with
— useteatest
to send key sequences and assert final view output.teatest.NewModel()
Key patterns
type model struct { choices []string cursor int selected map[int]struct{} width, height 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.choices)-1 { m.cursor++ } case "enter", " ": if _, ok := m.selected[m.cursor]; ok { delete(m.selected, m.cursor) } else { m.selected[m.cursor] = struct{}{} } } case tea.WindowSizeMsg: m.width, m.height = msg.Width, msg.Height } return m, nil }
Decision rules
- Never perform I/O in
— it is called on every render cycle.View() - Use
for async work (HTTP, file reads) — return commands fromtea.Cmd
.Update - Store all state in the Model — no global mutable state.
- Use
to combine multiple commands from a single Update.tea.Batch() - Prefer Bubbles library components (
,list
,table
) over hand-rolling.textinput
References
- https://github.com/charmbracelet/bubbletea
- https://github.com/charmbracelet/bubbles
- https://github.com/charmbracelet/lipgloss
Related skills
— CLI argument parsing before TUI launchcobra-go
— cross-language TUI patternstui-development
— non-interactive Go CLIscli-development-go