Closed Bug 1825958 Opened 3 years ago Closed 3 years ago

<select> popup uses dark or light color-scheme based on 'background-color', even if author requested color-scheme : dark

Categories

(Core :: Layout: Form Controls, defect)

defect

Tracking

()

RESOLVED WONTFIX

People

(Reporter: denis.migdal, Unassigned)

References

Details

Attachments

(2 files)

User Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0

Steps to reproduce:

  1. Create a select with options
  2. Set a background to the select
  3. Set a color-scheme: dark

Actual results:

"color-scheme : dark" is ignored for the options (white background and black font).

Expected results:

options should appear in dark color scheme (black background and white font).

The Bugbug bot thinks this bug should belong to the 'Core::Layout: Text and Fonts' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.

Component: Untriaged → Layout: Text and Fonts
Product: Firefox → Core

(In reply to denis.migdal from comment #0)

  1. Set a background to the select
  2. Set a color-scheme: dark

Actual results:

"color-scheme : dark" is ignored for the options (white background and black font).

It's actually not ignored; we actually change the color of the dropdown-popup depending on the darkness of the background that you specify for the select element in your steps-to-reproduce step 2. There's some logic that was added in bug 1782623 to do this.

It's a bit of a heuristic so it might not match your desired outcome for intermediate-darkness background colors, but it should do reasonably well at the extremes.

If this is causing a problem for you in a real-world case, perhaps you could attach a testcase to demonstrate the issue, with the actual colors/etc. that you're working with?

Flags: needinfo?(denis.migdal)

Here's a testcase to demonstrate that it actually depends on the choice of background. For me, in Firefox Nightly on Linux, the first three selects here have a dark-themed popup, and the last three have a light-themed popup.

(The 3rd and 4th ones only differ by 1 in their color-channel value -- 117 vs 118 -- to demonstrate that that seems to be the threshold, for gray colors at least.)

Component: Layout: Text and Fonts → Layout: Form Controls
Depends on: 1782623
Version: Firefox 111 → Trunk

Thanks for your answer.

In my case, I was making a dummy dark mode by using : "color-scheme: dark; filter: invert(1) hue-rotate(180deg);" on the root element.
For this dark mode, I want all selects' dropdowns to be black (hence the color-scheme).

Without background, selects and inputs are shown in white (and dropdown in black) because colors are inverted. Therefore I make a second " filter: invert(1) hue-rotate(180deg);" on all my select and input elements so I get what I want.

Of course, selects and inputs with backgrounds would have no changes to their colors as they are inverted twice, but I'm okay with it. However, I still want a black dropdown. But it seems, when computing the dropdown background color, that the filters and color-scheme options are ignored, thus giving me a white background (when I expect a black one).

TL;DR : If the select background color is inverted with "filter: invert(1) hue-rotate(180deg)" (e.g. on the select itself), the color of the dropdown background would be the opposite of what is expected.

I think that explicitly setting the color-scheme on the select should override this behavior and force a black dropdown background.

<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>
  :root {
	color-scheme: dark;
	background-color: white;

	filter: invert(1) hue-rotate(180deg);
}

	select,input {
		filter: invert(1) hue-rotate(180deg);
	}
</style>
</head><body class="vsc-initialized"><select style="background: black">
  <option selected="selected">A</option>
  <option>B</option>
  <option>C</option>
</select>
<select style="background: brown">
  <option selected="selected">A</option>
  <option>B</option>
  <option>C</option>
</select>
<select style="background: rgb(117,117,117)">
  <option selected="selected">A</option>
  <option>B</option>
  <option>C</option>
</select>
<br><br>
<select style="background: rgb(118,118,118)">
  <option selected="selected">A</option>
  <option>B</option>
  <option>C</option>
</select>
<select style="background: salmon">
  <option selected="selected">A</option>
  <option>B</option>
  <option>C</option>
</select>
<select style="background: cyan">
  <option selected="selected">A</option>
  <option>B</option>
  <option>C</option>
</select>
<span style='color:black'>TEXTE</span>
<select>
  <option selected="selected">A</option>
  <option>B</option>
  <option>C</option>
</select>
</body></html>
Flags: needinfo?(denis.migdal)

Strange, it seems it posted a comment when I tried to add the HTML code (I got an error message while doing so).

Please ignore comment #4.

Attachment #9326710 - Attachment mime type: text/plain → text/html

(In reply to denis.migdal from comment #6)

Strange, it seems it posted a comment when I tried to add the HTML code (I got an error message while doing so).

Yeah, that's a Bugzilla papercut; if you paste in a large amount of text to a comment, bugzilla presents a popup inviting you to submit that pasted-data as an attachment instead (which is great!) but then as part of that, it posts your comment in its current state (which is not-great).

Please ignore comment #4.

I tagged it as obsolete which auto-hides it.

(In reply to denis.migdal from comment #5)

it seems, when computing the dropdown background color, that the filters and color-scheme options are ignored

Correct/sort-of-correct:

  • the popup doesn't care about the filter, just as it doesn't care about e.g. rotating for transform:rotate(45deg).
  • On macOS and Linux, the popup's background-color is determined to be dark or light based on the background-color of its select element, via https://hg.mozilla.org/mozilla-central/rev/469683bb7d81 . Thais means that, by default, it is influenced by the select element's color-scheme, but if you've overridden the select element's background, then yeah, color-scheme has no effect on the popup's background-color.
  • On Windows, the popup adopts the same background-color that its select element has. You can see this on macOS and Linux by setting about:config pref dom.forms.select.customstyling to true.

I think that explicitly setting the color-scheme on the select should override this behavior and force a black dropdown background.

I don't think this can work the way you describe. Firstly: since color-scheme is an inherited property, there's no difference (for a given select) between setting it on the select vs. setting it on the root element for the whole page -- the select element will end up with color-scheme:dark in its computed style either way. And secondly: on Windows at least, we use the select's own background-color and text color as the dropdown color (and the behavior on other platforms is meant to be sort-of analogous to that) -- so if we forced a dark background, you might end up with black text on a black background, for e.g. a <select style="background:white;color:black"> in a color-scheme:dark sort of page.

Thanks for your answer.

If I understand, one of the solution would be to invert "by hand", in the CSS, all the color/background-color set on select elements, when in my dark mode. Indeed, it doesn't seem possible to do it in JS as there is no way to observe a computed style.

That's not very practical. :'(
And darkgreen isn't even the inverted color of lightgreen... we've been lied to xD.

On Windows, the popup adopts the same background-color that its select element has. You can see this on macOS and Linux by setting about:config pref dom.forms.select.customstyling to true.

Thanks for this information, I think I'll use this.

But why such different behavior depending on the OS ? Why isn't it enabled by default ?

Fun fact, when enabled, I can now change the background and color of option elements. However "filter" property is ignored if put on an option element (only color and background are used).... Why do Firefox hate me so much ? xD

A solution would be to set the color and background of all options to the colors of the dark scheme :

option {
  background: #2d2935;
  color: white;
}

However, the scrollbar might be in the bad color, and it would depends whether or not the browser has this feature enabled or not...

Well that's better than nothing.

(In reply to denis.migdal from comment #9)

But why such different behavior depending on the OS ? Why isn't it enabled by default ?

This is sort of off-topic for this bug, so let's not get into too much of a side conversation about this :) but to answer the question, that's semi-documented in https://searchfox.org/mozilla-central/rev/98397ff4eac3d32b815fbb33bff147297fb972d7/modules/libpref/init/all.js#886-896

Essentially, on macOS, the "native"-looking popup (with light & dark background-color options that align with the user's local system theme) seems to be the platform convention and provide a better experience.

And on Linux, the dom.forms.select.customstyling config used to produce trivially broken/unreadable content in some cases due to bugs in our Linux theming code; I think those have been addressed, but it's not yet obvious if it's just simpler/cleaner to just stick with the native theme as we've currently got it there (similar to on Mac). If we use the web-developer-specified select background-color as the popup's background-color, that creates additional complexity and possibilities-for-brokenness/interop issues; see bug 1783498 as one example.

Fun fact, when enabled, I can now change the background and color of option elements. However "filter" property is ignored if put on an option element (only color and background are used).... Why do Firefox hate me so much ? xD

Yeah, option elements are extremely locked-down in terms of what CSS they respect, per
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#styling_with_css
(It seems like Firefox-with-dom.forms.select.customstyling:true supports more properties than other browsers there? But yeah, not filter.)

A solution would be to set the color and background of all options to the colors of the dark scheme :
[...] However, the scrollbar might be in the bad color,

Right - if you're coloring all of the option elements, I'd be worried about the scrollbar potentially ending up a bad color with respect to those options (since it's trying to offset from the background of the popup-as-a-whole, rather than the individual options).

I think (?) your double-filter approach is reasonable, as long as you use a sufficiently-dark background-color (or just let it use the default?) to make Firefox's heuristic detect the widget as being suitable for a dark popup.

It also might be simpler to just rethink whether you really want to use this filter-based synthetic dark mode, and instead just use a regular dark mode theme? Though I'm not sure what your constraints are.

Summary: color-scheme : dark is ignored on <option> if background is set on the <select> parent → <select> popup uses dark or light color-scheme based on 'background-color', even if author requested color-scheme : dark

I think (?) your double-filter approach is reasonable, as long as you use a sufficiently-dark background-color (or just let it use the default?) to make Firefox's heuristic detect the widget as being suitable for a dark popup.

Yep, in dark mode, I'll have to change the select background to get the opposite of the one I use in light mode.
I'm not fully comfortable with this solution as it is error prone : if a CSS rule is forgotten, I'd have a bad popup color in dark mode.
A shame that JS can't observe computed styles, that would have solved my issue.

On top of that I can force the popup background in dark (for Windows) to mitigate potential issues (and that'll also give me a darker popup background in Chromium - Chromium has a lighter popup background in dark mode compared to Firefox).

It also might be simpler to just rethink whether you really want to use this filter-based synthetic dark mode, and instead just use a regular dark mode theme? Though I'm not sure what your constraints are.

The filter is quite magical and works for everything... except for select with backgrounds xD
Setting manually all the colors I want for a dark mode would be quite time-consuming and error prone. Filter even works on graph I draw in canvas.

But, yeah, I should use CSS variables to declare my colors... Well... that's a ~6 y.o. code I wrote with really no time to spare, that grew bigger and bigger. Since I discovered lot of good things like WebComponents... and made several big refactoring. I made quite a few errors in my past, due to time constraints (that made me lose even more time afterwards to pay for the technical debt).

So yeah, filter is really practical for me : a dark mode in 2 minutes.... except for select with backgrounds. For a feature that is not essential (so that I can't spend too much time on it).

Well, that's not a perfect solution, but I guess it'll work.


I also noticed :

Styling the <option> element is highly limited. Options don't inherit the font set on the parent. In Firefox, only color and background-color can be set however in Chrome or Safari it's not possible to set any properties.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#styling_with_css

Color and background-color can be set on Chromium. This phrase might not be true anymore.

(In reply to denis.migdal from comment #11)

A shame that JS can't observe computed styles, that would have solved my issue.

JS does have e.g. getComputedStyle, not sure if that's what you're referring to? Or maybe you're saying JS can't detect the actual rendered pixel-color after the color-filtering operation? (If so: I think that's true, yeah.)

The filter is quite magical and works for everything... except for select with backgrounds xD
Setting manually all the colors I want for a dark mode would be quite time-consuming and error prone. Filter even works on graph I draw in canvas.
[...]
So yeah, filter is really practical for me : a dark mode in 2 minutes.... except for select with backgrounds. For a feature that is not essential (so that I can't spend too much time on it).

Well, that's not a perfect solution, but I guess it'll work.

Sounds good. :)

(Note that there's a good reason that select-popups don't get filtered -- they're rendered independently from the rest of the document, "anchored" to their select element's position but otherwise in their own layer. That's why they don't get e.g. rotated/scaled by transforms, and why they can escape the bounds of overflow:scroll areas as in e.g.

data:text/html,<div style="overflow:scroll;height:50px;width:50px;border:1px solid black"><select><option>abc</option><option>def</option>

)

Anyway: I'll go ahead and close this bug since I think things are essentially working-as-intended here. As discussed above, the automatic background-color-based light/dark-theming-choice for the popup is an intentional behavior, which makes the popup and the select element look similar in terms of light/darkness (in the absence of things like color-inverting filters, at least), and it helps to ensure that our dark/light scrollbar is visible if we happen to be using the select's background-color as the dropdown's background-color (e.g. on Windows).

Status: UNCONFIRMED → RESOLVED
Closed: 3 years ago
Resolution: --- → WONTFIX

As discussed above, the automatic background-color-based light/dark-theming-choice for the popup is an intentional behavior, which makes the popup and the select element look similar in terms of light/darkness (in the absence of things like color-inverting filters, at least), and it helps to ensure that our dark/light scrollbar is visible if we happen to be using the select's background-color as the dropdown's background-color (e.g. on Windows).

Could it be technically possible to make something like :

let isPopupDark = ..... ; // computed normally.
let ancestors = .... ; // get all ancestors of the select.
for( let ancestor of ancestors )
     if( ancestor.filter.invert > 0.5 ) // pseudo-code
           isPopupDark = ! isPopupDark

JS does have e.g. getComputedStyle, not sure if that's what you're referring to? Or maybe you're saying JS can't detect the actual rendered pixel-color after the color-filtering operation? (If so: I think that's true, yeah.)

I meant that JS can't put an observer on a CSS computed property style, in order to call a callback when it changes. It can detect attributes changes, but if the CSS rule doesn't depends on the current element attributes, but, e.g. on an ancestor attribute, that'll start become trickier. Or if a CSS stylesheet is injected.

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: