Forced Colors Mode Futility

by Matthias Zöchling published on

Are you aware of Forced Colors Mode? If not, there are some resources at the end. If so, did you also know that this accessibility feature can be used as an entry-level debugging tool?

Comparing HTMHell.dev: Three screenshots taken in Microsoft Edge browser. Although subtle tweaks could be made, overall the site works really well in Forced Colors Mode.
From left to right: The HTMHell Advent Calendar 2024 in its intended theme, when viewed in Windows 11 contrast theme “Night Sky”, and “Desert”.

Note: I’ve created a CodePen with all the upcoming examples, so you can follow along.

Premise

When the forced colors feature is turned on, colors will be replaced with CSS System Colors. Elements like buttons and links get special colors assigned, so wherever improper elements are used, things will fall out of place.

Bad advice

Okay, surely there’s a way around this, in true HTMHell.dev spirit‽

Warning: Sarcasm ahead! Don’t try this at home!

Let’s use a <div> to create a button, and <u> to create a link. By doing the latter, our links are already underlined, but we avoid this pesky menu on right-click that feels out of place in our carefully crafted user interface.[1]

<div class="button">“Button”</div>
<u data-href="https://example.com/very-bad">“Link”</u>

Thanks to the data-href on the link we know where to go. And yes, this means a little JavaScript will be needed to actually go there. Most likely we anyhow have a Single Page Application, so we’ll stay put.

Now all that is left to be done is style our buttons and add color to our links.[2]

.button {
border: .125rem solid; /* etc. */
}
u {
color: light-dark(oklch(0.43 0.3 264.05), oklch(0.69 0.17 281.16));
}

All good? Sadly no, I’m sure the accessibility police will soon be all over the place.

Fine, let’s make our elements interactive. For brevity, I’m not gonna talk about all the JavaScript required, but rest assured to make things accessible we’ll need a metric fuckton of it.[3]

To fix the HTML, I asked artifical “intelligence”, and several prompts later I learned that we need to sprinkle some ARIA roles and tabindexes on top.

<div role="button" tabindex="0" class="button">“Button”</div>
<u role="link" tabindex="0" data-href="https://example.com/bad">“Link”</u>

We’re good? Again, no, this is where Forced Colors Mode comes in. No amount of ARIA turns our elements into their semantic counterparts. As such, they all will be displayed in plain CanvasText color.

Alright, let’s manually add those colors, but inside a media query, so they won’t leak into our regular theme.

@media (forced-colors: active) {
.button {
color: ButtonText;
background-color: ButtonFace;
}
u {
color: LinkText;
}
}

I was about to ask “Is this it?”, but I just realized we need a database to keep track of all the links that have already been clicked, so we can visually indicate them as .visited, ... — I’m gonna stop myself right there.

Good advice

Enough of the Sisyphean labor. Let’s do the following instead.

<button type="button">Button</button>
<a href="https://example.com/good">Link</a>

For styling, all you have to do is inherit the font for buttons, which is what your CSS reset might do anyhow for all form controls.

In Forced Colors Mode, things just work, buttons and links will be shown in proper CSS system colors. Semantics for the win![4]

While we eventually can make our fake elements look like the real deal even in Forced Colors Mode, we should avoid all that work and use proper elements instead.
The accompanying CodePen in “Night Sky” (top) and “Desert” (bottom) contrast theme.

So do we have an entry-level debugging tool? Well, unless someone goes all the way like we did in the last step of our bad example (highly unlikely!), in Forced Colors Mode you don’t need to be a developer to recognize real buttons and links. Unless the theme prevents it: Buttons are yellow and regular text is white in the “Night Sky” theme, but in “Desert” the difference is way too subtle. No need to stick to the defaults tough, you can create your own theme instead.

Full disclosure

I didn’t need to write this article, because common sense exists, right? Then again, there’s a reason why HTMHell.dev exists. (BTW, thanks for having me, it’s been an honor to be part of the 2024 Advent Calendar among all those talented people.)

Initially I wrote something else, but it ended up being too long for a calendar entry. The original article is now available on my blog. If you are up for actual Forced Colors Mode advice, may I suggest you read it, and then consider joining my movement to make December the least colorful time of the year.

Resources

Footnotes

  1. Some even argue underlined links go against an app-like feel, but let’s not go there. ↩︎
  2. Look at those CSS functions our Fancy Pants author uses to add color to a link. ↩︎
  3. And it’s very likely that we’ll still fail to do so. ↩︎
  4. Mmm 🤤, semantics! If you wanna dig deeper, here’s a CodePen with HTML elements (unstyled, and with CSS system colors for reference) for you to view in Forced Colors Mode. Bonus points if you explore the differences between browers. ↩︎

About Matthias Zöchling

Matthias is a CSS aficionado, and the Web Accessibility Advocate at George Labs, birthplace of the “George” online banking. He’s in pursuit of improving the accessibility for ten million customers across six countries, one day at a time. Additionally, he has been writing about web stuff on his own site for quite a while. 2024 has been his best blogging year so far, even though things have significantly slowed down after the summer, when his wife gave birth to their third child.

Website/Blog: CSSence.com
Mastodon & beyond: @CSSence