作ったものとか » Hugo テーマ

Hugo テーマ 4. ダークモード

2021-11-01 22:39:58 hugo

現在作成途中のテーマ cheep をもとに、ダークモードへの取り組みを解説します。 今回は、比較的 Hugo 成分は少なめです。

昨今では、ダークモードの浸透が進んでいます。

PC関係の OS でもスマホでもダークモードに対応していることもあり、ウェブサイトとしても違和感の少ない表示をする重要性は高くなってきています。

なお、今回のやり方はモードの能動的な切替を意識したものではなく、ページのロード時に OS やブラウザーがどちらのモードで表示しようとしているかで切り替える方法です。

単純だけど面倒な対応

最も単純なのは、メディアクエリーで、ダークモードの場合にスタイルを追加する(結果的にスタイルが上書きされる)方法です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* 普通のスタイル定義 */
body {
  background-color: white;
  color: black;

  padding: 0;
  margin: 0;
}

@media (prefers-color-scheme: dark) {
  /* ダークモードの場合のスタイル定義 */
  body {
    background-color: black;
    color: white;
  }
}

この方法では、色に関する定義を半ば重複する形で記述する必要があるのが難点です。

「普通のスタイル定義」の方では、配置やページ構成に関する定義と、ダークモードではない場合の色の定義をしておくことになるでしょう。

カスタムプロパティの利用

カスタムプロパティ(Can I use )を使うことで、重複した色の設定定義を省くことができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/* 普通のスタイル(色)定義 */
:root {
  --bgcolor: white;
  --color: black;
}

@media (prefers-color-scheme: dark) {
  /* ダークモードの場合のスタイル(色)定義 */
  :root {
    --bgcolor: black;
    --color: white;
  }
}

body {
  background-color: var(--bgcolor);
  color: var(--color);

  padding: 0;
  margin: 0;
}

-- から始まる --bgcolor--color がカスタムプロパティです。

最初に色の定義をして、その後にダークモードを意識せずに background-color 等に値を設定しています。

コードブロックのシンタックスハイライト

Hugo のシンタックスハイライト機能を使っている場合、ダークモードでは暗めのスタイルにしたい事があると思います。

CSS の @import でもメディアクエリーを条件にすることはできるようなのですが、not 演算がうまく働かなかったり、SCSS からの変換時に削ぎ落とされてしまったりします。 そのため、cheep ではこの方法をとっていません。

標準的なシンタックスハイライトをする方法としては、hugo gen chromastyles --style=xxx > static/css/syntax.css などとしてハイライト用の CSS をファイルとして保存し、そのファイルを読み込みます。

ダークモードの場合に動的に CSS を生成する、ということはできないので、予め通常時とダークモード時の2通りのファイルを生成しておき場合に応じてファイルを切り替えるというアプローチにしました。

cheep では、JavaScript を使って切り替えます。 JavaScript でメディアクエリー(ダークモードか)にマッチした場合、ダークモード版の CSS を読み込み、そうでなければ通常時の CSS を読み込みます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<script>
    document.addEventListener("DOMContentLoaded", function() {
        var css=document.createElement("link");
        css.setAttribute("rel","stylesheet");
        css.setAttribute("type","text/css");
        if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
            css.setAttribute("href","{{ .Site.BaseURL }}css/syntax_dark.css");
        } else {
            css.setAttribute("href","{{ .Site.BaseURL }}css/syntax.css");
        } 
        document.getElementsByTagName("head")[0].appendChild(css);
        })
</script>

2行目: 動的に CSS を読み込む

6行目: メディアクエリーのマッチ確認

11行目: 変数 css にて構築していた link 要素を現在のドキュメントに追加し、CSS を読み込む

帳尻合わせ

動的に CSS を読み込んだ弊害が1つ出ます。 それは、シンタックスハイライトの CSS が後から読み込まれることにより、先に定義していたシンタックスハイライトに関するスタイルが上書きされてしまうことです。

head 要素中に link で CSS を読み込むようにしていますが、その中で定義していたシンタックスハイライトに関するスタイルがページロード後に読み込まれる CSS によって上書きされてしまいます。

上のコードブロックでもそうですが、強調表示している行は cheep が定義したスタイルに基づいています。 (特にダークモード時に強調表示が派手になってしまうのを抑制したい)

帳尻合わせする前のcheep スタイル:

1
2
3
4
5
6
.chroma .hl {
  background-color: var(--code_hl);
}
.chroma .hl .lnt {
  font-weight: bold;
}

帳尻合わせした後のcheep スタイル:

1
2
3
4
5
6
.chroma span.hl {
  background-color: var(--code_hl);
}
.chroma span.hl .lnt {
  font-weight: bold;
}

対象をより具体的に指定することで、(先に定義されているものながら) 優先順位を上げています。