Creating a High-Contrast Design System with CSS Custom Properties

Build accessible, flexible color systems that respect user preferences and maintain brand integrity

Why High Contrast Matters for Everyone

Web accessibility isn't a trade-off between beautiful design and inclusive experiences. Modern CSS provides powerful tools--particularly CSS custom properties (variables)--that enable developers to create sophisticated high-contrast systems while maintaining brand integrity.

The Business Case for Accessibility

Accessibility benefits extend far beyond compliance. When we design with contrast in mind, we create better experiences for all users. Poor contrast affects users in bright sunlight, those with newer or older displays, users with temporary visual impairments, and approximately 300 million people with color blindness worldwide. Smashing Magazine's guide on accessible design systems

Key contrast requirements:

  • WCAG Level AA: 4.5:1 for normal text, 3:1 for large text
  • WCAG Level AAA: 7:1 for normal text, 4.5:1 for large text

The web platform has evolved significantly, with features like CSS custom properties and media queries making contrast management more manageable than ever before. Rather than viewing accessibility as a burden, we can approach it as an opportunity to create more robust, flexible design systems. The prefers-contrast media query detects when users have requested enhanced contrast through their operating system settings, while forced-colors mode enables support for Windows High Contrast and similar accessibility themes. Together with CSS custom properties, these tools allow you to swap entire color schemes with minimal code changes, making accessibility implementation efficient and maintainable.

Debunking Common Accessibility Myths

Myth 1: We Have to Create Two Designs Entirely

With a thoughtful design system, you don't need separate designs for standard and high-contrast modes. What you need is careful planning of your color architecture. Instead of designing each component in isolation, think about how to replace colors systematically across your entire component library.

The key approach: Create two color definitions from the start--your primary design colors and high-contrast variants.

Myth 2: It Takes Too Much Time

While adding high-contrast support requires some upfront planning, the returns on investment are substantial. Creating extra color definitions is a one-time effort, while the benefits accrue for every user who encounters your site.

Myth 3: Contrast Is Subjective

Contrast is actually a measurable, objective quality. If achieving AAA compliance across your entire site is challenging, create a prioritized plan--highest contrast for main content and navigation, with AA compliance for secondary information.

Myth 4: It Conflicts with Brand Identity

Brands with low-contrast logos can still maintain visual identity while improving accessibility. Consider creating alternative versions of brand elements for high-contrast contexts. This approach is more cost-effective than complete rebranding. Smashing Magazine's guide on accessible design systems

Building Your Color Architecture with CSS Custom Properties

Base Color System

Start by defining your base color palette using CSS custom properties in your :root:

:root {
 /* Primary brand colors */
 --color-primary: #07BEB8;
 --color-secondary: #8F3985;
 --color-accent: #98DFEA;

 /* Background and surface colors */
 --color-background: #EFD9CE;
 --color-surface: #FAFAFA;

 /* Text colors */
 --color-text-primary: #25283D;
 --color-text-secondary: #4A4A4A;

 /* Functional colors */
 --color-border: #CCCCCC;
 --color-focus: #0066CC;
}

This foundation enables systematic color replacement through media queries and provides a single source of truth for your color system. By leveraging CSS custom properties effectively, you can create maintainable and flexible design systems.

Creating High-Contrast Variants

Define high-contrast alternatives that maintain brand relationship while meeting accessibility standards:

:root {
 /* High-contrast variants */
 --color-primary-hc: #006666;
 --color-secondary-hc: #5C1A4C;
 --color-accent-hc: #0066CC;

 /* High-contrast backgrounds */
 --color-background-hc: #FFFFFF;
 --color-surface-hc: #FFFFFF;

 /* High-contrast text */
 --color-text-primary-hc: #000000;
 --color-text-secondary-hc: #333333;

 /* High-contrast functional colors */
 --color-border-hc: #000000;
 --color-focus-hc: #0000EE;
}

Key principle: High-contrast colors aren't arbitrary--they maintain logical relationships with their base counterparts while ensuring sufficient contrast ratios.

Using prefers-contrast and forced-colors Media Queries

Detecting User Preferences with prefers-contrast

The prefers-contrast media query detects when users have requested more or less contrast:

@media (prefers-contrast: more) {
 :root {
 --color-primary: var(--color-primary-hc);
 --color-secondary: var(--color-secondary-hc);
 --color-background: var(--color-background-hc);
 --color-text-primary: var(--color-text-primary-hc);
 /* Apply all high-contrast variants */
 }
}

This approach respects user preferences at the operating system level, providing automatic contrast enhancement.

Handling forced-colors Mode

The forced-colors media feature detects when users have enabled forced colors mode, such as Windows High Contrast mode. In this mode, browsers enforce a limited color palette for accessibility.

@media (forced-colors: active) {
 /* Box-shadow is forced to none, so ensure borders exist */
 button {
 border: 2px ButtonText solid;
 }

 /* Use system colors for integration */
 .primary-button {
 background-color: ButtonFace;
 color: ButtonText;
 border-color: ButtonText;
 }
}

Properties affected by forced colors mode: color, background-color, border-color, outline-color, text-decoration-color, column-rule-color, and more. MDN's forced-colors documentation

Fine-Grained Control with forced-color-adjust

Understanding forced-color-adjust Values

The forced-color-adjust property provides control over how individual elements respond to forced colors mode:

/* Allow author-defined colors to take precedence */
.element {
 forced-color-adjust: none;
}

/* Preserve parent element's color */
.child-element {
 forced-color-adjust: preserve-parent-color;
}

/* Default behavior - allow forced colors */
.default-element {
 forced-color-adjust: auto;
}

Use forced-color-adjust: none when you need precise control over an element's appearance in forced colors mode, such as for data visualizations where color relationships convey meaning.

System Colors Reference

System colors provide names for UI elements that automatically adapt to forced colors mode:

System ColorPurpose
Canvas / CanvasTextPage background and text
ButtonFace / ButtonTextButton backgrounds and text
LinkText / VisitedTextHyperlink colors
Field / FieldTextForm input backgrounds and text
Highlight / HighlightTextSelected content colors
@media (forced-colors: active) {
 .card {
 background-color: Canvas;
 color: CanvasText;
 border: 1px solid ButtonText;
 }

 .button {
 background-color: ButtonFace;
 color: ButtonText;
 border: 1px solid ButtonBorder;
 }
}

Practical Implementation Patterns

Component-Level Contrast Management

Apply contrast principles at the component level for maintainable systems:

/* Button component */
.button {
 background-color: var(--color-primary);
 color: white;
 border: 2px solid transparent;
 padding: 0.75rem 1.5rem;
 border-radius: 4px;
}

@media (prefers-contrast: more) {
 .button {
 --color-primary: var(--color-primary-hc);
 border-color: var(--color-text-primary-hc);
 }
}

@media (forced-colors: active) {
 .button {
 background-color: ButtonFace;
 color: ButtonText;
 border: 2px solid ButtonText;
 }
}

Typography and Readability

Ensure text maintains readability across all modes:

body {
 color: var(--color-text-primary);
 background-color: var(--color-background);
 line-height: 1.6;
}

@media (prefers-contrast: more) {
 body {
 color: var(--color-text-primary-hc);
 background-color: var(--color-background-hc);
 }
}

/* Ensure links are always distinguishable */
a {
 text-decoration: underline;
 text-decoration-thickness: 2px;
 text-underline-offset: 2px;
}

Form Elements and Interactive Controls

Interactive elements require special attention for contrast compliance:

input {
 background-color: var(--color-surface);
 border: 2px solid var(--color-border);
 color: var(--color-text-primary);
 padding: 0.75rem;
}

input:focus {
 outline: 3px solid var(--color-focus);
 outline-offset: 2px;
}

@media (forced-colors: active) {
 input {
 background-color: Field;
 border-color: ButtonText;
 color: FieldText;
 }
}

Testing Your High-Contrast Implementation

Browser Developer Tools

Modern browsers provide tools for testing contrast and forced colors modes:

BrowserTesting Approach
Chrome DevToolsUse the Rendering tab to simulate forced colors
FirefoxUse about:config to test preference-based contrast
EdgeHigh Contrast mode testing via system settings

Automated Testing Approaches

Combine manual testing with automated tools for comprehensive coverage:

  • Use axe-core or Lighthouse for accessibility auditing
  • Create regression tests that check contrast ratios
  • Include forced-colors scenarios in your component test suite

User Testing Considerations

Test with actual users who rely on assistive technologies:

  • Screen reader users
  • Users with various types of color blindness
  • Users who depend on operating system accessibility settings

Tip: Start small--test one component at a time to build confidence in your approach.

Best Practices Summary

  1. Plan Ahead: Define both standard and high-contrast color variants from the start
  2. Use Custom Properties: Centralize color definitions for systematic updates
  3. Layer Media Queries: Support both prefers-contrast and forced-colors modes
  4. Test Comprehensively: Use browser tools and real-user testing
  5. Maintain Brand Identity: Create high-contrast alternatives that respect visual brand
  6. Use System Colors: Leverage system color keywords in forced-colors contexts
  7. Prioritize Content: Focus highest contrast on critical information and navigation

Remember: Building an accessible design system is not about choosing between aesthetics and inclusivity--it's about leveraging modern CSS to achieve both. Every user deserves a clear, readable experience.

For related topics on web development best practices, explore our guides on CSS custom properties and responsive design and building accessible user interfaces.

Frequently Asked Questions

Ready to Build Accessible Web Experiences?

Our team specializes in creating inclusive, high-performance web applications that serve all users effectively.