How Accessibility Themes Are Contrast-Checked in Ontology Playground: A WCAG 2.1 Implementation Guide

Microsoft Ontology Playground enforces WCAG 2.1 Level AA compliance by extracting CSS custom properties from src/styles/app.css, computing precise contrast ratios using WCAG-standard algorithms, and running automated tests that assert all theme token pairs meet minimum thresholds of 4.5:1 for text and 3:1 for non-text graphics.

The Ontology Playground repository by Microsoft implements a rigorous accessibility verification pipeline that ensures every visual theme meets strict color contrast requirements. By combining CSS token extraction with automated WCAG 2.1 calculations, the codebase prevents low-contrast themes from reaching production through comprehensive test coverage.

Extracting Theme Tokens from Source Stylesheets

The contrast verification process begins by reading actual CSS custom properties directly from the source stylesheet. In src/a11y/themeTokens.ts, the getThemeTokens() function parses src/styles/app.css to build a mapping of token names to computed values for each supported theme.

The function handles theme cascading by merging selectors in specificity order, starting with :root and progressing through theme-specific classes like .light-theme. This approach ensures the test suite evaluates the exact same CSS values that render in the browser, not hardcoded copies that could drift out of sync.

// src/a11y/themeTokens.ts
export function getThemeTokens(theme: ThemeId): Record<string, string> {
  const css = readCss();                     // reads src/styles/app.css once
  const merged: Record<string, string> = {};
  for (const selector of THEME_SELECTORS[theme]) {
    Object.assign(merged, extractVars(css, selector));
  }
  return merged;
}

Calculating WCAG 2.1 Contrast Ratios

The mathematical foundation lives in src/a11y/contrast.ts, which implements the exact WCAG 2.1 specifications for color contrast. The module exports four critical functions: parseColor() for hex/rgba parsing, composite() for alpha blending, relativeLuminance() for linear luminance calculation, and contrastRatio() for the final comparison.

When calculating ratios, the system first validates that background colors are opaque, then composites any translucent foregrounds over their backgrounds before computing luminance values. The algorithm follows the standard WCAG formula: (L1 + 0.05) / (L2 + 0.05), where L1 and L2 represent the relative luminance of the lighter and darker colors respectively.

// src/a11y/contrast.ts
export function contrastRatio(foreground: string, background: string): number {
  const bg = parseColor(background);
  if (bg.a < 1) {
    throw new Error(`Background color must be opaque …`);
  }
  const fg = parseColor(foreground);
  const fgOpaque: RGB = fg.a < 1 ? composite(fg, bg) : fg;
  const l1 = relativeLuminance(fgOpaque);
  const l2 = relativeLuminance(bg);
  return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
}

Automated Contrast Enforcement via Testing

The enforcement mechanism resides in src/a11y/themeContrast.test.ts, which defines the contract between design tokens and accessibility standards. The test suite iterates over all four themes—dark, light, aurora, and crimson—and evaluates every required foreground and background token pair against WCAG thresholds.

Normal text must achieve a 4.5:1 contrast ratio, while non-text graphics (UI components, icons) require 3:1. The test framework dynamically generates assertions for each combination, causing immediate build failures if any theme modification introduces insufficient contrast.

// src/a11y/themeContrast.test.ts
describe('WCAG 2.1 AA theme contrast (SC 1.4.3 / 1.4.11)', () => {
  for (const theme of THEMES) {
    const tokens = getThemeTokens(theme);
    describe(theme, () => {
      for (const pair of PAIRS) {
        const minimum = pair.kind === 'text' ? AA_NORMAL_TEXT : AA_NON_TEXT;
        it(`${pair.name} meets ${minimum}:1`, () => {
          const fg = tokens[pair.fg];
          const bg = tokens[pair.bg];
          const ratio = contrastRatio(fg, bg);
          expect(ratio).toBeGreaterThanOrEqual(minimum);
        });
      }
    });
  }
});

Handling Translucent Colors and Special Components

Beyond basic text and background pairs, the validation pipeline addresses complex UI scenarios involving transparency and special color treatments.

Stat-card tiles undergo verification that their composited backgrounds (translucent tints layered over sidebar surfaces) maintain the 3:1 non-text threshold. The amber foreground token (--ms-yellow-fg) receives explicit testing to ensure 4.5:1 contrast across all surfaces, including warning tile backgrounds. For progress bars, both gradient stops (--progress-from and --progress-to) are checked against the track color (--bg-tertiary) to guarantee visibility regardless of fill level.

Practical Implementation Example

Developers can programmatically verify custom token combinations using the exported utilities:

import { getThemeTokens } from './a11y/themeTokens';
import { contrastRatio, AA_NORMAL_TEXT } from './a11y/contrast';

// Example: Check primary text on the app background for the "light" theme
const tokens = getThemeTokens('light');
const fg = tokens['--text-primary'];   // e.g. "#212121"
const bg = tokens['--bg-primary'];     // e.g. "#ffffff"

const ratio = contrastRatio(fg, bg);
console.log(`Contrast ratio: ${ratio.toFixed(2)}:1`);
if (ratio >= AA_NORMAL_TEXT) {
  console.log('✅ Meets WCAG AA for normal text');
} else {
  console.warn('⚠️ Does NOT meet the required contrast');
}

Summary

  • Source-driven validation: The getThemeTokens() function extracts live CSS custom properties from src/styles/app.css, ensuring tests always evaluate current stylesheet values.
  • WCAG 2.1 Level AA compliance: The contrast.ts module implements standard algorithms for luminance calculation and alpha compositing, enforcing 4.5:1 ratios for text and 3:1 for non-text elements.
  • Comprehensive theme coverage: Automated tests in themeContrast.test.ts validate four distinct themes (dark, light, aurora, crimson) across all token pairs.
  • Special case handling: The suite accounts for translucent layers in stat-cards, high-visibility requirements for amber foregrounds, and multi-stop gradients in progress bars.

Frequently Asked Questions

What WCAG conformance level does Ontology Playground target?

The codebase targets WCAG 2.1 Level AA according to Success Criteria 1.4.3 (Contrast Minimum) and 1.4.11 (Non-text Contrast). This requires normal text to achieve a minimum contrast ratio of 4.5:1 against its background, while large text and non-text user interface components must meet a 3:1 ratio.

How does the contrast checker handle semi-transparent colors?

The contrastRatio() function in src/a11y/contrast.ts rejects translucent backgrounds and composites translucent foregrounds over their opaque backgrounds before calculation. This ensures the final ratio reflects the actual rendered appearance rather than the raw alpha values, preventing false positives on partially transparent UI elements.

Which themes are validated by the automated test suite?

The test suite validates four distinct themes: dark, light, aurora, and crimson. Each theme undergoes identical token extraction and contrast validation processes, ensuring consistent accessibility standards regardless of the user's selected color preference.

What happens if a theme fails the contrast check?

If themeContrast.test.ts detects any token pair with a contrast ratio below the WCAG threshold, the test assertion fails and prevents the build from passing. This integration into the continuous integration pipeline ensures that insufficient contrast ratios block deployment, maintaining accessibility compliance by default.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →