[JS/PHP] 選択した色が黒と白のどちらと相性が良いかを判別する

選択した文字色 (背景色) が明瞭に見えるのは、黒色と白色のどちらの背景色 (文字色) なのかを判別させる機会があったので、その際に作った JavaScriptコード と PHPコード を紹介します。

まずは、どういったことができるのかを確認するために、次の CodePen をご覧ください。

See the Pen getBlackOrWhite by nov (@numerofive) on CodePen.

この CodePen では、選択した文字色に応じて黒色か白色の背景色を設定します。色は CSS変数 を介して設定しているので、その CSS変数 と color-mix関数 を組み合わせて、選択した文字色の薄い色も自動生成するようにしてみました。

色を判別するための計算式

計算式は All WCAG 2.2 TechniquesG17: Ensuring that a contrast ratio of at least 7:1 exists between text (and images of text) and background behind the text に記載されているものを使用し、白色と選択した色の相対輝度のコントラスト比から、黒色か白色かを判別します。

  1. Measure the relative luminance of each letter (unless they are all uniform) using the formula:

    • L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:

      • if R sRGB <= 0.04045 then R = R sRGB /12.92 else R = ((R sRGB +0.055)/1.055) ^ 2.4
      • if G sRGB <= 0.04045 then G = G sRGB /12.92 else G = ((G sRGB +0.055)/1.055) ^ 2.4
      • if B sRGB <= 0.04045 then B = B sRGB /12.92 else B = ((B sRGB +0.055)/1.055) ^ 2.4

      Note

      and R sRGB, G sRGB, and B sRGB are defined as:

      • R sRGB = R 8bit /255
      • G sRGB = G 8bit /255
      • B sRGB = B 8bit /255

      Note

      The “^” character is the exponentiation operator.

    Note

    For aliased letters, use the relative luminance value found two pixels in from the edge of the letter.

  2. Measure the relative luminance of the background pixels immediately next to the letter using same formula.
  3. Calculate the contrast ratio using the following formula.

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

      • L1 is the relative luminance of the lighter of the foreground or background colors, and
      • L2 is the relative luminance of the darker of the foreground or background colors.
  4. Check that the contrast ratio is equal to or greater than 7:1

https://w3c.github.io/wcag/techniques/general/G17#procedure

計算式を組み込んだ JavaScript関数

function getBlackOrWhite(hex) {
  const
    _rgb = {
      r: parseInt(hex.substring(1, 3), 16) / 255,
      g: parseInt(hex.substring(3, 5), 16) / 255,
      b: parseInt(hex.substring(5, 7), 16) / 255
    },
    rgb = {};
  const contrastRatio = 1.05 / (0.2126 * rgb['r'] + 0.7152 * rgb['g'] + 0.0722 * rgb['b'] + 0.05);
  return 7 > contrastRatio ? 'black' : 'white';
}

この getBlackOrWhite関数 は、CodePen の中で使用しているものです。引数の hex に16進記法の色 (#000000 ~ #ffffff) を設定すると、black か white のどちらかが戻り値になります。主な使用方法は次のとおり。

// 文字色が #800000 のhtml要素に背景色を設定する例
document.documentElement.style.color = '#800000';
document.documentElement.style.backgroundColor = getBlackOrWhite( '#800000' );

// 条件式に使用する例
if ( 'black' === getBlackOrWhite( '#008000' ) ) {
  …
}

計算式を組み込んだ PHP関数

上記の getBlackOrWhite関数 の PHP版 です。

function get_black_or_white( $hex ) {
  $_rgb = [
    'r' => ( hexdec( substr( $hex, 1, 2 ) ) / 255 ),
    'g' => ( hexdec( substr( $hex, 3, 2 ) ) / 255 ),
    'b' => ( hexdec( substr( $hex, 5, 2 ) ) / 255 ),
  ];
  $rgb = [];
  foreach ( $_rgb as $key => $value ) {
    $rgb[ $key ] = ( $value <= 0.04045 ) ? $value / 12.92 : ( ( $value + 0.055 ) / 1.055 ) ** 2.4;
  }
  $contrast_ratio = 1.05 / ( 0.2126 * $rgb[ 'r' ] + 0.7152 * $rgb[ 'g' ] + 0.0722 * $rgb[ 'b' ] + 0.05 );
  return ( 7 > $contrast_ratio ) ? 'black' : 'white';
}

内容的には JavaScript版 と同じで、使用方法もほぼ同様です。

<!-- 文字色が #800000 のdiv要素に背景色を設定する例 -->
<div style="<?php echo 'color: #800000; background-color: ' . get_black_or_white( '#800000' ) . ';'; ?>"> … </div>

<!-- 条件式に使用する例 -->
<?php if ( 'white' === get_black_or_white( '#008000' ) ): ?>
…
<?php endif; ?>

WordPress のカスタマイザーやブロックエディタなどで、色を選択できるようにした際、それと連動して別の色を設定するといったケースで今回のコードを使用しました。

ユーザーが設定したものに対して、その他は良きに計らう親切設計な何かを作る際には役に立つかもしれません。

関連記事