Lip Gloss v2

repository·main·Indexed 11 days ago

https://github.com/charmbracelet/lipgloss

A declarative styling library for Go designed for building Terminal User Interfaces (TUIs). It provides a CSS-like API for managing colors, borders, padding, margins, and text alignment, featuring support for True Color, ANSI profiles, and automatic downsampling. Includes specialized sub-packages for rendering tables, lists, and trees, as well as a cell-based compositor for layered content.

Tokens
18.9K
Snippets
77
Records
92
Agent score
92%

What's inside Lip Gloss

  1. Compose layered content with Lip Gloss Compositor

    main

    Lip Gloss provides a cell-based compositor for rendering multiple layers of content at specific coordinates. You can create layers using lipgloss.NewLayer(content), position them using .X(int), .Y(int), and .Z(int) (for depth), and then render them using a compositor.Compose(...) call.

    // Create some layers.
    a := lipgloss.NewLayer(pickles).X(4).Y(2).Z(1)
    b := lipgloss.NewLayer(bitterMelon).X(22).Y(1)
    c := lipgloss.NewLayer(sriracha).X(11).Y(7)
    
    // Composite 'em and render.
    output := compositor.Compose(a, b, c).Render()
  2. How the v2 Color System works

    main

    The color system has moved from string-based types to standard library integration:

    • lipgloss.Color(string) color.Color: This is now a function that returns an image/color.Color. It accepts hex strings or ANSI color indices.
    • ANSIColor: Now an alias for ansi.IndexedColor.
    • Named ANSI Constants: v2 exports constants for the 16 basic colors: lipgloss.Black, lipgloss.Red, lipgloss.Green, lipgloss.Yellow, lipgloss.Blue, lipgloss.Magenta, lipgloss.Cyan, lipgloss.White, and their Bright variants.
    • Adaptive Colors: Instead of the AdaptiveColor type, it is recommended to use the lipgloss.LightDark(hasDark bool) helper, which returns a function to pick colors based on background detection.
    // v2 usage
    var c color.Color = lipgloss.Color("#ff00ff")
    
    // Using LightDark for adaptive colors
    hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
    lightDark := lipgloss.LightDark(hasDark)
    color := lightDark(lipgloss.Color("#0000ff"), lipgloss.Color("#000099"))
  3. Automatically downsample colors for compatibility

    main

    If a terminal does not support Truecolor or color at all, Lip Gloss automatically downsamples colors to the best available profile.

    To ensure this automatic downsampling works when using Lip Gloss standalone (outside of Bubble Tea), use lipgloss.Println or lipgloss.Sprint (and their variants) instead of standard library printing functions.

  4. How background detection and adaptive colors work in v2

    main

    Unlike v1, v2 does not detect background color globally. You must explicitly provide the input and output streams to lipgloss.HasDarkBackground(in, out).

    Standalone usage:

    1. Detect background: hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout).
    2. Create a picker: lightDark := lipgloss.LightDark(hasDark).
    3. Apply colors: fg := lightDark(lightColor, darkColor).

    Bubble Tea usage: To react to terminal theme changes, request the background color in your model's Init() method using tea.RequestBackgroundColor. In your Update() loop, listen for tea.BackgroundColorMsg to update your styles.

    // Bubble Tea pattern
    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
    }
  5. Copy and inherit styles

    main

    Styles in Lip Gloss are pure value types. Assigning a style to a new variable creates a true copy.

    Inheritance: Use .Inherit(otherStyle) to inherit rules from another style. Only rules that are currently unset on the receiver will be inherited from the parent.

    Unsetting Rules: You can remove a specific rule using Unset[Rule]() (e.g., UnsetBold(), UnsetBackground()). Unset rules are not inherited or copied.

    // Copying
    style := lipgloss.NewStyle().Foreground(lipgloss.Color("219"))
    copiedStyle := style 
    
    // Inheritance
    var styleA = lipgloss.NewStyle().Foreground(lipgloss.Color("229")).Background(lipgloss.Color("63"))
    var styleB = lipgloss.NewStyle().Foreground(lipgloss.Color("201")).Inherit(styleA)
    // styleB now has Foreground 201 and Background 63
    
    // Unsetting
    var style = lipgloss.NewStyle().Bold(true).UnsetBold()
  6. Use Adaptive Colors for Light and Dark Backgrounds

    main

    You can render different colors at runtime based on whether the user's terminal has a light or dark background.

    Standalone Usage

    Use lipgloss.HasDarkBackground(stdin, stdout) to detect the background type, then use lipgloss.LightDark(hasDarkBG) to create a helper function. This helper function accepts two colors: the first for light backgrounds and the second for dark backgrounds.

    Integration with Bubble Tea

    In a Bubble Tea application, do not query the background manually in Init. Instead:

    1. Return tea.RequestBackgroundColor from your Init() method.
    2. Listen for the tea.BackgroundColorMsg in your Update() loop.
    3. Use msg.IsDark() to determine the background state and update your styles accordingly.
    // Standalone
    hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stderr)
    lightDark := lipgloss.LightDark(hasDarkBG)
    
    // Returns light color if background is light, dark color if background is dark
    myColor := lightDark(lipgloss.Color("#C5ADF9"), lipgloss.Color("#864EFF"))
    
    style := lipgloss.NewStyle().Foreground(myColor)
  7. Upgrade from Lip Gloss v1 to v2

    main

    When migrating from Lip Gloss v1 to v2, several core behaviors and API patterns have changed. Key changes include a new module path, changes to how adaptive colors are handled, and a shift in how terminal background detection and output rendering work.

    Key Migration Summary

    Taskv1v2
    Import Path"github.com/charmbracelet/lipgloss""charm.land/lipgloss/v2"
    Adaptive Colorlipgloss.AdaptiveColor{Light: "#fff", Dark: "#000"}compat.AdaptiveColor{Light: lipgloss.Color("#fff"), Dark: lipgloss.Color("#000")}
    Print with downsamplingfmt.Println(s.Render("hi"))lipgloss.Println(s.Render("hi"))
    Detect dark bglipgloss.HasDarkBackground()lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
    Light/dark colorlipgloss.AdaptiveColor{...}lipgloss.LightDark(isDark)(light, dark)
    Whitespace stylingWithWhitespaceForeground(c)WithWhitespaceStyle(lipgloss.NewStyle().Foreground(c))
    Underlines.Underline(true)s.Underline(true) or s.UnderlineStyle(lipgloss.UnderlineCurly)
  8. Render output with downsampling in v2

    main

    In v2, instead of using fmt.Println on a rendered string, you should use lipgloss.Println. This ensures that Lip Gloss can handle color downsampling correctly for the target terminal.

    // Instead of fmt.Println(s.Render("hi"))
    lipgloss.Println(s.Render("hi"))
  9. Upgrade from Lip Gloss v1 to v2

    main

    To migrate from v1 to v2, follow these primary steps:

    1. Update Import Paths: Change github.com/charmbracelet/lipgloss to charm.land/lipgloss/v2 and update subpackages (e.g., github.com/charmbracelet/lipgloss/table becomes charm.land/lipgloss/v2/table).
    2. Install v2: Run go get charm.land/lipgloss/v2.
    3. Update Color Usage: lipgloss.Color is now a function returning image/color.Color instead of a string type. Replace lipgloss.TerminalColor with color.Color from the image/color package.
    4. Handle Color Downsampling: In v2, Style.Render() always emits full-fidelity ANSI. You must use Lip Gloss writer functions (like lipgloss.Println) for automatic downsampling, or rely on Bubble Tea v2 which handles this internally.
    5. Remove Renderer Logic: The Renderer type and associated functions (like DefaultRenderer) are removed. Style is now a plain value type. Use lipgloss.NewStyle() instead of renderer-based style creation.
    go get charm.land/lipgloss/v2
  10. Use the compat package for v1-style adaptive colors

    main

    If you want a quick path to migrate adaptive or complete colors without refactoring your logic to use LightDark or Complete helpers, use the compat package. Note that compat types require lipgloss.Color values rather than raw strings.

    To customize the global compat behavior (which reads stdin/stdout like v1), you can initialize it in your init() function.

    import "charm.land/lipgloss/v2/compat"
    
    // v1
    color := lipgloss.AdaptiveColor{Light: "#f1f1f1", Dark: "#cccccc"}
    
    // v2
    color := compat.AdaptiveColor{Light: lipgloss.Color("#f1f1f1"), Dark: lipgloss.Color("#cccccc")}
  11. Use Lip Gloss writers for automatic color downsampling

    main

    In v2, color downsampling is handled at the output layer rather than during Render(). To ensure colors are correctly downsampled for the user's terminal, use Lip Gloss's built-in writer functions instead of fmt.Println.

    If you are using Bubble Tea v2, this step is unnecessary as Bubble Tea handles downsampling for you.

    // v1
    fmt.Println(s)
    
    // v2
    lipgloss.Println(s)