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が定義している計算式を見てきましょう。
以下の公式を用いて、各文字(すべて同一ではない限り)の相対輝度を測る:
色の相対輝度 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ピクセルの部分の相対輝度の値を使用する。
同じ公式を用いて、文字のすぐ隣の背景のピクセルの相対輝度を測る。
次の公式を用いて、コントラスト比を算出する。
コントラスト比が7:1と同じ、又はそれ以上である。【訳注:この計算式を用いているチェックツールで、コントラスト比が7:1以上であることを確認すればよい。】
引用元: G17: テキスト(及び画像化された文字)とその背景の間に、少なくとも 7:1 以上のコントラスト比をもたせる
引用文を読むと、下から順に計算していく必要があることがわかります。
まずは、以下の部分から求めていきます。
注記: また、RsRGB, GsRGB, 及び BsRGBは以下のように定義される: RsRGB = R8bit/255 GsRGB = G8bit/255 BsRGB = B8bit/255
上記の RsRGB = R8bit/255 という RsRGB と R8bit の頭文字の R は Red のことを指しており、sRGB と 8bitカラーを計算に使うということがわかります。
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で検索すると色んなコードがでてくるので、好みのものを使うと楽ができます。