https://github.com/floatpane/bubble-overlay
ANSI-aware overlay painter for Bubble Tea / lipgloss views
https://github.com/floatpane/bubble-overlay
ansi ansi-escape-codes bubbletea charmbracelet cli command-line go golang golang-library lipgloss modal overlay popup rendering terminal terminal-ui text-ui tooltip tui ui-components
Last synced: about 2 months ago
JSON representation
ANSI-aware overlay painter for Bubble Tea / lipgloss views
- Host: GitHub
- URL: https://github.com/floatpane/bubble-overlay
- Owner: floatpane
- License: mit
- Created: 2026-05-30T11:46:13.000Z (2 months ago)
- Default Branch: master
- Last Pushed: 2026-06-22T06:25:33.000Z (about 2 months ago)
- Last Synced: 2026-06-22T08:15:39.182Z (about 2 months ago)
- Topics: ansi, ansi-escape-codes, bubbletea, charmbracelet, cli, command-line, go, golang, golang-library, lipgloss, modal, overlay, popup, rendering, terminal, terminal-ui, text-ui, tooltip, tui, ui-components
- Language: Go
- Homepage: https://bubble.floatpane.com
- Size: 82 KB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# bubble-overlay
**ANSI-aware overlay painter for Bubble Tea / lipgloss views.**
[](https://golang.org)
[](https://pkg.go.dev/github.com/floatpane/bubble-overlay)
[](https://github.com/floatpane/bubble-overlay/releases)
[](https://github.com/floatpane/bubble-overlay/actions/workflows/ci.yml)
[](LICENSE)
`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](https://github.com/charmbracelet/bubbletea) /
[lipgloss](https://github.com/charmbracelet/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
```bash
go get github.com/floatpane/bubble-overlay
```
Requires Go 1.26+.
## Usage
```go
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))
}
```
### Center a floating popup
For the common case of a centered modal — command palette, confirmation dialog,
tooltip — `Center` handles the positioning automatically:
```go
composited := overlay.Center(baseView, popupView, termWidth, termHeight)
```
It computes the centered `(row, col)` from the popup's visual size and your
screen dimensions, then calls `Block`. Rows/columns clamp to zero if the popup
is larger than the screen.
### Low-level API
```go
// Center places popup as a floating layer centered over base within a screen
// of screenW × screenH cells.
func Center(base, popup string, screenW, screenH int) string
// 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
```
## 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](https://pkg.go.dev/github.com/floatpane/bubble-overlay)
## Contributing
PRs welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
## Security
Report vulnerabilities privately via [SECURITY.md](SECURITY.md).
## License
MIT. See [LICENSE](LICENSE).