Use Case
Fixing "!" accessibility scores, improving WCAG 2.1 compliance
Category
Web Performance & SEOTags
Documentation
色コントラストによるアクセシビリティ問題を修正。
Agent Skill: Accessibility 对比度审计报错修复
Skill Type: Technical Debugging & Optimization Domain: Web Accessibility, SEO, Frontend Performance Difficulty: Intermediate Impact: High (直接影响 SEO 排名和用户体验)
📋 Skill Overview
What This Skill Solves
Problem: PageSpeed Insights (PSI) 的 Accessibility 分数显示 感叹号("!") 而非正常数字分数。
Root Cause: 不是分数低,而是 测量失败!
color-contrast审计在执行过程中直接报错- Canvas
getImageData()调用失败 - 无法计算颜色对比度 → 无法给出分数
Impact:
- ❌ SEO 排名下降(Accessibility 是 Google 排名因素)
- ❌ 无法通过 Google 质量检查
- ❌ 视障用户体验受损
- ❌ 部分市场(美国、欧盟)法律合规风险
🔍 Diagnostic Process
Step 1: Identify the Problem
Symptom Check:
PageSpeed Insights → Accessibility → "!" (exclamation mark)
Chrome DevTools → Lighthouse → Accessibility → Error
Key Indicators:
- 显示感叹号,而非分数(如 85、90 等)
- Console 可能显示
getImageData相关错误 - color-contrast 审计标记为 "Error" 或 "Incomplete"
Step 2: Root Cause Analysis
核心触发器(按优先级排序):
-
CSS Filter ⚠️ 最常见
backdrop-blur,filter: brightness(),filter: contrast()mix-blend-mode- 直接导致 canvas 像素采样失败
-
OKLCH/OKLAB 颜色空间
- 新色彩模型导致 axe-core 计算偏差
- 与 sRGB 转换不稳定
-
超低透明度 (< 0.4)
- 背景透明度太低(如
/10,/20,/30) - 让 canvas 采样结果不可靠
- 背景透明度太低(如
-
复杂渐变遮罩
mask-image: linear-gradient(...)+ 低 opacity- 跨域图片背景 + 文字覆盖
Step 3: Quick Diagnostic Commands
# 1. 检查颜色空间(OKLCH/OKLAB)
grep -r "oklch\|oklab" app/ components/ styles/
# 2. 检查低透明度(< 0.4)
grep -r "/10\|/20\|/30\|opacity-25\|opacity-0" components/ app/
# 3. 检查 CSS Filter(关键!)
grep -r "backdrop-blur\|filter:\|mix-blend-mode" components/ app/
# 4. 检查渐变文字
grep -r "background-clip.*text\|color.*transparent" components/ app/
🛠️ Fix Implementation
Phase 1: Remove CSS Filters (Priority: Critical)
Why: CSS filters 是触发 getImageData 报错的主要原因。
What to Fix:
// ❌ BEFORE: Triggers getImageData error
<div className="bg-card/50 backdrop-blur-sm">
<h2>Content</h2>
</div>
// ✅ AFTER: Use solid background instead
<div className="bg-card/80">
<h2>Content</h2>
</div>
Affected Properties:
backdrop-blur-*→ Remove completelyfilter: brightness()→ Remove or use opacity insteadfilter: contrast()→ Removemix-blend-mode→ Remove
Search & Replace:
# Find all backdrop-blur usage
grep -r "backdrop-blur" components/
# Replace pattern (manual review recommended)
# backdrop-blur-sm → (remove)
# bg-card/50 → bg-card/80
Phase 2: Convert Color Space (Priority: High)
Why: OKLCH/OKLAB 颜色空间导致对比度计算偏差。
What to Fix:
/* ❌ BEFORE: OKLCH color space */
:root {
--background: oklch(0.99 0 0);
--foreground: oklch(0.15 0.01 270);
--primary: oklch(0.55 0.22 264);
}
/* ✅ AFTER: Stable HSL color space */
:root {
--background: hsl(0, 0%, 99%);
--foreground: hsl(270, 5%, 15%);
--primary: hsl(250, 60%, 50%);
}
Conversion Table (OKLCH → HSL):
| OKLCH | Approximate HSL | Description |
|---|---|---|
oklch(0.99 0 0) | hsl(0, 0%, 99%) | Near white |
oklch(0.15 0.01 270) | hsl(270, 5%, 15%) | Dark gray |
oklch(0.55 0.22 264) | hsl(250, 60%, 50%) | Primary blue |
oklch(0.12 0.01 270) | hsl(270, 5%, 12%) | Very dark (dark mode) |
oklch(0.95 0.01 270) | hsl(270, 5%, 95%) | Light gray (dark mode text) |
Files to Check:
app/globals.cssapp/theme.csstailwind.config.ts(if using custom colors)
Phase 3: Increase Opacity Thresholds (Priority: Medium)
Why: 透明度 < 0.4 导致 canvas 采样不稳定。
Target: 所有背景透明度 ≥ 0.4(推荐 ≥ 0.6)
What to Fix:
// ❌ BEFORE: Too low opacity
className="bg-muted/20" // 20%
className="from-primary/10" // 10%
className="opacity-25" // 25%
// ✅ AFTER: Safe opacity threshold
className="bg-muted/40" // 40% (min safe)
className="from-primary/20" // 20% (gradient ok)
className="opacity-40" // 40%
// ✅ BETTER: Recommended opacity
className="bg-muted/60" // 60%
className="from-primary/40" // 40%
className="opacity-60" // 60%
Pattern Replacement:
/10→/20(gradients) or/40(backgrounds)/20→/40/30→/60opacity-25→opacity-40
Phase 4: Handle Gradient Text (Priority: Low)
Why: color: transparent + background-clip: text 让对比度无法计算。
Solution: 提供纯色降级
/* ❌ AVOID: Pure gradient text (no fallback) */
.gradient-text {
background: linear-gradient(90deg, #0ea5e9, #6366f1);
-webkit-background-clip: text;
background-clip: text;
color: transparent; /* ← Problem! */
}
/* ✅ SOLUTION: Solid color fallback */
.title {
color: #0a0a0a; /* Default: solid color (accessible) */
}
/* Apply gradient only when safe */
@media (prefers-contrast: no-preference) {
.title.with-gradient {
background: linear-gradient(90deg, #0ea5e9, #6366f1);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
}
/* Remove gradient in high-contrast mode */
@media (forced-colors: active) {
.title.with-gradient {
background: none;
color: CanvasText;
forced-color-adjust: auto;
}
}
Phase 5: Add Overlay for Image Backgrounds (Priority: Medium)
Why: 文字直接覆盖图片,背景无法被可靠采样。
Solution: 添加不透明遮罩(≥ 0.6)
// ❌ BEFORE: Text directly on image
<div className="relative">
<img src="/hero.jpg" alt="Hero" />
<h1 className="absolute top-1/2 left-1/2 text-white">
Title
</h1>
</div>
// ✅ AFTER: Add opaque overlay
<div className="relative">
<img src="/hero.jpg" alt="Hero" />
{/* Semi-transparent overlay */}
<div className="absolute inset-0 bg-black/60"></div>
{/* Text above overlay */}
<h1 className="relative top-1/2 left-1/2 text-white">
Title
</h1>
</div>
Overlay Opacity Guidelines:
- Dark overlay + White text:
bg-black/60(60% opacity) - Light overlay + Dark text:
bg-white/80(80% opacity) - Branded overlay:
bg-primary/70(70% opacity)
✅ Verification Checklist
Pre-Deployment Check
## Color Space
- [ ] All colors use `hsl()` or `rgb()` (no `oklch` or `oklab`)
- [ ] Check `app/globals.css`, `app/theme.css`, `tailwind.config.ts`
## Opacity
- [ ] All background opacity ≥ 0.4 (prefer ≥ 0.6)
- [ ] No Tailwind classes: `/10`, `/20`, `/30` in backgrounds
- [ ] No `opacity-0`, `opacity-25` in visible elements
## CSS Filters (CRITICAL!)
- [ ] No `backdrop-blur-*` classes
- [ ] No `filter: brightness()` or `filter: contrast()`
- [ ] No `mix-blend-mode`
- [ ] No `backdrop-filter` in CSS
## Gradient Text
- [ ] Gradient text has solid color fallback
- [ ] Uses `@media (prefers-contrast)` for safe application
- [ ] Uses `@media (forced-colors)` for accessibility
## Image Backgrounds
- [ ] Text over images has opaque overlay (≥ 0.6 opacity)
- [ ] Contrast ratio ≥ 4.5:1 (small text) or ≥ 3:1 (large text)
Local Testing
# 1. Build verification (must pass)
npm run build
# or
pnpm build
# 2. Chrome DevTools Lighthouse
# Open browser → F12 → Lighthouse → Accessibility
# Should show a number (85-100), NOT "!"
# 3. axe DevTools CLI (optional)
npx @axe-core/cli http://localhost:3000
Post-Deployment Verification
# 1. PageSpeed Insights (wait 24-48 hours after deployment)
# https://pagespeed.web.dev/
# Input: Your production URL
# Check: Accessibility should show a number, NOT "!"
# 2. Check specific contrast issues
# Chrome DevTools → Elements → Accessibility pane
# Or: axe DevTools browser extension
📊 Success Metrics
Before Fix
- ❌ Accessibility score: "!" (error)
- ❌ color-contrast audit: Error/Incomplete
- ❌ Cannot calculate contrast ratios
After Fix
- ✅ Accessibility score: 85-100 (numeric score)
- ✅ color-contrast audit: Pass (or specific items listed)
- ✅ All contrast ratios calculable
🚀 Quick Fix Workflow
5-Minute Emergency Fix
If you need to fix this urgently:
# Step 1: Remove backdrop-blur (CRITICAL!)
# Find & replace in all components
grep -r "backdrop-blur" components/ app/
# Replace: backdrop-blur-sm → (delete)
# Step 2: Increase low opacity
# Find & replace in all components
grep -r "/10\|/20\|/30" components/
# Replace: /10 → /40, /20 → /40, /30 → /60
# Step 3: Build & verify
npm run build
# Step 4: Deploy
git add .
git commit -m "Fix accessibility contrast audit errors"
git push
Comprehensive Fix (Recommended)
Follow all 5 phases:
- Remove CSS Filters (~10 min)
- Convert Color Space (~15 min)
- Increase Opacity Thresholds (~10 min)
- Handle Gradient Text (~15 min, if applicable)
- Add Image Overlays (~10 min, if applicable)
Total Time: ~1 hour for thorough fix
🔧 Tools & Resources
Diagnostic Tools
Online:
Browser Extensions:
- axe DevTools (Chrome/Firefox)
- WAVE (Chrome/Firefox)
CLI Tools:
# axe-core CLI
npx @axe-core/cli https://your-site.com
# Lighthouse CLI
npm install -g lighthouse
lighthouse https://your-site.com --only-categories=accessibility
Contrast Standards (WCAG 2.1)
| Text Size | Min Contrast | Example |
|---|---|---|
| Small (< 18pt) | 4.5:1 | Body text, buttons |
| Large (≥ 18pt) | 3.0:1 | Headings, hero text |
| Large bold (≥ 14pt) | 3.0:1 | Bold headings |
Reference Documentation
Official Standards:
GitHub Issues (for deep dive):
- Lighthouse #15002 - getImageData errors
- axe-core #4628 - Gradient background support
⚠️ Common Mistakes to Avoid
❌ Mistake 1: Thinking "!" = Low Score
Reality: "!" means measurement failed, not score is low. Fix: Address the root cause (CSS filters, color space), not try to "improve score"
❌ Mistake 2: Only Fixing Obvious Contrast Issues
Reality: Hidden CSS filters or color space issues are often the culprit. Fix: Use systematic checklist, don't cherry-pick
❌ Mistake 3: Skipping Local Testing
Reality: PageSpeed Insights updates with 24-48 hour delay. Fix: Test locally with Lighthouse/axe before deployment
❌ Mistake 4: Using OKLCH for "Better Colors"
Reality: OKLCH breaks axe-core contrast calculations. Fix: Use HSL/RGB for production, even if OKLCH looks better
❌ Mistake 5: Relying on Build Success
Reality: npm run build success ≠ Accessibility compliance.
Fix: Always run Lighthouse after build
🎯 Prevention Strategy
For New Projects
1. Template Configuration
/* ✅ Use HSL from day one */
:root {
--background: hsl(0, 0%, 100%);
--foreground: hsl(0, 0%, 10%);
/* No OKLCH! */
}
2. Tailwind Safeguards
// tailwind.config.js
module.exports = {
// Avoid low opacity utilities
theme: {
extend: {
// Use named opacity values
opacity: {
'safe': '0.6', // Instead of /10, /20, /30
}
}
}
}
3. Development Checks
# Add to pre-commit hook or CI/CD
npm run build && lighthouse http://localhost:3000 --only-categories=accessibility
For Existing Projects
Quarterly Audit:
- Run full Lighthouse check
- Review new components for CSS filters
- Check for new low-opacity backgrounds
- Validate color space in theme files
📈 Expected Outcomes
Immediate Impact
- ✅ Accessibility score displays normally (85-100)
- ✅ No more getImageData errors
- ✅ All contrast ratios calculable
Long-term Benefits
- 📈 Improved SEO rankings (Accessibility is a ranking factor)
- 👥 Better user experience for all users
- ⚖️ Legal compliance (ADA, WCAG 2.1 AA)
- 🤖 Better visibility in AI/LLM results (GEO)
🎓 Skill Mastery Checklist
You've mastered this skill when you can:
- Diagnose "!" vs numeric score in under 2 minutes
- Identify CSS filter issues without tools (code review)
- Convert OKLCH to HSL accurately
- Know when opacity is "too low" (< 0.4) at a glance
- Fix most issues in under 1 hour
- Prevent issues in new projects from day one
- Explain the root cause to non-technical stakeholders
📚 Advanced Topics
Understanding getImageData Failure
Why it happens:
- Canvas is "tainted" by cross-origin images
- CSS filters make pixel data unreliable
- New color spaces (OKLCH) not fully supported by axe-core
- Low opacity makes sampling results unstable
Technical deep dive:
- axe-core uses canvas to sample background colors
- Calls
getImageData()to read pixels - If canvas is tainted or data is unreliable → Error
- Error in color-contrast audit → No accessibility score
CI/CD Integration
# .github/workflows/accessibility.yml
name: Accessibility Check
on: [push, pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Lighthouse CI
uses: treosh/lighthouse-ci-action@v9
with:
urls: |
http://localhost:3000
uploadArtifacts: true
temporaryPublicStorage: true
budgetPath: ./budget.json
Skill Version: 1.0 Last Updated: 2025-10-19 Tested On: Next.js 15, React 18, Tailwind CSS 4
🤝 Contributing
Found a new trigger or fix? This skill document can be updated with:
- New edge cases
- Additional diagnostic methods
- Faster fix workflows
- Tool recommendations
Keep the skill sharp and up-to-date! 🚀
