Skip to Content
Component ExamplesForced Colors

High Contrast / Forced Colors Mode

Designing for Windows High Contrast Mode and the CSS forced-colors media query

Maintained by Martin KrugerLast reviewed

Overview

Windows High Contrast Mode (now called Contrast Themes) overrides author-defined colors with a limited system palette. CSS exposes this via the forced-colors: active media query. In forced colors mode, the browser strips most decorative styling — backgrounds, box-shadows, and background images disappear. What survives: text color, borders, outlines, and SVG currentColor. Designs that rely solely on background color for visual meaning become invisible rectangles. The key defensive pattern is the transparent border trick: borders that are invisible in normal mode but become visible in forced colors.

WCAG Criteria:

Key requirements:

  • Forced colors removes: background-color, box-shadow, background-image, text-shadow
  • Forced colors preserves: border, outline, text color, SVG currentColor, forced-color-adjust: none
  • Use border: 2px solid transparent on interactive elements — invisible normally, visible in forced colors
  • Use outline instead of box-shadow for focus indicators
  • Use currentColor for SVG icons so they adapt to the system color scheme
  • Test with the @media (forced-colors: active) query to add specific overrides using CSS system colors like ButtonText, Canvas, LinkText, Highlight
  • The forced-color-adjust: none property opts an element out of forced colors — use sparingly and only when you provide adequate contrast yourself

Forced Colors Compatibility

Forced Colors Safe Buttons vs. Background-Only Buttons

Inaccessible

These buttons rely on background-color for distinction and box-shadow for focus. In forced colors mode, they become borderless, indistinguishable rectangles with no focus ring.

View inaccessible code
<!-- Relies on background-color and box-shadow only --> <button style="background: #2563eb; color: white; border: none; border-radius: 6px;" onfocus="this.style.boxShadow = '0 0 0 3px rgba(37,99,235,0.5)';" onblur="this.style.boxShadow = 'none';"> Primary </button> <button style="background: #64748b; color: white; border: none; border-radius: 6px;" onfocus="this.style.boxShadow = '0 0 0 3px rgba(100,116,139,0.5)';" onblur="this.style.boxShadow = 'none';"> Secondary </button> <button style="background: #dc2626; color: white; border: none; border-radius: 6px;" onfocus="this.style.boxShadow = '0 0 0 3px rgba(220,38,38,0.5)';" onblur="this.style.boxShadow = 'none';"> Delete </button> <!-- In forced colors mode: - background-color is overridden → buttons lose visual distinction - box-shadow is removed → no focus indicator - border: none → no visible boundary at all -->
Accessible

These buttons use transparent borders (visible in forced colors) and outline for focus. In forced colors mode, borders become visible using system colors.

View accessible code
<style> .btn:focus-visible { outline: 3px solid #005fcc; outline-offset: 2px; } @media (forced-colors: active) { .btn { border-color: ButtonText; } .btn:focus-visible { outline-color: Highlight; } .btn.danger { border-color: LinkText; } } </style> <!-- Transparent border: invisible normally, visible in forced colors --> <button class="btn" style="background: #2563eb; color: white; border: 2px solid transparent; border-radius: 6px;"> Primary </button> <button class="btn" style="background: #64748b; color: white; border: 2px solid transparent; border-radius: 6px;"> Secondary </button> <button class="btn danger" style="background: #dc2626; color: white; border: 2px solid transparent; border-radius: 6px;"> Delete </button> <!-- In forced colors mode: - transparent borders become visible with system colors - outline focus ring is preserved - CSS system colors (ButtonText, Highlight, LinkText) adapt to the user's chosen contrast theme -->

What’s wrong with the bad example?

  • border: none means there is no border at all — in forced colors mode, the buttons have no visible boundary
  • background-color is overridden by the system palette, so all three buttons look identical — the visual distinction between Primary, Secondary, and Delete is completely lost
  • box-shadow is stripped in forced colors mode, so the focus indicator disappears entirely
  • Keyboard users in high contrast mode cannot tell which button has focus

The transparent border trick:

  • border: 2px solid transparent is invisible in normal mode (transparent blends with the background)
  • In forced colors mode, the browser replaces transparent with the system text color, making the border visible
  • This gives every button a clear visual boundary without changing the normal appearance
  • The @media (forced-colors: active) block can further customize border colors using CSS system colors

What users see in forced colors mode:

VersionAppearance in High Contrast
Bad (border: none, box-shadow focus)Borderless text floating on the system background, no focus ring
Good (transparent border, outline focus)Clear bordered buttons with system-color focus ring

Resources