文件预览

color-contrast.md

查看 Accessibility 技能包中的文件内容。

文件内容

references/color-contrast.md

# Color Contrast Requirements

WCAG 2.1 requires specific contrast ratios to ensure text and UI components are readable for users with low vision or color blindness.

---

## WCAG AA Contrast Requirements

### Text Contrast (WCAG 1.4.3)

| Text Type | Minimum Ratio | Example |
|-----------|---------------|---------|
| **Normal text** (under 18pt or under 14pt bold) | **4.5:1** | Body copy, paragraphs, small headings |
| **Large text** (18pt+ or 14pt+ bold) | **3:1** | Large headings, hero text |

### UI Component Contrast (WCAG 1.4.11)

| Component | Minimum Ratio | Example |
|-----------|---------------|---------|
| **UI components** | **3:1** | Buttons, form borders, focus indicators |
| **Graphical objects** | **3:1** | Icons, chart elements, required info graphics |

---

## Testing Tools

### Browser DevTools

**Chrome DevTools**:
1. Inspect element
2. Look for contrast ratio in Styles panel next to color
3. Icons show AA/AAA pass/fail
4. Click color swatch → contrast ratio details

**Firefox DevTools**:
1. Inspect element
2. Color picker shows contrast ratio
3. Accessibility panel highlights issues

### Browser Extensions

- **axe DevTools** - Comprehensive scanner
- **WAVE** - Visual feedback overlay
- **Color Contrast Analyzer** - Manual checking

### Online Tools

- **WebAIM Contrast Checker**: https://webaim.org/resources/contrastchecker/
- **Coolors Contrast Checker**: https://coolors.co/contrast-checker
- **Color Review**: https://color.review/

---

## Common Color Combinations

### Passing Combinations (4.5:1+)

```css
/* Black on white */
color: #000000;
background: #ffffff;
/* Ratio: 21:1 ✅ AAA */

/* Dark gray on white */
color: #595959;
background: #ffffff;
/* Ratio: 4.6:1 ✅ AA */

/* White on dark blue */
color: #ffffff;
background: #0066cc;
/* Ratio: 5.7:1 ✅ AA */

/* White on black */
color: #ffffff;
background: #000000;
/* Ratio: 21:1 ✅ AAA */

/* Dark text on light gray */
color: #1a1a1a;
background: #f5f5f5;
/* Ratio: 14.8:1 ✅ AAA */
```

### Failing Combinations (under 4.5:1)

```css
/* Light gray on white */
color: #999999;
background: #ffffff;
/* Ratio: 2.8:1 ❌ FAIL */

/* Medium gray on light gray */
color: #666666;
background: #f0f0f0;
/* Ratio: 3.4:1 ❌ FAIL (AA normal text) */
/* ✅ PASS for large text (3:1+) */

/* Blue on white */
color: #4d90fe;
background: #ffffff;
/* Ratio: 2.9:1 ❌ FAIL */
```

---

## Building Accessible Color Palettes

### Strategy 1: Start with Extremes

```css
:root {
  /* Define extremes first */
  --background: #ffffff;  /* Lightest */
  --foreground: #000000;  /* Darkest */

  /* Fill in between */
  --text-primary: #1a1a1a;    /* 16.1:1 on white */
  --text-secondary: #4d4d4d;  /* 7.5:1 on white */
  --text-muted: #737373;      /* 4.7:1 on white */
  --text-disabled: #a3a3a3;   /* 2.9:1 - use only on large text */
}

.dark {
  --background: #0a0a0a;
  --foreground: #ffffff;

  --text-primary: #f5f5f5;    /* 17.6:1 on black */
  --text-secondary: #d4d4d4;  /* 11.2:1 on black */
  --text-muted: #a3a3a3;      /* 5.9:1 on black */
}
```

### Strategy 2: Semantic Colors with Variants

```css
:root {
  /* Primary (brand) */
  --primary: #0066cc;         /* Base (might not pass on white) */
  --primary-dark: #004080;    /* 6.5:1 on white ✅ */
  --primary-light: #3399ff;   /* Use only on dark backgrounds */

  /* Success */
  --success: #10b981;         /* 2.7:1 on white ❌ */
  --success-dark: #059669;    /* 4.6:1 on white ✅ */

  /* Warning */
  --warning: #f59e0b;         /* 2.2:1 on white ❌ */
  --warning-dark: #d97706;    /* 3.9:1 on white - use large only */

  /* Error */
  --error: #ef4444;           /* 3.3:1 on white ❌ */
  --error-dark: #b91c1c;      /* 6.2:1 on white ✅ */
}

/* Usage */
.button-primary {
  background: var(--primary);
  color: white;  /* White on #0066cc = 5.7:1 ✅ */
}

.text-success {
  color: var(--success-dark);  /* Use dark variant for text */
}
```

### Strategy 3: Contrast-Safe Defaults

```css
:root {
  /* Always-safe text colors */
  --text-on-light: #1a1a1a;   /* 16.1:1 on white */
  --text-on-dark: #f5f5f5;    /* 17.6:1 on black */

  /* Always-safe backgrounds */
  --bg-primary: #ffffff;
  --bg-secondary: #f5f5f5;
  --bg-tertiary: #e5e5e5;

  /* Brand colors (decorative/large text only) */
  --brand-blue: #0066cc;
  --brand-green: #10b981;
}

/* Apply text colors automatically */
.bg-light {
  background: var(--bg-primary);
  color: var(--text-on-light);
}

.bg-dark {
  background: #1a1a1a;
  color: var(--text-on-dark);
}
```

---

## Component-Specific Patterns

### Buttons

```css
/* Primary button */
.button-primary {
  background: #0066cc;
  color: #ffffff;       /* 5.7:1 ✅ */
  border: 2px solid #0066cc;
}

.button-primary:hover {
  background: #004080;  /* Darker on hover */
}

.button-primary:focus-visible {
  outline: 2px solid #0066cc;
  outline-offset: 2px;  /* 3:1 against page background ✅ */
}

/* Secondary button */
.button-secondary {
  background: transparent;
  color: #004080;       /* 6.5:1 on white ✅ */
  border: 2px solid #737373;  /* 4.7:1 on white ✅ */
}
```

### Form Inputs

```css
input, textarea, select {
  background: #ffffff;
  color: #1a1a1a;       /* 16.1:1 ✅ */
  border: 1px solid #737373;  /* 4.7:1 ✅ */
}

input:focus {
  border-color: #0066cc;
  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.25);
  /* Visible focus indicator ✅ */
}

input::placeholder {
  color: #737373;       /* 4.7:1 - borderline ⚠️ */
  /* Consider darker for AA compliance */
}
```

### Error States

```css
.error {
  color: #b91c1c;       /* 6.2:1 on white ✅ */
}

.input-error {
  border-color: #dc2626;  /* 4.5:1 on white ✅ */
}

.error-message {
  color: #991b1b;       /* 7.5:1 on white ✅ */
  background: #fef2f2;
}
```

### Links

```css
a {
  color: #0066cc;       /* 5.7:1 on white ✅ */
  text-decoration: underline;  /* Not just color */
}

a:visited {
  color: #551a8b;       /* 7.7:1 on white ✅ */
}

a:hover, a:focus {
  color: #004080;       /* 6.5:1 on white ✅ */
  text-decoration: none;
}
```

---

## Dark Mode Considerations

### Avoid Pure Black (#000000)

Pure black can cause eye strain on OLED screens. Use near-black instead:

```css
.dark {
  --background: #0a0a0a;      /* Not #000000 */
  --background-elevated: #1a1a1a;
}
```

### Reduce Contrast Slightly

Very high contrast (white on black) can be harsh. Slightly reduce:

```css
.dark {
  --text-primary: #f5f5f5;    /* Not #ffffff */
  --text-secondary: #d4d4d4;
}
```

### Test Both Modes

```css
:root {
  --background: #ffffff;
  --text: #1a1a1a;
}

.dark {
  --background: #0a0a0a;
  --text: #f5f5f5;
}

/* Both pass 4.5:1 ✅ */
```

---

## Color Blindness Considerations

### Don't Rely on Color Alone

```html
<!-- ❌ WRONG - red text only -->
<span style="color: red;">Error</span>

<!-- ✅ CORRECT - icon + text -->
<span class="error">
  <svg aria-hidden="true"><!-- Error icon --></svg>
  Error: Invalid email
</span>
```

### Use Patterns/Textures

```css
/* Chart bars */
.bar-1 { background: #0066cc url('pattern-1.svg'); }
.bar-2 { background: #10b981 url('pattern-2.svg'); }
.bar-3 { background: #f59e0b url('pattern-3.svg'); }
```

### Test with Simulators

- **Chrome DevTools**: Rendering → Emulate vision deficiencies
- **Colorblind Web Page Filter**: https://www.toptal.com/designers/colorfilter/
- **Coblis Color Blindness Simulator**: https://www.color-blindness.com/coblis-color-blindness-simulator/

---

## Quick Testing Workflow

1. **Pick foreground color**
2. **Pick background color**
3. **Test in contrast checker**:
   - Is ratio ≥ 4.5:1 for normal text? ✅
   - Is ratio ≥ 3:1 for large text/UI? ✅
4. **Adjust if needed**:
   - Darken foreground OR lighten background
   - Test again
5. **Verify in browser**:
   - Check with axe DevTools
   - View with simulated color blindness

---

## Common Failures and Fixes

| Failure | Ratio | Fix |
|---------|-------|-----|
| Light gray (#999) on white | 2.8:1 ❌ | Darken to #595959 (4.6:1 ✅) |
| Blue (#4d90fe) on white | 2.9:1 ❌ | Darken to #0066cc (5.7:1 ✅) |
| Green (#10b981) on white | 2.7:1 ❌ | Darken to #059669 (4.6:1 ✅) |
| Orange (#f59e0b) on white | 2.2:1 ❌ | Darken to #d97706 (3.9:1, large only) or #b45309 (5.0:1 ✅) |
| White on yellow (#fbbf24) | 1.4:1 ❌ | Use black text instead (13.6:1 ✅) |

---

**Key Takeaway**: Always test color contrast. Use browser DevTools or online checkers before committing to a color palette.