JavascriptでWC3のコントラスト計算式を書いてみる

W3Cの「WCAG (Web Content Accessibility Guidelines) 2.0」では、コントラストによる視認性の基準が定められています。

この基準をざっくりと言うと「ロービジョン(弱視)や色覚異常の人でも読みやすいように7:1のコントラスト比があると良し」というお話です。

詳細は以下のリンクから参照してください。

WCAG 2.0 解説書 コントラスト (最低限) :達成基準 1.4.3 を理解する WCAG 2.0 解説書 コントラスト (高度) :達成基準 1.4.6 を理解する WCAG 2.0 達成方法 G17: テキスト(及び画像化された文字)とその背景の間に、少なくとも 7:1 以上のコントラスト比をもたせる

コントラスト計算式

さて、早速W3Cが定義している計算式を見てきましょう。

  1. 以下の公式を用いて、各文字(すべて同一ではない限り)の相対輝度を測る:

    • 色の相対輝度 L = 0.2126 * R + 0.7152 * G + 0.0722 * B と定義されている。この場合のR, G 及び B は:

      • RsRGB <= 0.03928 の場合:R = RsRGB/12.92、それ以外の場合: R = ((RsRGB+0.055)/1.055) ^ 2.4

      • GsRGB <= 0.03928 の場合:G = GsRGB/12.92、それ以外の場合:G = ((GsRGB+0.055)/1.055) ^ 2.4

      • BsRGB <= 0.03928 の場合:B = BsRGB/12.92、それ以外の場合:B = ((BsRGB+0.055)/1.055) ^ 2.4

      注記: また、RsRGB, GsRGB, 及び BsRGBは以下のように定義される:

      • RsRGB = R8bit/255

      • GsRGB = G8bit/255

      • BsRGB = B8bit/255

      注記: "^"記号は指数演算子である。

    注記: エイリアス文字では文字の端から2ピクセルの部分の相対輝度の値を使用する。

  2. 同じ公式を用いて、文字のすぐ隣の背景のピクセルの相対輝度を測る。

  3. 次の公式を用いて、コントラスト比を算出する。

    • (L1 + 0.05) / (L2 + 0.05)

      • L1は前景または背景色の明るい方の相対輝度である。及び、

      • L2は前景または背景色の暗い方の相対輝度である。

  4. コントラスト比が7:1と同じ、又はそれ以上である。【訳注:この計算式を用いているチェックツールで、コントラスト比が7:1以上であることを確認すればよい。】

引用元: G17: テキスト(及び画像化された文字)とその背景の間に、少なくとも 7:1 以上のコントラスト比をもたせる

引用文を読むと、下から順に計算していく必要があることがわかります。

まずは、以下の部分から求めていきます。

注記: また、RsRGB, GsRGB, 及び BsRGBは以下のように定義される: RsRGB = R8bit/255 GsRGB = G8bit/255 BsRGB = B8bit/255

上記の RsRGB = R8bit/255 という RsRGBR8bit の頭文字の RRed のことを指しており、sRGB8bitカラーを計算に使うということがわかります。

sRGBとは、色空間における国際的な規格のことです。

そして、8bitというのは、0~255の数値のことです。 CSSで色を指定する時に color: rgb(255, 255, 255); などと記述するので、これはお馴染みですね。

つまり、RsRGB = R8bit/255 というのは、0~255の数値で表すRedを使ってsRGB規格のRedを求めているわけです。

JavaScriptで書くと以下のようになります。

function getSRGB(_8bitColor) {
	return _8bitColor / 255;
}
// output: 1
console.log(getSRGB(255));

これで、RsRGB, GsRGB, BsRGB, を求める関数ができたので、次は以下の式を求めていきます。

RsRGB <= 0.03928 の場合:R = RsRGB/12.92、それ以外の場合:R = ((RsRGB+0.055)/1.055) ^ 2.4 GsRGB <= 0.03928 の場合:G = GsRGB/12.92、それ以外の場合:G = ((GsRGB+0.055)/1.055) ^ 2.4 BsRGB <= 0.03928 の場合:B = BsRGB/12.92、それ以外の場合:B = ((BsRGB+0.055)/1.055) ^ 2.4 注記: "^"記号は指数演算子である。

これは、相対輝度の計算に使うための計算式です。 この条件式をJavaScriptに書き換えると、以下のようになります。

function getRGBForCalculateLuminance(rgb) {
	if (rgb <= 0.03928){
		return rgb / 12.92;
	} else {
		return Math.pow(((rgb + 0.055) / 1.055), 2.4);
	}
}
// output: 1
getRGBForCalculateLuminance(getSRGB(255));

引用文には、^記号は指数であるという旨の注意書きがあるので、べき乗計算を行う関数Math.pow()を使っています。

次に、相対輝度を計算する式が以下のようになっています。

L = 0.2126 * R + 0.7152 * G + 0.0722 * B

これもそのままJavaScriptに書き換えます。

function getRelativeLuminance(r, g , b) {
	let R = getRGBForCalculateLuminance(getSRGB(r));
	let G = getRGBForCalculateLuminance(getSRGB(g));
	let B = getRGBForCalculateLuminance(getSRGB(b));
	return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}
// output: 1
console.log(getRelativeLuminance(255, 255, 255));

さて、これで最後はコントラスト比の計算式だけです。

(L1 + 0.05) / (L2 + 0.05) L1は前景または背景色の明るい方の相対輝度である。及び、 L2は前景または背景色の暗い方の相対輝度である

相対輝度の明るい方と暗い方を振り分けて計算します。 JavaScriptで書くと以下のようになります。

function getContrast(l1, l2) {
	let bright = (l1 > l2) ? l1 : l2; // 明るい方の相対輝度
	let dark   = (l1 < l2) ? l1 : l2; // 暗い方の相対輝度
	return (bright + 0.05) / (dark + 0.05);
}

let luminance1 = getRelativeLuminance(153, 153, 153);
let luminance2 = getRelativeLuminance(255, 255, 255);

// output: 2.849027755287037
console.log(getContrast(luminance1, luminance2));

これでJavaScriptでコントラスト比が求めらるようになりました。

アクセシビリティを担保できるコントラスト比は、コントラスト比が7:1と同じ、又はそれ以上であるとされています。

先程の例では、コントラスト比が2.849027755287037で7に達していないため、アクセシブルではないコントラスト比になっている、ということです。

最後に

最後に、全部のスクリプトをまとめて記載しておきます。

// 相対輝度
let luminance1 = getRelativeLuminance(153, 153, 153);
let luminance2 = getRelativeLuminance(255, 255, 255);

// コントラスト比
let contrast = getContrast(luminance1, luminance2);

// output: 2.849027755287037
console.log(contrast);

// ==================
// ===== 関 数 =====
// ==================

// sRGBを返す
function getSRGB(_8bitColor) {
	return _8bitColor / 255;
}

// 相対輝度計算に使うためのRGBを返す
function getRGBForCalculateLuminance(rgb) {
	if (rgb <= 0.03928){
		return rgb / 12.92;
	} else {
		return Math.pow(((rgb + 0.055) / 1.055), 2.4);
	}
}

// 相対輝度を返す
function getRelativeLuminance(r, g , b) {
	let R = getRGBForCalculateLuminance(getSRGB(r));
	let G = getRGBForCalculateLuminance(getSRGB(g));
	let B = getRGBForCalculateLuminance(getSRGB(b));
	return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}

// コントラスト比を返す
function getContrast(l1, l2) {
	let bright = (l1 > l2) ? l1 : l2; // 明るい方の相対輝度
	let dark   = (l1 < l2) ? l1 : l2; // 暗い方の相対輝度
	return (bright + 0.05) / (dark + 0.05);
}

余談ですが、実際に使う時にはユーザーに、RGBのカラーコードはカラーピッカーなどで取得させると思います。

その際に、HEX形式(例:#FFFFFF)からRGB形式(例:rgb(255, 255, 255))に変換しないといけません。

hex2rgbで検索すると色んなコードがでてくるので、好みのものを使うと楽ができます。

人気記事すべて表示

WEBすべて表示