name: lipgloss-v2 description: >- Generates, explains, and debugs terminal styling and layout code using Charm's Lip Gloss v2 library for Go. Use when building styled terminal output, TUI layouts, color gradients, bordered boxes, tables, lists, or trees. Covers Style API, adaptive colors, LightDark detection, compositing layers, and all subpackages (list, table, tree). Also handles migration from Lip Gloss v1 to v2. allowed-tools: Read, Write, Edit, Bash(go *) metadata: category: go-cli version: "2.0"
Lip Gloss v2
Lip Gloss v2 (charm.land/lipgloss/v2) provides style definitions for
terminal layouts. It is a pure value type library — no global renderer, no
hidden state. Colors are downsampled at the output layer.
go get charm.land/lipgloss/v2@v2.0.0-beta.3.0.20251205162909-7869489d8971
Quick Reference
| v1 | v2 |
|---|---|
github.com/charmbracelet/lipgloss |
charm.land/lipgloss/v2 |
type Color string |
func Color(string) color.Color |
lipgloss.AdaptiveColor{...} |
compat.AdaptiveColor{...} or LightDark(isDark)(light, dark) |
HasDarkBackground() |
HasDarkBackground(os.Stdin, os.Stdout) |
fmt.Println(s.Render("hi")) |
lipgloss.Println(s.Render("hi")) |
renderer.NewStyle() |
lipgloss.NewStyle() |
See references/MIGRATION.md for the full migration guide.
Style API
Styles are immutable value types. Chain setters, end with .Render(str).
s := lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FF5F87")).
Background(lipgloss.Color("#1a1a2e")).
Padding(1, 2).
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("#874BFD")).
Width(40).
Align(lipgloss.Center)
lipgloss.Println(s.Render("Hello!"))
Text properties
| Method | Description |
|---|---|
Bold(bool) |
Bold text |
Italic(bool) |
Italic text |
Underline(bool) |
Underline (single) |
UnderlineStyle(Underline) |
UnderlineSingle/Double/Curly/Dotted/Dashed/None |
UnderlineColor(color.Color) |
Color the underline separately |
Strikethrough(bool) |
Strikethrough |
StrikethroughSpaces(bool) |
Apply strikethrough to spaces between words |
Faint(bool) |
Faint/dim text |
Blink(bool) |
Blinking text |
Reverse(bool) |
Reverse foreground/background |
Foreground(color.Color) |
Text color |
Background(color.Color) |
Background color |
Hyperlink(url, params...) |
Clickable hyperlink |
Layout properties
| Method | Description |
|---|---|
Width(int) |
Set width (pads to fill) |
Height(int) |
Set height (pads to fill) |
MaxWidth(int) |
Truncate at max width |
MaxHeight(int) |
Truncate at max height |
Align(Position) |
Horizontal text alignment |
AlignVertical(Position) |
Vertical text alignment |
Inline(bool) |
Render inline (no newlines) |
TabWidth(int) |
Tab-to-space width (NoTabConversion to disable) |
Transform(func(string) string) |
Post-render transform |
Position constants: lipgloss.Left, lipgloss.Center, lipgloss.Right,
lipgloss.Top, lipgloss.Bottom.
Padding & margin
s.Padding(1, 2) // top/bottom=1, left/right=2
s.Padding(1, 2, 3, 4) // top, right, bottom, left
s.PaddingTop(1).PaddingLeft(2)
s.PaddingChar('·') // fill padding with this character
s.Margin(1, 2)
s.MarginChar('·')
s.MarginBackground(color.Color)
Borders
s.Border(lipgloss.RoundedBorder())
s.Border(lipgloss.NormalBorder(), true) // all sides
s.Border(lipgloss.ThickBorder(), true, false) // top+bottom only
s.BorderForeground(lipgloss.Color("#874BFD"))
s.BorderForegroundBlend(from, to) // gradient border
s.BorderStyle(lipgloss.NormalBorder()).BorderBottom(true)
Border constructors: NormalBorder(), RoundedBorder(), ThickBorder(),
DoubleBorder(), BlockBorder(), HiddenBorder(), ASCIIBorder(),
MarkdownBorder(), InnerHalfBlockBorder(), OuterHalfBlockBorder().
Custom border:
b := lipgloss.Border{
Top: "─", Bottom: "─", Left: "│", Right: "│",
TopLeft: "╭", TopRight: "╮",
BottomLeft: "╰", BottomRight: "╯",
}
Style inheritance
base := lipgloss.NewStyle().Padding(0, 1).Foreground(lipgloss.Color("5"))
// Copy and override
active := base.Bold(true).Background(lipgloss.Color("#FF5F87"))
// Inherit (fills only unset props)
child := lipgloss.NewStyle().Inherit(base)
Render helpers
s.Render("text") // returns styled string
s.String() // alias for Render with SetString content
s.SetString("content") // bind content to style
Unset methods
All style properties can be unset:
s.UnsetBold()
s.UnsetForeground()
s.UnsetPadding()
s.UnsetBorder()
// etc.
Color System
color.Color is the standard image/color.Color interface.
import "charm.land/lipgloss/v2"
// Hex or ANSI256
c := lipgloss.Color("#FF5F87")
c := lipgloss.Color("200")
// Named 4-bit ANSI constants
c := lipgloss.Red // lipgloss.Black/Red/Green/.../BrightWhite
// ANSI256
c := lipgloss.ANSIColor(134)
// NoColor - absence of color styling
c := lipgloss.NoColor{}
Adaptive colors (light/dark terminals)
Standalone:
hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
lightDark := lipgloss.LightDark(hasDark)
fg := lightDark(lipgloss.Color("#333333"), lipgloss.Color("#f1f1f1"))
s := lipgloss.NewStyle().Foreground(fg)
Bubble Tea:
func (m model) Init() tea.Cmd {
return tea.RequestBackgroundColor
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.BackgroundColorMsg:
m.styles = newStyles(msg.IsDark())
}
return m, nil
}
func newStyles(isDark bool) styles {
ld := lipgloss.LightDark(isDark)
return styles{
title: lipgloss.NewStyle().Foreground(ld(
lipgloss.Color("#333333"),
lipgloss.Color("#f1f1f1"),
)),
}
}
Color profile selection (Complete)
import "github.com/charmbracelet/colorprofile"
p := colorprofile.Detect(os.Stdout, os.Environ())
complete := lipgloss.Complete(p)
c := complete(
lipgloss.Color("5"), // ANSI fallback
lipgloss.Color("200"), // ANSI256 fallback
lipgloss.Color("#ff34ac"), // TrueColor
)
Color manipulation
lipgloss.Darken(c, 0.2) // 20% darker
lipgloss.Lighten(c, 0.2) // 20% lighter
lipgloss.Alpha(c, 0.5) // 50% alpha
lipgloss.Complementary(c) // 180° on color wheel
Color gradients
// 1D linear blend across N steps
colors := lipgloss.Blend1D(40, from, to, extra...)
// 2D gradient (angle 0=left→right, 180=right→left)
colors := lipgloss.Blend2D(width, height, 45.0, c1, c2, c3)
// Access: colors[y*width + x]
Layout Functions
// Join side-by-side, aligned at Top/Center/Bottom
row := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB, blockC)
// Stack vertically, aligned at Left/Center/Right
col := lipgloss.JoinVertical(lipgloss.Left, blockA, blockB)
// Place content in a box (fills with whitespace)
out := lipgloss.Place(width, height, lipgloss.Center, lipgloss.Center, content,
lipgloss.WithWhitespaceChars("·"),
lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().Foreground(subtle)),
)
// One-axis variants
lipgloss.PlaceHorizontal(width, lipgloss.Center, content)
lipgloss.PlaceVertical(height, lipgloss.Center, content)
Printing (Important!)
Always use Lip Gloss writers to ensure correct color downsampling:
lipgloss.Println(s) // stdout + newline
lipgloss.Print(s) // stdout
lipgloss.Printf("%s\n", s) // stdout, formatted
lipgloss.Fprintln(os.Stderr, s)
lipgloss.Sprint(s) // returns string for stdout profile
lipgloss.Sprintf("...", s)
// Customize default writer
lipgloss.Writer = colorprofile.NewWriter(os.Stderr, os.Environ())
In Bubble Tea, downsampling is automatic — just return strings normally.
Text Utilities
w := lipgloss.Width(str) // cell width (Unicode-aware)
h := lipgloss.Height(str) // line count
w, h := lipgloss.Size(str) // both
wrapped := lipgloss.Wrap(str, 80, "") // wrap preserving ANSI
// Style specific rune indices
out := lipgloss.StyleRunes(str, []int{0, 1, 2}, matched, unmatched)
// Style character ranges
out := lipgloss.StyleRanges(str,
lipgloss.NewRange(0, 5, boldStyle),
lipgloss.NewRange(6, 10, italicStyle),
)
Compositing (Layers)
Compose styled strings at arbitrary positions:
base := lipgloss.NewLayer(document)
modal := lipgloss.NewLayer(floatingBox).X(10).Y(5)
overlay := lipgloss.NewLayer(badge).X(80).Y(2).Z(10)
comp := lipgloss.NewCompositor(base, modal, overlay)
lipgloss.Println(comp.Render())
// Hit testing
hit := comp.Hit(x, y) // returns LayerHit with ID
if !hit.Empty() {
fmt.Println("Clicked layer:", hit.ID())
}
Canvas
Low-level cell buffer for manual composition:
canvas := lipgloss.NewCanvas(width, height)
canvas.Compose(layer)
result := canvas.Render()
Subpackages
list (charm.land/lipgloss/v2/list)
import "charm.land/lipgloss/v2/list"
l := list.New("Item A", "Item B", "Item C").
Enumerator(list.Roman).
EnumeratorStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)).
ItemStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("255")))
// Nested lists
l.Item(
list.New("Sub A", "Sub B"),
)
// Conditional styling
l.ItemStyleFunc(func(items list.Items, i int) lipgloss.Style {
if i == selected {
return selectedStyle
}
return normalStyle
})
lipgloss.Println(l)
Built-in enumerators: list.Bullet, list.Dash, list.Roman, list.Arabic,
list.Alphabet, list.Asterisk.
Custom: func(items list.Items, i int) string.
table (charm.land/lipgloss/v2/table)
import "charm.land/lipgloss/v2/table"
t := table.New().
Border(lipgloss.ThickBorder()).
BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99"))).
StyleFunc(func(row, col int) lipgloss.Style {
if row == table.HeaderRow {
return lipgloss.NewStyle().Bold(true).Align(lipgloss.Center)
}
if row%2 == 0 {
return lipgloss.NewStyle().Foreground(lipgloss.Color("245"))
}
return lipgloss.NewStyle().Foreground(lipgloss.Color("241"))
}).
Headers("NAME", "LANG", "STARS").
Rows([][]string{
{"BubbleTea", "Go", "★★★★★"},
{"Lipgloss", "Go", "★★★★★"},
}...)
lipgloss.Println(t)
tree (charm.land/lipgloss/v2/tree)
import "charm.land/lipgloss/v2/tree"
t := tree.New().
Root("project/").
Child(
".git",
tree.Root("src/").
Child("main.go", "util.go"),
tree.Root("docs/").
Child("README.md"),
"go.mod",
).
Enumerator(tree.RoundedEnumerator).
EnumeratorStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99"))).
ItemStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("255")))
lipgloss.Println(t)
Integration with huh v2 Forms
Lip Gloss v2 styles power huh v2 forms. Create custom themes by implementing huh.Theme:
import (
"charm.land/huh/v2"
"charm.land/lipgloss/v2"
)
// Create a custom theme
customTheme := huh.ThemeFunc(func(isDark bool) *huh.Styles {
s := &huh.Styles{}
// Form container
s.Form.Base = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("#7D56F4")).
Padding(2, 4)
// Focused (active) field styles
s.Focused.Title = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#7D56F4"))
s.Focused.SelectSelector = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FF6AD2")).
SetString("▸ ")
s.Focused.FocusedButton = lipgloss.NewStyle().
Background(lipgloss.Color("#FF6AD2")).
Foreground(lipgloss.Color("#FFFFFF")).
Padding(0, 3)
// Blurred (inactive) field styles
s.Blurred = s.Focused
s.Blurred.Title = s.Blurred.Title.Foreground(lipgloss.Color("#666666"))
return s
})
// Apply theme to form
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().Title("Name").Value(&name),
huh.NewConfirm().Title("Proceed?").Value(&ok),
),
).WithTheme(customTheme)
huh Style Structure
| Style | Purpose |
|---|---|
s.Form.Base |
Container border/padding |
s.Focused.Title |
Active field title |
s.Focused.Description |
Active field help text |
s.Focused.SelectSelector |
Selection indicator (▸) |
s.Focused.Option |
Select/menu options |
s.Focused.FocusedButton |
Active button (Confirm) |
s.Focused.TextInput.Prompt |
Input prompt character |
s.Blurred.* |
Inactive field styles |
s.Help.* |
Keybinding help text |
See references/_examples/huh-theme.go for a complete custom theme example.
Examples Index
See references/_examples/ for runnable examples:
| File | Demonstrates |
|---|---|
color-standalone.go |
HasDarkBackground + LightDark in standalone mode |
color-bubbletea.go |
tea.BackgroundColorMsg + adaptive styles in BubbleTea |
layout.go |
Full layout: tabs, title, dialog, lists, status bar, compositing |
table-languages.go |
Styled table with headers, alternating rows, custom column widths |
list-grocery.go |
Custom enumerator + per-item style functions |
blending-1d.go |
Blend1D color gradients rendered as color bars |
compositing.go |
Layer compositing with hit testing |
huh-theme.go |
Custom huh form theme using Lip Gloss styles |
References
references/API.md— complete API cheat sheetreferences/MIGRATION.md— v1 → v2 migration guide (also suitable for LLM-driven migration)