静的サイトジェネレーターHugoで使えるネタバレ回避用のShortcodeを作りました。
ネタバレ部分の表示を切り替えられるようにしたいという方、そのまま使うなり書き換えるなりお好きなようにして使ってみてくださいね。

使い方

ショートコードを準備

Githubの該当コードがあるところのコードをコピーするなりダウンロードするなりして spoiler.html という名前で保存します。
(rawボタンを右クリックしてリンク先を保存するメニューでhtmlとして保存する形が早いと思います。)
使っているHugoブログのフォルダー内の layouts/shortcodes フォルダーに spoiler.html を配置して準備完了です。

投稿Markdown内で呼び出し

Markdown中に {{% spoiler title="ネタバレ表示ボタンに表示するテキスト" %}}内容{{% /spoiler %}} の形で書き込むことでショートコードを呼び出します。
内容は伏せられて、ネタバレ表示切り替えボタンが表示されるはずです。
spoilerショートコード内にfigureなどの 別のショートコードを埋め込むことも可能 です。

使い方の例

1
2
3
{{% spoiler title="ネタバレを含む内容を表示" %}}
千羽鶴ちゃんはかわいい
{{% /spoiler %}}

ビルドやプレビューをすると以下のように表示されているはずです。

千羽鶴ちゃんはかわいい

仕組み

重要そうなところだけ紹介します。詳しくはコードを見てみてください。
このショートコードではJavaScriptを使わずCSSのみでネタバレ部分の表示・非表示を切り替えています。

ラジオボタンのチェック状態で表示制御

Label要素 をクリックすることで for属性 に指定されている要素にフォーカスが移動してきたり、選択状態の変更がかかったりすることを利用します。

1
2
<input type="checkbox" id="spoiler-switch" />
<label for="spoiler-switch" >このラベルを押すとチェックボックスにチェックが入る</label>

CSSのセレクタ 属性セレクタ でチェック済みのチェックボックスをプロパティ設定対象に指定します。

チェック済みのチェックボックスと 隣接セレクタ(E + Fセレクタ) を使ってネタバレ表示を切り替えるボタンを指定することが出来ます。
CSSのプロパティの設定内容はボタンが非表示にするようなものを設定します。
これによってボタンが押された(チェックボックスにチェックが付いた)ときにネタバレ表示ボタンを非表示に出来ます。

チェック済みのチェックボックスと 間接セレクタ(E ~ Fセレクタ) を使ってネタバレ内容を指定します。 今度のプロパティはネタバレ内容を表示するように設定します。
これによってボタンが押された(チェックボックスにチェックが付いた)ときにネタバレ内容を表示することが出来ます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* ネタバレ部分 */
.spoiler-body {
  filter: blur(6px);
  user-select: none;
  position: relative;
}

/* ネタバレ解除されたときのネタバレ解除ボタン */
.spoiler-switch:checked + .spoiler-button {
  display: none;
}

/* ネタバレ解除されたときのネタバレ部分 */
.spoiler-switch:checked ~ .spoiler-body {
  filter: blur(0);
  user-select: auto;
  transition: 0.4s;
}

Hugoの関数でMarkdownの内容を埋め込み

Hugoの関数を使ってHTMLとMarkdownで書いた投稿内容の連携をとっていきます。

ショートコードHTML内で {{ .Inner }} とするとショートコードに囲われた内容を取得することが出来ます。
これによってspoilerショートコードで囲った内容をネタバレ部分として扱う要素に埋め込むことが出来ます。

1
<div class="spoiler-body">{{ .Inner }}</div>

ショートコードHTML内で {{ .Get "title" }} とするとショートコードのtitleに指定された内容を取得することが出来ます。
これによってtitleに指定した内容をネタバレ表示を切り替えるボタンのタイトルとして設定することが出来きます。

1
<label for="spoiler-switch" >{{ .Get "title" }}</label>

ちなみに {{ .Get "foo" }}{{ .Get "bar" }} のように自由な設定を受け渡すことも出来ます。

Hugoの関数で複数のネタバレ埋め込みに対応

一つの投稿内にネタバレのショートコードを置くとid属性かぶってしまいます。
これでは複数のネタバレを同一投稿内に分けて配置することが出来ません。

そこでHugoでMarkdownをHTMLにビルドする際にネタバレショートコードごとに一意なid属性をつけてあげるようにします。

1
{{ $spoilerId := md5 (printf "%s%s" .Inner now) }}

ショートコードの先頭でネタバレ内容と現在時刻から作ったMD5を計算しておきます。
この値を使ってネタバレの表示切り替えで使うセレクタにidをつけていくようにHTMLとCSSを書きます。

一部例

1
2
3
4
5
6
7
8
<input type="checkbox" id="spoiler-switch-{{ $spoilerId }}" />
<label for="spoiler-switch-{{ $spoilerId }}" class="spoiler-button">{{ .Get "title" }}</label>
<style>
/* ネタバレ解除されたときのネタバレ解除ボタン */
.spoiler-switch-{{ $spoilerId }}:checked + .spoiler-button {
  display: none;
}
</style>

これによってネタバレ解除ボタン(Label要素とチェックボックス)とネタバレ内容の組み合わせが一意になり、複数のネタバレを配置することができるようになります。

なんでつくったのか

Hexoだとビルド環境にNode.jsが必要でそのたびにDockerを立ち上げるのもローカルにNode.jsをインストールするのも抵抗がありました。
そこでHugoに移行したのですがHexoにあったようなネタバレカバーがありませんでした。 WordPressからHexoに移行してサイトリセットして以来ぼやいてばかりいて、その内容もネタバレを含むことが多かったので今回のようなものを作りました。

おわりに

静的サイトジェネレーターで作ったサイトは最終的に投稿が単純なHTMLファイルで扱われるのでWordPressなどのCMSと比べてページ読み込みの際の通信や処理が少なくて高速といわれています。
静的サイトジェネレーターの一つHugoにはプラグインの機能がありませんが、このようにHugo備え付けの関数のおかげでHTMLとCSSだけでちょっとした機能なら作ることが出来ます。
Hugoの関数混じりのコードだとエディターで警告が出てしまうなどの問題がありますが、よかったら試してみてくださいね。