Bulma 1.0.0 の automatic Dark mode を制御する
今回も引き続き Bulma 1.0.0 ネタだ。
前回 Bulma 1.0.0 に合わせてスタイル調整した話を書いた。 加えて Bulma 1.0.0 で automatic Dark mode が導入されたのにも触れたが、これをうまく制御するのに多少のコツが必要だっので記しておく。
ドキュメントにも記載あるが、 Bulma の automatic Dark mode は prefers-color-scheme
の media query を使ってる。これが automatic Dark mode の automatic の正体だ。
手動で light/dark mode を使い分けるには data-theme
を指定する。この data-theme="light
data-theme="dark"
属性を HTML に差し込んだり、 ユーザごとに設定を保存するのは、 JavaScript を書かないといけない。
そういう仕組みなので light/dark mode に関する style は以下のようにするのが多分 Bulma の推奨パターンのはず。
blog-fable/sass/style.scss at aa668a3e991a46e9b3b3d0302bdbc717d146665e · krymtkts/blog-fable
@media (prefers-color-scheme: light) {
:root {
@include light-theme();
}
[data-theme="dark"] {
@include dark-theme();
}
}
@media (prefers-color-scheme: dark) {
:root {
@include dark-theme();
}
[data-theme="light"] {
@include light-theme();
}
}
このように prefers-color-scheme
の内側に逆の色の [data-theme="*"]
を仕込むことで、システムのテーマ設定に関わらず指定のカラーテーマにできる。
システムにテーマを表明されないときは prefers-color-scheme
が light
になるらしい。
確認したことはないが、古い OS でカラーテーマが取れないときとか? prefers-color-scheme - CSS: Cascading Style Sheets | MDN
ただ、この仕組みにしていると多少問題がある。
prefers-color-scheme
が取れるタイミングと [data-theme="*"]
が取れるタイミングに差がある実装だと、ページ遷移時にスタイルの切り替わりで画面がちらつく。
「prefers-color-scheme
のカラースキーマ反映 -> JavaScript で data-theme
設定される -> data-theme
のカラースキーマ反映」となるとダメ。
このチラつきは Bulma の official page でも起こってる。
これを解消するには、HTML 文書で JavaScript → CSS の読み込み順になるよう調整し、かつ JavaScript 読み込み時にユーザ設定値を復元して data-theme
に設定する。
ただし注意点としては、 data-theme
の設定は JavaScript 読み込み時にやりたくても、テーマ切り替えのボタンに event を binding するのは後回しの方が良さそう。
blog-fable ではあまりに早過ぎて event を bind したい要素が準備されてなかったようで bind できなかったので、 load
event で binding するようにした。
以下 blog-fable の例。 blog-fable/src/Handler.fs
module Handler
open Browser.Dom
open Browser.WebStorage
// NOTE: Don't use Fable library in this file because it is directly bundled int HTML files.
// This includes, discriminated union, List, etc.
let private themeKey = "theme-mode"
let private themeAttributeName = "data-theme"
let private setThemeMode (t: string) =
match t with
| "light"
| "dark" ->
localStorage.setItem (themeKey, t)
document.documentElement.setAttribute (themeAttributeName, t)
| _ ->
localStorage.removeItem themeKey
document.documentElement.removeAttribute themeAttributeName
localStorage.getItem "theme-mode" |> setThemeMode
let private init _ =
let els = document.querySelectorAll (".theme-toggle")
for i = 0 to els.length - 1 do
let el = els.item (i)
let themeMode = el.getAttribute "data-theme"
el.addEventListener ("click", (fun _ -> setThemeMode themeMode))
window.addEventListener ("load", init)
blog-fable は F# からトランスパイルした JavaScript を成果物にコピっているだけなので、 Fable のライブラリのインポートを必要とするコードは書けないという縛りがある。
なので discriminated union や List.*
といったものも使わない質素な実装になっている。
ここまでやったら、 blog-fable のページ遷移に限ってかも知れないがチラつきは一切起こらなくなり、マニュアルでのテーマ切り替えも動作するようになった。
他にもちまちまとスタイルの補正をしている箇所はあるが、これにて大体 Bulma 1.0.0 対応ができたもといえそう。 前より自作スタイルの CSS のサイズが 2 倍超デカくなってるのは気になるが、これも少しずつ改善重ねていけれたらよいか。