# How Diagram-Design Ensures WCAG AA Contrast Compliance During Brand Onboarding

> Diagram-design ensures WCAG AA contrast compliance by validating ink on paper and muted on paper colors during brand onboarding, then re-verifies with a post-build self-check.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: best-practices
- Published: 2026-09-06

---

**Diagram‑design enforces accessibility by validating that `ink` on `paper` and `muted` on `paper` meet a 4.5:1 contrast ratio before writing any tokens, then re-verifies every generated diagram with a post-build self‑check.**

The `cathrynlavery/diagram-design` repository implements a multi-layered defense against contrast violations. When you onboard a new brand, the skill automatically extracts color values, maps them to semantic roles, and gates the entire pipeline on WCAG AA compliance—preventing inaccessible diagrams from ever reaching production.

---

## The Brand Onboarding Pipeline

The onboarding flow in [`skills/diagram-design/references/onboarding.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/onboarding.md) orchestrates five sequential stages that guarantee WCAG AA contrast compliance.

### 1. Color Extraction

The skill fetches the target website or reads an existing skill folder to capture the brand's visual identity.

```python

# Extraction phase: pull dominant colors and font stacks

background = extract_background(url)      # → maps to 'paper'

primary_text = extract_primary_text(url)  # → maps to 'ink'

secondary_text = extract_secondary_text(url)  # → maps to 'muted'

accent = extract_accent_color(url)        # → maps to 'accent'

```

This extraction occurs at lines 70-74 of the onboarding specification, capturing the raw values needed for semantic mapping.

### 2. Semantic Role Mapping

Extracted colors are assigned to **semantic tokens** that define how the diagram system uses them:

| Semantic Role | Typical Source | Usage |
|-------------|--------------|-------|
| `paper` | Page background | Diagram canvas |
| `ink` | Primary text | Headings, borders, main content |
| `muted` | Secondary text | Captions, labels, metadata |
| `accent` | Brand highlight | CTAs, focus states |

This mapping is defined at lines 103-113 of [`onboarding.md`](https://github.com/cathrynlavery/diagram-design/blob/main/onboarding.md), establishing a consistent vocabulary across all generated diagrams.

### 3. WCAG AA Contrast Validation

Before any token is persisted, the skill enforces two hard constraints from [`style-guide.md`](https://github.com/cathrynlavery/diagram-design/blob/main/style-guide.md) (lines 152-154):

- **`ink` on `paper`**: Must achieve ≥ 4.5:1 contrast
- **`muted` on `paper`**: Must achieve ≥ 4.5:1 for body text ≥ 11 px

```python
from colours import contrast_ratio  # internal helper

def check_contrast(ink, paper):
    """
    Returns True if the contrast ratio meets WCAG AA (≥4.5:1).
    """
    return contrast_ratio(ink, paper) >= 4.5

# Validation call site (onboarding.md lines 22-26)

assert check_contrast(ink_value, paper_value), "WCAG AA violation: ink on paper"
assert check_contrast(muted_value, paper_value), "WCAG AA violation: muted on paper"

```

Failure at either assertion blocks token generation.

### 4. Automatic Adjustment with User Approval

When validation fails, the onboarding flow does not reject the brand outright—it **proposes an adjusted value** and explains the mathematical rationale (lines 121-125).

```bash
$ diagram-design:onboard https://example.com

⚠  Contrast check failed: ink #8B7355 on paper #F5F5DC = 3.2:1 (below 4.5:1)
→ Proposed adjustment: darken ink to #5C4033 (contrast 7.1:1)

[style-guide.md]  (diff view)
-  --ink: #8B7355;
+  --ink: #5C4033;

Approve this change? [y/N/diff/edit]

```

The user must explicitly approve the diff before [`style-guide.md`](https://github.com/cathrynlavery/diagram-design/blob/main/style-guide.md) is written. This human-in-the-loop design preserves brand intent while guaranteeing accessibility.

### 5. Post-Generation Self-Check

After tokens are persisted and diagrams are generated, the pipeline runs [`scripts/self_check.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/self_check.py) as a final defensive gate (lines 30-34):

```bash
python3 skills/diagram-design/scripts/self_check.py my-diagram.html

# Output: OK — or build abort with contrast violation details

```

Any contrast violation at this stage triggers a build failure, ensuring that **no diagram can be emitted with insufficient contrast** regardless of upstream assumptions.

---

## File Architecture for Contrast Compliance

| File Path | Responsibility |
|-----------|---------------|
| [`skills/diagram-design/references/onboarding.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/onboarding.md) | Defines the five-stage pipeline including extraction, mapping, and validation |
| [`skills/diagram-design/references/style-guide.md`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/style-guide.md) | Codifies WCAG AA constraints (≥4.5:1) for `ink` and `muted` roles |
| [`skills/diagram-design/scripts/self_check.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/self_check.py) | Runtime re-verification on generated diagram HTML |
| [`README.md`](https://github.com/cathrynlavery/diagram-design/blob/main/README.md) | Documents contrast-check behavior for end users |

---

## End-to-End Verification Workflow

```bash

# 1. Onboard a new brand with automatic WCAG AA checks

/diagram-design:onboard https://example.com

# 2. Review proposed adjustments (if any) and approve

# 3. Generate diagrams using approved tokens

diagram-design build --brand example --output ./diagrams/

# 4. Verify final output passes the contrast gate

python3 skills/diagram-design/scripts/self_check.py ./diagrams/*.html

```

---

## Summary

- **Proactive enforcement**: WCAG AA validation occurs during onboarding, before any tokens are written
- **Automatic remediation**: Failed checks trigger color adjustments with user-controlled diff approval
- **Defensive verification**: [`self_check.py`](https://github.com/cathrynlavery/diagram-design/blob/main/self_check.py) re-validates every generated diagram, aborting builds on violation
- **Dual constraints**: Both `ink` on `paper` and `muted` on `paper` must meet 4.5:1 contrast
- **Semantic abstraction**: Raw colors are mapped to roles (`paper`, `ink`, `muted`, `accent`) ensuring consistent application

---

## Frequently Asked Questions

### What contrast ratio does diagram-design enforce for WCAG AA compliance?

The skill enforces a **minimum 4.5:1 contrast ratio** for normal body text, as defined in [`style-guide.md`](https://github.com/cathrynlavery/diagram-design/blob/main/style-guide.md). This applies to both `ink` on `paper` and `muted` on `paper` combinations. The 4.5:1 threshold is the WCAG AA requirement for text below 18 pt (or 14 pt bold).

### Can I override the automatic color adjustments during onboarding?

Yes. When the skill proposes an adjusted value, you can choose to **approve**, **view the diff**, **edit manually**, or **reject**. The onboarding flow will not write [`style-guide.md`](https://github.com/cathrynlavery/diagram-design/blob/main/style-guide.md) until you explicitly confirm the final values. This preserves your ability to maintain brand identity while meeting accessibility requirements.

### What happens if a generated diagram fails the self_check.py verification?

The build **aborts with a non-zero exit code** and reports the specific contrast violation. This prevents any non-compliant diagram from reaching production. You must resolve the issue—typically by adjusting the source tokens in [`style-guide.md`](https://github.com/cathrynlavery/diagram-design/blob/main/style-guide.md) and regenerating.

### Does diagram-design support WCAG AAA contrast requirements?

The current implementation targets **WCAG AA (4.5:1)** as the baseline. The architecture supports stricter thresholds: you can modify the `contrast_ratio` assertion in [`onboarding.md`](https://github.com/cathrynlavery/diagram-design/blob/main/onboarding.md) and [`self_check.py`](https://github.com/cathrynlavery/diagram-design/blob/main/self_check.py) to enforce 7:1 for WCAG AAA, though this is not the default configuration in the repository.