charmbracelet / charmbracelet/bubbletea

bug: Double border when `Base` style has a border and textarea shows placeholder

Open
#1,643 2 comments 0 reactions 0 assignees View on GitHub
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.

Image

Image

**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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.