apppublic/aidocs/wcag_checker.md
2025-10-05 11:23:33 +08:00

245 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 颜色对比度计算工具技术文档
本模块提供了一组用于计算颜色对比度和评估其是否符合 **WCAGWeb Content Accessibility Guidelines** 标准的函数。这些函数可用于网页设计、UI 开发等场景中,确保文本与背景之间的可读性和无障碍访问性。
---
## 目录
- [功能概述](#功能概述)
- [函数说明](#函数说明)
- [`calculate_luminance(rgba)`](#calculate_luminancergba)
- [`get_contrast_ratio(lumA, lumB)`](#get_contrast_ratioluma-lumb)
- [`get_color_contrast_ratio(color1, color2)`](#get_color_contrast_ratiocolor1-color2)
- [`wcag_check(color1, color2, font_size=14)`](#wcag_checkcolor1-color2-font_size14)
- [使用示例](#使用示例)
- [注意事项与已知问题](#注意事项与已知问题)
- [参考标准](#参考标准)
---
## 功能概述
该模块实现了以下核心功能:
1. 计算给定 RGBA 颜色的相对亮度Luminance
2. 计算两种颜色之间的对比度比值。
3. 判断对比度是否满足 WCAG 2.0 的 AA 或 AAA 级别要求,考虑字体大小的影响。
> ⚠️ 当前代码存在多个拼写错误和变量名错误,实际使用前必须修复!
---
## 函数说明
### `calculate_luminance(rgba)`
**功能:**
计算输入颜色的相对亮度Relative Luminance依据 [WCAG 2.0 Suggestion E2](https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-CalcStep) 中定义的加权公式。
**参数:**
- `rgba` (`tuple`): 表示颜色的四元组 `(R, G, B, A)`,其中 R、G、B 为 0255 范围内的整数AAlpha可选不参与计算
> ✅ 注意:此函数仅使用 RGB 分量,忽略 Alpha 通道。
**返回值:**
- `float`: 相对亮度值,范围在 `0.0`(黑色)到 `1.0`(白色)之间。
**公式:**
```
L = 0.2126 * R + 0.7152 * G + 0.0722 * B
```
其中 R、G、B 应先归一化为 01 范围(即除以 255并应用伽马校正sRGB 转线性亮度)。但当前实现未包含归一化与伽马校正步骤,**这是一个严重缺陷!**
> 🔴 **警告:当前实现有误!**
> - 变量名错误:`color` 和 `colr` 混用 → 应统一为 `rgba`
> - 缺少归一化处理(需将 0255 映射到 01
> - 缺少 sRGB 到线性亮度的转换(非线性响应)
✅ 正确做法应如下:
```python
def srgb_to_linear(c):
c_norm = c / 255.0
return c_norm / 12.92 if c_norm <= 0.04045 else ((c_norm + 0.055) / 1.055) ** 2.4
lum = 0.2126 * srgb_to_linear(r) +
0.7152 * srgb_to_linear(g) +
0.0722 * srgb_to_linear(b)
```
---
### `get_contrast_ratio(lumA, lumB)`
**功能:**
根据两个颜色的相对亮度计算它们之间的对比度比值。
**参数:**
- `lumA`, `lumB` (`float`): 两个颜色的相对亮度值(来自 `calculate_luminance` 输出)
**返回值:**
- `float`: 对比率,取值范围通常为 `1:1`(无对比)到 `21:1`(最大对比)
**公式:**
```
contrast = (lighter + 0.05) / (darker + 0.05)
```
> ⚠️ 错误:函数体内使用了未定义的变量 `lumX`,应为 `lumA`
🔴 存在 Bug
```python
darker = min(lumX, lumB) # ❌ lumX 未定义
```
✅ 应改为:
```python
darker = min(lumA, lumB)
```
---
### `get_color_contrast_ratio(color1, color2)`
**功能:**
直接传入两个颜色,自动计算其对比度比值。
**参数:**
- `color1`, `color2`: 合法的颜色元组 `(R, G, B[, A])`
**返回值:**
- `float`: 两颜色间的对比度比值
⚠️ 错误:调用了不存在的函数 `get_contrast_Ratio`(大小写错误)
🔴 原始代码错误:
```python
return get_contrast_Ratio(lum1, lum2) # ❌ 函数名拼写错误
```
✅ 应为:
```python
return get_contrast_ratio(lum1, lum2)
```
---
### `wcag_check(color1, color2, font_size=14)`
**功能:**
判断两个颜色组合是否满足 WCAG 的可访问性标准AA 和 AAA 级别)。
**参数:**
- `color1`, `color2`: 颜色元组 `(R, G, B)`
- `font_size` (`int` or `float`): 字体大小(像素),用于决定对比度阈值
**返回值:**
- `tuple(bool, bool)`:
- 第一个布尔值表示是否达到 **AA 级别**
- 第二个布尔值表示是否达到 **AAA 级别**
**逻辑规则:**
| 字体大小 | AA 标准 | AAA 标准 |
|----------------|---------|---------|
| ≥ 18px 或粗体≥14px | 3.0 | 4.5 |
| < 18px | 4.5 | 7.0 |
> ⚠️ 实际 WCAG 规则更复杂涉及“大字号”定义18pt 正常 / 14pt 粗体 ≈ 24px此处简化处理。
🔴 当前代码错误
```python
return ratio >= aa, radio >= aaa # ❌ 'radio' 是拼写错误
```
应为
```python
return ratio >= aa, ratio >= aaa
```
---
## 使用示例
假设我们想检查白色文字 `(255, 255, 255)` 在深蓝背景 `(0, 0, 128)` 上的可读性
```python
# 示例(需先修复所有 bug 后方可运行)
white = (255, 255, 255)
blue = (0, 0, 128)
aa, aaa = wcag_check(white, blue, font_size=16)
print(f"AA compliant: {aa}") # True?
print(f"AAA compliant: {aaa}") # False?
```
预期输出修复后
```
AA compliant: True
AAA compliant: False
```
---
## 注意事项与已知问题
📌 **当前代码存在多个致命错误,不能直接使用!**
| 问题 | 描述 | 修复建议 |
|------|------|----------|
| `color` / `colr` / `lumX` 变量名错误 | 引用未定义变量导致运行时异常 | 统一变量名为正确拼写 |
| `get_contrast_Ratio` 大小写错误 | Python 区分大小写函数不存在 | 改为 `get_contrast_ratio` |
| `radio` 拼写错误 | 导致 NameError | 改为 `ratio` |
| 未归一化 RGB | 输入是 0255但公式期望 01 | 添加 `/ 255.0` 并应用伽马校正 |
| 缺少伽马校正 | 直接线性计算亮度不准确 | 使用 sRGB 线性转换函数 |
| 字体大小判断逻辑不完整 | 未考虑粗体情况 | 建议增加 `bold` 参数 |
---
## 推荐修复版本(完整修正版)
```python
def srgb_to_linear(c):
"""Convert sRGB component to linear light."""
c_norm = c / 255.0
return c_norm / 12.92 if c_norm <= 0.04045 else ((c_norm + 0.055) / 1.055) ** 2.4
def calculate_luminance(rgba):
r, g, b = rgba[:3]
lr = srgb_to_linear(r)
lg = srgb_to_linear(g)
lb = srgb_to_linear(b)
return 0.2126 * lr + 0.7152 * lg + 0.0722 * lb
def get_contrast_ratio(lumA, lumB):
lighter = max(lumA, lumB)
darker = min(lumA, lumB)
return (lighter + 0.05) / (darker + 0.05)
def get_color_contrast_ratio(color1, color2):
lum1 = calculate_luminance(color1)
lum2 = calculate_luminance(color2)
return get_contrast_ratio(lum1, lum2)
def wcag_check(color1, color2, font_size=14, bold=False):
# 简化判断:>=18px 或 (>=14px 且粗体) 视为“大字号”
is_large_text = font_size >= 18 or (bold and font_size >= 14)
aa_threshold = 3.0 if is_large_text else 4.5
aaa_threshold = 4.5 if is_large_text else 7.0
ratio = get_color_contrast_ratio(color1, color2)
return ratio >= aa_threshold, ratio >= aaa_threshold
```
---
## 参考标准
- [WCAG 2.0 Guideline 1.4.3: Contrast (Minimum)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html)
- [Understanding Success Criterion 1.4.3 | W3C](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html)
- [Relative Luminance Calculation - Wikipedia](https://en.wikipedia.org/wiki/Relative_luminance)
---
## 结语
尽管原始代码意图良好但由于多处语法和逻辑错误无法正常工作本文档不仅描述了其设计目标还指出了关键问题并提供了可运行的修复方案建议开发者在实际项目中使用经过验证的库 `webcolors``tinycss2` 或专用 contrast checker 或基于本修复版本进行封装