Katashin .info

任意の背景色に対して読みやすい文字色を選択する方法

GitHub の Issue ラベルなど、任意の色の中に文字を入れたい場合があります。このとき文字色が一色のみだと、背景色と似たような色のときに読みづらくなってしまいます。

以下のスクショでわかるように、GitHub は背景色によって文字色を黒か白のどちらにするかを計算しているようです。

個々のサービスの実装についてはわかりませんが、WCAG (Web Content Accessibility Guidelines) で定義されているコントラスト比を使うことで、背景色に対して読みやすい文字色を選択することができます。

WCAG で定義されているコントラスト比 (contrast ratio) #

WCAG は Web のコンテンツを障害者がより利用しやすくするためにはどうするべきかが記された指標です。また一般的に、WCAG にそった Web ページを作ることで、全体的なユーザビリティの向上も期待できるとされています。

WCAG には様々な項目がありますが、ガイドライン 1.4.3 の Contrast (Minimum) に注目します。

The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, except for the following:

--- 翻訳 ---

文字や画像文字の可視表現は少なくとも 4.5:1 のコントラスト比を持つこと。ただし、次のものは除く (省略)

4.5:1 というのは一つの目安なのですが、ここでは読みやすさの指標にコントラスト比というものを使用している点が重要です。背景色と文字色のコントラスト比を計算することができれば、それをもとに読みやすい文字色を選択できると考えられます。

コントラスト比の定義を見てみると、以下のようになっています。

(L1 + 0.05) / (L2 + 0.05)

--- 翻訳 ---

(L1 + 0.05) / (L2 + 0.05)

相対輝度というものが出てきたので更にたどってみます。以下は相対輝度の定義です。

the relative brightness of any point in a colorspace, normalized to 0 for darkest black and 1 for lightest white
Note 1: For the sRGB colorspace, the relative luminance of a color is defined as L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:

and RsRGB, GsRGB, and BsRGB are defined as:

--- 翻訳 ---

色空間の任意の地点における相対的な明るさ。最も暗い黒は 0、最も明るい白は 1 に正規化される。
注 1: sRGB 色空間では、ある色の相対輝度は L = 0.2126 * R + 0.7152 * G + 0.0722 * B である。ただし R, G, B は以下の通り。

そして RsRGB, GsRGB, BsRGB は以下のように定義される。

CSS の色の値は sRGB なので、上記の式がそのまま使えます。これらの情報を使って文字色を選択します。

背景色とのコントラスト比が高い色を文字色にする #

文字色にはたいてい黒か白を使用すると考えられるので、黒と背景色、白と背景色の 2 つのコントラスト比を計算し、大きい方の色を文字色として採用するという方法をとってみます。

実装は以下のようになります。

function chooseTextColor(red, green, blue) {
  // sRGB を RGB に変換し、背景色の相対輝度を求める
  const toRgbItem = (item) => {
    const i = item / 255
    return i <= 0.03928 ? i / 12.92 : Math.pow((i + 0.055) / 1.055, 2.4)
  }
  const R = toRgbItem(red)
  const G = toRgbItem(green)
  const B = toRgbItem(blue)
  const Lbg = 0.2126 * R + 0.7152 * G + 0.0722 * B

  // 白と黒の相対輝度。定義からそれぞれ 1 と 0 になる。
  const Lw = 1
  const Lb = 0

  // 白と背景色のコントラスト比、黒と背景色のコントラスト比を
  // それぞれ求める。
  const Cw = (Lw + 0.05) / (Lbg + 0.05)
  const Cb = (Lbg + 0.05) / (Lb + 0.05)

  // コントラスト比が大きい方を文字色として返す。
  return Cw < Cb ? 'black' : 'white'
}

// 適当な要素で試してみる
const body = document.body
body.innerHTML = 'Test'
body.style.backgroundColor = 'rgb(50, 50, 255)'
body.style.color = chooseTextColor(50, 50, 255) // 'white'

上記を動かすと以下のようになります。

様々な色で試すと以下のようになります。

背景色が暗い場合は白、明るい場合は黒が文字色として選択されていることがわかります。

まとめ #

任意の背景色に対して読みやすい文字色を選択するために WCAG のコントラスト比を使いました。背景色と白、背景色と黒、それぞれのコントラスト比を求め、大きい方を文字色として採用します。これによって、背景色が暗い場合は白、明るい場合は黒が文字色として選択され、なにが背景色にきても文字を読みやすくすることができます。