Skip to content
Back to skills

Color Contrast Auditor

ASecurity

Detects and fixes color contrast violations using WCAG 2.1 guidelines and perceptual analysis. Expert in contrast ratio calculation, color blindness simulation, and providing accessible alternatives. Activate on "check contrast", "color accessibility", "WCAG audit", "readability check", "contrast ratio", "hard to read", "can't see text". NOT for general color theory (use color-theory-palette-harmony-expert), brand color selection (use web-design-expert), or non-visual accessibility (use ux-fr...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
toolsjavascriptpythongojavabashnodetestingapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 10 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add curiositech/port-daddy --skill color-contrast-auditor --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Color Contrast Auditor?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Color Contrast Auditor
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/curiositech-color-contrast-auditor-port-daddy/badge)](https://www.skillsdirectory.com/skills/curiositech-color-contrast-auditor-port-daddy)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
license: Apache-2.0
name: color-contrast-auditor
description: Detects and fixes color contrast violations using WCAG 2.1 guidelines and perceptual analysis. Expert in contrast ratio calculation, color blindness simulation, and providing accessible alternatives. Activate on "check contrast", "color accessibility", "WCAG audit", "readability check", "contrast ratio", "hard to read", "can't see text". NOT for general color theory (use color-theory-palette-harmony-expert), brand color selection (use web-design-expert), or non-visual accessibility (use ux-friction-analyzer).
allowed-tools: Read,Write,Edit,Bash,WebFetch,Glob,Grep
metadata:
  category: Design & Creative
  tags:
    - color-contrast
    - accessibility
    - wcag
    - audit
    - a11y
  provenance:
    kind: first-party
    owners:
      - port-daddy
  pairs-with:
    - skill: beautiful-gui-design
      reason: Implement the accessible color fixes this audit recommends inside an actual interface design.
    - skill: web-design-expert
      reason: Choose brand/palette colors up front; this skill verifies the resulting pairs actually meet WCAG contrast floors.
    - skill: ux-friction-analyzer
      reason: Places a contrast failure in the broader context of a user's cognitive and visual friction, not just the ratio.
  io-contract:
    kind: deliverable
    consumes:
      - kind: color-pair-spec
        format: json
      - kind: design-brief
        format: markdown
    produces:
      - kind: contrast-audit-report
        format: json
      - kind: contrast-audit-report
        format: markdown
---
# Color Contrast Auditor

Detects color contrast violations that make text unreadable and provides WCAG-compliant fixes. Uses both mathematical contrast ratio analysis and perceptual evaluation via vision capabilities.

## When to Use

**Activate on:**
- Screenshots of websites/apps with suspected contrast issues
- CSS/Tailwind files for color audit
- "I can't read this" or "this is hard to see"
- Pre-launch accessibility checks
- Design system color validation

**NOT for:**
- Choosing brand colors (use `web-design-expert`)
- Color harmony/aesthetics (use `color-theory-palette-harmony-expert`)
- Non-visual accessibility (screen readers, keyboard nav)

---

## WCAG 2.1 Contrast Requirements

### Minimum Ratios (AA - Required)

| Text Type | Minimum Ratio | Example |
|-----------|---------------|---------|
| **Normal text** (<24px, <18.66px bold) | **4.5:1** | Body copy, labels, buttons |
| **Large text** (≥24px or ≥18.66px bold) | **3:1** | Headlines, hero text |
| **UI components** (borders, icons) | **3:1** | Form inputs, icons, focus rings |
| **Graphical objects** | **3:1** | Charts, infographics |

### Enhanced Ratios (AAA - Recommended)

| Text Type | Minimum Ratio |
|-----------|---------------|
| Normal text | **7:1** |
| Large text | **4.5:1** |

### Non-Text Elements

| Element | Requirement |
|---------|-------------|
| Focus indicators | 3:1 against adjacent colors |
| Form field borders | 3:1 against background |
| Icons conveying meaning | 3:1 against background |
| Disabled elements | No requirement (but consider UX) |

---

## Contrast Ratio Formula

```
Contrast Ratio = (L1 + 0.05) / (L2 + 0.05)

Where L1 = lighter color's relative luminance
      L2 = darker color's relative luminance
```

### Calculating Relative Luminance

```javascript
function relativeLuminance(r, g, b) {
  // Convert 0-255 to 0-1
  let [rs, gs, bs] = [r, g, b].map(c => c / 255);

  // Apply gamma correction
  const gamma = c => c <= 0.03928
    ? c / 12.92
    : Math.pow((c + 0.055) / 1.055, 2.4);

  const [R, G, B] = [rs, gs, bs].map(gamma);

  // Weighted sum (human eye sensitivity)
  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}

function contrastRatio(color1, color2) {
  const l1 = relativeLuminance(...color1);
  const l2 = relativeLuminance(...color2);
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);
  return (lighter + 0.05) / (darker + 0.05);
}
```

---

## Common Failing Patterns

### 1. Light Text on Light Background

```
❌ FAILING EXAMPLE (from user screenshot):

┌─────────────────────────────────────────────┐
│  Background: #F5F2E8 (beige/cream)          │
│  Text: #C8FF00 (lime green)                 │
│                                             │
│     "Scan. Crawl. Match. Report."           │
│     ← UNREADABLE                            │
│                                             │
│  Calculated Ratio: ~1.5:1                   │
│  Required: 4.5:1 (normal) or 3:1 (large)    │
│  Verdict: FAIL by 3x                        │
└─────────────────────────────────────────────┘

✅ FIXED OPTIONS:
   • Darken text to #5A7300 → Ratio: 4.5:1
   • Darken background to #2A2A2A → Ratio: 12:1
   • Use dark green #1A4D00 → Ratio: 8:1
```

### 2. Gray Text Syndrome

```
❌ COMMON FAILURE:
   Background: #FFFFFF
   Text: #AAAAAA (light gray)
   Ratio: 2.3:1 ← FAIL

✅ FIXES:
   • Text: #767676 → Ratio: 4.5:1 (minimum AA)
   • Text: #595959 → Ratio: 7:1 (AAA)
```

### 3. Saturated Colors That Look Bright

```
❌ DECEPTIVE FAILURE:
   Background: #FFF8E7 (warm white)
   Text: #FF6B6B (coral/salmon)
   Ratio: 2.8:1 ← FAIL (looks "colorful" but fails)

✅ FIXES:
   • Text: #C62828 (darker red) → Ratio: 5.2:1
   • Text: #8B0000 (dark red) → Ratio: 8.1:1
```

### 4. Trendy Low-Contrast Aesthetic

```
❌ "MINIMALIST" FAILURE:
   Background: #FAFAFA
   Text: #E0E0E0
   Ratio: 1.3:1 ← SEVERELY FAILING

This is NOT minimalism. This is inaccessible.

✅ MINIMALIST + ACCESSIBLE:
   Background: #FAFAFA
   Text: #616161 → Ratio: 5.7:1
```

### 5. Placeholder Text Too Light

```
❌ COMMON FORM FAILURE:
   Input background: #FFFFFF
   Placeholder: #CCCCCC
   Ratio: 1.6:1 ← FAIL

✅ FIX:
   Placeholder: #757575 → Ratio: 4.6:1
```

### 6. Gradient Backgrounds

```
❌ VARIABLE CONTRAST:
   Gradient: #FFFFFF → #000080
   Text: #FFFFFF (fixed)

   Top of gradient: 1:1 (invisible!)
   Bottom of gradient: 8.6:1 (good)

✅ SOLUTIONS:
   • Add text shadow/outline
   • Use semi-transparent overlay behind text
   • Ensure ALL gradient stops pass contrast
```

---

## Audit Methodology

### Step 1: Visual Scan (Screenshot Analysis)

When given a screenshot, identify:

1. **Text elements by size:**
   - Headlines (large text → 3:1 required)
   - Body copy (normal text → 4.5:1 required)
   - UI labels (buttons, links → 4.5:1)
   - Captions/fine print (4.5:1 required)

2. **Interactive elements:**
   - Button borders/backgrounds
   - Form field borders
   - Focus states
   - Icons with meaning

3. **Red flags to look for:**
   - Light text on light backgrounds
   - Gray text on white
   - Colored text on colored backgrounds
   - Text over images without overlay

### Step 2: Extract Colors

From CSS/code:
```bash
# Find all color declarations
grep -E "(color:|background:|#[0-9a-fA-F]{3,8}|rgb|hsl)" styles.css
```

From Tailwind:
```bash
# Find text/bg color classes
grep -E "(text-|bg-)" *.tsx *.jsx
```

### Step 3: Calculate Ratios

For each text/background pair:
1. Convert colors to RGB
2. Calculate relative luminance
3. Compute contrast ratio
4. Compare to WCAG requirement

### Step 4: Generate Report

```markdown
# Contrast Audit Report

## Summary
- Total color pairs tested: X
- Passing (AA): Y
- Failing: Z
- Critical failures (&lt;2:1): N

## Failures by Severity

### Critical (Ratio &lt; 2:1)
| Location | Foreground | Background | Ratio | Required | Fix |
|----------|------------|------------|-------|----------|-----|
| Hero tagline | #C8FF00 | #F5F2E8 | 1.5:1 | 3:1 | #5A7300 |

### Moderate (Ratio 2:1 - 3:1)
...

### Minor (Ratio 3:1 - 4.5:1, normal text only)
...

## Recommended Fixes
[Specific color replacements with new ratios]
```

---

## Color Blindness Considerations

Contrast requirements help but don't fully address color blindness. Additional checks:

### Types to Consider

| Type | Affected | Consideration |
|------|----------|---------------|
| **Deuteranopia** | 6% of males | Red/green confusion |
| **Protanopia** | 2% of males | Red appears dark |
| **Tritanopia** | &lt;1% | Blue/yellow confusion |

### Best Practices

1. **Never rely on color alone** for meaning
   - Add icons, patterns, or text labels
   - Red/green for error/success needs icons too

2. **Test problematic pairs:**
   - Red + Green (stop/go)
   - Blue + Purple
   - Green + Brown
   - Light green + Yellow

3. **Use sufficient lightness difference**
   - Even with same hue, different lightness helps

---

## Quick Reference: Safe Color Pairs

### On White (#FFFFFF)

| Use Case | Color | Hex | Ratio |
|----------|-------|-----|-------|
| Body text | Dark gray | #333333 | 12.6:1 |
| Secondary text | Medium gray | #767676 | 4.5:1 |
| Links | Blue | #0066CC | 5.3:1 |
| Success | Green | #2E7D32 | 5.1:1 |
| Error | Red | #C62828 | 6.0:1 |
| Warning | Orange-brown | #E65100 | 4.5:1 |

### On Black (#000000)

| Use Case | Color | Hex | Ratio |
|----------|-------|-----|-------|
| Body text | Light gray | #E0E0E0 | 13.4:1 |
| Secondary | Medium gray | #9E9E9E | 6.3:1 |
| Accent | Light blue | #90CAF9 | 7.3:1 |

### On Dark Gray (#1A1A1A)

| Use Case | Color | Hex | Ratio |
|----------|-------|-----|-------|
| Body text | Off-white | #F5F5F5 | 14.1:1 |
| Secondary | Light gray | #BDBDBD | 8.3:1 |

---

## Tools & Validation

### Online Checkers
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
- [Coolors Contrast Checker](https://coolors.co/contrast-checker)
- [Adobe Color Accessibility](https://color.adobe.com/create/color-accessibility)

### Browser DevTools
```javascript
// Chrome DevTools: Elements → Styles → hover color swatch
// Shows contrast ratio automatically

// Firefox: Accessibility Inspector
// Shows color contrast issues
```

### Automated Testing
```javascript
// axe-core (popular a11y testing library)
const axe = require('axe-core');
axe.run(document, { rules: ['color-contrast'] });

// Lighthouse (built into Chrome)
// Performance → Accessibility → Color contrast
```

### CLI Tools
```bash
# Lighthouse CLI
npx lighthouse https://example.com --only-categories=accessibility

# Pa11y
npx pa11y https://example.com
```

---

## Integration with This Skill

When analyzing a screenshot or codebase:

1. **I will identify** all text/background color pairs
2. **I will calculate** contrast ratios for each
3. **I will flag** anything below WCAG AA thresholds
4. **I will suggest** specific hex values that pass
5. **I will provide** before/after comparisons

For the example screenshot (lime on beige):
```
AUDIT RESULT: CRITICAL FAILURE

Element: Hero tagline "Scan. Crawl. Match. Report."
Foreground: ~#C8FF00 (lime green)
Background: ~#F5F2E8 (beige)
Calculated Ratio: ~1.5:1
Required (large text): 3:1
Required (normal text): 4.5:1
Status: ❌ FAILS BY 2-3x

RECOMMENDED FIXES:
1. Darken text to #5A7300 (olive) → 4.8:1 ✓
2. Darken text to #3D5C00 (dark olive) → 7.1:1 ✓✓
3. Keep lime, darken BG to #3D3D3D → 8.2:1 ✓✓
4. Use #1B5E20 (dark green) → 8.4:1 ✓✓
```

---

## Checklist for New Designs

Before shipping:

- [ ] All body text has ≥4.5:1 contrast
- [ ] All large text has ≥3:1 contrast
- [ ] All form borders have ≥3:1 contrast
- [ ] All icons conveying meaning have ≥3:1 contrast
- [ ] Placeholder text is readable (≥4.5:1)
- [ ] Focus states are clearly visible (≥3:1)
- [ ] Links are distinguishable without color alone
- [ ] Error/success states use icons, not just color
- [ ] Tested with color blindness simulators
- [ ] Automated accessibility scan passes

---

**Philosophy:** Beautiful design and accessibility are not mutually exclusive. High contrast can be striking, dramatic, and intentional. Low contrast isn't "minimalist"—it's exclusionary. Every unreadable word is a user lost.

---

## Deterministic Audit

Everything above is judgment and reference material for a human-in-the-loop review. `scripts/contrast_audit.mjs` is the machine-checkable half: given a JSON list of foreground/background pairs (and, optionally, which pieces of meaning are conveyed by color alone), it computes the **real** WCAG relative-luminance contrast ratio for every pair — never an eyeballed or self-reported one — and fails closed.

```bash
node scripts/contrast_audit.mjs --input <contrast-spec>.json
```

Input matches `schemas/contrast-spec.schema.json`: a `pairs[]` array of `{ name, foreground, background, usage }` (hex colors; `usage` is one of `body-text`, `large-text`, `ui-component`, `decorative`), plus an optional `semanticSignals[]` of `{ name, conveyedByColorOnly }`.

Output is `{ pass, score, findings, recommendations }`. Findings:

| id | severity | Fires when |
| --- | --- | --- |
| `contrast-below-threshold` | critical | A non-decorative pair's computed ratio is below its required floor (4.5:1 body-text, 3:1 large-text/ui-component). Names the pair and states the real computed ratio, not an estimate. |
| `invalid-color` | critical | A declared `foreground`/`background` isn't a parseable `#RGB`/`#RRGGBB` hex — fails closed rather than assuming an unreadable value is fine. |
| `color-only-signal` | high | A declared semantic signal (`conveyedByColorOnly: true`) has no icon/text/pattern backup — WCAG 1.4.1, independent of any contrast ratio. |

`decorative` pairs are exempt from the ratio check (but still validated for parseable hex). `pass = !hasCritical && score >= 75`; a single `contrast-below-threshold` or `invalid-color` finding always fails the audit regardless of score.

See `examples/sample-input.json` (all pairs pass) and `examples/expected-output.md` (a `#777777`-on-`#FFFFFF` body-text pair that *looks* fine but computes to 4.48:1 — just under the 4.5:1 floor — audited, fixed, and re-audited to `pass:true`).

## Output Contract

A completed contrast audit carries:

- `pass` / `score`: whether every non-decorative pair clears its WCAG floor and no signal relies on color alone.
- `findings`: one entry per violation, each naming the specific pair or signal, its computed ratio (for contrast findings), and the required threshold.
- `recommendations`: a concrete fix per finding — a specific alternative hex from `references/safe-color-pairs.md`, or the non-color indicator to add.
- A written report (see `templates/output-template.md`) that a designer or reviewer can act on without re-deriving the math.

## Anti-Patterns

### Eyeballing a "Close Enough" Gray

**Novice**: Sees `#777777` body text on white, judges it "basically mid-gray, should be fine," and ships without computing the ratio.
**Expert**: Always computes the real WCAG relative-luminance ratio before judging a pair — `#777777` on `#FFFFFF` is 4.48:1, just under the 4.5:1 body-text floor, close enough to fool the eye but not close enough to pass.
**Detection**: `contrast_audit.mjs` returns `contrast-below-threshold` (critical) whenever a `body-text`, `large-text`, or `ui-component` pair's computed ratio is below its required threshold, quoting the exact computed value.

### Trendy Low-Contrast Aesthetic

**Novice**: Ships `#E0E0E0` text on `#FAFAFA` background and calls it "minimalist," treating the near-invisibility as an intentional design choice.
**Expert**: Low contrast isn't minimalism, it's exclusion — enforces the same WCAG floor on a "quiet" design as on a loud one, using `references/safe-color-pairs.md` for pre-verified alternatives that keep the restrained palette.
**Detection**: Same `contrast-below-threshold` finding; the scorer does not special-case a pair because it was labeled intentional or on-brand.

### Color as the Only Signal

**Novice**: Marks a form field's error state with a red border and nothing else, or a status dot that's "green means good, red means bad" with no label or icon.
**Expert**: Declares every such signal explicitly and backs it with a non-color indicator (icon, text, pattern) so meaning survives color blindness and grayscale rendering — contrast alone doesn't fix a color-only-signal defect.
**Detection**: `contrast_audit.mjs` returns `color-only-signal` (high) whenever a `semanticSignals[]` entry has `conveyedByColorOnly: true`, independent of whether the color itself has good contrast.

## References

| File | Load When |
| --- | --- |
| `references/safe-color-pairs.md` | Need a pre-verified, ready-to-use color combination instead of guessing a fix by hand. |
| `schemas/contrast-spec.schema.json` | Need to validate a contrast-spec JSON payload's structure before auditing it. |
| `examples/sample-input.json` | Need a minimal all-passing spec to see the expected input shape. |
| `examples/expected-output.md` | Need to see a spec with a subtle near-miss ratio, an invalid hex, and a color-only signal audited, then fixed and re-audited to `pass:true`. |
| `templates/output-template.md` | Need a reusable audit-report template to fill in for a design review. |
| `scripts/contrast_audit.mjs` | Need deterministic, real-math scoring of a set of color pairs. |
| `agents/openai.yaml` | Need a subagent descriptor for delegated contrast auditing. |

## Layout QA gate (mechanical — run before shipping)

Before calling any rendered page, artifact, dashboard, deck, or component done,
run the mechanical overflow/collision checker. It renders the page headlessly and
flags text-vs-text collisions, clipped/ellipsis-truncated elements, text escaping
its container, and horizontal page scroll — the visual defects a screenshot hides
and that only appear at a specific width or in one theme.

Resolve `layout-overflow-guard` from the active skill catalog before running it.
The command below shows the standard Claude install path; use the path reported
by your harness. If the skill is absent, install or sync it instead of skipping
this gate.

```bash
python3 ~/.claude/skills/layout-overflow-guard/scripts/check_layout.py <file-or-url> \
  --widths 1280,1100,860,720,390 --themes light,dark
```

You do **not** need to read `check_layout.py` — invoke it with the Bash tool and
act on its report and exit code (non-zero = a defect). The script's source never
enters your context; only its findings do. Drive it to zero violations across
every width and both themes before you ship. Full detail: the
`layout-overflow-guard` skill.

<!-- BEGIN BUNDLE INDEX (auto: index_references.py) -->

## Skill Bundle Index

*Every file in this skill, and when to open it. Auto-generated; run `scripts/index_references.py --fix`.*

**root**
- [`CHANGELOG.md`](CHANGELOG.md) — Color Contrast Auditor — Changelog — - Imported from the global jury_rig skill catalog into the repo.
- [`README.md`](README.md) — Color Contrast Auditor — Detects color contrast violations that make text and UI unreadable, and provides WCAG-compliant fixes backed by a real relative-luminance ca

**`agents/`**
- [`agents/openai.yaml`](agents/openai.yaml) — openai (data/schema)

**`examples/`**
- [`examples/expected-output.md`](examples/expected-output.md) — Example Output: Color Contrast Auditor — Scenario: a design brief eyeballs `#777777` gray body text over a white card because it "reads fine on the design file" — the classic gray-t
- [`examples/sample-input.json`](examples/sample-input.json) — sample input (data/schema)

**`references/`**
- [`references/safe-color-pairs.md`](references/safe-color-pairs.md) — Pre-Calculated Safe Color Pairs — Ready-to-use color combinations that pass WCAG AA (4.5:1 for normal text, 3:1 for large text).

**`schemas/`**
- [`schemas/contrast-spec.schema.json`](schemas/contrast-spec.schema.json) — contrast spec.schema (data/schema)

**`scripts/`**
- [`scripts/contrast_audit.mjs`](scripts/contrast_audit.mjs)

**`templates/`**
- [`templates/output-template.md`](templates/output-template.md) — Contrast Audit Report Template — Fill in every section before handing this back to a designer or reviewer.

<!-- END BUNDLE INDEX -->

Files in this skill

  • CHANGELOG.md1.1 KB
  • README.md1.5 KB
  • SKILL.md19.7 KB
  • agents/openai.yaml887 B
  • examples/expected-output.md4.3 KB
  • examples/sample-input.json683 B
  • references/safe-color-pairs.md7.2 KB
  • schemas/contrast-spec.schema.json2.2 KB
  • scripts/contrast_audit.mjs8.4 KB
  • templates/output-template.md1.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…