【Advance】ダークモード対応のためのCSS

ダークモード対応とは

現在のOSではアプリモード (Windows)や外観モード (Mac)を切り替えることで、外観の色調を変更できます。
時間帯によって切り替わるよう、設定されている方もいらっしゃるのではないでしょうか。

暗色ベースの“ダークモード”設定時に、真っ白い画面が表示されると目に刺さりますよね。
そこでWebページやWebアプリの元のデザインが暗色ベースの配色でない場合は、ダークモード用のカラーを設定して見やすくすることがあります。
ダークモードの人が見やすいように最適化することを“ダークモード対応”とか“ダークモード対策”などと呼びます。

ダークモード表示の確認方法は?

ダークモードでのWebページ表示は、大きく以下3つの方法で確認できます。

1.ご利用のOSで切り替える
2.ブラウザ設定で切り替える
3.検証モードで切り替える

コーディング時にはライトモードとダークモード、どちらも見なければいけません。
いちいちシステム設定とかを開くのは面倒ですので、『3』が望ましいです。

Mozilla Firefoxでのダークモード検証方法

個人的に、ライト/ダークモードのカラー確認におすすめなのがMozilla Firefox。
検証ツール(※右クリックメニューから“調査(Q)”で起動)に切り替え機能が付属しています。

アイコンをクリックしても、表示が変わらない時はリロード(再読み込み)してください。
ダークモード表示に変わります。

Google Chromeでのダークモード検証方法

Google Chromeの検証ツールにも、ダークモードの検証機能がついています。
Firefoxよりちょっと面倒ですが、以下の方法で切り替えられます。

Step1. Renderingを表示する

検証ツールの右上にあるアイコン(Customize and control DevTools)をクリック。
More tools から [ Rendering ]をクリックして下さい。

Renderingのタブが追加されます。

Step2. ダークモード表示にする

Renderingをスクロールダウンして、Emulate CSS media feature prefers-color-schemeという項目を見つけて下さい。
プルダウンから [ prefers-color-scheme: dark ] を選択します。

こちらもすぐ切り替わらない場合は、リロード(再読み込み)してください。

これでダークモード表示が確認できます。
ライトモードに戻す時は、再びプルダウンで切り替えて下さい。

ちなみに、一つ上にあるEnable automatic dark modeは、チェックを入れるとダークモード非対応のページでもブラウザが自動で配色を変える機能です。CSSを書くときには邪魔になるので、使用しません。

例えば、SkillHubのブログ。
ダークモード設定はされていないので [ prefers-color-scheme: dark ]で表示は変わりません。
しかし、Enable automatic dark modeを有効にすると表示が変わります。

prefers-color-schemeによるダークモード対応

ダークモード用のCSSを書いて、それを適用させる方法はいくつかあります。

  • CSSのメディア特性prefers-color-schemeを使う
  • CSSのlight-dark()関数を使う
  • JavaScriptでidもしくはclassを付け外しして切替える
  • JavaScript+HTMLのカスタムデータ属性(data-〇〇)を使う

この中で最もオーソドックスなのが、『prefers-color-scheme』を使う方法。
Google Chromeで、ダークモード表示させるために切り替えたやつですね。

今回は、こちらの設定方法を見ていきましょう。

prefers-color-schemeとは

prefers-color-schemeはCSS のメディア特性で、ユーザーがシステムに要求したカラーテーマが明色か暗色かを検出するために使用します。

引用元:prefers-color-scheme - CSS: カスケーディングスタイルシート | MDN

メディア特性とは、メディアクエリー@media()でスタイルを適用するための“条件”に使える特徴のこと。今回のprefers-color-schemeであれば「ユーザーが明るい・暗い、どちらの色遣いを望んでいるか」を検出し、それを元にCSSを振り分ける形です。

CSSでprefers-color-schemeを使う時は以下のように書きます。
ユーザーが選択しているモードが明色系(light)か、暗色系(dark)かを検知して、それぞれ振り分けてくれます。

@media (prefers-color-scheme: dark) {
  /* ダークモードの場合の設定を書く */

}

@media (prefers-color-scheme: light) {
  /* ライトモードの場合の設定を書く */

}

ただし、darkとlightの両方を書く必要はありません。
これは、レスポンシブコーディングをするときと同じで、@media() { ... }括られていないスタイル指定は全体に効くためです。

明色系のデザインで、ダークモードのみ表示色を変えたい場合、@media (prefers-color-scheme: dark) { }の中にCSSを書くだけでOKです。

ダークモード表示の確認

以下の簡単なHTML&CSSで実験してみましょう。
完全黒(#000000)や白(#FFFFFF)だと目に刺さるので、黒部分は#151515・白部分は#F7F7F7を使います。

sample3.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ダークモード設定</title>
  <style>
    body,
    .bg-default{
      background: #FFFFFF;/* 白 */
      color: #000000;/* 黒 */
    }
    .bg-main{
      background-color: #669900; /* 緑 */
      color: #FFFFFF;
    }
    .bg-accent{
      background-color: #F15A24; /* 橙 */
      color: #FFFFFF;
    }
    /* ダークモードの時 */
    @media (prefers-color-scheme: dark) {
      body{
        background: #151515;/* 黒 */
        color: #F7F7F7;/* 白 */
      }
    }
  </style>
</head>
<body>
  <header class="bg-main">
    .bg-main{}の設定色
  </header>
  <p>body{}の設定色</p>
  <div class="bg-accent" style="padding:2.5rem;">
    <p>.bg-accent{}の設定色</p>
    <p class="bg-default">.bg-default{}の設定色</p>
  </div>
</body>
</html>

ブラウザで表示し、カラースキームを切り替えてみます。

ページ全体の背景色と、body直下のp要素「body{}の設定色」の文字色が変わりました。
@media (prefers-color-scheme: dark) { }内にスタイル指定があれば上書きされる、ということが確認できました。

CSS変数を使って、変更を最低限に

@media (prefers-color-scheme: dark) { }内に、ダークモードの時に変更して欲しい部分を一つずつ書いても表示は問題ありません。

ただ、実際のWebサイト/ページを構成するCSSには、背景色・文字色・線色など“色”の指定が沢山あります。
これを逐一、拾い上げて上書きするのは大変

先程の実験用HTMLでも「緑とオレンジを、もうちょっと目に優しい感じにしたい」とか、細かく設定していくとこんな感じになります。

@media (prefers-color-scheme: dark) {
    body,
    .bg-default{
        background: #151515;
        color: #F7F7F7;
    }
    .bg-main{
        background-color: #638420;
        color: #F7F7F7;
    }
    .bg-accent{
        background-color: #d95626;
        color: #F7F7F7;
    }
}

もし.bg-mainだけではなく、.text-mainや.border-mainなどのクラスもあれば、それらも個別に上書き。
デモコードではなく実際のWebサイト/ページであれば結構な量になりますし、直し忘れも出てきそうですよね。

そこで、CSS変数が役立ちます。
CSS変数で色を管理する形にすれば、変数宣言箇所だけ書き換えれば終わります。

:root {
    --color_default-base: #FFFFFF;/* 白 */
    --color_default-text: #000000;/* 黒 */
    --color_main: #669900; /* 緑 */
    --color_accent: #F15A24; /* 橙 */
}
body,
.bg-default{
    background: var(--color_default-base);
    color: var(--color_default-text);
}
.bg-main{
    background-color: var(--color_main);
    color: var(--color_default-text);
}
.bg-accent{
    background-color: var(--color_accent); 
    color: var(--color_default-text);
}  
/* ダークモードの時 */
@media (prefers-color-scheme: dark) {
    :root{
        --color_default-base: #151515;
        --color_default-text: #F7F7F7;
        --color_main: #638420;
        --color_accent: #d95626;
    }
}

メディアクエリの中の記述が少なく済み、楽です。
CSSの記述量、色関連の設定が多いほど、CSS変数を使うメリットを感じられると思います。

ダークモードCSS作成時の注意点

@media (prefers-color-scheme: dark) { }でも、CSSはファイルの上から読み込まれる(重複箇所は上書きする)というルールは変わりません
ダークモード配色を設定したメディアクエリの後ろ(下)で、別の色指定を書くと、そちらが優先されます。

例えば、以下のような場合です。

/* ダークモードの時 */
@media (prefers-color-scheme: dark) {
    body,
    .bg-default{
        background: #151515;
        color: #F7F7F7;
    }
}

.bg-default{
    background: #666;
}

通常表示でも、ダークモード表示でも、最後に書かれた#666が適用されています。

ちょっと紛らわしいのが、CSS変数を上書きした以下のようなケースです。
このように書いた場合、ダークモードの表示はどちらになるでしょう。

.bg-default{
    --color_default-base: #666;
}
/* ダークモードの時 */
@media (prefers-color-scheme: dark) {
    :root{
        --color_default-base: #151515;
        --color_default-text: #F7F7F7;
        --color_main: #638420;
        --color_accent: #d95626;
    }
}

灰色になりました。
これはスコープ(セレクタ)による優先度が関係しています。

メディアクエリ内で書いたのは :root 、HTMLだとhtml{}とほぼ同じ扱いになります。
対して、変数の上書きは.bg-default{}で行われています。

CSSではclassセレクタの指定の方が、要素セレクタよりも優先して採用されます。
複数の要素やclassを並べる、idセレクタで指定すると更に優先順位が高くなります。

変数を使わない場合も、以下のように書くと、下のメディアクエリより優先されます。

body .bg-default{
    background: #666;
}

/* ダークモードの時 */
@media (prefers-color-scheme: dark) {
    body,
    .bg-default{
        background: #151515;
        color: #F7F7F7;
    }

これと同じようなことが、CSS変数の上書きでも起きます。
:rootに対する指定よりも、classをスコープに上書きしたものの方が優先順位が高いと見做され、採用されるのです。

細かくCSS変数を上書きしてしまうと、ダークモード設定時やサイトの配色を変えたい時に「なんでや!」となります。
色の変数に関しては上書きを極力避け、定数的な使い方をしたほうが、CSS全体を作りやすいと思います。

ダークモードの画像について

目に優しいように、という意識でダークモード向けカラーを設定していくと、画像だけ目立つ・眩しく見えることがあります。
明るい写真、特に白背景は浮きますね。

CSSだけで、ダークモードでも見やすいよう画像の色味を調整することも出来ます。
「画像はいかなるときも鮮明に見せたい」という場合はそのままでも良いですが、せっかくなのでやってみましょう。

CSS filter

画像の色味を調整するにはCSSのfilterプロパティを使います。
filterは、要素に対して彩度や色相の調整、ブラー(ぼかし)などの効果を加えられるプロパティです。
呼び名通り、簡単なフィルターをかけたような効果を与えることが出来ます。

与えるフィルター効果は、プロパティ値部分で関数を使って指示します。
例えば、彩度を変えたい場合はsaturate()関数、明度を変えたい場合はbrightness()関数を使う、といった具合です。

どちらも100%が元画像の状態、100%より低ければ彩度・明度を下げる、100%より大きい場合は彩度・明度を上げる形で働きます。

@media (prefers-color-scheme: dark) {
    :root{
        --color_default-base: #151515;
        --color_default-text: #F7F7F7;
        --color_main: #638420;
        --color_accent: #d95626;
    }
    img {
        filter: saturate(90%) brightness(85%);
    }
}

検証ツールでfilterプロパティの有効/無効を切り替えてみると、どのくらい変わっているか分かりやすいと思います。

単にbrightness()で明度だけを変えると、黒潰れしたり、見にくくなります。
上例のようにsaturate()で彩度を下げるか、セピア効果を追加するsepia()あたりを組み合わせて使うのがおすすめ。
セピアも目に優しい感はありますね。

CSS filterで使える関数については、MDNでまとめられています。
ダークモード以外にも活用幅は広いので、ぜひご参照ください。

filter - CSS: カスケーディングスタイルシート | MDN
https://developer.mozilla.org/ja/docs/Web/CSS/filter

無料ビデオ講座のお知らせ

Skillhub [スキルハブ]では無料の動画講座を多数公開しています。他校だと数万円するような講座が無料で受講できます。

無料講座一覧を見る

×