Skip to content

floatpane/bubble-overlay

bubble-overlay

ANSI-aware overlay painter for Bubble Tea / lipgloss views.

Go Version Go Reference GitHub release (latest by date) CI License: MIT

bubble-overlay paints rectangular blocks of styled text on top of an existing ANSI-styled string at a given (row, col) cell position. It's the missing primitive for modals, popups, tooltips, filepickers, and floating panels in Bubble Tea / lipgloss apps — render your base view as a single string, render your popup as another, then composite.

Features

  • SGR-safe. Existing ANSI styles around the overlaid region are preserved; cells underneath are replaced. The overlay is terminated with \x1b[0m so its styles don't bleed into the row's tail.
  • Cell-accurate. Uses charmbracelet/x/ansi for width — wide-character / emoji handling matches what the terminal actually renders, not byte counts.
  • Auto-grow. Overlays that extend past the bottom of the base string append new lines instead of truncating.
  • Auto-pad. Overlays past the right edge of a short row are padded with spaces, so a popup on row 3 column 40 still lands correctly when the base row is only 10 cells wide.
  • Tiny. Two functions. No state. Drop-in.

Install

go get github.com/floatpane/bubble-overlay

Requires Go 1.26+.

Usage

package main

import (
    "fmt"

    "github.com/charmbracelet/lipgloss"
    "github.com/floatpane/bubble-overlay"
)

func main() {
    base := lipgloss.NewStyle().
        Foreground(lipgloss.Color("240")).
        Render("a quiet inbox view\nwith two lines\nand a third")

    popup := lipgloss.NewStyle().
        Background(lipgloss.Color("57")).
        Foreground(lipgloss.Color("231")).
        Padding(0, 1).
        Render("are you sure?\nyes / no")

    block := strings.Split(popup, "\n")
    fmt.Println(overlay.Block(base, block, 1, 4))
}

API

// Paint a multi-line block on top of base at (row, col).
func Block(base string, block []string, row, col int) string

// Paint a single overlay line on top of base at col.
func Line(base, overlay string, col int) string

That's the whole API.

When to use this

You have a Bubble Tea View() returning a styled multi-line string and you want to render a modal/popup over it without:

  • Re-rendering the base view with a "modal-shaped hole" cut out of it.
  • Walking the ANSI sequences yourself.
  • Truncating styles that span across the modal region.

bubble-overlay does the composite for you. The base view stays a single string; the modal stays a single string; you call Block and emit the result.

Documentation

Full API reference: pkg.go.dev/github.com/floatpane/bubble-overlay

Contributing

PRs welcome. See CONTRIBUTING.md.

Security

Report vulnerabilities privately via SECURITY.md.

License

MIT. See LICENSE.