やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

WCAGのAPCAで見やすい色を自動算出する

 白い背景なら黒い字にする等を自動化する。

成果物

See the Pen Untitled by ytyaru (@ytyaru) on CodePen.

特徴

  • 背景と文字の色から見やすさの指標を算出する
    • WCAG2と3の指標を両方算出する
  • 背景から文字色を白黒のいずれか算出する
  • 色と太さから見やすい最小サイズを算出する

 デザインに詳しくなくても適当にUIをイジっていれば適切な色やサイズを提示してくれる。あるいは見やすさの指標を提示するので目安にできる。

要件

 任意の背景色を指定したら自動的に見やすい文字色を白黒のいずれかで返して欲しい。

 それを実現するために見やすさの指標が欲しい。

解答

WCAG ver name range 指標
2.2 Contrast 1〜21 7, 4.5, 3
3.0 Luminance Contrast(Lc) 0〜106(-108) 90, 75, 60, ...

 Lcは背景のほうが明るいと+106、背景のほうが暗いと-108を返す。これを絶対値にして用いる。

WCAG 2.2

対象 AA AAA
テキスト 4.5 7
大きな字 3 4.5
UI 3 3
フォーカス有無差 3 3
ロゴ、非活性UI - -

WCAG 2.2 & 3

2 3 概要
7.0 90 本文に推奨される
4.5 75 本文が満たすべき最小値
3.0 60 非本文が満たすべき最小値
- 45 見出しなど大きく太ければ見える
- 30 テキスト最小値
- 15 非テキスト最小値
- - 人には見えないものと扱うべき
  • 2.2のコントラスト比では同じ値でも色の選択によっては見づらいものもあり均一でない
  • 3.0のコントラスト比は最新の方法でLc値を絶対値にして使う

 以下参考。

WCAG? APCA?

略語 用語 意味
WCAG Web Content Accessibility Guidelines 老人や障害者でも認識しやすいWebコンテンツを作る方法
APCA Advanced Perceptual Contrast Algorithm WCAG3におけるコントラスト基準。

 WCAGはUXを実現する方法論。今Web業界ではアクセシビリティ関係が盛んに更新されている。色盲などのハンデがある人でも参照しやすいコンテンツを作るための方法論を体系化している。それがWCAG。次の四大原則がある。

No 原則 詳細
1 知覚可能(Perceivable) ユーザーが情報を知覚できる形式で提供すること。例えば、テキストの代替テキストを提供したり、色覚特性を持つユーザーのために十分なコントラスト比を確保したりすること。
2 操作可能(Operable) ユーザーがUIを操作できる状態にすること。例えば、キーボードで全ての機能が操作できるようにしたり、十分な時間制限を提供したりすること。
3 理解可能(Understandable) ユーザが情報や操作方法を理解できるようにすること。例えば、コンテンツの言語を明確にしたり、予測可能なインターフェースを提供したりすること。
4 堅牢(Robust) ユーザーエージェント(ブラウザや支援技術)と互換性があるようにコンテンツを開発すること。例えば、HTMLのマークアップを適切に使用したり、支援技術がコンテンツを正しく解釈できるようにすること。

 WCAGに従えばクオリティの高いコンテンツが作れる。とりあえずWCAG 2.2 仕様書の目次だけでも流し読みすれば、どこに気をつけるべきか見当が付く。

既存ライブラリ

 じつは自分で実装しなくても済む。

chroma.js

規格 メソッド
WCAG2 chroma.contrast()
WCAG3 chroma.contrastAPCA()

実装

 自分が欲しい機能だけを持った小さなライブラリを作りたかった。

 Google先生に聞けばAIが答えてくれた。特にWCAG2に関してはコードが短いため丸ごとコピペできるレベル。

  1. WCAG2コントラスト比(4.5以上)
  2. APCA(90以上)

1. WCAG2コントラスト比(4.5以上)

 js 色 コントラスト差 算出でググったらコードが出た。

function getContrast(color1, color2) {
  const lum1 = relativeLuminance(color1);
  const lum2 = relativeLuminance(color2);
  const brightest = Math.max(lum1, lum2);
  const darkest = Math.min(lum1, lum2);
  return (brightest + 0.05) / (darkest + 0.05);
}

function relativeLuminance(color) {
  const r = parseInt(color.substring(1, 3), 16) / 255;
  const g = parseInt(color.substring(3, 5), 16) / 255;
  const b = parseInt(color.substring(5, 7), 16) / 255;
  const a = [r, g, b].map((v) => {
    return v <= 0.03928
      ? v / 12.92
      : Math.pow((v + 0.055) / 1.055, 2.4);
  });
  return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}

// 使用例
const color1 = "#ff0000"; // 赤
const color2 = "#0000ff"; // 青
const contrastRatio = getContrast(color1, color2);
console.log(`コントラスト比: ${contrastRatio}`);

// コントラスト比が4.5以上であれば、WCAGのAA基準を満たすと判断できる
if (contrastRatio >= 4.5) {
    console.log("AA基準を満たしています");
} else {
    console.log("AA基準を満たしていません");
}

 もう自分でコード書かなくていいやん。全部Google先生にやってもろたらええやん。

 私から趣味を奪わないで! AI優秀すぎィ!

 ありがたくパクらせてもらおう。

2. APCA(90以上)

 js APCA contrast 算出ググるとAPCA算出ライブラリが提示された。そのコードであるapca-w3.jsを見ると外部ライブラリに依存したNode.jsコードだったので、これをブラウザで動作する依存しないPure JSコードに書き直した。

 ふ、AI君もまだまだだね。人間様がいなければコードを出せないらしい。やれやれ、仕方ないな。私がコードを読んでパクって整理してやろう。

 コードの詳細はCODE参照。主要なコードは以下。

color.js

 color.jsには3つのクラスがある。

クラス 概要
Color 整数配列や文字列から色データを取得できる
WCAG WCAG2.2のコントラスト比を算出する
APCA WCAG3.0のLcを算出する
WCAG.contrast(color1, color2); // 1〜21
APCA.calc(fore, back); // 0〜106(-108)

 名前が微妙。APCAはWCAGに含まれるので適切ではない。以下のようにするほうが正確だが、長くなるので上記のようにした。WCAG自体よく知らないので適切な名付けができない。

WCAG メソッド
2.2 WCAG.contrast(color1, color2) 1〜21
3.0 WCAG.luminanceContrast(fore, back) 0〜106(-108)
Color.of([R,G,B])
Color.of([R,G,B,A])
Color.of('#RGB')
Color.of('#RGBA')
Color.of('#RRGGBB')
Color.of('#RRGGBBAA')

 WCAGAPCAの内部でColorを使っているため、色は数列、文字列、Colorインスタンスの3種類のいずれかの方法で入力できる。

 [R,G,B]0255の整数値、#RGBR,G,Bそれぞれ0Fまでの16進数で表す文字列。

 たとえば以下のようになる。

WCAG.contrast('#000', '#fff'); // 21
APCA.calc('#000', '#fff'); // 106

 あとは以下の基準値に照らし合わせて適時適切にお好みで色々やればいい。たとえばAPCAの90以上のみ許容するようにするとか。デザイン知識ゼロでも見やすい色を設定できるはず。

WCAG APCA 概要
7.0 90 本文に推奨される
4.5 75 本文が満たすべき最小値
3.0 60 非本文が満たすべき最小値
- 45 見出しなど大きく太ければ見える
- 30 テキスト最小値
- 15 非テキスト最小値
- - 人には見えないものと扱うべき

 ちなみに生成したColorに対する反対色/補色/白黒を返すゲッターも用意した。

const c = Color.of('#000');
c.mono.code // 白黒のいずれかコントラストが強いほうを返す
c.reversal // 反対色
c.complementary // 補色

 これらを使ってDEMOを作った。

所感

 本番では素直にchroma.js使ったほうが良いかもね。今回は自分で実装してみたくて書いたけど。