🌟

このページの色をかけた魂の戦い

2022/06/15に公開

皆さんこんにちは。この記事では、筆者が最近勉強した CSS Color Adjustment Module Level 1 の内容をご紹介します。

この仕様は記事執筆現在Candidate Recommendationという段階にあり、一部ブラウザでの実装もされています。

どんな仕様?

仕様の名前が示している通り、この仕様はページの色の調整に関するCSSプロパティやその関連概念を定義するものです。特に、この仕様はページの色(カラースキーマ)をどう調整するかに関して著者(ページのCSSを書く人)とユーザーとの間のネゴシエーションを可能とする点が特徴的です。

color-scheme: サポートするカラースキーマの表明

昨今、ダークモードという概念がWebやその周辺を圧巻し、多くのアプリやウェブサイトがダークモードに対応しています。CSSにおいては、@mediaprefers-color-schemeを使って、ダークモードを希望するユーザーに対しては暗いスタイルを提供できることがよく知られています。

従来は、このようにユーザー(ユーザーエージェント)側から一方的に要求が伝えられるだけでした。color-schemeプロパティを使うと、Webページの側からもサポートするカラースキーマを主張することができます。実際に適用されるカラースキーマ(現在のところライトテーマかダークテーマか)は双方の主張を勘案して決められます。このように、双方から主張を出すことができるのがネゴシエーションです。

また、これがプロパティであることから分かるように、color-schemeはページ全体ではなく要素ごとの設定ができます。その結果として、ページの一部分だけ適用されるカラースキーマが違うということも可能になります。

color-schemeの構文①

基本的なcolor-schemeの構文を紹介します。ライトテーマをlight、ダークテーマをdarkで表し、サポートするテーマを次のように列挙します。

/* 両方サポートする場合 */
color-scheme: light dark;
/* ライトテーマのみサポートする場合 */
color-scheme: light;
/* ダークテーマのみサポートする場合 */
color-scheme: dark;

ちなみに、デフォルトはcolor-scheme: normal;であり、これは「何もサポートしない」ことを表します。この場合、ブラウザのデフォルトテーマ(一般的な設定ではライトテーマ)になります。

ページ側の主張(color-scheme)とユーザー側の主張(prefers-color-scheme)は、基本的にページ側の主張が優先されます。具体的には、要素に適用されるテーマは次のマトリクスのように決められます[1]

prefers-color-scheme: light prefers-color-scheme: dark
color-scheme: light dark ライトテーマ ダークテーマ
color-scheme: light ライトテーマ ライトテーマ
color-scheme: dark ダークテーマ ダークテーマ
color-scheme: normal ブラウザのデフォルトテーマ ブラウザのデフォルトテーマ

このように、基本的にcolor-scheme側が優先されますが、color-schemeが両方のサポートを表明した場合はユーザーの選好(prefers-color-scheme)が用いられます。

なお、「ブラウザのデフォルトテーマ」はprefers-color-schemeと直接関係ないことに注意してください。ブラウザをダークモードにしても、color-scheme: normalに対してダークテーマが適用されるわけではありません。これは主に後方互換性の理由からだと思われます。color-schemeに明示的に何か値をセットすることで、要素ごとのカラースキーマという概念にオプトインすることができるのです。

カラースキーマが与える影響

このように、color-schemeを使うと要素にカラースキーマを適用することができます。このとき、要素はどのような影響を受けるのでしょうか。仕様書によれば、要素が受ける影響は以下の通りです。

  • システムカラーキーワードが示す色がカラースキーマによって変わる。
  • ブラウザが描画するUI全般(スクロールバー、フォームコントロールなど)の色が変わる。

システムカラーキーワードとは、CSS仕様で定義されている17個のキーワードのことで、ユーザー(が使用しているOSやブラウザ)によって用意されたカラーパレットを反映しています。例えば文書の背景色にふさわしい色はCanvasであり、文書のテキストにふさわしい色はCanvasTextです(キーワードとしてはcase insensitiveです)。これらのキーワードが、要素がライトテーマかダークテーマかによって変化するのです。

以上のことから、color-schemeとシステムカラーキーワードを使うと、@mediaを一切使わなくてもダークモードに対応したページを作ることができます(その代わり色を自分で選ぶことはできませんが)。具体的には、次のようにするだけです。

body {
  color-scheme: light dark;
  background-color: Canvas;
  color: CanvasText;
}

下に実際にやってみた例を示します。ブラウザをライトモードやダークモードに変更してみると、それにしたがってCanvasCanvasTextの具体的な色が変わることが確認できるはずです。

また、ブラウザが描画するUIもカラースキーマによって変わります。スクロールバーの例をみてみましょう。

こちらの例では、color-scheme: light;color-scheme: dark;を用いて、2つのdivに対してそれぞれライトテーマとダークテーマを強制しています。

筆者のMac上のGoogle Chromeでは、次の画像のようにlightかdarkかによってスクロールバーの色が異なることが観察できます。筆者が試した限りでは、Safariもスクロールバーの色が変わりました。Mac上のFirefoxはcolor-scheme自体はサポートしていますがスクロールバーの色は変わりませんでした。

codepenの描画結果のスクリーンショット。lightではライトテーマに適した色のスクロールバーが出ており、darkではダークテーマに適した色のスクロールバーが出ていることを確認できる。

このようにブラウザが描画するUIの色もカラースキーマに合わせてもらうのは、@media (prefers-color-scheme: dark)では簡単にはできません。ですから、システムカラーを使わない(色は自分で決める)場合でもcolor-schemeを使う価値があります。ダークモードに対応したページを作る際にはcolor-schemeプロパティも合わせて指定することで、より一貫したUIをユーザーに見せることができます。ただし、先述の通り、color-scheme: dark;としたら「ダークモードのみ対応」という意味になってしまい、ユーザーがダークモードを有効にしていないときでもダークモード用のUIになってしまうので注意しましょう(上の例がまさにその場合の例になっていますね)。@mediaを使って両対応している場合は、そのことをブラウザに伝えるためにcolor-scheme: light dark;とします。

ユーザーによるカラースキーマの強制

先ほど、color-schemeの方がユーザーの選好(prefers-color-scheme)よりも優先されると説明しました。しかし、これでは何だかユーザーの力が弱い気がします。ページの方でcolor-scheme: light;と書くだけでダークモードの適用を拒否されてしまいます。

そこで、ユーザーはカラースキーマのオーバーライドをすることができると定められています。ユーザーがオーバーライドを求めた場合、その選好はcolor-schemeよりも優先されます。つまり、この場合ネゴシエーションの結果は次のようになります。

prefers-color-scheme: light オーバーライド prefers-color-scheme: dark オーバーライド
color-scheme: light dark ライトテーマ ダークテーマ
color-scheme: light ライトテーマ ダークテーマ(強制)
color-scheme: dark ライトテーマ(強制) ダークテーマ
color-scheme: normal ライトテーマ(強制) ダークテーマ(強制)

お分かりの通り、ユーザーがオーバーライドという強権を発動した場合、color-schemeの値はカラースキーマの決定に関与できません。たとえcolor-scheme: lightだったとしても強制的にダークモードにされます。

ただし、実はこの場合でもcolor-schemeの指定は無価値ではありません。なぜなら、color-schemeの指定はユーザーエージェントによる色の強制書き換えの挙動に関与するからです。

ユーザーエージェントによる色の強制書き換えは、ユーザーがカラースキーマをオーバーライドした場合に、CSSで明示的に指定されている色すらカラースキーマに合うように改変してしまう(かもしれない)というものです。たとえば、ユーザーがダークモードによるオーバーライドを要求した場合、color: black;と書いてあってもその色が白に変えられる可能性があります。

このとき、そのテーマを元々サポートしていた要素は色を改変されないということになっています。元々サポートしていたというのは、上の表で「(強制)」と書かれていない場合が相当します。つまり、color-scheme: darkのような指定は「この要素はダークテーマをサポートしているから色の強制改変をしなくていいよ」という意思表示になります。

一方で、color-scheme: lightのような指定だった場合、この要素はダークテーマをサポートしていないということになるため、ユーザーエージェントはそれを強制的にダークテーマに適合させるために色の改変を行うことになります。

以上のように、color-schemeを使用することは、オーバーライドが発生した際に予期せぬ色の変更から要素を守るために使うことができます。ただし、その場合はユーザーの希望に添えるようにWebページ側で適切に対応しなければいけません。color-schemeの指定は「この要素は指定のカラースキーマに対応していますよ」と謳う宣言ですから、嘘をついてしまうとユーザーの不利益につながってしまうでしょう。

ちなみに、現在のところGoogle Chromeが実験的機能としてこのオーバーライドに対応する「強制ダークモード」を提供していますが、まだ上記のような仕様はサポートしておらず、color-schemeを無視して全部色を改変するようです。

color-schemeの構文②: ユーザーの強権に対抗する

上述のように、ユーザーが強制オーバーライドを発動させた場合はcolor-schemeを指定してもカラースキーマの決定に関与できません。

しかし、実はcolor-schemeにはonlyというキーワードを持っており、これをつけた場合は強制オーバーライドを拒否できます。onlylightdarkと一緒に指定します。

/* 絶対にライトモードのみの場合 */
color-scheme: only light;
/* 絶対にダークモードのみの場合 */
color-scheme: only dark;

onlyがついているカラースキーマは、ユーザーが強制オーバーライドを発動しても自身が宣言したカラーテーマを保ちます。前の表にonlyつきの場合を書き加えるとこうなります。

prefers-color-scheme: light オーバーライド prefers-color-scheme: dark オーバーライド
color-scheme: only light dark ライトテーマ ダークテーマ
color-scheme: light dark ライトテーマ ダークテーマ
color-scheme: only light ライトテーマ ライトテーマ
color-scheme: light ライトテーマ ダークテーマ(強制)
color-scheme: only dark ダークテーマ ダークテーマ
color-scheme: dark ライトテーマ(強制) ダークテーマ
color-scheme: normal ライトテーマ(強制) ダークテーマ(強制)

このように、onlyがあればユーザーの強制オーバーライドにも負けずに自身のテーマを維持できます。強制オーバーライドを拒否したので、ユーザーエージェントによる色の強制改変も受けません。

まとめると、要素のカラースキーマ決定に対する影響力の強さは4段階あります。弱い方から強い方へ並べると次の順番になります。

影響力 内容 備考
1 prefers-color-scheme (オーバーライドなし) color-scheme: light dark の場合にのみ決定に関与できる
2 color-schemeonlyなし)
3 prefers-color-scheme (オーバーライド)
4 color-schemeonlyあり)

ただし、onlyについては現段階だとサポートしているブラウザがSafariのみとされています。筆者はSafariでオーバーライドありのprefers-color-schemeを発動させる方法がわからずonlyの挙動が確かめられませんでした。Safariマスターの方の助言をお待ちしております。

以上でcolor-schemeの話は終わりです。今回の仕様の内容はあと2つあるので軽く紹介します。

forced-color-adjust: カラーパレットの強制に対抗する

ユーザーエージェントは、さらに強力なカラーパレットの強制機能を有効化する場合があります。これは、使用可能な色を前述のシステムカラー17種類に制限してしまうものです。一般的に、カラーパレットの強制はユーザーがハイコントラストモードを使用している場合に起こるとされています[2]。この場合も、やはりWebページ側でCSSから指定した色が強制的に書き換えられます。

これに対して次のようにforced-color-adjustを使用することで、カラーパレットの強制を無効化することができます。

forced-color-adjust: none;

これを試してみたのが次のCodePenです。

Google Chromeではforced-colors: activeをエミュレートする機能があり、これを有効にするとカラーパレットの強制(ハイコントラストモード)を試すことができます。実際にやってみると、次のスクリーンショットのようになります。

スクリーンショット

このように、カラーパレットの強制がされている状況では、色に関するCSSの指定が無視されます。そのため、colorとしてredbluepurpleを指定しても全部黒色にされています。ただし、forced-color-adjust: noneで囲まれた要素の中ではカラーパレットの強制が行われず、色の指定が有効になります。

このように、forced-color-adjustを使うとカラーパレットの強制を拒否できます。

ただし、対抗という紹介の仕方をしておいて何ですが、拒否するためにこのプロパティを使うべきではありません。それはアクセシビリティに悪いからです。むしろ、ブラウザに任せるより自分でやったほうがアクセシビリティを良くできる場合に、@media (forced-colors: active)と一緒に使って表示を調整するのに使うべきでしょう。

ちなみに、forced-color-adjust: preserve-parent-colorという値もありますが、これはユースケースが限定的なので説明を省略します。

余談ですが、カラーパレットの強制では、色はシステムカラーに統一されます。そのため、元々システムカラーがキーワードで指定されていたところはカラーパレットの強制を受けないという特徴があります。たとえば、Canvas(文書の背景色用のシステムカラー)が#ffffffだったとしても、color: #ffffff;color: Canvas;ではカラーパレットの強制下では違いが生まれるかもしれません。

/* カラーパレットの強制下では白背景に黒文字になる */
background-color: #000000;
color: #ffffff;

/* カラーパレットの強制下では白背景に白文字になる(迷惑) */
background-color: #000000;
color: Canvas;

このことから、システムカラーを中途半端に使うのは良くありません。システムカラーは大抵背景色用と文字色用がセットになっていますから、それらをセットで運用するのが吉です。

print-color-adjust: 印刷時のインクの節約に対抗する

最後に紹介するのはprint-color-adjustです。ページの印刷というのもまた、色の調整が起こりやすいところです。インク資源の問題などから白黒印刷の需要は大きく、そのためにはページを白と黒だけで表現しなければいけません。また、黒背景で白文字のページなどを真に受けて印刷すると紙がインクまみれになるため、勝手に白背景に黒文字に直すといったことも行われます。

print-color-adjustを用いるとそのような修正を許可するかどうかを設定できます。従来の慣習に合わせてかデフォルト値は許可する(economy)と定義されているので、このプロパティのサポートによって許可しないという選択肢が生まれたことになります。

/* 許可する(デフォルト値) */
print-color-adjust: economy;
/* 許可しない */
print-color-adjust: exact;

プロパティの値が面白いですね。autononeなどではなくeconomyexactです。インクの節約といった資源上のユースケースを意識してeconomyとしているのでしょう。

まとめ

この記事ではCSS Color Adjustment Module Level 1の内容を紹介しました。この仕様ではユーザーエージェントによる色の自動調整と、それに割り込んだりネゴシエーションしたりするためのCSSプロパティが定義されています。色の自動調整はアクセシビリティにも大きく関わる部分ですから、アクセシビリティも保ちつつこの記事の内容を活用しましょう。

Q&A

Q. 記事のタイトルが意味分からないんだけど。

A. トリプルデラックス面白いですよ。

参考リンク

この記事の説明の内容を網羅した日本語記事は見つかりませんでしたが、断片的な内容については既存の日本語記事があったので紹介します。

https://web.havincoffee.com/css/css3/modules/color_adjustment.html

https://hacknote.jp/archives/11460/

脚注
  1. ただし、ブラウザがライトテーマとダークテーマの両方をサポートしている場合です。仕様上、ブラウザはライトテーマのみサポートしたりダークテーマのみサポートしたりすることも許されており、その場合は異なる結果になります。 ↩︎

  2. ただし、カラーパレットの強制という概念自体はハイコントラストモード以外にも対応する一般的な概念として定義されています。場合によってはローコントラストなカラーパレットや特定の色相に偏ったパレットに強制されるといったことが起こるかもしれません。 ↩︎

GitHubで編集を提案

Discussion