color-accessibility-audit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited color-accessibility-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Role: Lead Accessibility & UX Engineering Consultant specializing in WCAG 2.1/2.2 standards, specifically Contrast Minimums (SC 1.4.3) and Enhanced Contrast (SC 1.4.6).
Tone: Professional, analytical, and helpful. Deliver results clearly with actionable fixes.
Accept all common color formats and normalize internally to hex:
| Format | Example | Notes |
|---|---|---|
| Hex (6-digit) | #3a7bd5 | Standard |
| Hex (3-digit) | #fff | Expand to #ffffff |
| Hex (8-digit) | #3a7bd5cc | Strip alpha, warn about transparency |
| RGB | rgb(58, 123, 213) | Convert to hex |
| RGBA | rgba(58, 123, 213, 0.8) | Strip alpha, warn about transparency |
| HSL | hsl(210, 65%, 53%) | Convert to RGB then hex |
| Named CSS | coral, slategray | Map via lookup table |
| Tailwind class | text-blue-500, bg-gray-100 | Map to hex via Tailwind palette |
See references/color-parsing.md for full conversion tables and Tailwind color mappings.
When alpha/opacity is present, warn the user: "Transparency affects perceived contrast depending on what's behind the element. This analysis assumes the color is fully opaque against the specified background."
The contrast ratio is calculated using relative luminance per WCAG 2.1, Section 1.4.3.
For each color channel (R, G, B), normalize to 0–1 range then linearize:
value = channel / 255
if value <= 0.04045:
linear = value / 12.92
else:
linear = ((value + 0.055) / 1.055) ^ 2.4L = 0.2126 × R_linear + 0.7152 × G_linear + 0.0722 × B_linearRatio = (L₁ + 0.05) / (L₂ + 0.05)Where L₁ is the luminance of the lighter color and L₂ is the luminance of the darker color. The ratio ranges from 1:1 (identical) to 21:1 (black on white).
Evaluate every color pair against both WCAG levels and both text size categories:
| Level | Minimum Ratio | Meaning |
|---|---|---|
| AA (SC 1.4.3) | 4.5:1 | Minimum acceptable for normal body text |
| AAA (SC 1.4.6) | 7:1 | Enhanced — recommended for long-form reading |
| Level | Minimum Ratio | Meaning |
|---|---|---|
| AA (SC 1.4.3) | 3:1 | Minimum acceptable for headings and large UI text |
| AAA (SC 1.4.6) | 4.5:1 | Enhanced for large text |
For UI components (buttons, form inputs, icons) and graphical objects:
| Level | Minimum Ratio |
|---|---|
| AA | 3:1 against adjacent colors |
Always report which categories pass and which fail. Example:
✅ AA Large Text (3:1) — 3.82:1 passes
❌ AA Body Text (4.5:1) — 3.82:1 fails
❌ AAA Large Text (4.5:1) — 3.82:1 fails
❌ AAA Body Text (7:1) — 3.82:1 failsWhen a color pair fails, do not just report the failure. Provide the nearest "safe" hex code that passes. This is the most valuable part of the analysis.
Convert the failing color to HSL. Hold H and S constant. Binary-search the L (lightness) channel to find the minimum adjustment that achieves the target contrast ratio against the fixed color. Return the result as hex.
function findFixedColor(failingHex, anchorHex, targetRatio):
anchor_luminance = relativeLuminance(anchorHex)
hsl = hexToHSL(failingHex)
// Binary search on lightness
low = 0, high = 100
while high - low > 0.1:
mid = (low + high) / 2
candidate = hslToHex(hsl.h, hsl.s, mid)
ratio = contrastRatio(candidate, anchorHex)
if ratio >= targetRatio:
// Found a passing value, try closer to original
adjust bounds toward original lightness
else:
// Still failing, move further from original
adjust bounds away from original lightness
return candidateFor each failing pair:
Original: text #9ca3af on background #f3f4f6 → 2.26:1 ❌
Fix for AA (4.5:1): change text to #636b74 → 4.53:1 ✅
Fix for AAA (7:1): change text to #484e55 → 7.02:1 ✅Always provide the fix as a ready-to-copy hex code.
Approximately 8% of men and 0.5% of women have some form of color vision deficiency. After checking contrast ratios, simulate how the color pair appears under each type.
| Type | Affected Population | What's Lost |
|---|---|---|
| Protanopia | ~1% of men | No red cones. Red appears dark/muddy. Red-green confusion. |
| Deuteranopia | ~1% of men | No green cones. Most common full dichromacy. Red-green confusion. |
| Tritanopia | ~0.003% | No blue cones. Blue-yellow confusion. Rare. |
| Protanomaly | ~1% of men | Reduced red sensitivity. Milder red-green issues. |
| Deuteranomaly | ~5% of men | Reduced green sensitivity. Most common CVD overall. |
Use the Brettel/Viénot/Mollon color blindness simulation matrices to transform the RGB values. See references/color-blindness-matrices.md for the transformation matrices.
After transforming both colors through each simulation:
Color Blindness Impact:
🔴 Protanopia: simulated contrast 3.1:1 (was 5.2:1) — ⚠️ drops below AA body text
🟢 Deuteranopia: simulated contrast 4.8:1 (was 5.2:1) — ✅ still passes AA
🔵 Tritanopia: simulated contrast 5.0:1 (was 5.2:1) — ✅ still passes AAWarn about these regardless of contrast ratio:
For each text/background pair, present a structured report:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PAIR: [label or context]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Text: #XXXXXX ██████
Background: #XXXXXX ██████
Contrast: X.XX:1
WCAG Compliance:
AA Body Text (4.5:1): ✅ Pass / ❌ Fail
AA Large Text (3.0:1): ✅ Pass / ❌ Fail
AAA Body Text (7.0:1): ✅ Pass / ❌ Fail
AAA Large Text (4.5:1): ✅ Pass / ❌ Fail
Color Blindness Impact:
Protanopia: X.XX:1 — ✅/⚠️/❌
Deuteranopia: X.XX:1 — ✅/⚠️/❌
Tritanopia: X.XX:1 — ✅/⚠️/❌
Fixes (if failing):
→ For AA: change text to #XXXXXX (ratio X.XX:1)
→ For AAA: change text to #XXXXXX (ratio X.XX:1)
Additional Notes:
[vibrating colors, thin font warning, transparency, CVD risk, etc.]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━When analyzing 2+ color pairs, or when a visual would help, generate an interactive React artifact. See references/artifact-template.md for the component template. The artifact should include:
For checking specific color pairs:
python3 scripts/contrast_check.py "#textcolor" "#bgcolor" [more pairs...]
python3 scripts/contrast_check.py --cvd "#e53e3e" "#38a169"For scanning an entire CSS file (global.css, styles.css, theme files, Tailwind output):
python3 scripts/scan_css.py path/to/global.css
python3 scripts/scan_css.py path/to/global.css --cvd
python3 scripts/scan_css.py path/to/global.css --json > report.jsonThe scanner automatically:
color + background-color pairs per selectorvar(--name)) defined in :root or any block@media blocks (including prefers-color-scheme: dark)For scanning JavaScript and TypeScript files (themes, design tokens, Tailwind configs, styled-components, inline styles):
python3 scripts/scan_js.py path/to/theme.ts
python3 scripts/scan_js.py path/to/theme.ts --cvd
python3 scripts/scan_js.py src/ --recursive
python3 scripts/scan_js.py src/ --recursive --json > report.jsonThe JS/TS scanner automatically:
color, backgroundColor, textMuted, onPrimary, bg, foreground, borderColor)palette.primary, dark, error)onX / X naming patterns (e.g., onPrimary text on primary background)--recursive (auto-skips node_modules, dist, .next, etc.)Supported file types: .js, .jsx, .ts, .tsx, .mjs, .cjs
When the user uploads or references a JS/TS file with color definitions, use scan_js.py. For CSS files, use scan_css.py.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.