- 著者
- app-hacks
- 公開日
- 更新日
- 確認日
WCAG / アクセシビリティ / CSS
WCAGに基づくカラーコントラスト比の計算方法
カラーコードコントラストチェッカーの実装を基に、相対輝度、コントラスト比、WCAG判定基準と境界例を解説します。
WCAGに基づくカラーコントラスト比の計算方法
文字色と背景色の見え方は、画面の明るさや個人の感覚だけでは安定して判断できません。WCAGのコントラスト比は、二色の相対輝度から読みやすさの目安を数値化する方法です。色を選ぶ段階で比を確認すれば、公開後に本文やボタンの文字が読みにくいことへ気付きやすくなります。
ここでは、HEXカラーからコントラスト比を求める流れ、AA・AAAの判定、実装時の注意点を説明します。実際の二色を確認する場合は、カラーコード・コントラストチェッカーを利用できます。
HEXをRGBへ正規化する
入力は#fffのような3桁、#FFFFFFのような6桁のどちらもあり得ます。計算の前に3桁を6桁へ展開し、#の有無や英字の大文字・小文字をそろえます。形式が違う値をそのままparseInt()へ渡すと、利用者が入力ミスに気付きにくくなります。
const HEX_PATTERN = /^#?([\da-f]{3}|[\da-f]{6})$/i;
function expandHex(source: string) {
return source.length === 3
? [...source].map((part) => part + part).join('')
: source;
}
正規化後の各チャンネルは0〜255のRGB値として扱います。入力エラーは比を0として表示するのではなく、計算前に利用者へ知らせます。
sRGBを線形化して相対輝度を求める
RGBの数値をそのまま足して明るさを出すことはできません。sRGBのチャンネルを線形化し、赤・緑・青に重みを掛けて相対輝度を求めます。
function linearize(channel: number): number {
const srgb = channel / 255;
return srgb <= 0.04045
? srgb / 12.92
: ((srgb + 0.055) / 1.055) ** 2.4;
}
function relativeLuminance({ r, g, b }: Rgb): number {
return 0.2126 * linearize(r)
+ 0.7152 * linearize(g)
+ 0.0722 * linearize(b);
}
この順序を省くと、見た目の明るさに近い計算になりません。相対輝度は後の比率計算にだけ使い、画面には利用者が入力したHEX・RGB・HSLを別に表示すると分かりやすくなります。
明るい色と暗い色の順に比を計算する
二色の相対輝度を大きい順に並べ、次の式でコントラスト比を出します。
function contrastRatio(first: Rgb, second: Rgb): number {
const [lighter, darker] = [
relativeLuminance(first),
relativeLuminance(second),
].sort((a, b) => b - a);
return (lighter + 0.05) / (darker + 0.05);
}
黒と白は21:1、同じ色同士は1:1になります。入力順を入れ替えても同じ比になることをテストしておくと、前景色・背景色のどちらを先に入力しても判定が変わりません。
AA・AAAの基準を表示する
通常テキストはAAが4.5:1以上、AAAが7:1以上です。大きいテキストはAAが3:1以上、AAAが4.5:1以上です。ツールでは数値だけでなく、どの条件で通過・不通過なのかを併記すると、次の調整を判断しやすくなります。
比の表示を小数第2位などへ丸めても、合否は丸め前の値で判断します。たとえば境界に近い色は、表示が4.50に見えても実値が4.5未満ならAAを通過としてはいけません。
比率だけでアクセシビリティを判断しない
コントラスト比は不透明な二色の組み合わせを対象にします。透明度、グラデーション、画像の上に重なる文字は、背景が場所ごとに変わるため別途確認が必要です。また、色の比率が通過しても、キーボード操作、フォーカス表示、ラベル、見出し構造まで保証するものではありません。
本文、ボタン、エラー表示、リンクなど実際のUIで確認し、色だけで状態を伝えないようにします。計算を正しく実装することと、画面全体を利用しやすくすることを分けて確認するのが大切です。