「スマートに書く」 とかいうと無駄にハードル上がって目から鱗のテクニック的なものを期待されちゃいそうですが......
IE を無視していい環境であれば、すでに多くの人がこういう書き方してると思うんですけども、ダークモード (@media (prefers-color-scheme: dark)) に対応した CSS を書く際に、CSS カスタムプロパティ (CSS Variables) を使用するととっても楽ですよというお話。
ちなみに、CSS カスタムプロパティと、prefers-color-scheme メディアクエリのブラウザサポート状況は下記の通り。
CSS カスタムプロパティはすでに数年前から、EdgeHTML 版の Edge (ややこしいな...... 所謂 Edge Legacy と呼ばれているやつ) を含め、IE 以外は問題なく使用できます。
prefers-color-scheme メディアクエリに関しては、Chromium 版 Edge 待ちではありますが、主要なブラウザではサポートの足並みがそろった状態。まぁ、prefers-color-scheme メディアクエリの方は別に対応してなきゃしてないでダークモードにならないだけなので特に問題はないのですが。
なので、冒頭に書いたとおり、IE って何ですか? という (恵まれた) 環境で開発をしている人にとってはごく当たり前に使う CSS の機能かと思います。
具体的なサンプルソース
いきなりですが本題。具体的には下記のように CSS カスタムプロパティを使用して、選択されたカラーテーマごとに色の設定をしてあげます。
/* ダークモード以外(ライトモード)向けの基本カラー設定 */
:root {
--bg-color: rgba(250, 250, 250, 1);
--bg-elevation-01-color: rgba(255, 255, 255, 1);
--bg-shadow-color: rgba(18, 18, 18, .1);
--font-color: rgba(18, 18, 18, 1);
--link-color: rgba(194, 24, 91, .95);
--link-hover-color: rgba(194, 24, 91, 1);
--link-visited-color: rgba(142, 36, 170, 1);
}
/* ダークモード向けのカラー設定 */
@media (prefers-color-scheme: dark) {
:root {
--bg-color: rgba(18, 18, 18, 1);
--bg-elevation-01-color: rgba(31, 31, 31, 1);
--font-color: rgba(255, 255, 255, .95);
--link-color: rgba(236, 64, 122, .95);
--link-hover-color: rgba(236, 64, 122, 1);
--link-visited-color: rgba(186, 104, 200, 1);
}
}
この時、基本カラー設定の方を @media (prefers-color-scheme: light) {...} 内に記述してもよいのですが、セットするカスタムプロパティに差があると、場合によってはダークモード時にカスタムプロパティが定義されていない状態になってしまいますので、今回は書いていません。
あとは下記のように呼び出せば、ライトモード、ダークモードで別々の CSS を書かなくても簡単にダークモードに対応させることができます。
body {
background-color: var(--bg-color);
color: var(--font-color);
...(略)...
}
a:any-link {
color: var(--link-color);
text-decoration: underline;
}
a:hover,
a:focus {
color: var(--link-hover-color);
}
a:visited {
color: var(--link-visited-color);
}
header {
background-color: var(--bg-elevation-01-color);
box-shadow: 0 3px 3px -2px var(--bg-shadow-color),
0 3px 4px 0 var(--bg-shadow-color),
0 1px 8px 0 var(--bg-shadow-color);
}
main {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: minmax(200px, auto);
grid-gap: 20px;
padding: 20px;
}
article {
background-color: var(--bg-elevation-01-color);
box-shadow: 0 3px 3px -2px var(--bg-shadow-color),
0 3px 4px 0 var(--bg-shadow-color),
0 1px 8px 0 var(--bg-shadow-color);
padding: 20px;
}
これで、下記のようにライトモード、ダークモード、それぞれにスタイルを適用することができます。

上記画像が、通常 (ライト) モード。下の画像が、ダークモードに切り替えたときの表示。

こういう、状況に応じて値を簡単に変更できるのが、定義した値を動的に呼び出すことができる CSS カスタムプロパティの便利なところですね。
ちなみに今回の例で使用した HTML / CSS は下記にサンプルとして置いてあります。HTML はかなり適当なのであれですが、参考まで。
ちなみに、IE に対応しないといけない環境でも使いたい、という場合は、IE 向けにだけ頑張って別の CSS 書くか、Ponyfill を使うというのも手かもしれません (本番環境で使ったことがないので安易におすすめはできないですけども)。
などと書いておきながら、この Blog をリニューアル (参考エントリー) してダークモード対応した時は CSS カスタムプロパティ使ってないんですよね。長いこと運用している Web サイトなど、過去に書いたコードを再利用しながらリニューアル、みたいな場合はちょっと難しいです。やっぱ最初からきちんと CSS を設計して書かないといけません......
あと、CSS カスタムプロパティについては過去に記事 (下記) を書いてるんですが、2014年とかさすがに昔過ぎるだろということで、もしかすると改めて解説記事書くかも。この辺は calc() 関連の記事も同様なのでちょっと頑張って書き直そうかな。