skills/color-contrast-auditor/SKILL.md
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).
npx skillsauth add curiositech/windags-skills color-contrast-auditorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
Activate on:
NOT for:
web-design-expert)color-theory-palette-harmony-expert)| 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 |
| Text Type | Minimum Ratio | |-----------|---------------| | Normal text | 7:1 | | Large text | 4.5:1 |
| 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 = (L1 + 0.05) / (L2 + 0.05)
Where L1 = lighter color's relative luminance
L2 = darker color's relative luminance
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);
}
❌ 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
❌ 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)
❌ 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
❌ "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
❌ COMMON FORM FAILURE:
Input background: #FFFFFF
Placeholder: #CCCCCC
Ratio: 1.6:1 ← FAIL
✅ FIX:
Placeholder: #757575 → Ratio: 4.6:1
❌ 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
When given a screenshot, identify:
Text elements by size:
Interactive elements:
Red flags to look for:
From CSS/code:
# Find all color declarations
grep -E "(color:|background:|#[0-9a-fA-F]{3,8}|rgb|hsl)" styles.css
From Tailwind:
# Find text/bg color classes
grep -E "(text-|bg-)" *.tsx *.jsx
For each text/background pair:
# Contrast Audit Report
## Summary
- Total color pairs tested: X
- Passing (AA): Y
- Failing: Z
- Critical failures (<2:1): N
## Failures by Severity
### Critical (Ratio < 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]
Contrast requirements help but don't fully address color blindness. Additional checks:
| Type | Affected | Consideration | |------|----------|---------------| | Deuteranopia | 6% of males | Red/green confusion | | Protanopia | 2% of males | Red appears dark | | Tritanopia | <1% | Blue/yellow confusion |
Never rely on color alone for meaning
Test problematic pairs:
Use sufficient lightness difference
| 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 |
| 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 |
| Use Case | Color | Hex | Ratio | |----------|-------|-----|-------| | Body text | Off-white | #F5F5F5 | 14.1:1 | | Secondary | Light gray | #BDBDBD | 8.3:1 |
// Chrome DevTools: Elements → Styles → hover color swatch
// Shows contrast ratio automatically
// Firefox: Accessibility Inspector
// Shows color contrast issues
// 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
# Lighthouse CLI
npx lighthouse https://example.com --only-categories=accessibility
# Pa11y
npx pa11y https://example.com
When analyzing a screenshot or codebase:
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 ✓✓
Before shipping:
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.
tools
Building resilient distributed systems with circuit breakers, retries with full-jitter exponential backoff, retry budgets (per-request 3-attempt + per-client 10% ratio per Google SRE), deadline propagation, and the cascading-failure math (4 layers × 3 retries = 64x amplification). Grounded in Resilience4j, Microsoft Cloud Patterns, AWS Architecture Blog (Marc Brooker), and Google SRE Book.
testing
Designing HTTP cache headers that work correctly across browsers, CDNs, and shared proxies — `Cache-Control` directives per RFC 9111, `stale-while-revalidate` and `stale-if-error` per RFC 5861, the Vary header for varying responses, and surrogate keys for tag-based purging. Grounded in IETF RFCs and Cloudflare/Fastly docs.
development
Use when designing or fixing a Content Security Policy on a real site, choosing between nonce-based and hash-based CSP, adding strict-dynamic, debugging "Refused to execute inline script" errors, deploying CSP in report-only mode first, configuring report-to / report-uri, or auditing an existing policy for unsafe-inline / unsafe-eval / wildcards. Triggers: "CSP blocks legitimate inline script", strict-dynamic, nonce-{RANDOM}, sha256-{HASH}, object-src none, base-uri none, frame-ancestors, Trusted Types, X-Content-Security-Policy obsolete, report-only vs enforced. NOT for general HTTP security headers (HSTS, COOP/COEP), Trusted Types deep dive, CORS configuration, or building a WAF.
tools
Choosing and operating an HTTP API versioning strategy that doesn't break clients — Stripe's date-based pinned versions, the Deprecation/Sunset header pair (RFC 9745 + RFC 8594), URI vs header vs media-type approaches, and the version-transformer pattern. Grounded in Stripe's published architecture and IETF RFCs.