High Contrast Mode and SVG Icons: Ensuring Visibility

Learn how to optimize SVG icons for Windows High Contrast Mode (Forced Colors) using CSS media queries and the currentColor attribute.

Amit Yadav
Amit Yadav

Millions of users with visual impairments rely on high-contrast themes provided by their operating system. The most prominent example is Windows High Contrast Mode (WHCM).

When a user enables this mode, the browser forcibly overrides your website’s CSS. Backgrounds turn pitch black (or glaring white), text turns bright yellow, and links turn neon cyan.

If your SVG icons are hardcoded with specific fill or stroke hex values, they will likely become completely invisible against these forced backgrounds. Here is how to fix it.

Understanding the forced-colors Media Query

Modern browsers expose Windows High Contrast Mode (and similar OS-level themes) to CSS via the forced-colors: active media query.

When forced-colors is active, the browser overrides your colors using a specific set of system color keywords:

  • Canvas (Background)
  • CanvasText (Main text)
  • LinkText (Links)
  • ButtonFace (Button backgrounds)
  • ButtonText (Button text)

The Golden Rule: Use currentColor

The easiest way to ensure your SVGs survive high contrast mode is to never hardcode colors in the SVG markup.

<!-- Bad: Hardcoded fill will disappear if the Canvas is forced to #6B7280 -->
<svg fill="#6B7280">...</svg>

<!-- Good: Inherits the color from the surrounding text -->
<svg fill="currentColor">...</svg>

If you use fill="currentColor", and the user’s OS forces all text (CanvasText) to be bright yellow, your SVG icon will automatically turn bright yellow as well.

Fixing Complex, Multi-Color SVGs

If you have a complex illustration or a multi-color icon (e.g., a “Warning” icon with a yellow triangle and a black exclamation mark), currentColor alone won’t save you.

When WHCM overrides the background to black, the black exclamation mark will disappear.

You must manually map the SVG paths to system colors using the @media (forced-colors: active) query.

<svg class="warning-icon" viewBox="0 0 24 24">
  <path class="warning-bg" fill="#FBBF24" d="..." /> <!-- Yellow Triangle -->
  <path class="warning-mark" fill="#000000" d="..." /> <!-- Black Exclamation -->
</svg>
/* Standard styling */
.warning-bg { fill: #FBBF24; }
.warning-mark { fill: #000000; }

/* High Contrast Overrides */
@media (forced-colors: active) {
  .warning-icon {
    /* Ensures the icon respects the active system color */
    forced-color-adjust: auto; 
  }
  
  .warning-bg {
    /* Use the system text color for the outline/background */
    fill: CanvasText; 
  }
  
  .warning-mark {
    /* Use the system background color to "cut out" the exclamation mark */
    fill: Canvas; 
  }
}

The forced-color-adjust Property

Sometimes, you want an element to keep its original colors, even in High Contrast Mode (for example, a color swatch picker). You can opt out of the browser’s overrides using forced-color-adjust: none.

Use Sparingly

Only use forced-color-adjust: none if forcing a system color would destroy the fundamental meaning of the graphic. For UI icons, you should almost never use it.

Testing High Contrast Mode

You don’t need a Windows machine to test this. In Google Chrome / Edge:

  1. Open DevTools.
  2. Press Cmd + Shift + P (Mac) or Ctrl + Shift + P (Windows) to open the Command Menu.
  3. Type Emulate CSS forced-colors: active and press Enter.

Your site will instantly strip its colors, revealing exactly which SVGs have hardcoded values that disappear into the background.

Summary

To make SVGs accessible to users with low vision:

  1. Strip hardcoded fill and stroke attributes.
  2. Rely heavily on currentColor so icons adapt to system-enforced text colors.
  3. Use the @media (forced-colors: active) query and system color keywords (CanvasText, LinkText) to fix multi-color SVGs.
Share this post