charmbracelet / charmbracelet/bubbletea
bug: Double border when `Base` style has a border and textarea shows placeholder
- Dominant language
- Go
- Stars
- 44.9k
- Forks
- 1.3k
- PR merge metrics
- No merged PRs in 30d
Description
**Describe the bug**
When setting a `Border` on the textarea's `styles.Focused.Base` (or `Blurred.Base`), the border is rendered **twice** while the placeholder is visible (empty textarea). Once the user types any text, the double border disappears and only a single border is shown.
The inner border appears shifted by the border + padding offset, producing a nested-box effect.
**Setup**
Please complete the following information along with version numbers, if applicable.
- OS: macOS
- Shell: zsh
- Terminal Emulator: Ghostty
- Terminal Multiplexer: N/A
**To Reproduce**
Run the following code snippet below
**Source Code**
```go
package main
import (
"fmt"
"os"
"charm.land/bubbles/v2/textarea"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
)
type model struct {
ta textarea.Model
}
func initialModel() model {
ta := textarea.New()
ta.Placeholder = "Type something..."
ta.ShowLineNumbers = false
ta.Prompt = ""
ta.SetHeight(5)
// Set a border on Base — this triggers the double-render bug.
styles := ta.Styles()
styles.Focused.Base = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("63")).
Padding(0, 1)
styles.Blurred.Base = styles.Focused.Base
styles.Focused.CursorLine = lipgloss.NewStyle()
ta.SetStyles(styles)
ta.Focus()
return model{ta: ta}
}
func (m model) Init() tea.Cmd {
return m.ta.Focus()
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
if msg.String() == "ctrl+c" {
return m, tea.Quit
}
case tea.WindowSizeMsg:
m.ta.SetWidth(msg.Width)
}
var cmd tea.Cmd
m.ta, cmd = m.ta.Update(msg)
return m, cmd
}
func (m model) View() tea.View {
return tea.NewView(fmt.Sprintf("\n%s\n\nctrl+c to quit\n", m.ta.View()))
}
func main() {
p := tea.NewProgram(initialModel())
if _, err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
```
**Expected behavior**
No inner or double border, just a single border in the textarea, regardless of if text is in the box or not.
**Screenshots**
See above
**Additional context**
This report is largely AI generated.
go mod:
```go
module test
go 1.26.0
require (
charm.land/bubbles/v2 v2.0.0
charm.land/bubbletea/v2 v2.0.2
charm.land/lipgloss/v2 v2.0.2
)
require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/charmbracelet/colorprofile v0.4.2 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/charmbracelet/x/termios v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-runewidth v0.0.20 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.42.0 // indirect
)
```
Contributor guide
Research direction
Start by running the provided Go reproduction with the textarea.Model, placeholder, and custom Focused.Base and Blurred.Base styles. Trace how textarea.View renders the empty placeholder versus entered text, then verify that the result has only one border in both states.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100