filterを使用したダークモード対応

Posted on
filterを使用したダークモード対応

ウェブサイトをダークモード対応する方法の一つとして以下のようなCSSのメディアクエリである「prefers-color-scheme: dark」を使用する方法があります。


@media (prefers-color-scheme: dark) {
	html {
		background: #000;
		color: #FFF;
	}
}

この中にスタイルを記述することでユーザーがダークモードをONにしているときに適用されるCSSを指定することができます。
方法は単純なのであとは色を変更したい箇所を記述していくだけですが、やるとなるとサイトの規模によっては修正範囲が広くなりそうです。

そこで要素ごとに色を指定しなくてもCSSのfilterで全体をいい感じに調整する方法を試してみました。

やりたいこと

  • CSSのfilterを使用してダークモードに対応する
  • ユーザーが設定しているモードに合わせて表示を出し分ける
  • モードの切り替えボタンを設置する
  • モードを切り替えた際にローカルストレージに値を保存して次回訪れたときも同じモードで表示する

filterによる色の調整

html要素に以下のようにfilterでinvertとhue-rotateを指定します。これだけでページ全体の白と黒が反転します。
モードの切り替えボタンを設置するため、html要素のdata-modeというカスタムデータ属性がdarkだったときにfilterが効くようにしています。切り替えについては後述します。

html[data-mode="dark"] {
    filter: invert(1) hue-rotate(180deg);
}

invert
配色を反転させる指定で、黒は白に、白は黒になります。
すべての色を反転させる効果があるので緑は赤になります。

hue-rotate
色相を変更させる指定で、白と黒以外の色で有効です。
上記のinvert(1)で緑が赤に反転するので色相を180度回転させて元の緑に戻します。

invertとhue-rotateを組み合わせることで白と黒だけを反転させて、それ以外の色はそのままにしておくことができます。

画像の調整

html要素にfilterをかけると画像の色まで変わってしまうのでimgは以下の記述で更に反転させて色を戻す必要があります。

html[data-mode="dark"] img {
		filter: invert(1) hue-rotate(180deg);
}

背景画像の調整

背景画像も色が変わるので戻したいのですが背景画像の色だけをうまく戻すことができなかったので、
背景画像を指定している要素自体の色を戻したうえで別途ダークモード用の色を設定します。

html[data-mode="dark"] .bgimg_area {
		filter: invert(1) hue-rotate(180deg);
		color: #FFF;
}

暗いエリアの調整

もともと背景色が暗い色だったエリアはfilterで逆に明るくなってしまうので調整します。

html[data-mode="dark"] .dark_color_area {
	filter: invert(1) hue-rotate(180deg);
}

黒い画像の調整

imgの色を戻しているため黒い画像は黒い画像として表示されるので黒背景に埋もれて見えづらくなってしまいます。そのため反転を戻して明るく調整します。

html[data-mode="dark"] .black_img {
	filter: invert(0) hue-rotate(0);
}

以上の調整を行い、filterによるダークモードは完成です。
あとはモードの切り替えボタンの設置やユーザーの設定によるモードの出し分けを行います。

dark / lightの切り替えボタンを設置

ユーザーが自由にモードを変更できるよう切り替えボタンを設置します。

<div class="mode_change">
<button data-mode="dark">Dark</button>
<button data-mode="light">Light</button>
</div>

カスタムデータ属性を使用してDarkのボタンにはdata-mode”dark”を、Lightのボタンにはdata-mode=”light”を設定しています。

$('.mode_change button').on('click', function(){
	let selectMode = $(this).attr('data-mode');
	localStorage.setItem('mode', selectMode);
	selectMode == 'light' ? $('html').attr('data-mode', 'light') : $('html').attr('data-mode', 'dark')
});

JSではクリックしたボタンのdata-modeを取得してhtmlのdata-modeの値を変更します。
また、次回サイトに来たときも切り替えたモードで表示させたいのでローカルストレージにモードの値を保存しておきます。

モードの出し分け

ダークモードにしているユーザーにばダークモードで表示し、
ローカルストレージにモードが保存されていればそのモードを優先して表示するようにします。

let isdark = window.matchMedia('(prefers-color-scheme: dark)').matches,
		localMode = localStorage.getItem('mode');

if (localMode) {
	$('html').attr('data-mode', localMode);
} else {
	isdark ? $('html').attr('data-mode', 'dark') : $('html').attr('data-mode', 'light')
}

まずローカルストレージにモードが保存されていればそのモードをhtmlのdata-modeに入れます。
ローカルストレージにモードが保存されていなければ、window.matchMediaでdarkモードかを判別しダークモードだったらdata-modeにdarkを入れます。

以上で完成です。

まとめ

  • 割と手軽になんちゃってダークモードができましたが背景画像の要素など細かい調整は必要だった。
  • htmlにfilterをかけることで全体を反転してくれるので透過pngの場合はダークモード用の画像を用意する手間が省けた。
  • firefoxでposition: fixed;を使っている要素がabsoluteに変わってしまう。(filterをかける場所によっては回避できるかも?)
  • ローカルストレージがcookieよりも手軽。
  • filterはあくまで簡易版。ちゃんとやるならCSSの変数とか使って切り替えるのが良さそう。

おしまい。