# How the contrast-ratio Rule Checks WCAG Compliance in DESIGN.md

> Learn how the contrast-ratio rule in DESIGN.md ensures WCAG AA compliance by checking text and background color luminance ratios against the 4.5:1 threshold.

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

---

**The `contrast-ratio` rule enforces WCAG AA compliance by calculating the luminance ratio between every component's `backgroundColor` and `textColor`, flagging any pair that falls below the 4.5:1 minimum threshold.**

The `contrast-ratio` rule in the DESIGN.md CLI linter ensures design systems meet accessibility standards by automatically validating color contrast ratios. According to the google-labs-code/design.md source code, this rule inspects resolved design tokens and applies the WCAG 2.1 luminance formula to guarantee text remains readable against its background.

## Rule Architecture and Entry Point

The rule is exported from [`packages/cli/src/linter/linter/rules/contrast-ratio.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/contrast-ratio.ts) as **`contrastCheckRule`**. The `run` function (lines 54‑58) serves as the entry point, delegating execution to the `contrastCheck` function.

This architecture separates the rule declaration from the validation logic, allowing the linter engine to register the rule while the implementation handles the specific WCAG compliance checks.

## Processing Components and Resolving Colors

The `contrastCheck` function iterates through every component defined in the resolved `DesignSystemState` (`state.components`). For each component, it extracts the **`backgroundColor`** and **`textColor`** properties (lines 27‑30).

If either property is missing, the component is skipped. Otherwise, the raw design-token values are converted into **`ResolvedColor`** objects using the `resolveToColor` helper (lines 47‑51). This resolution step ensures that token references like `{colors.brand}` are evaluated to their actual hex or RGB values before contrast calculation begins.

## WCAG Contrast Ratio Calculation

The actual mathematical validation 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 standard WCAG 2.1 luminance contrast algorithm:

```typescript
const L1 = Math.max(a.luminance, b.luminance);
const L2 = Math.min(a.luminance, b.luminance);
return (L1 + 0.05) / (L2 + 0.05);

```

(See handler.ts lines 100‑106.)

This formula calculates the relative luminance of both colors, then divides the lighter value by the darker one (after adding 0.05 to each to avoid division by zero). The result represents the contrast ratio between the two colors.

## Threshold Enforcement and Violation Reporting

The computed ratio is compared against the constant **`WCAG_AA_MINIMUM = 4.5`** (defined in [`contrast-ratio.ts`](https://github.com/google-labs-code/design.md/blob/main/contrast-ratio.ts) line 19). When a ratio falls below this threshold, the rule generates a warning `RuleFinding` (lines 36‑42).

Each warning includes:
- The hex values of both colors
- The actual calculated ratio (rounded to two decimal places)
- The required 4.5:1 minimum

The function returns the complete list of findings, which the linter reports as warnings for any non-compliant component.

## Practical Implementation Example

The following example demonstrates how the rule validates a design system definition:

```typescript
import { resolveDesign } from '@design-md/cli';
import { lint } from '@design-md/cli';

// Example design snippet (YAML)
const design = `
colors:
  brand: '#0044cc'
  onBrand: '#ffffff'

components:
  button:
    backgroundColor: '{colors.brand}'
    textColor: '{colors.onBrand}'
`;

const state = resolveDesign(design);
const findings = lint(state, { rules: ['contrast-ratio'] });

if (findings.length) {
  console.log('Contrast issues found:');
  findings.forEach(f => console.log(f.message));
}

```

Running this code prints a warning only when the contrast ratio is less than 4.5:1, ensuring the design system maintains WCAG AA compliance before deployment.

## Summary

- The `contrast-ratio` rule lives in [`packages/cli/src/linter/linter/rules/contrast-ratio.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/contrast-ratio.ts) and exports `contrastCheckRule`.
- It validates every component's `backgroundColor`/`textColor` pair against the WCAG AA minimum of 4.5:1.
- Colors are resolved to `ResolvedColor` objects using `resolveToColor` before calculation.
- The `contrastRatio` function in [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts) implements the WCAG 2.1 luminance formula: `(L1 + 0.05) / (L2 + 0.05)`.
- Violations generate detailed warnings containing actual and required contrast values.

## Frequently Asked Questions

### What WCAG standard does the contrast-ratio rule enforce?

The rule enforces **WCAG 2.1 Level AA** compliance, specifically the minimum contrast ratio requirement of 4.5:1 for normal text. This threshold is defined as the constant `WCAG_AA_MINIMUM` in the source code.

### How does the rule handle design tokens that reference other variables?

The rule uses the `resolveToColor` helper function to resolve token references (like `{colors.brand}`) to their actual color values before calculating contrast. This ensures the validation works against the final resolved color values rather than the raw token strings.

### What happens when a component has only one color defined?

If a component is missing either `backgroundColor` or `textColor`, the rule skips that component entirely (lines 27‑30). The contrast check only runs when both color properties are present in the component definition.

### Where is the contrast calculation formula implemented?

The mathematical implementation resides 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) within the `contrastRatio` function (lines 100‑106). This function calculates relative luminance using the standard WCAG formula `(L1 + 0.05) / (L2 + 0.05)` and is imported by the contrast-ratio rule for validation.