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:
- 1.4.11 Non-text Contrast — UI components and graphical objects must have 3:1 contrast against adjacent colors
- 1.4.3 Contrast (Minimum) — text must have 4.5:1 contrast (3:1 for large text)
Key requirements:
- Forced colors removes:
background-color,box-shadow,background-image,text-shadow - Forced colors preserves:
border,outline, textcolor, SVGcurrentColor,forced-color-adjust: none - Use
border: 2px solid transparenton interactive elements — invisible normally, visible in forced colors - Use
outlineinstead ofbox-shadowfor focus indicators - Use
currentColorfor 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 likeButtonText,Canvas,LinkText,Highlight - The
forced-color-adjust: noneproperty 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
<!-- 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
-->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.
<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
-->These buttons use transparent borders (visible in forced colors) and outline for focus. In forced colors mode, borders become visible using system colors.
What’s wrong with the bad example?
border: nonemeans there is no border at all — in forced colors mode, the buttons have no visible boundarybackground-coloris overridden by the system palette, so all three buttons look identical — the visual distinction between Primary, Secondary, and Delete is completely lostbox-shadowis 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 transparentis invisible in normal mode (transparent blends with the background)- In forced colors mode, the browser replaces
transparentwith 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:
| Version | Appearance 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 |