whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
7.86k stars 2.57k forks source link

Pages with `color-scheme: dark` have too low link color contrast #5426

Open tomayac opened 4 years ago

tomayac commented 4 years ago

Now that the standardized <meta name="color-scheme"> and the corresponding CSS property color-scheme have landed in Chrome and Safari, an accessibility issue around link colors occurs:

The colors defined in §14.3.4 Phrasing content

:link { color: #0000EE; }
:visited { color: #551A8B; }
:link:active, :visited:active { color: #FF0000; }
:link, :visited { text-decoration: underline; cursor: pointer; }

…do not have sufficient contrast on a dark background. See this demo (note the [Top] links):

demo-dark

This issue should probably also affect the definition of the CSS System Colors:

I have also opened bugs for Chrome, WebKit, and Firefox for awareness and to get this fixed.

annevk commented 4 years ago

cc @whatwg/a11y @whatwg/css

zcorpan commented 4 years ago

Other colors used in the spec's Rendering section

https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3

dialog {
  ...
  background: white;
  color: black;
}

https://html.spec.whatwg.org/multipage/rendering.html#phrasing-content-3

mark { background: yellow; color: black; } /* this color is just a suggestion and can be changed based on implementation feedback */

https://html.spec.whatwg.org/multipage/rendering.html#tables-2

table[rules=none i], table[rules=groups i], table[rules=rows i],
table[rules=cols i], ...... {
  border-color: black;
}

https://html.spec.whatwg.org/multipage/rendering.html#the-hr-element-2

hr {
  color: gray;
  border-style: inset;
  ...
}

https://html.spec.whatwg.org/multipage/rendering.html#phrasing-content-3

The initial value for the 'color' property is expected to be black. The initial value for the 'background-color' property is expected to be 'transparent'. The canvas's background is expected to be white.

domenic commented 4 years ago

I'm a little confused by this issue. Could you provide more background?

In particular, does using one of the features mentioned in the OP automatically change the html/body element's background? The links don't go to specs, but instead to issues with problem statements, so I can't really tell.

I'm also wondering if this is a general problem whenever authors use CSS/HTML features to change their page's background. Or is it specific to these particular features in some way?

tomayac commented 4 years ago

I'm a little confused by this issue. Could you provide more background?

In particular, does using one of the features mentioned in the OP automatically change the html/body element's background? The links don't go to specs, but instead to issues with problem statements, so I can't really tell.

The spec for this feature is here. I have described color-scheme in an article (this is a staging link, please don't circulate it).

I'm also wondering if this is a general problem whenever authors use CSS/HTML features to change their page's background. Or is it specific to these particular features in some way?

This is only an issue with the default user agent stylesheet rendering, that is, without any author-provided CSS. Note that the demo is completely CSS-free.

When an author changes the page background themselves, it's up to them to make sure the contrast is sufficient.

domenic commented 4 years ago

Sorry, I'm still confused. The spec you link to is for a CSS property and meta tag, so presumably the author must be using a CSS property or meta tag to change the page background? Or no?

The demo is completely CSS free, but also does not contain a meta tag, and does not "toggle between dark and light every three seconds" (even on Chrome Canary with experimental web platform features enabled). So I am unsure what it is meant to illustrate. (Edit: I see now that the demo is not completely CSS free after all, but instead is setting an inline style="" on the HTML element? At least in my browser that style has no effect though.)

tomayac commented 4 years ago

The spec you link to is for a CSS property and meta tag, so presumably the author must be using a CSS property or meta tag to change the page background? Or no?

Correct. But if you say color-scheme: dark (either via CSS or via meta tag), you opt in your browser to UA stylesheet optimizations for dark mode. Try the demo on Safari; in Chrome, we're still working on improving form elements rendering etc.

The demo is completely CSS free, but also does not contain a meta tag, and does not "toggle between dark and light every three seconds" (even on Chrome Canary with experimental web platform features enabled). So I am unsure what it is meant to illustrate.

The demo inserts the CSS property dynamically and toggles its value. There is no other CSS apart from that.

In Chrome, you need to switch your OS to dark mode for the demo to have an effect (I have pinged @lilles to see if this is the correct behavior). Again try the demo on Safari; there, it works independent from the OS theme.

domenic commented 4 years ago

I see. So then I am confused why we would adjust the default rendering of, e.g., links, when the page changes their background using color-scheme: dark, but not when the page changes their background using background: black. They seem the same to me.

tomayac commented 4 years ago

The difference is the following:

/**
  The site supports dark theme. Dear UA stylesheet,
  please handle everything I haven't handled. And
  since the one rule below is my only CSS, handle it all.
*/
:root {
  color-scheme: dark;
}

versus:

/**
  The site supports dark and light theme. Dear UA stylesheet,
  please handle everything I haven't handled. And
  since I handle link colors, handle everything else.
*/
:root {
  color-scheme: dark light;
}

a {
  /* I know what I'm doing, I mess with the default styling */
  color: white;
  background-color: black;  
}
@media (prefers-color-scheme: dark) {
  a {
    /* I know what I'm doing, I mess with the default styling */
    color: black;
    background-color: white;  
  }
}
tabatkins commented 4 years ago

Yes, switching on the color scheme is assumed to switch all the "default" coloring as well. The color-scheme property is not a "make this dark" command, but an "I'm okay with making this dark, feel free to do so if the user wants" hint. (Without it, the page remains in standard "light" mode even if the user has their OS set to dark mode, for backwards compatibility.)

When the UA flips into dark mode, the default background and text color change, as do the colors of inputs. Links should follow along, as should any other default colors applied by the UA stylesheet.

Two reasonable ways to approach this.

The first, and simplest, is to switch as much of these colors as possible to using the allowed system color keywords, which work not only with light/dark mode, but with forced-colors mode as well. As long as these keywords are only used in the properties with special resolved-value behavior, then it shouldn't even be a detectable change; calling getComputedStyle() will still give you the rgba color, not the keyword. (But any other color usage will expose the keyword, so I'd have to review the list of properties used.)

The second way is to explicitly add an @media (prefers-color-scheme: dark) {...} block to the HTML UA stylesheet, setting the colors appropriately for dark mode.

If there are properties not on the resolved-values list, we can do option 2 for just those, and rely on option 1 for the rest for simplicity; or we could do option 2 for everything for consistency. I have no opinion on the matter.

tomayac commented 4 years ago

To me, Option 1 sounds like the more consistent and more scalable approach; more scalable especially given the other color adjustments forced-color-adjust and color-adjust (and potentially future others, who knows). System colors (theoretically at least) need to be defined only once on the system level, and every app can then make use of them.

annevk commented 4 years ago

Ideally that would have been done as part of standardizing color-scheme. It doesn't seem like system colors cover all our use cases (e.g., mark or dialog) and it also seems like there might be unintentional side effects there, depending on how they are implemented.

cc @smfr

tomayac commented 4 years ago

For <mark>, there could be new system colors MarkText and Mark. FWIW, purely in the context of dark mode (but probably also OK in high contrast scenarios): the current yellow on black we have in Chrome, Firefox, and Safari seems to work fine universally. Probably still better to have a system colors pair.

For <dialog>, Chrome at least doesn't seem to do anything special (apart from the backdrop that might need adjustment).

Screen Shot 2020-04-02 at 10 09 15

Chrome with manually injected <meta name="color-scheme" content="dark">

It's early days for <dialog> in Firefox and Safari, but from enabling the features manually, they both seem to use Canvas and CanvasText.

Screen Shot 2020-04-02 at 10 06 13

Safari with manually injected <meta name="color-scheme" content="dark">

Screen Shot 2020-04-02 at 10 07 08

Firefox (doesn't support color-scheme yet) ⤴

tabatkins commented 4 years ago

Yes, if we need to we can add more system colors; if they're needed just to express the default UA visual semantics, that's a good argument for their inclusion.

But also you can just use the MQ to manually address those particular color needs explicitly, and use the system colors for everything else for simplicity.

tomayac commented 4 years ago

But also you can just use the MQ to manually address those particular color needs explicitly, and use the system colors for everything else for simplicity.

(I read this as concerning developer-provided CSS, not as using the MQ in the user-agent stylesheet.)

If the out-of-the box user-agent stylesheet experience is not accessible, I don't think this can be the solution.

No question, most developers will override the default look and feel and have link colors that are on-brand etc., but also some won't. Prominent timely example: http://lite.cnn.com/en (you can see all their CSS in the screenshot, I have inserted the meta tag manually):

Screen Shot 2020-04-03 at 10 34 40
annevk commented 4 years ago

No, Tab meant in the UA stylesheet.

I do think that if we were to switch to system colors their light/dark defaults would have to be written down somewhere.

tomayac commented 4 years ago

No, Tab meant in the UA stylesheet.

Gotcha, I wasn't 100% sure, so I added my (wrong) interpretation. In that case, sure, MQing in the UA stylesheet is an option, following the nomenclature from above, that'd be Option 2. Personally, I still prefer Option 1.

I do think that if we were to switch to system colors their light/dark defaults would have to be written down somewhere.

+1.

lilles commented 4 years ago

I do think that if we were to switch to system colors their light/dark defaults would have to be written down somewhere.

You mean if we start using LinkText, VisitedText, ActiveText in the UA sheet we need to spec the exact RGBA values for both light and dark versions, e.g. in CSS Color?

Wrt @media queries in the UA stylesheet, that won't work since the media query depends on the preferred color-scheme and the UA colors could be different for different elements in the same document since the color-scheme property has a per element computed and used value. If the preferred color scheme is dark, the two links below would be rendered with different colors (with system colors in the UA sheet):

<a href="link.html" style="color-scheme: dark light">Link 1</a>
<a href="link.html" style="color-scheme: light">Link 2</a>

But this UA sheet would end up with orange for both:

@media (prefers-color-scheme: dark) { a:link { color: orange } }
@media (prefers-color-scheme: light) { a:link { color: blue } }
annevk commented 4 years ago

@lilles yeah, exactly. I guess it's an existing problem that it's not defined what getComputedStyle() returns for (default) system colors and this adds to that.

It sounds like CSS needs to define system colors for all the different colors used in HTML's Rendering section and define their default values for various color schemes.

patrickhlauke commented 4 years ago

just wanted to check...am i missing something fundamental? if i have a vanilla HTML document with no styling defined, load it in Chrome, and switch (on Windows) between light/dark app mode, I see Chrome's UI change, but the vanilla page itself does not change at all (using Chrome 80)

and to clarify, i know that i as an author can then go in and make prefers-color-scheme changes specifically, but at that point i would not expect the UA to magically handle things/change styles for me, as the UA wouldn't know WHAT specific changes I made - other than perhaps assuming i made the backgrounds dark. but what if i didn't? what if i explicitly defined my author styles to remain light even in that scenario, would i THEN have to fight against the UA's assumption?

[edit: ah, seems i got the wrong end of the stick here ... so this is explicitly about when the page opts into dark mode via the meta, rather than adapting to user preference?]

annevk commented 4 years ago

@patrickhlauke https://drafts.csswg.org/css-color-adjust/#color-scheme-prop

tomayac commented 4 years ago

@patrickhlauke To quickly test this, go to https://lite.cnn.com/en, then paste the snippet below in the console: document.head.innerHTML += '<meta name="color-scheme" content="dark">'

tabatkins commented 4 years ago

Yup, backwards compat constrains what we can do by default; too many pages are authored with the assumption of a white background or black text, and would become unreadable if swapped to a dark background or light text. By default, the only result of your OS being in dark mode is that the (prefers-color-scheme: dark) MQ now matches, so you can manually adjust all of your colors as desired.

To opt your page into UA-provided dark-mode colors, the color-scheme property (or <meta> value) must be used, as @annevk links to.

patrickhlauke commented 4 years ago

yeah, sorry for coming in sideways there...took me a few re-reads, but i get it now and agree.

zcorpan commented 4 years ago

https://github.com/w3c/csswg-drafts/issues/4924 now has introduced more system color keywords.

annevk commented 4 years ago

Are defaults defined for various color schemes? (Doesn't look like it, so this isn't unblocked yet.)

tabatkins commented 4 years ago

Not yet, @fantasai and I ran out of time to address that in our last working day. We'll get to it soon.

awfulcooking commented 2 years ago

Came across this while looking into dark mode support for Geary, which uses WebKit to render email bodies.

Using @media (prefers-color-scheme: dark) I can override the background and text color for the composer pane:

Screenshot of email composer showing low contrast link color on dark background

And now I must choose a link color for readability.

Ideally, though, Geary could simply use color-scheme: light dark; on the root to achieve all of this :-)

Aside from Geary, I also think this is a moderately important feature for the web.

So this is a friendly bump!

domenic commented 2 years ago

It looks like the remaining work here is:

  1. Have CSS define the actual values for the various <system-color> keywords, matching the HTML spec's existing values for the corresponding color in the UA stylesheet.
  2. Send a PR to the HTML spec to update the UA stylesheet.
  3. Write web platform tests that will fail in browsers not implementing the HTML spec PR/will pass otherwise.
  4. Get implementer commitment to matching the updated specs.
tomayac commented 2 years ago

FWIW, these are the colors as implemented by Chromium (link color in the last row):

FB33B284-71CD-476A-8AFB-0577FEF22BA5

Seirdy commented 2 years ago

Colors in the blue-purple-red range (esp. blue) generally appear darker to the human eye, which is exacerbated by many common forms of color deficiencies. Blue text is a good fit for light backgrounds, but not dark backgrounds.

Colors in the orange-yellow-green range (esp. yellow) generally appear lighter; they work well against a dark background.

The above observations are reflected by the (experimental) Advanced Perceptual Contrast Algorithm, which can be enabled in Chromium's devtools's "Experiments" settings window.

I think that it might be worth experimenting with making the orange-yellow-green range the norm for colored foreground text in dark modes, including browser default stylesheets. There's still time before these are set in stone.

aminomancer commented 2 years ago

Yeah there are especially problems with purple, very very weak on dark backgrounds. I run into that all the time and often have to deviate from the rest/neutral, hover/brighter, active/darker schema when I use purple. Although yellow definitely provides way better contrast with the background, it's much less recognizable, much less associated with hyperlinks. I don't think contrast with the background is really the main issue. Any hue can have sufficient contrast with dark gray if you adjust the lightness to compensate for the well-known asymmetries in human color perception. I was just reading an interesting article about this exact subject last week. I had always thought the blue tradition was because of Tim Berners-Lee but apparently he originally used green. Which seems so strange to me, but I still find red kind of awkward, so maybe it's just a personal prejudice. The only colors I've ever used generically for hyperlinks are variations of blue. And hues close enough to be called blue, the extreme ends of which I'd estimate at #9999ff to #99ffff.

I don't think variations in color vision have much bearing on the choice of rest hue, because with hyperlinks hue differentiation isn't that important, except insofar as a link needs to be distinguishable from ordinary white or black text to be perceived as a link. And by that metric, yellow is the worst because it's the hardest to distinguish from white, which will usually be the base text color for a dark scheme, whereas indigo would be the easiest. I don't think that rules yellow out but just worth noting. Differentiating red from green may be important in buttons where you use them to distinguish a negative from an affirmative, but in links it's mainly just important to distinguish it from black/white. The visited and active states should have sufficient contrast with the rest state obviously, but that can be achieved irrespective of where you start the scheme.

This looks good to me and works with the most common color vision variations:

I might reduce the red component of the rest color (like, rotate the hue by -20°) so it's further from the visited color. But generally I think this is great. The high degree of lightness mitigates perceptual issues. And the contrast with white is inversely proportional to the perceptual issues because the perceptual issues are a lack of contrast with the dark background. So although increasing the lightness decreases the blue/purple's contrast with the white text, the base blue hsl(240, 100%, 50%) already starts with a much higher contrast against white than a yellow hsl(60, 100%, 50%) has. So 70% lightness is a sweet spot for blue—sufficient contrast vs. white text and sufficient contrast vs. dark gray background. Whereas for yellow, I believe the sweet spot is closer to 50% lightness.

Idk if this is quite what you had in mind, but it was the least gaudy scheme I could come up, with the premise of using yellow as the base and achieving higher contrast than the above scheme. Yellow, orange, and green don't have optimal contrast with each other, so I did yellow, red, blue. Which does have better contrast but looks so bad to me. Maybe it's just me, but yellow is among my least favorite colors for design, especially where more than 2 major hues are called for. But where yellow is obligatory, it pains me not to use blue with it. In any case, this color scheme just feels kinda "halloweeny" for me. Blue has its issues, but it's clean, it evokes the sky, it's the color of inspiration, and simultaneously an extremely neutral color. So it seems better as a rest color than yellow, which seems to have connotations of "warning" or "highlight" at least in western culture. Definitely a subject worth researching though. The same could be said for red, (and red is my least favorite part of the above scheme) but that's probably fine as long as it's not the rest color. If this scheme below evokes anything else for me, it's... peanut butter cups?

Seirdy commented 2 years ago

On Wed, Dec 29, 2021 at 01:26:08AM -0800, aminomancer wrote:

Maybe it's just me, but yellow is among my least favorite colors for design, especially where more than 2 major hues are called for.

I would agree, but yellow accents on black tend to look great. Black(ish) backgrounds change lots of the rules.

But where yellow is obligatory, it pains me not to use blue with it. In any case, this color scheme just feels kinda "halloweeny" for me. Blue has its issues, but it's clean, it evokes the sky, it's the color of inspiration, and simultaneously an extremely neutral color.

Blue is great...for light backgrounds. To the human eye, blue is a dark color; on a black background, blue needs to be very light to have good perceptual contrast.

When it's dark, we turn on artificial lights and many screens automatically adjust gamma levels at night (c.f. "night color" and "night light" OS features and software like redshift/gammastep). Yellowish/orangish hues are the norm in the dark.

If this scheme below evokes anything else for me, it's... peanut butter cups?

I use a yellow base on my site, https://seirdy.one/. I don't use a completely different hue for visited text: I use nearly-white yellow for visited links to mimic the effect of "almost the same color as text" that I saw with purple links. It should have good APCA values, with the exception of superscripts/subscripts.

Another example might be https://whalecoiner.com by @whalecoiner. This also passes the APCA for normal font sizes. Unlike my site, this uses many different hues.

I think that whalecoiner got it down pretty well, though I might swap out green with whitish yellow for visited links; that'd look more "de-emphasized" since the color would blend in with regular text.

-- /Seirdy

aminomancer commented 2 years ago

The base link color on your site is a good tone for yellow, but the nearly-white yellow active state is indistinguishable from the text color. That might be fine for an individual website but it's probably not gonna become a web standard. At that lightness it's also less distinct than chromium's (and now firefox's) light indigo. The whalecoiner scheme is similar to the color scheme I tested, but it lacks an active state, apparently in lieu of a subtle hover state. The difficulty of adding a third color is the biggest problem with this scheme. The green visited state looks terrible to me, but swapping it out for near-white would make the scheme really deficient in state contrast. That is, base color is peach, active color is peach, visited color would be practically indistinguishable from regular text.

By the way, I also think there's something to be said for the dark color scheme corresponding (at least somewhat) to the light color scheme. It's true that perceptual contrast for hues essentially reverses with the background lightness, so there is an argument for reversing the hues. But most people won't notice the color complementarity as a correspondence. It's such an extreme flip, and such an extreme break with tradition. Most browsers treat blue as a sort of base accent color, to be used in both the chrome and UA styles. And most websites built in the last 20+ years have been built with the expectation that hyperlinks will be blue. So not only are there gonna be websites that never overrode the anchor element UA styles, but many websites will have integrated that expectation into their design. Very few websites even use color-scheme at present so it would be quite a burden supporting not only a dark and a light mode, but a blue and a yellow mode as well. Whereas the chromium colors only break slightly from tradition and they don't clash with the more solid traditional colors.

whalecoiner commented 2 years ago

Another example might be https://whalecoiner.com by @whalecoiner. This also passes the APCA for normal font sizes. Unlike my site, this uses many different hues. I think that whalecoiner got it down pretty well, though I might swap out green with whitish yellow for visited links; that'd look more "de-emphasized" since the color would blend in with regular text.

yo! anything that I did on that site was so hella rushed it was unreal, but thank you!

(hi to familiar names on this thread - long time no see since abandoning the twitters)

Myndex commented 2 years ago

Hello everyone — I will try to be as succinct as possible, I will say that @Seirdy is providing solid information and good examples.

WCAG 2 and Dark Mode

WCAG 2 provides meaningless values for a black background. In fact, when both colors are darker than about #999, WCAG2 contrast has no real utility.

I recently published an article on this, here's a link that bypasses the paywall so you can read it for free: atangledwebweweave.com/whats-red-black-also-not-read

APCA Does Dark Mode

If designing for dark mode, APCA gives useful contrast guidance. The current canonical tool is: myndex.com/APCA/

Bridge-PCA for WCAG 2 Backwards Compatibility

Because of concerns over backwards compatibility, I released Bridge-PCA. It's backwards compatible with WCAG 2 contrast, but using APCA technology. "It fixes dark mode when used as directed". There's a simple demonstrator at myndex.com/BPCA/

Both are available as npm packages:

npm i apca-w3
npm i bridge-pca

Both are open-source for use with web content.

Topsy Turvey Colors

Blue has very little luminance. The eye's S cone (blue) does not contribute to luminance (it can subtract). Plus, there are no blue cones in the central foveal area, and they are sparsely scattered in the periphery, so they contribute nothing to detail — so you just can not use a pure primary blue on black. And the same goes for pure reds.

threepanel compare

Direct Comparison

This chart shows WCAG 2 degrading contrast as colors become darker. APCA maintains readability across the visible range.

ColumnCompareAll400

Any questions or comments are welcome here or at the APCA repo.

Thank you,

Andy

sarajw commented 2 years ago

I am coming across this now - I've found that chrome on Android renders links in dark mode with just enough contrast, but if I want to use that same color elsewhere and have it change automatically with color-scheme, I cannot, as it appears to be different from LinkText

I've tweeted some examples, the code and my frustration here: https://twitter.com/sarajwallen/status/1523967142789451776

image

sarajw commented 2 years ago

The implementation of these system color variables is very different per browser - this is on Chrome/Chromium: image

Here they are as seen through Firefox: image