Repository: Textualize/rich

Issue:
[BUG]In the latest versions rich "Title" without color
rich v 13.0.1  Title is painted over, see screenshot
rich v13.9.4r Title unpainted, see screenshot

OS GNU/Linux and OS Windows

```python
from rich.console import Console
from rich.panel import Panel
from rich.style import Style
import os

console = Console()
console.print(Panel(str(os.environ), title='[bold white]Title[/bold white]', style=Style(color="white", bgcolor="blue")))
```
![rich](https://github.com/user-attachments/assets/9eb0fd2e-13f9-4e2a-9b09-710c7d48f7ff)
Title should be like on the first screen (white on blue), in the second case (updated rich) white on dark (bug?)

Hints from issue comments:
@github-actions: Thank you for your issue. Give us a little time to review it.

PS. You might want to check the [FAQ](https://github.com/textualize/rich/blob/master/FAQ.md) if you haven't done so already.

This is an automated reply, generated by [FAQtory](https://github.com/willmcgugan/faqtory)@snooppr: `console.print(Panel(str(os.environ), title='[white on blue]Title[/white on blue]', style=Style(color="white", bgcolor="blue")))
`
Filling the title with a separate color does not solve the problem (it is also not completely filled).
![2024-11-30](https://github.com/user-attachments/assets/8e3d7256-39c8-4967-97f3-aa791f094cef)

Correct result (1 screen, completely painted over header). Workaround is to use rich v 13.0.1.
**Is there a fix planned?**@snooppr: @willmcgugan 
can you tell me something useful about the open issue that hasn't been updated for 5 months. Because of this problem, I'm forced not to update rich in my projects since version 13.0.1.@TomJGooding: @snooppr I'm not Will, but perhaps as a workaround in the meantime you could try setting the `border_style` to the same background as the panel? For example:

```python
from rich.console import Console
from rich.panel import Panel
from rich.style import Style

console = Console()
console.print(
    Panel(
        "Content",
        title="[bold white]Title[/bold white]",
        style=Style(color="white", bgcolor="blue"),
        border_style=Style(bgcolor="blue"),
    )
)
```

---

It looks like there's a history of issues related to the panel title style, so any fix for this issue should be careful not to cause any regressions...
 
- #2466 
- #2745 
- #3493@snooppr: @TomJGooding 
Thanks, the "`border_style`" hack solves the problem. Works in both old and new versions of rich.@github-actions: I hope we solved your problem.

If you like using Rich, you might also enjoy [Textual](https://textual.textualize.io)

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9947656..1f24a4ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+
 ## [Unreleased]
 
 ### Changed
@@ -17,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Fixed extraction of recursive exceptions https://github.com/Textualize/rich/pull/3772
 - Fixed padding applied to Syntax https://github.com/Textualize/rich/pull/3782
+- Fixed `Panel` title missing the panel background style https://github.com/Textualize/rich/issues/3569
 
 ### Added
 
diff --git a/rich/panel.py b/rich/panel.py
index d411e291..07587e3b 100644
--- a/rich/panel.py
+++ b/rich/panel.py
@@ -146,8 +146,7 @@ class Panel(JupyterMixin):
             Padding(self.renderable, _padding) if any(_padding) else self.renderable
         )
         style = console.get_style(self.style)
-        partial_border_style = console.get_style(self.border_style)
-        border_style = style + partial_border_style
+        border_style = style + console.get_style(self.border_style)
         width = (
             options.max_width
             if self.width is None
@@ -206,7 +205,7 @@ class Panel(JupyterMixin):
 
         title_text = self._title
         if title_text is not None:
-            title_text.stylize_before(partial_border_style)
+            title_text.stylize_before(border_style)
 
         child_width = (
             width - 2
@@ -255,7 +254,7 @@ class Panel(JupyterMixin):
 
         subtitle_text = self._subtitle
         if subtitle_text is not None:
-            subtitle_text.stylize_before(partial_border_style)
+            subtitle_text.stylize_before(border_style)
 
         if subtitle_text is None or width <= 4:
             yield Segment(box.get_bottom([width - 2]), border_style)


Test patch:
diff --git a/tests/test_panel.py b/tests/test_panel.py
index 65eb9bc0..33ce1afa 100644
--- a/tests/test_panel.py
+++ b/tests/test_panel.py
@@ -124,6 +124,54 @@ def test_title_text() -> None:
     assert result == expected
 
 
+def test_title_text_with_border_color() -> None:
+    """Regression test for https://github.com/Textualize/rich/issues/2745"""
+    panel = Panel(
+        "Hello, World",
+        border_style="blue",
+        title=Text("title", style="red"),
+        subtitle=Text("subtitle", style="magenta bold"),
+    )
+    console = Console(
+        file=io.StringIO(),
+        width=50,
+        height=20,
+        legacy_windows=False,
+        force_terminal=True,
+        color_system="truecolor",
+    )
+    console.print(panel)
+
+    result = console.file.getvalue()
+    print(repr(result))
+    expected = "\x1b[34m╭─\x1b[0m\x1b[34m───────────────────\x1b[0m\x1b[31m title \x1b[0m\x1b[34m────────────────────\x1b[0m\x1b[34m─╮\x1b[0m\n\x1b[34m│\x1b[0m Hello, World                                   \x1b[34m│\x1b[0m\n\x1b[34m╰─\x1b[0m\x1b[34m──────────────────\x1b[0m\x1b[1;35m subtitle \x1b[0m\x1b[34m──────────────────\x1b[0m\x1b[34m─╯\x1b[0m\n"
+    assert result == expected
+
+
+def test_title_text_with_panel_background() -> None:
+    """Regression test for https://github.com/Textualize/rich/issues/3569"""
+    panel = Panel(
+        "Hello, World",
+        style="on blue",
+        title=Text("title", style="red"),
+        subtitle=Text("subtitle", style="magenta bold"),
+    )
+    console = Console(
+        file=io.StringIO(),
+        width=50,
+        height=20,
+        legacy_windows=False,
+        force_terminal=True,
+        color_system="truecolor",
+    )
+    console.print(panel)
+
+    result = console.file.getvalue()
+    print(repr(result))
+    expected = "\x1b[44m╭─\x1b[0m\x1b[44m───────────────────\x1b[0m\x1b[31;44m title \x1b[0m\x1b[44m────────────────────\x1b[0m\x1b[44m─╮\x1b[0m\n\x1b[44m│\x1b[0m\x1b[44m \x1b[0m\x1b[44mHello, World\x1b[0m\x1b[44m                                  \x1b[0m\x1b[44m \x1b[0m\x1b[44m│\x1b[0m\n\x1b[44m╰─\x1b[0m\x1b[44m──────────────────\x1b[0m\x1b[1;35;44m subtitle \x1b[0m\x1b[44m──────────────────\x1b[0m\x1b[44m─╯\x1b[0m\n"
+    assert result == expected
+
+
 if __name__ == "__main__":
     expected = []
     for panel in tests: