Stage 1 · Code
Building a Terminal Dashboard with Bubble Tea
Styling With Lip Gloss
Terminals don't have CSS, but Lip Gloss — Charm's styling library — gives you the same mental model: define a style once, apply it anywhere, and let it handle color, padding, and borders for you.
Why a Styling Library at All
You could color terminal text by hand, writing raw ANSI escape codes like \x1b[38;5;196m directly into your fmt.Sprintf calls. A few programs do exactly that. It works, right up until you need to combine three colors, a border, and some padding on the same line — at which point the escape codes multiply, nest badly, and become nearly impossible to read or maintain.
Lip Gloss solves this the same way a CSS framework solves it for the web: you describe a style declaratively — bold, this foreground color, this background, this padding — once, as a reusable value, and then call .Render(text) on it wherever you need that look. The escape-code plumbing happens once, inside the library, and you never touch it again.
Building a Palette
The first thing burnrate's internal/tui package does — before defining a single style — is name its colors. This matters for the same reason naming a constant matters over sprinkling a literal number everywhere: when you decide the "critical" color should be a slightly different shade of red, you change it in one place.
burnrate/internal/tui/styles.gocreateResponsibility: Defines every color and Lip Gloss style the dashboard uses, in one place.
Why now: Keeping all visual styling in one file means the View function (built in the next lesson) reads as layout logic, not a wall of inline styling calls.
Composable, Reusable Styles
Notice frameStyle: a rounded border with padding, applied once around the entire dashboard in the next lesson's View function. That single style is what makes burnrate look like a bordered panel instead of loose lines of text scrolling in the terminal — and it costs one style definition, reused everywhere the whole screen needs a frame.
Calling .Bold(true) on a style does not mutate it — it returns a new style with that property set. That means you can safely define titleStyle once at package level and call titleStyle.Render(text) from anywhere without worrying about one call site's rendering interfering with another's.
Conditional Styling for Status
A dashboard needs more than static styles — it needs to say "this window is fine," "this one is worth watching," or "this one is critical," and change color accordingly. Lip Gloss styles are just Go values, so building one conditionally is ordinary Go: a function that inspects state and returns the right style.
With the palette and styles in place, you have every visual building block burnrate needs. The next lesson scopes the whole project properly — the same disciplined, write-the-spec-before-you-code approach the capstone module used — before wiring these styles into a real, running Bubble Tea model.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.