背景色が勾配を持つ時のコントラスト比について考える

## はじめに
Web上で利用されるアクセシビリティに対する自動テストのツールの1つとして[`axe`](https://www.deque.com/axe/)があります。
このツールは直接の利用はもちろん、[`eslint-plugin-a11y`](https://github.com/infofarmer/eslint-plugin-jsx-a11y)や[`storybook-addon-a11y`](https://github.com/storybookjs/storybook/tree/next/code/addons/a11y)等を介して多くの人が利用していると思います。

さて、このツールを利用していると前景色と背景色のコントラスト比による違反を発見しました。違反に対する詳細は「Element's background color could not be determined due to a background gradient」のように記述されていました。

しかし、コントラスト比についてWCAG 2.1に記述されている[AA基準](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html)と[AAA基準](https://www.w3.org/WAI/WCAG21/Understanding/contrast-enhanced.html)の両方を確認しましたが、背景色の勾配に対する言及は見つかりませんでした(別のところに言及があれば確認不足なのでコメント等で教えていただけると幸いです)。


そこで、`axe`の[`color-contrast`についてのルールの定義](https://dequeuniversity.com/rules/axe/4.10/color-contrast)を見ると、コントラスト比を十分に計測できないために違反と見做されるケースとして背景がCSSのグラデーションを用いた時が紹介されていました。

つまり、この違反は色覚異常を持つ人々の見えにくさに依る制限ではなく、自動テストにおける扱いづらさが起因するものと考えることもできます。

この記事ではこの考察をもうひとつ前進させるために、背景色が勾配を持つときの色のコントラスト比がWCAGのルールで違反することがあるか、利用する妥当性があるかどうかを確認します(勾配を構成する色が前景色のコントラスト比に違反しないことを前提にします)。

:::note
ところで、色のコントラストの求め方はご存知でしょうか？
気になった方は[色のコントラスト比の求め方についての記事](https://www.k8o.me/blog/color-contrast)も書いているので良ければ見てください。
:::

## 背景色の勾配
CSSで背景色に勾配を持たせるときは`linear-gradient()`と`radial-gradient()`が主に使われます(CSSのグラデーションについては[MDN](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_images/Using_CSS_gradients)によくまとまっていました)。
下に`linear-gradient()`と`radial-gradient()`を使用した例を用意しました。
例では`axe-core`を用いてコントラスト比についてテストを実行しており、`console`に`incomplete`な結果を吐き出すようにしています。最初に紹介した通りのエラーが出ていることが確認できるはずです(`failureSummary`の部分です)。

<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="mybEbzM" data-pen-title="gradient" data-editable="true" data-user="kokisakano" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kokisakano/pen/mybEbzM">
  gradient</a> by KokiSakano (<a href="https://codepen.io/kokisakano">@kokisakano</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

`linear-gradient`は直線的に、`radial-gradient()`は放射線状に`--primary-color`から`--secondary`まで連続的に色が変化します。
色の変化する座標系が異なるだけで、色の変化自体はそれぞれ同じですから、この記事では以後`linear-gradient`だけを考えます。
さらに、今後具体的な例を考えるときは、この例と同じように前景色は`#030717`、背景の勾配は`#50e2d2`から`#0ea5e9`に変化するとします。色のコントラスト比としての基準は`#030717`と`#50e2d2`、`#030717`と`#0ea5e9`の両者がAAA基準の全てのパターンで満たしています。

## 勾配中でコントラスト比が最小となる箇所で計測する
WCAG 2.1では勾配自体に制約がないことから、勾配中のコントラスト比が最小となる箇所が基準を上回っていることが、勾配中に前景を配置してよい条件と考えられます。

[アメリカ合衆国のデジタルサービス改善を支援する18F](https://18f.gsa.gov/)が提供する[色とコントラストについてのアクセシビリティガイドライン](https://guides.18f.gov/accessibility/color/)では色のコントラストをテストする手順を3つに分けて紹介しています(DeepLによる翻訳)。

1. カラーコントラストチェッカーを使って、テキストの色と背景の色を比較する
2. 色がグラデーションであったり、プログラムで決定できない場合は、カラーピッカーを使用してテキストの最も明るい部分と背景の最も明るい部分を比較してください。次に、テキストの最も暗い部分と背景の最も暗い部分を比較します
3. 比率が[4.5:1]より大きいことをチェックする

2でチェックする内容は先ほど提示した条件と同じです。今やっていることが明後日の方向を向いていないことがわかりました。

勾配中でコントラスト比が最小となる箇所を求めます。
`<canvas>`要素に`createLinearGradient()`で勾配した色を貼って、1pxごとに前景色とコントラスト比を求めて最小のコントラスト比を探します(`<canvas>`要素に投影した後の色を取得しているのでカラーコードが多少ズレることもあります)。

<p class="codepen" data-height="300" data-default-tab="js" data-slug-hash="JoPKGxz" data-pen-title="color" data-editable="true" data-user="kokisakano" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kokisakano/pen/JoPKGxz">
  color</a> by KokiSakano (<a href="https://codepen.io/kokisakano">@kokisakano</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

`console`に結果が出ています。494pxということは`#0ea5e9`の点から6pxずれた点が最小でした。コントラスト比が[7.23:1]なので、この記事で紹介した例の背景は問題ないことがわかります。

例では`<canvas>`要素の`width`を500pxにしていますが、より大きくするとより正確な結果を、小さくすると粗い結果になります。

最小のコントラスト比を持つ点は点勾配の端ではないことが確認できました。勾配の端の色が前景色とコントラスト比を満たしていれば良いわけではなく、最小の点を探してコントラスト比を満たす必要があるというわけです。

## デモ環境で見え方を確認する
この記事で利用した例の背景色は計算上で問題ないことを確認しました。
次に、実際の見え方を確認するために、色覚タイプごとに見え方をシミュレーションします。下の例では`<select>`要素を切り替えることでさまざまな色覚タイプの見え方を切り替えられます。

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="OPLXNpg" data-pen-title="color" data-editable="true" data-user="kokisakano" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kokisakano/pen/OPLXNpg">
  color</a> by KokiSakano (<a href="https://codepen.io/kokisakano">@kokisakano</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

今回の配色では背景色によって前景色が見えにくくなることを確認できませんでした(主観なので異議があれば教えてください)。
背景色に勾配を持たせるように特殊な配色をする場合は実際の見え方に問題ないかを確認することをお勧めします。

実際の見え方と何度も言ってますが、例は色覚異常を持つ方が同じように見せてくれるわけではなく、色覚タイプに合わせた見え方を擬似的に表示しているだけに過ぎないことに気をつけてください。

## おわりに
背景色が勾配を持つ時のコントラスト比について考えてきました。
前景色と背景色のコントラスト比が最小となる点で基準を満たしていた場合には問題ないと考えて良さそうなことがわかりました。
そして、このように背景色が特殊なケースでは色覚タイプをシミュレーションして見え方を確認すると良いことも紹介しました。

最後に断りを入れておくと、コントラスト比を満せば背景色の勾配に問題がないわけではありません。コントラスト比以外の観点で、単一色に比べて見えにくいなどいくつかの点で欠点があります。
作っているサービスのデザインの観点から勾配を設けることを重視したいと判断したときに利用することをお勧めします。そのときは、この記事で紹介した項目をチェックしてくれると嬉しいです。