charmbracelet/lipgloss: Declarative Terminal Styling for Go TUIs
Style definitions for nice terminal layouts đź‘„
At a glance
- What is it?
- Lip Gloss brings CSS-like style definitions to Go terminal interfaces. It is a rendering library for people building TUIs, and its value sits in layout and color handling, not in widgets.
- Who is it for?
- Adopt Lip Gloss if you are writing a Go TUI and want layout, borders and color handled declaratively instead of by hand. Do not adopt it if you need ready-made widgets or a non-Go stack; it renders strings and nothing more.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 17 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Lip Gloss solves for Go TUI authors
Rendering styled text in a terminal by hand means writing ANSI escape sequences, counting display cells for wide characters, and redoing all of it when the user's terminal supports fewer colors than yours. Lip Gloss targets that layer. The README describes it as "Style definitions for nice terminal layouts. Built with TUIs in mind." Its audience is Go developers building command-line interfaces, dashboards or interactive tools who want to describe appearance rather than emit escape codes.
The library is declarative in the CSS sense. You construct a style value, chain method calls onto it, and call Render on a string. The README's opening example sets Bold, a foreground and background color, top padding of 2, left padding of 4, and a width of 22, then prints the result. Nothing in that chain touches escape sequences directly. That is the whole proposition: the style object owns the formatting decisions, and the library decides what the terminal actually receives.
How the style chain and color downsampling work
A style is built from lipgloss.NewStyle() and configured through chained setters. The repository splits the work across files whose names map to the concerns: style.go for the style type, color.go for color handling, borders.go for border definitions, align.go for alignment, join.go for placing blocks side by side, position.go, size.go and wrap.go for measurement and text flow, and layer.go and canvas.go for compositing. The examples directory mirrors the same concerns with runnable programs for layout, canvas, table, list, tree, blending, brightness and ssh.
The mechanism worth understanding is color adaptation. The README states that Lip Gloss was designed to handle terminals without truecolor by automatically downsampling colors to the best available profile. It documents profiles from ANSI 16 colors through ANSI 256 and 24-bit truecolor down to a 1-bit ASCII profile that is black and white only. The practical consequence: when you use Lip Gloss with Bubble Tea, the README says there is nothing to do. Standalone, you get the same behavior by printing through lipgloss.Println or lipgloss.Sprint and their variants rather than writing to stdout yourself. If you bypass those helpers, you own the profile decision.
Colors are declared as strings. "5" is magenta in the 4-bit profile, "86" is aqua in 8-bit, and "#04B575" is a truecolor value. Named constants exist for the 16 standard ANSI colors, from lipgloss.Black through lipgloss.BrightWhite. On top of that sit utility functions: Darken, Lighten, Complementary and Alpha all take a color and return a modified one. The go.mod lists github.com/lucasb-eyer/go-colorful and github.com/charmbracelet/colorprofile as dependencies, which is consistent with the color math and profile detection being delegated rather than hand-rolled.
Installing Lip Gloss and rendering a first styled block
Lip Gloss is a Go module. The README gives one install command, run from your project directory, and it pulls the v2 module path. Note that the import path in the README is charm.land/lipgloss/v2, which matches the module line in go.mod rather than the older github.com path.
go get charm.land/lipgloss/v2After that, a minimal program imports the package, defines a style, and prints. The README's first example is the shortest complete illustration of the API shape:
import "charm.land/lipgloss/v2"
var style = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FAFAFA")).
Background(lipgloss.Color("#7D56F4")).
PaddingTop(2).
PaddingLeft(4).
Width(22)
lipgloss.Println(style.Render("Hello, kitty"))What you should see is the text with a padded block around it, a purple background and a near-white foreground, in whatever color depth your terminal supports. If your terminal is limited, the hex values are reduced rather than dropped. Printing through lipgloss.Println is what triggers that reduction, so keep the print call rather than reaching for fmt.Println.
Borders follow the same pattern. You set BorderStyle with one of the built-in border functions such as NormalBorder() or RoundedBorder(), then optionally BorderForeground, BorderBackground, and per-edge toggles like BorderTop(true) and BorderLeft(true). The README also shows a raw lipgloss.Border struct with fields Top, Bottom, Left, Right and the four corners, which is how you build a custom border when the built-ins do not fit.
Where Lip Gloss stops: layout, not widgets
Lip Gloss renders strings. It has no event loop, no input handling, no component model and no focus management. If you need a scrollable list or an interactive table, the repository ships list/, table/ and tree/ directories, but the README does not document their APIs, and the library's own framing is styling and layout. Treating Lip Gloss as a UI toolkit leads to building the toolkit yourself on top of it.
The second limitation is measurement. Width and height are set in cells, and the library has to decide how wide a string actually is. The go.mod depends on github.com/clipperhouse/displaywidth, github.com/rivo/uniseg and github.com/mattn/go-runewidth, which indicates the width calculation accounts for grapheme clusters and East Asian character widths. That is the right problem to solve, but it also means output depends on the terminal agreeing with the library about character width. Terminals that disagree will misalign borders, and no styling library can fix that from its side.
The third is versioning friction. The module is on v2 with a retracted v2.0.0-beta1, and the README points readers upgrading from v1 to UPGRADE_GUIDE_V2.md. The import path changed from the github.com form to charm.land/lipgloss/v2. Anyone with existing v1 code has a migration, not a drop-in upgrade.
Lip Gloss compared with writing ANSI codes directly
The realistic alternative is not another styling library so much as formatting strings yourself with ANSI escapes, or using a lower-level package such as charmbracelet/x/ansi, which Lip Gloss already depends on. The difference is where the decisions live. With raw escape codes, you decide the color depth, you compute padding and alignment, and you handle the case where a terminal supports 16 colors instead of 16 million. With Lip Gloss, you declare a hex color and a width and let the library downsample and pad.
The trade-off is control and dependency weight. Raw escapes add no dependency and produce exactly the bytes you write, which matters in log files or when piping output to a tool that parses escape sequences. Lip Gloss's automatic downsampling is a feature in a terminal and a complication in a log. The README acknowledges this directly: output might not support color at all, "for example, in logs," which is why the library reduces colors instead of emitting them blindly. If your output is consumed by a machine rather than a person, that reduction is a reason to keep the styling layer out of that code path entirely.
Maintenance, releases and the MIT licence
The repository is not archived, and its last push was on 2026-09-13. Recent releases are v2.0.6 on 2026-08-11, v2.0.5 on 2026-07-03 and v2.0.4 on 2026-06-12, so the project is on a steady patch cadence. The go.mod declares go 1.26.7, which sets a floor on the toolchain your project needs.
Upgrade cost is mostly the v1 to v2 boundary. The presence of a dedicated UPGRADE_GUIDE_V2.md and a retracted beta release suggests the maintainers treat that transition as a documented event rather than a silent break. For teams already on v2, the patch releases in the 2.0.x line are the routine path.
The licence is MIT, which is permissive and imposes no copyleft obligation on your own code. That is a statement about the licence text, not legal advice; if your organization has policies about dependency licences, the LICENSE file at the repository root is the document to check.
Editorial conclusion
Adopt Lip Gloss if you are writing a Go TUI and want layout, borders and color handled declaratively instead of by hand. Do not adopt it if you need ready-made widgets or a non-Go stack; it renders strings and nothing more. Before committing, open UPGRADE_GUIDE_V2.md and confirm your color assumptions survive downsampling on a 256-color terminal.
Frequently asked questions
How do I install charmbracelet/lipgloss in a Go project?
Run go get charm.land/lipgloss/v2 from your project directory. The README gives that single command, and the import path in code is the same charm.land/lipgloss/v2.
How do I use charmbracelet/lipgloss to style text?
Build a style with lipgloss.NewStyle() and chain setters such as Bold, Foreground, Background, PaddingTop and Width, then call Render on your string. Print the result with lipgloss.Println or lipgloss.Sprint so color downsampling applies.
What is charmbracelet/lipgloss?
It is a Go library for style definitions in terminal layouts, described in the README as built with TUIs in mind. It takes a declarative approach to terminal rendering, similar in spirit to CSS.
Does charmbracelet/lipgloss handle terminals without truecolor support?
Yes. The README states that Lip Gloss automatically downsamples colors to the best available profile, covering ANSI 16, ANSI 256, 24-bit truecolor and a 1-bit ASCII profile. With Bubble Tea no extra work is needed; standalone, print through lipgloss.Println or lipgloss.Sprint.
How do I upgrade a project from lipgloss v1 to v2?
The README points to UPGRADE_GUIDE_V2.md in the repository. The import path also changed to charm.land/lipgloss/v2, and the v2.0.0-beta1 release was retracted.
What licence does charmbracelet/lipgloss use?
The repository is licensed under MIT, with the LICENSE file at the root. That is permissive and does not impose copyleft terms on your own code.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/charmbracelet-lipgloss)