textual icon indicating copy to clipboard operation
textual copied to clipboard

Footer with ANSI colors broken in v3.1.0

Open TomJGooding opened this issue 6 months ago • 9 comments

As mentioned in #5862, the footer descriptions are unreadable when ansi_colors=True in the app. Unfortunately it looks like the footer colors were broken in Textual v3.1.0.

Here's a comparison showing the footer example from the docs but ran with app = FooterApp(ansi_color=True):

v3.0.0

Image

v3.1.0

Image

Setting the theme to textual-ansi doesn't help:

Image

TomJGooding avatar Jun 15 '25 21:06 TomJGooding

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

github-actions[bot] avatar Jun 15 '25 21:06 github-actions[bot]

Having just learned about ansi_colors=True recently, I had assumed this was always a problem. If this is a new issue then I would also really like to see it fixed

edward-jazzhands avatar Jun 15 '25 23:06 edward-jazzhands

Would either of you like to tackle this one? Ping me if you need any assistance.

willmcgugan avatar Jun 16 '25 09:06 willmcgugan

@willmcgugan I did spend a fair while looking into this, but unless I'm missing something obvious, this isn't easy to track down. I'm happy to have another crack at it, but I'm afraid I'll need a hint!

TomJGooding avatar Jun 24 '25 13:06 TomJGooding

so I did some digging into this myself. I found a kind of related solution, I believe. Which is that in my opinion, if there is a footer in the app then it shouldn't be affected by transparency. My guess would be that most people would expect that the footer will stay opaque. So, in my own testing I have done this

Replacing at widgets/_footer.py line 144 https://github.com/Textualize/textual/blob/6e4f77bb48080aacac03c6141eeed0cf11b5e92e/src/textual/widgets/_footer.py#L144

        &:ansi {
            /* background: ansi_default; */
            .footer-key--key {
                /* background: ansi_default; */
                color: ansi_magenta;
            }
            .footer-key--description {
                /* background: ansi_default; */
                color: ansi_default;
            }
            FooterKey:hover {
                text-style: underline;
                /* background: ansi_default; */
                color: ansi_default;
                .footer-key--key {
                    /* background: ansi_default; */
                }
            }
            FooterKey.-command-palette {
                /* background: ansi_default; */
                border-left: vkey ansi_black;
            }
        }
    }

Now I have no idea if this would be acceptable, I'm just brainstorming. From what I can tell, most of Textual does not change when ansi_color is set to True? Most widgets retain their colors, its not dropping the entire app immediately down to 16 colors or anything like that.

So what I'm wondering here is, can we possibly just prevent the Footer from changing the background as I've done above? In my own testing in Windows Terminal, this basically solves the problem. Like I said I would expect that the footer stays opaque.

edward-jazzhands avatar Jun 24 '25 19:06 edward-jazzhands

@edward-jazzhands I don't think this is the fix I'm afraid. Have you tried also setting the theme to textual-ansi?

TomJGooding avatar Jun 24 '25 20:06 TomJGooding

Ah, no I had not tried that. I was setting the ansi_color = True variable on the app. Either when creating an app with TextualApp(ansi_color=True).run() or by setting self.ansi_color = True on the app class.

It seems that using the ansi_color=True variable works and looks great, because its not dropping every color in the app down to 16 colors. Whereas setting self.theme = "textual-ansi" actually forces the entire app down to 16 colors. You should also try it out if you have not already to see the difference.

At this point I would like to recommend thinking of this as two different problems. Transparency with ansi_color=True looks great, because all its doing is enabling transparency without forcing the entire app down to 16 colors. self.theme = "textual-ansi" is doing the latter and creates and entirely new set of problems. It should be possible to enable transparency without forcing the app into 16 colors. I think a proper solution might need to account for both situations.

edward-jazzhands avatar Jun 24 '25 20:06 edward-jazzhands

I deleted my previous post as it didn't really explain the problem.

Regardless of the theme, previously background: ansi_default worked the same in the Footer as all other widgets that set this in the :ansi pseudo class.

This ANSI theming isn't really documented (and according to the FAQ doesn't even exist!), so perhaps I have just misunderstood how this is supposed to work.

TomJGooding avatar Jun 24 '25 22:06 TomJGooding

I've had another crack at this, but I'm afraid I still don't know how to fix it.

The Footer is composed of FooterKey widgets. These render Rich Text which gets converted to Textual Content objects.

The problem is that this converts the background Color('default', ColorType.DEFAULT) to Color(217, 217, 217).

Following the code path from visual.py -> content.py -> style.py -> color.py:

https://github.com/Textualize/textual/blob/11580e999adb7fb5104edbb4ed28c50997944993/src/textual/color.py#L195

The problem is that this will return the theme foreground color:

rich/color.py

        else:  # self.type == ColorType.DEFAULT:
            assert self.number is None
            return theme.foreground_color if foreground else theme.background_color

When background: ansi_default, presumably we don't actually want any background color?

TomJGooding avatar Sep 12 '25 21:09 TomJGooding