# How the WCAG Contrast Ratio Checker Calculates Compliance in Design.md

> Learn how the WCAG contrast ratio checker calculates compliance by comparing text and background luminance. Discover the formula used and WCAG AA minimums.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: how-to-guide
- Published: 2026-06-28

---

**The WCAG contrast ratio checker calculates compliance by comparing the relative luminance of text and background colors using the formula `(L1 + 0.05) / (L2 + 0.05)`, automatically flagging any component with a ratio below the 4.5:1 WCAG AA minimum.**

The contrast ratio checker is a built-in lint rule in the **google-labs-code/design.md** repository that enforces accessibility standards directly within your design system workflow. It operates as part of the Design.md CLI, parsing component definitions and validating color combinations against WCAG 2.1 guidelines. This ensures that text remains readable against its background before code ever reaches production.

## How the Contrast Ratio Calculation Works

The checker follows a three-stage pipeline: resolving color tokens to luminance values, applying the WCAG contrast formula, and validating against the AA threshold.

### Color Resolution and Luminance Pre-computation

First, the CLI parses the [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md) file into a `DesignSystemState`. Each component's color properties—specifically `backgroundColor` and `textColor`—are resolved to `ResolvedColor` objects. These objects contain a pre-computed **luminance** value, a linearized lightness metric where 0 represents pure black and 1 represents pure white.

### The WCAG Contrast Formula Implementation

The core calculation happens in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts). The `contrastRatio` function implements the official WCAG 2.1 algorithm:

```typescript
// https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts#L400-L406
export function contrastRatio(a: ResolvedColor, b: ResolvedColor): number {
  const L1 = Math.max(a.luminance, b.luminance);
  const L2 = Math.min(a.luminance, b.luminance);
  return (L1 + 0.05) / (L2 + 0.05);
}

```

This formula divides the lighter color's luminance plus 0.05 by the darker color's luminance plus 0.05, producing a ratio where 1:1 indicates identical colors and 21:1 indicates black-on-white.

### Compliance Validation Against WCAG AA Standards

The `contrastCheck` rule in [`packages/cli/src/linter/rules/contrast-ratio.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/rules/contrast-ratio.ts) iterates through every component and compares the result against the **WCAG_AA_MINIMUM** constant of **4.5**:

```typescript
// https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/rules/contrast-ratio.ts#L19-L44
const WCAG_AA_MINIMUM = 4.5;

export function contrastCheck(state: DesignSystemState): RuleFinding[] {
  const findings: RuleFinding[] = [];
  for (const [compName, comp] of state.components) {
    const bgValue = comp.properties.get('backgroundColor');
    const textValue = comp.properties.get('textColor');
    if (!bgValue || !textValue) continue;

    const bgColor = resolveToColor(bgValue);
    const textColor = resolveToColor(textValue);
    if (!bgColor || !textColor) continue;

    const ratio = contrastRatio(bgColor, textColor);
    if (ratio < WCAG_AA_MINIMUM) {
      findings.push({
        path: `components.${compName}`,
        message: `textColor (${textColor.hex}) on backgroundColor (${bgColor.hex}) has contrast ratio ${ratio.toFixed(2)}:1, below WCAG AA minimum of ${WCAG_AA_MINIMUM}:1.`,
      });
    }
  }
  return findings;
}

```

When the ratio falls below 4.5:1, the rule generates a `RuleFinding` warning that includes the specific hex values and computed ratio.

## Running the Contrast Checker via CLI

Execute the lint command to validate your design system:

```bash

# Lint a DESIGN.md file and see contrast warnings

$ design-md lint path/to/DESIGN.md --format=text

```

Sample output shows detailed failure messages:

```json
{
  "findings": [
    {
      "path": "components.Button",
      "message": "textColor (#777777) on backgroundColor (#FFFFFF) has contrast ratio 3.98:1, below WCAG AA minimum of 4.5:1."
    }
  ],
  "summary": { "warnings": 1, "errors": 0 }
}

```

## Programmatic Usage of the Contrast Function

You can reuse the calculation in custom tooling:

```typescript
import { contrastRatio } from '@design-md/cli';
import { resolveColor } from '@design-md/cli/model';

const bg = resolveColor('#ffffff');    // luminance ≈ 1.0
const txt = resolveColor('#777777');  // luminance ≈ 0.42

const ratio = contrastRatio(bg, txt);
console.log(`Contrast ratio: ${ratio.toFixed(2)}:1`); // → 3.98:1

```

## Summary

- The **WCAG contrast ratio checker** operates as a lint rule within the Design.md CLI, validating accessibility at the design system level.
- It calculates ratios using the standard formula `(L1 + 0.05) / (L2 + 0.05)` implemented in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts).
- The **4.5:1 minimum threshold** for WCAG AA compliance is enforced by the `contrastCheck` rule in [`packages/cli/src/linter/rules/contrast-ratio.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/rules/contrast-ratio.ts).
- Color resolution happens automatically, handling token indirection and aliases through the `ResolvedColor` type.

## Frequently Asked Questions

### What WCAG conformance level does the checker enforce?

The checker enforces **WCAG 2.1 Level AA** compliance, which requires a minimum contrast ratio of **4.5:1** for normal text. This threshold is hardcoded as the `WCAG_AA_MINIMUM` constant in the contrast ratio rule.

### Does the checker support color tokens or only hex values?

The checker supports **color tokens and aliases** through automatic resolution. When processing a [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md) file, the linter resolves all token references to `ResolvedColor` objects containing computed luminance values, ensuring that indirect color definitions are still validated against WCAG standards.

### How does the linter handle missing background or text colors?

If a component lacks either `backgroundColor` or `textColor` properties, or if these properties cannot be resolved to valid colors, the `contrastCheck` rule skips that component silently using `continue` statements. Only components with both valid colors receive contrast validation.

### Can I use the contrast calculation in my own lint rules?

Yes. The `contrastRatio` function is exported from [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts) and accepts two `ResolvedColor` objects. You can import it to build custom accessibility rules that check color combinations beyond the standard text-background pairs.