Skip to content

PHP 中的 WCAG 对比度验证:构建无障碍颜色工具

简介

颜色对比度是网页上最常见的无障碍问题之一。当用户可以为其个人资料、主题或内容选择自定义颜色时,你需要验证他们的选择是否保持可读性。本文将展示如何实现两个 PHP 工具函数来解决这个问题:一个用于快速亮度检查,另一个用于完整的 WCAG 合规性验证。

理解 WCAG 对比度要求

Web 内容无障碍指南(WCAG)定义了文本的最小对比度比率:

标准普通文本大文本
WCAG AA4.5:13:1
WCAG AAA7:14.5:1

大文本定义为 18pt(24px)或 14pt(18.66px)加粗。

TIP

虽然 4.5:1 符合合规要求,但这只是最低标准,而不是最高标准。许多用户仍然难以阅读最低对比度的内容。在可能的情况下,尽量使用更高的对比度。

检查颜色是浅色还是深色

对于简单的用例——比如决定在彩色背景上使用白色还是黑色文字——你可以使用基于 W3C 公式的快速亮度检查:

php
public static function isLightColor(string $hex): bool
{
    // 如果存在则移除 #
    $hex = ltrim($hex, '#');

    // 将 3 位 hex 转换为 6 位
    if (strlen($hex) === 3) {
        $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
    }

    // 将 hex 转换为 RGB
    $r = hexdec(substr($hex, 0, 2));
    $g = hexdec(substr($hex, 2, 2));
    $b = hexdec(substr($hex, 4, 2));

    // 使用 W3C 公式计算亮度
    $luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255;

    // 如果颜色是浅色则返回 true
    return $luminance > 0.5;
}

公式对绿色的权重最高(0.587),因为人眼对绿光最为敏感。

使用示例

php
$backgroundColor = '#3498db';

if (isLightColor($backgroundColor)) {
    $textColor = '#000000'; // 在浅色背景上使用黑色文字
} else {
    $textColor = '#ffffff'; // 在深色背景上使用白色文字
}

完整的 WCAG 对比度验证

要正确符合无障碍要求,你需要完整的 WCAG 相对亮度计算。此函数验证颜色与白色背景是否具有足够的对比度:

php
/**
 * 验证 hex 颜色与白色背景是否具有足够的对比度
 *
 * @param string $hexColor 要验证的 hex 颜色(带或不带 # 前缀)
 * @param float $minContrastRatio 最小对比度比率(WCAG AA 为 4.5)
 * @return bool 如果颜色具有足够的对比度则返回 True
 */
public static function hasGoodContrastWithWhite(
    string $hexColor,
    float $minContrastRatio = 4.5
): bool {
    // 将 hex 转换为 RGB
    $hex = ltrim($hexColor, '#');

    // 处理 3 位 hex
    if (strlen($hex) === 3) {
        $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
    }

    $r = hexdec(substr($hex, 0, 2));
    $g = hexdec(substr($hex, 2, 2));
    $b = hexdec(substr($hex, 4, 2));

    // 计算相对亮度(根据 WCAG)
    $r = $r / 255;
    $g = $g / 255;
    $b = $b / 255;

    // 线性化 sRGB 值
    $r = $r <= 0.03928 ? $r / 12.92 : pow(($r + 0.055) / 1.055, 2.4);
    $g = $g <= 0.03928 ? $g / 12.92 : pow(($g + 0.055) / 1.055, 2.4);
    $b = $b <= 0.03928 ? $b / 12.92 : pow(($b + 0.055) / 1.055, 2.4);

    // 使用 WCAG 系数计算亮度
    $luminance = 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;

    // 计算与白色的对比度比率(亮度 = 1)
    $contrastRatio = ($luminance + 0.05) / (1 + 0.05);

    // WCAG AA 要求普通文本至少 4.5:1
    return (1 / $contrastRatio) >= $minContrastRatio;
}

重要

两个亮度公式看起来相似,但用途不同:

  • W3C 公式(0.299, 0.587, 0.114):快速感知亮度检查
  • WCAG 公式(0.2126, 0.7152, 0.0722):带 sRGB 线性化的精确无障碍合规性

理解 sRGB 线性化

WCAG 计算包含一个简单公式省略的关键步骤:sRGB 线性化。计算机显示器不会线性显示颜色——它们会应用伽马校正。0.03928 的阈值和转换公式将显示值转换为实际光强度:

php
// 对于低值(深色)
$linearValue = $srgbValue / 12.92;

// 对于较高值
$linearValue = pow(($srgbValue + 0.055) / 1.055, 2.4);

这种线性化就是为什么 #777777 通过 WCAG AA 而 #787878 可能失败的原因,尽管它们在人眼看来几乎相同。

实际使用示例

验证用户选择的颜色

php
public function updateUserTheme(Request $request): Response
{
    $primaryColor = $request->input('primary_color');

    if (!hasGoodContrastWithWhite($primaryColor)) {
        return response()->json([
            'error' => '此颜色的文字对比度不足。请尝试更深的色调。'
        ], 422);
    }

    // 保存已验证的颜色
    $user->update(['primary_color' => $primaryColor]);

    return response()->json(['success' => true]);
}

支持不同的 WCAG 级别

php
// WCAG AA 普通文本(默认)
$passesAA = hasGoodContrastWithWhite($color);

// WCAG AA 大文本(14pt 加粗或 18pt)
$passesAALarge = hasGoodContrastWithWhite($color, 3.0);

// WCAG AAA 普通文本
$passesAAA = hasGoodContrastWithWhite($color, 7.0);

// WCAG AAA 大文本
$passesAAALarge = hasGoodContrastWithWhite($color, 4.5);

快速参考:常用颜色

以下是常用颜色在白色背景上的表现:

颜色HexWCAG AA (4.5:1)
黑色#000000通过
深灰色#333333通过
中灰色#767676通过(最低)
浅灰色#777777失败
海军蓝#000080通过
标准蓝#0066CC通过
亮绿色#00FF00失败
橙色#FF6600失败
黄色#FFFF00失败

测试你的实现

php
describe('hasGoodContrastWithWhite', function () {
    it('深色通过', function () {
        expect(hasGoodContrastWithWhite('#000000'))->toBeTrue();
        expect(hasGoodContrastWithWhite('#333333'))->toBeTrue();
        expect(hasGoodContrastWithWhite('#0066CC'))->toBeTrue();
    });

    it('浅色失败', function () {
        expect(hasGoodContrastWithWhite('#FFFFFF'))->toBeFalse();
        expect(hasGoodContrastWithWhite('#EEEEEE'))->toBeFalse();
        expect(hasGoodContrastWithWhite('#FFFF00'))->toBeFalse();
    });

    it('处理 3 位 hex 表示法', function () {
        expect(hasGoodContrastWithWhite('#000'))->toBeTrue();
        expect(hasGoodContrastWithWhite('#fff'))->toBeFalse();
    });

    it('遵循自定义对比度比率', function () {
        $mediumGray = '#888888';
        expect(hasGoodContrastWithWhite($mediumGray, 4.5))->toBeFalse();
        expect(hasGoodContrastWithWhite($mediumGray, 3.0))->toBeTrue();
    });
});

结论

构建无障碍应用程序需要以编程方式验证颜色选择。isLightColor 函数提供了一种快速选择对比文字颜色的方法,而 hasGoodContrastWithWhite 确保用户选择的颜色完全符合 WCAG 标准。

请记住:无障碍合规性在许多司法管辖区是法律要求。从 2026 年 4 月起,美国州和地方政府网站必须符合 WCAG 2.1 AA 标准,欧盟和其他地区也有类似的法规。