このページの色をかけた魂の戦い
皆さんこんにちは。この記事では、筆者が最近勉強した CSS Color Adjustment Module Level 1 の内容をご紹介します。
この仕様は記事執筆現在Candidate Recommendationという段階にあり、一部ブラウザでの実装もされています。
どんな仕様?
仕様の名前が示している通り、この仕様はページの色の調整に関するCSSプロパティやその関連概念を定義するものです。特に、この仕様はページの色(カラースキーマ)をどう調整するかに関して著者(ページのCSSを書く人)とユーザーとの間のネゴシエーションを可能とする点が特徴的です。
color-scheme
: サポートするカラースキーマの表明
昨今、ダークモードという概念がWebやその周辺を圧巻し、多くのアプリやウェブサイトがダークモードに対応しています。CSSにおいては、@media
とprefers-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;
}
下に実際にやってみた例を示します。ブラウザをライトモードやダークモードに変更してみると、それにしたがってCanvas
やCanvasText
の具体的な色が変わることが確認できるはずです。
また、ブラウザが描画するUIもカラースキーマによって変わります。スクロールバーの例をみてみましょう。
こちらの例では、color-scheme: light;
とcolor-scheme: dark;
を用いて、2つのdivに対してそれぞれライトテーマとダークテーマを強制しています。
筆者のMac上のGoogle Chromeでは、次の画像のようにlightかdarkかによってスクロールバーの色が異なることが観察できます。筆者が試した限りでは、Safariもスクロールバーの色が変わりました。Mac上のFirefoxはcolor-scheme
自体はサポートしていますがスクロールバーの色は変わりませんでした。
このようにブラウザが描画する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
というキーワードを持っており、これをつけた場合は強制オーバーライドを拒否できます。only
はlight
やdark
と一緒に指定します。
/* 絶対にライトモードのみの場合 */
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-scheme (only なし) |
|
3 |
prefers-color-scheme (オーバーライド) |
|
4 |
color-scheme (only あり) |
ただし、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
としてred
やblue
やpurple
を指定しても全部黒色にされています。ただし、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;
プロパティの値が面白いですね。auto
やnone
などではなくeconomy
とexact
です。インクの節約といった資源上のユースケースを意識してeconomy
としているのでしょう。
まとめ
この記事ではCSS Color Adjustment Module Level 1の内容を紹介しました。この仕様ではユーザーエージェントによる色の自動調整と、それに割り込んだりネゴシエーションしたりするためのCSSプロパティが定義されています。色の自動調整はアクセシビリティにも大きく関わる部分ですから、アクセシビリティも保ちつつこの記事の内容を活用しましょう。
Q&A
Q. 記事のタイトルが意味分からないんだけど。
A. トリプルデラックス面白いですよ。
参考リンク
この記事の説明の内容を網羅した日本語記事は見つかりませんでしたが、断片的な内容については既存の日本語記事があったので紹介します。
Discussion