WHCM and System Colors

There is a ton of prior content discussing Windows High Contrast Mode (WHCM) and web content. The catch is that content covers four (five?) different implementations across more than a decade of support: Internet Explorer, Edge, the other Edge, Edge plus IE aliasing, and hardly anything on Firefox. Which means much of it no longer applies.

The best overview of the current situation is from Microsoft: Styling for Windows high contrast with new standards for forced colors, posted mid-September 2020. As the standards process moves forward and Edge continues to update, expect this overview to be updated (or point to updates).

That post covers the basics of the feature (I do not). Because I want you to try my examples, this is how to activate WHCM:

Please remember that High Contrast Mode is used by some to produce low contrast screens.

The typical contrast of the operating system or the web can be uncomfortable for many people. The most common use case might be someone prone to migraines. High Contrast Mode allows them to create a low contrast palette to help mitigate that.

Low Contrast theme High Contrast #1 theme High Contrast Black theme High Contrast White theme
Four variations on WHCM themes. The low contrast theme (first in this set) is not pre-configured; I created it to reflect what I have seen in use.

Remember as you go down this path that your role as the developer is not to increase the contrast of everything on the screen, but to honor the user settings and system color preferences.

As you read this, I want your primary takeaway to be that you likely will need to do little for standard document pages. If you have some novel custom widgets, then please add and use styles with keywords that correspond to function. If you need to retain some coloring for important contextual reasons, then look at the overrides.

Feature Queries

Since Microsoft introduced WHCM, Internet Explorer had good reason to support a feature query to detect it and allow developers to tweak and override how the styles are applied.

First I want to qualify that if you are using standard HTML, you should not need these. Plain text and native controls adapt just fine. The problems come when you start to introduce custom widgets, ARIA properties, necessary visual flourishes, and so on.

You should only ever use these feature queries to spackle over gaps (found in most libraries / frameworks) or enhance an experience that may be lacking.

Proprietary, or Internet Explorer Only

In Internet Explorer you can use a proprietary feature query to detect if WHCM is active, along with another to detect if the WHCM setting is light on dark, or dark on light. I advise you to only check if it is active (the first from the following set).

@media screen and (-ms-high-contrast: active) {
  /* put your custom styles here */
}
@media screen and (-ms-high-contrast: black-on-white) {
  /* put your custom styles here */
}
@media screen and (-ms-high-contrast: white-on-black) {
  /* put your custom styles here */
}

I identify the six system colors available to you below in the WHCM Proprietary Feature Query Color Mappings section. However, you shouldn’t be too worried about those unless you are fixing broken custom controls. I encourage you to read Greg Whitworth’s overview of how to use -ms-high-contrast well.

Instead you can use this feature query to tweak things like spacing or even disable some unwanted features via -ms-high-contrast-adjust:

@media screen and (-ms-high-contrast: active) {
  selector {
    -ms-high-contrast-adjust: none;
  }
}

If you are still somehow supporting Legacy Edge (Ledgacy), these feature queries and custom properties apply there as well.

Standards Track, or Edge Only

To disambiguate, when I reference Edge here I am talking about ChromiEdge, or the version of Edge built on Chromium. As of this writing, Legacy Edge is nearly gone in the wild and will be wiped from hold-out computers in non-locked-down environments in April 2021.

Sometime in the future I hope to remove the “or Edge Only” from that heading. I hope that the work Edge has done in Chromium gets slurped up to the Chrome engine, copied into Firefox, and somehow maybe even replicated in Safari with a similar macOS-level contrast setting.

Until then, the proposed forced colors mode feature of the CSS Color Adjustment Module Level 1 Working Draft has just the one browser supporting it.

In this case the syntax is easier since either the forced colors mode is on or not.

@media screen and (forced-colors: active) {
  /* put your custom styles here */
}

The following properties are affected:

I was glad to see scrollbar in there given the resurgence of folks making terribly styled scrollbars

As with the proprietary feature, you also have the option to opt out of the color overrides. This is handy in cases where the colors are important to convey something without changing, such as paint, lipstick, or underpants. In those scenarios you would use forced-color-adjust:

@media screen and (forced-colors: active) {
  selector {
    forced-color-adjust: none;
  }
}

Frankenquery’s Monster

Frankenquery is the name of the scientist. Its monster is this:

@media screen and (-ms-high-contrast: active),
  screen and (forced-colors: active) {
    selector {
      -ms-high-contrast-adjust: none;
      forced-color-adjust: none;
    }
}

You could use this to target all the supporting browsers in one block. But be certain your system color keywords work across them all or that you override in the correct order. You may also want more targeted styles if you are trying to replicate native WHCM colors with your custom controls, considering native elements are a bit different between IE11 and Edge.

System Colors

Pulling from system colors means you will generally be leaning on what the user prefers, or at least what they are used to. CSS 2 introduced system font keywords in 1998 (which I wrote about in 2015 when Apple did its own thing), so the notion of using the operating system as a source is not new.

CSS2 System Color Keywords

CSS2 defined 28 keywords which were later carried into CSS Color Module Level 3 System Color Keywords (and then deprecated). From the names, you can guess on the effort to replicate the 3D effects of Microsoft Windows.

In the context of my anecdote, when Windows High Contrast Mode was introduced, using the system color keywords meant no extra work was needed because we had already mapped all the system colors to their native HTML equivalents. Even with the reduced set supported in WHCM, no extra work was needed on our part.

  1. ActiveBorder
  2. ActiveCaption
  3. AppWorkspace
  4. Background
  5. ButtonFace
  6. ButtonHighlight
  7. ButtonShadow
  8. ButtonText
  9. CaptionText
  10. GrayText
  11. Highlight
  12. HighlightText
  13. InactiveBorder
  14. InactiveCaption
  15. InactiveCaptionText
  16. InfoBackground
  17. InfoText
  18. Menu
  19. MenuText
  20. Scrollbar
  21. ThreeDDarkShadow
  22. ThreeDFace
  23. ThreeDHighlight
  24. ThreeDLightShadow
  25. ThreeDShadow
  26. Window
  27. WindowFrame
  28. WindowText

WHCM Proprietary Feature Query Color Mappings

Internet Explorer recognizes a reduced set of these keywords in WHCM. Hyperlinks use the proprietary -ms-hotlight. Handy if using the proprietary feature query in Internet Explorer

  1. ButtonFace
  2. ButtonText
  3. Highlight
  4. HighlightText
  5. Window
  6. WindowText
  7. GrayText
  8. -ms-hotlight

CSS4 System Color Keywords

CSS Color Module Level 4 introduced a different set of system color keywords, dropping the count to 19 (updated 13 July 2022, it was 15 when I first wrote this post) and eliminating all the 3D detail.

This reduced set of keywords comes with a warning to ensure you pair background and foreground sets for better contrast.

  1. Canvas
  2. CanvasText
  3. LinkText
  4. VisitedText
  5. ActiveText
  6. ButtonFace
  7. ButtonText
  8. ButtonBorder
  9. Field
  10. FieldText
  11. Highlight
  12. HighlightText
  13. Mark
  14. MarkText
  15. GrayText
  16. SelectedItem
  17. SelectedItemText
  18. AccentColor
  19. AccentColorText

While this list may be longer than the set supported in Internet Explorer, bear in mind the colors themselves may be duplicates. For example LinkText and VisitedText use the same color when WHCM is active (which was decided based on user feedback).

Browser Support

Unsurprisingly, each browser is going to have its own take on how best to support WHCM. With a proprietary start that is now moving down the standards track, support will continue to evolve.

Internet Explorer

Internet Explorer was the first to support the proprietary feature query. With some IE11 still in the wild, and given its historic support for WHCM, you may want to double up on your feature query and be careful of relying on background images.

Legacy Edge (Ledgacy)

The original version of Edge carried over the support from Internet Explorer with some adjustments. For example, in a later release it retained background images but added a backplate behind any text set over an image. With the browser nearly gone and Microsoft set to wipe it from hold-out computers in non-locked-down environments in April 2021, you can probably ignore it.

Chromium Edge (Chromiedge)

Microsoft has been building the standards-track support into Edge, and it is working. It also appears Edge still honors the proprietary media query, so you will want to be careful with the cascade when you use both.

Firefox

Firefox supports WHCM, but not the media queries and the media query. Be certain to close and re-open the browser after you enable WHCM, otherwise Firefox does not pick up on it.

8 June 2021: Firefox 89, released May 31, supports the forced-colors media query (thanks to Nicolas in the comments for letting me know). There is more detail in the post Looking fine with Firefox 89 at Mozilla Hacks. Note that the images in this post use Firefox 85, before the feature query support existed.

Firefox does not support forced-color-adjust, at least not through version 100. This will be a problem if you are counting on overriding how WHCM is applied.

Chrome

Nope. In fact, there is a bug where if you fail to set the page background, Chrome will use the WHCM window color which is often black. If your text is black (or not set), then your entire page is black.

See the Pen WHCM and Chrome Bug by Adrian Roselli (@aardrian) on CodePen.

Before and after in Chrome showing black on black text then black on white text.
Chrome 88. The left part of the image shows the default display. Activating the button adds a background color to the page, causing Chrome to ignore the assignment from WHCM and working around the bug.

Chrome 89

Chrome 89 supports High Contrast Mode. I can find no release notes that state this, but the change introduced in Edge 79 has seemingly made it into Chrome as of 89. The Chrome 89 beta release notes discuss the addition of forced-colors detection and forced-color-adjust support (Thanks to Šime Vidas for finding that) as well as the Chrome platform status notes.

The test page in Chrome 89 showing it supports the system color scheme. The version screen in Chrome showing it is version 89 and is also honoring system colors.
Chrome 89 demonstrating its support for WHCM.

Examples

I have taken a bunch of the stuff I covered and made some demos. I screen shat (shat is the past tense of shot) them in Firefox, IE11, and Edge. From experience, most WHCM users are not Firefox users, but they are still out there and rely on it working.

Backgrounds

Background images and gradients are treated slightly differently across the three browsers. Firefox sets a text backplate when there is a background image and dumps background gradients; it ignores any feature query overrides. IE11 sets no backplate for either, and dumps both background images and gradients unless you override it. Edge sets a text backplate on a background image, but dumps background gradients, overriding both if you do so in the feature query.

The following embedded pen shows the code, but you can test the debug version of the backgrounds pen.

Side-by-side view of the differences in background display.
Firefox 85, Internet Explorer 11, and Edge 88.

See the Pen WHCM and Backgrounds by Adrian Roselli (@aardrian) on CodePen.

Inline SVGs

Inline SVGs grant you the ability to use CSS at the page level to manipulate their colors. While you can write feature queries into the SVG directly, when calling one inline you may want to handle it in your main stylesheet. In this example I have one SVG that uses currentColor, so it should always inherit from the text. Two more have no colors defined at all, so they default to black, but for one of them I use the WHCM feature queries to set it to use GrayText. The last one has its own colors defined throughout, so is not at risk and needs no overrides.

With Firefox and IE11, currentColor is your friend (along with explicit colors). IE11 honors the feature query (GrayText was just to show it in action). Edge takes it a step further and brings in the text color when no color is defined. Test it yourself in the debug version of this inline SVG pen.

Side-by-side view of the differences in SVG display.
Firefox 85, Internet Explorer 11, and Edge 88.

See the Pen WHCM and SVGs by Adrian Roselli (@aardrian) on CodePen.

Added 9 November 2021: Melanie Richards gives some tips on papering over a (hopefully) short-term disparity between Edge and a CSS specification change:

The proper way to deal with this problem in the short term is to explicitly apply system colors to the SVG parts in forced colors modes. In this case, we would use the LinkText system color:

SVGs via <img>s

If you have an SVG called via <img> that generally follows a line-art style (icons, for example) then those are most at risk of disappearing completely if their color matches the WHCM window color. Granted, this is true with line-art transparent PNGs, but at least with Edge you can mitigate the effect.

In this example I use filter: drop-shadow() in an effort to create an outline, albeit a faint one, on the first SVG. This is not an ideal solution. The third SVG file has a feature query to override its fill from currentColor to GrayText, and both IE11 and Edge honor it. Give the debug version of SVG <img> pen a test on your own.

Side-by-side view of the differences in SVG display.
Firefox 85, Internet Explorer 11, and Edge 88. IE11 does not recognize the filter property.

See the Pen WHCM and SVG <img>s by Adrian Roselli (@aardrian) on CodePen.

Form Fields

Form fields will also take on a different look, but default form fields will do so in a predictable way. Because WHCM users will expect different field types in different states to look a certain way, it behooves you to know the defaults so you can mimic them in your own work — whether restoring styles you overrode or making a custom control look native.

The set of images below capture disabled, required, and error states. Notice how the placeholder field looks the same as the disabled field in IE11. Or how similar they are in Edge. Or the lack of an error style in Edge. Anyway, test the debug version of this forms pen as you see fit.

Side-by-side view of the differences in native form fields.
Firefox 85, Internet Explorer 11, and Edge 88.
Side-by-side view of the differences in native field error messages.
The default error message from each browser. Firefox 85, Internet Explorer 11, and Edge 88.
Side-by-side view of the differences in native field error states.
The default error state from each browser; note Edge does not distinguish the errored field. Firefox 85, Internet Explorer 11, and Edge 88.

See the Pen WHCM and Form Controls by Adrian Roselli (@aardrian) on CodePen.

Custom Controls

Sadly, it is not uncommon for (terrible) developers to role-up a <div> into a button. While that is rightly offensive, there are also plenty of controls that are not native to HTML, such as the oft misapplied tab. In each of the cases you want to look to the native (HTML or platform) implementation. Try to ensure custom controls give the appropriate visual cues in WHCM after all the fancy gradients and drop shadows and smells are stripped away.

In this pen I compare the styles of a native <button> to a <div>, and offer WHCM styles (proprietary and standards track) to try to reset them to native styles. I even included hover styles appropriate to the browser. You can, of course, test these fake buttons in the debug version of the pen.

Side-by-side view of the differences in native and custom buttons.
Firefox 85, Internet Explorer 11, and Edge 88.

See the Pen WHCM and Custom Buttons by Adrian Roselli (@aardrian) on CodePen.

Collected Examples

I gathered a few samples from above and made screenshots of them in Edge under each of the four themes I showed at the start of this post. Hopefully this reinforces the variety in display, and how using smart defaults will make your job easier. I left the Windows taskbar in there for context.

Demonstrating Low Contrast theme Demonstrating High Contrast #1 theme Demonstrating High Contrast Black theme Demonstrating High Contrast White theme
Examples under each of the themes Low Contrast (custom), High Contrast #1, High Contrast Black, and High Contrast White.

Wrap-up

Your job for WHCM users is to respect their color choices and use them appropriately. Do not override the feature because you may find something ugly. Ugly and usable trumps pretty and unusable every time.

To recap the tips:

References

Windows 11 HCM (5 December 2021)

Windows 11 is out and has updated this feature, both in name and color themes. Now rebranded as “Contrast Themes”, but still found under accessibility features, it better represents that not all themes are necessarily high contrast.

The feature query, how system colors are applied via CSS, and ability to customize them has not changed.

Since Windows 11 is still somewhat new and I know many are not upgrading to it yet, I have gathered screen shots of the four default themes. They show the configuration screen, the CSS system colors, and 4 of the samples from this post.

Demonstrating Aquatic theme. Demonstrating Desert theme. Demonstrating Dusk theme. Demonstrating Night Sky theme.
Examples under each of the themes Aquatic, Desert, Dusk, and Night Sky
Aquatic CSS system colors. Aquatic theme configuration screen.
The Aquatic theme.
Desert CSS system colors. Desert theme configuration screen.
The Desert theme.
Dusk CSS system colors. Dusk theme configuration screen.
The Dusk theme.
Night Sky CSS system colors. Night Sky theme configuration screen.
The Night Sky theme.

Bear in mind these themes are not as stark in default color choices (except maybe Night Sky) as in previous versions of Windows. I suspect some users may end up in them without understanding the implications for their experience across the web. As a developer, this may be more reason to avoid unintentionally creating a problematic experience for these users.

Emulation in Edge on macOS (8 March 2022)

As of Edge 98 on macOS you can emulate CSS forced colors mode, which so far only exists on Windows. In the dev tools, go to … → More Tools → Rendering. Then scroll down.

The limitation is that it only offers one color scheme. You cannot choose a low contrast scheme or set up one of the new Windows 11 contrast themes.

The CSS 4 system colors part of the post. The emulated forced colors view of the CSS 4 system colors part of the post.
A before and after in Edge 99 on macOS showing how the CSS4 system color keyboards render with the Edge forced colors emulation.
Background images and gradients with text. SVG display. SVG display when called via image element. Native form fields. Native and custom buttons.
The examples from this post as seen with the macOS Edge 99 forced colors emulation.

Emulation Thoughts (22 March 2022)

Melanie Richards wrote First Impressions of Forced Colors Emulation where she also hopes more themes will become available (for similar reasons as I have noted elsewhere).

I also added a link to her post in the References section above.

CSS4 Updates (13 July 2022)

The CSS Color Module Level 4 has been updated since I first wrote this post, adding 4 colors to the system color keywords. I updated the swatches in this post with the new ones.

WCAG Note, added 7 June 2023

I should have added this years ago since this post is my go-to reference for WHCM or whatever variation you are calling it.

WCAG question 623: Do issues with Windows high contrast mode fall under WCAG 2.1? from February 2019 says no, WHCM is not covered by WCAG:

The AG group appreciates that having customisations like High-Contrast-Mode (HCM) work properly are vital to many users.

However, WCAG 2.x does not explicitly address HCM. There are some failure techniques today such as failure techniques for pseudo content and CSS Background images as well as defining both foreground and background colors.

The place for more specification would be on the user-agent side of the equation, at least for the moment. Any criteria created for HCM could become rapidly outdated, possibly harmful if the user-agents change their behaviour.

If there were sufficient interest (i.e. volunteers) for creating a note or WAI-guidance page for authors, that is something we could tackle. It is also something that AG members could raise with vendors (e.g. Microsoft) to work with them to get more aspects of HCM publicly specified.

You can see the responses to the working group survey for additional feedback and thoughts.

The GitHub issue is still open since they might add a note about it to an Understanding document. Might.

4 Comments

Reply

From now on, people are undoubtedly going to think something’s wrong with me every time I hear someone use the term “screenshot” when I start laughing uncontrollably. I hope you’re happy.

Great post though, will share far and wide.

In response to James C. Reply

I am just glad I could maybe coin a new term. Thanks!

Reply

It seems that Firefox is now supporting the media query: https://www.mozilla.org/en-US/firefox/89.0/releasenotes/ (See “Web Platform” at the bottom of the release notes)

In response to Nicolas. Reply

Post updated. Thanks!

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>