Skip to content

[BUG] Some emoji with variation selectors cause button layout misalignment in Ghostty terminal #5980

@RMNCLDYO

Description

@RMNCLDYO

Some buttons containing emoji with Unicode variation selectors exhibit layout issues including misaligned borders and incorrect spacing when running in Ghostty terminal. The same code works correctly in macOS Terminal, indicating this is a terminal emulator compatibility issue rather than a Textual bug.

Steps to Reproduce

  1. Create a simple Textual app with a button containing eye emoji:
from textual.app import App, ComposeResult
from textual.widgets import Button

class TestApp(App):
    def compose(self) -> ComposeResult:
        yield Button("👁️ Test")  # With variation selector - broken in Ghostty

TestApp().run()
  1. Run the app in Ghostty terminal - observe misaligned borders
  2. Run the same app in macOS Terminal - observe correct rendering
  3. Compare with the Unicode escape version:
yield Button("\U0001F441 Test")  # Without variation selector - works in both terminals

Minimal Reproduction Code

from textual.app import App, ComposeResult
from textual.widgets import Button
from textual.containers import Vertical

class EmojiButtonTest(App):
    def compose(self) -> ComposeResult:
        yield Vertical(
            Button("👁️ With Variation Selector"),    # Broken in Ghostty only
            Button("\U0001F441 Without Variation Selector"),  # Works in both
            Button("👍 No Variation Selector Needed"),  # Works in both
            Button("❤️ Heart with VS"),              # Broken in Ghostty only
            Button("⭐️ Star with VS"),               # Works in both
            Button("Regular Button"),                 # Works in both
        )

if __name__ == "__main__":
    EmojiButtonTest().run()

Expected Behavior

All button variants should:

  • Have identically aligned borders
  • Maintain consistent spacing and padding
  • Display the emoji characters properly
  • Render consistently across different terminal emulators

Actual Behavior

In Ghostty terminal:

  • 👁️ (eye with variation selector) - has misaligned borders and spacing issues
  • ❤️ (heart with variation selector) - has misaligned borders and spacing issues
  • ⭐️ (star with variation selector) - works fine
  • Other buttons work correctly

In macOS Terminal:

  • All buttons render correctly with proper alignment
  • No layout issues with any emoji variants

Environment

  • Textual version: 4.0.0
  • Terminal: Ghostty 1.1.3 (issue present) / macOS Terminal (works correctly)
  • OS: macOS Darwin 24.5.0
  • Python version: 3.13.5

Additional Context

This issue only affects users running Textual applications in Ghostty terminal. The same applications work correctly in other terminal emulators like macOS Terminal.

I've opened a discussion in the Ghostty repository about this issue since it appears to stem from how Ghostty handles Unicode variation selector width calculations: Specific emoji with variation selectors cause layout issues in TUI applications

Pattern observed:

  • 👍 (U+1F44D) - works in both terminals
  • ⭐️ (U+2B50 + U+FE0F) - works in both terminals
  • 👁️ (U+1F441 + U+FE0F) - broken in Ghostty only
  • ❤️ (U+2764 + U+FE0F) - broken in Ghostty only

Impact

  • Affects Textual applications only when run in Ghostty terminal
  • Makes certain popular emoji unusable in Ghostty for Textual apps
  • Users may experience inconsistent behavior across different terminals
  • Not a widespread issue since it's terminal-specific

Workaround

For users experiencing this issue in Ghostty, use base Unicode characters without variation selectors:

# Instead of: Button("👁️ Watch") or Button("❤️ Love")
Button("\U0001F441 Watch")  # Eye without variation selector
Button("\U00002764 Love")   # Heart without variation selector

# Or switch to a different terminal emulator where the issue doesn't occur

Related Issues

Reproduction

Reproduction in Ghostty (showing layout issues)
error-ghostty

Reproduction in Terminal (macOS) (showing correct rendering)
error-terminal

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