Skip to content

feature: default flexShrink=0 for text elements #435

@remorses

Description

@remorses

When multiple text elements overflow a fixed-height box, the text becomes scrambled with characters overlapping. This happens because flexbox shrinks text elements to fit, causing them to render on top of each other.

Example without flexShrink={0}:

<box flexDirection="column" height={10} border>
  <text>Line 1: Short text</text>
  <text>Line 2: Another line</text>
  <text fg="red">Line 3: Red text</text>
  <text fg="green">Line 4: Green text</text>
  <text>Line 5: More content</text>
  <!-- more lines that overflow -->
</box>

Result - scrambled text with characters from different lines overlapping:

┌────────────────────────────────────────┐
│Line 2: Anothereshort line              │
│Line 5: GreenocoloredeteXtf the box     │
│Liner7: Bluetextchere it is quite long  │
│Line 12:lAlmostpatnthe end              │
│Line 15: Definitelysoverflowinglnow     │
└────────────────────────────────────────┘

Adding flexShrink={0} fixes it:

<box flexDirection="column" height={10} border>
  <text flexShrink={0}>Line 1: Short text</text>
  <text flexShrink={0}>Line 2: Another line</text>
  <!-- ... -->
</box>

Result - clean text that overflows correctly:

┌────────────────────────────────────────┐
│Line 1: Short text                      │
│Line 2: Another line                    │
│Line 3: Red text                        │
│Line 4: Green text                      │
│Line 5: More content                    │
└────────────────────────────────────────┘
 Line 6: Overflow renders outside box
Full reproduction code
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"

function App() {
  return (
    <box flexDirection="column" height={15} border title="Overflow Text Example">
      <text>Line 1: Short text</text>
      <text>Line 2: Another short line</text>
      <text>
        Line 3: This is a much longer line of text that should wrap to the next line when it reaches the edge of the
        box container
      </text>
      <text fg="red">Line 4: Red colored text</text>
      <text fg="green">Line 5: Green colored text</text>
      <text>
        Line 6: Another wrapping line with lots of content that will definitely need to wrap around because it is quite
        long and verbose
      </text>
      <text fg="blue">Line 7: Blue text here</text>
      <text>Line 8: Regular text</text>
      <text fg="yellow">Line 9: Yellow warning style</text>
      <text>Line 10: More content</text>
      <text>Line 11: This wrapping text contains styled parts</text>
      <text>Line 12: Almost at the end</text>
      <text fg="brightRed">Line 13: Bright red text</text>
      <text>Line 14: This line should overflow</text>
      <text>Line 15: Definitely overflowing now</text>
      <text>Line 16: Way past the box height</text>
      <text>Line 17: Still going</text>
      <text fg="brightGreen">Line 18: Bright green at the bottom</text>
      <text>Line 19: Final line of overflow content</text>
      <text>Line 20: The very last line</text>
    </box>
  )
}

const renderer = await createCliRenderer()
createRoot(renderer).render(<App />)

flexShrink={0} should be the default for text elements:

  1. Current behavior produces garbled output that looks like a rendering bug
  2. Text shrinking vertically to fit a container doesn't make sense - text should wrap, truncate, or overflow
  3. Users who want shrinking behavior can wrap text in a box
  4. Matches browser behavior - in CSS, text nodes don't shrink to fit their container, they overflow

Related to #96. I can open a PR for this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions