CSS の「:target」だけを用いて脚注をモーダル内に表示する
Markdown の脚注
皆さんは Markdown で脚注を挿入した経験はありますか。実際のところ,脚注機能は Markdown のパーサー次第ですから,使ったことがない人もいらっしゃるかもしれません。ちなみに,脚注はこのように表示されます。[1]
さて,脚注を含む Markdown をそれに対応したパーサーで HTML に変換すると,本文と脚注の関係が次のような形で表現されていると分かります。
Hugo(Goldmark)の場合
<!-- 本文 -->
<p
>脚注表示のテスト。<sup id="fnref:1"
><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup
></p
>
<!-- (中略) -->
<!-- 脚注 -->
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn:1" role="doc-endnote">
<p>
ここに脚注の内容が入ります。
<a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a>
</p>
</li>
</ol>
</section>
Zenn の場合
<!-- 本文 -->
<p
>脚注表示のテスト。<sup class="footnote-ref"
><a href="#fn1" id="fnref1">[1]</a></sup
></p
>
<!-- (中略) -->
<!-- 脚注 -->
<section class="footnotes">
<div class="footnotes-title">
<img
src="https://twemoji.maxcdn.com/2/svg/1f58b.svg"
class="emoji footnotes-twemoji"
loading="lazy"
width="20"
height="20"
/>脚注</div
>
<ol class="footnotes-list">
<li id="fn1" class="footnote-item">
<p
>ここに脚注の内容が入ります。
<a href="#fnref1" class="footnote-backref">↩︎</a></p
>
</li>
</ol>
</section>
すなわち,本文中にある脚注番号が書かれたリンクをクリックすると,ページ下部にある対応箇所へジャンプする,というわけです。さらに,その脚注にある「↩︎」を押せば,本文へ戻ることもできます。また,脚注がリスト形式で表現されているのも特徴と言えるでしょう。書籍の体裁を参考にしたのでしょうか。なお,上記コードは省いた部分があるため,分かりづらいですが,脚注は本文の後に続けて挿入されます。気になるのであれば,開発者ツールでこのページのソースコードを確かめてみてください。本当にそうなっています。
リスト表示の落とし穴
ところで,この脚注の数が厖大化したらどうなるのか,先ほどのコードを基に少し考えてみましょう。既述の通り,脚注はリストで構成されていますから,当然そこに含まれる <li>
の数は増えていきます。
Hugo で脚注が 20 個あるページを生成した結果
Zenn で脚注が 20 個ある場合はこのような表示になる
上の画像はその例ですが,どのように捉えたでしょうか。項目数が多く少しごちゃごちゃしていますね。幸いにも注釈の文字数が少ないため,著しい読みづらさこそ感じませんが,だからといって,いつでもこうとは限りません。それとは別に,本文へ戻ろうと思い,結果として別の脚注にある矢印を押してしまう可能性もあり得そうです。
モーダルウィンドウという選択肢
そこで,別な脚注の表示方法の 1 つとして,モーダルウィンドウによるアプローチを考えてみます。本文の脚注リンクを押下すると,対応する注釈だけがモーダルウィンドウで表示される,という仕組みです。実際に,Wikipedia のモバイル版サイトがそのような実装をしています。
Wikipedia モバイル版で注釈をクリックすると,画面下部にモーダルウィンドウが現れる
このような独立表示にすることで,他の脚注と読み間違えたり,本文の違うところへ遷移したりする可能性を排除できます。ただ,そのためには JavaScript を必要としたり,併せて HTML 自体を改修したりしなければなりません。場合によっては,少々骨が折れることになりそうです。
ところが,わざわざそんなことをせずとも,CSS を 20 行程度書くだけでこれを実現できるのです。百聞は一見に如かずと言いますから,今すぐブラウザーの開発者ツールを開き,以下の CSS を貼り付けて脚注を参照してみましょう。[2]
/* Zenn の脚注をモーダルウィンドウ内に表示する */
.footnote-item:target {
background: var(--c-base-bg);
bottom: 0;
box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.5);
inline-size: 100%;
left: 0;
padding: 60px !important;
position: fixed;
z-index: 10;
}
.footnote-item:target .footnote-backref {
block-size: 200%;
inline-size: 100%;
left: 0;
position: fixed;
top: -50%;
z-index: -10;
}
すると,下の画像のように脚注が出てくるはずです。
上記の CSS を適用した状態で脚注リンクを押下すると,画面下部に注釈が表示される
暗部をクリックするとウィンドウが閉じ,元通りになります。
:target
の秘める可能性
なぜ,あの CSS だけでモーダルウィンドウを実装できるのでしょうか。そのカギは脚注関係の HTML と :target
という疑似クラスにあります。
:target
とは何か
MDN によると,:target
というは次の役割を持つ疑似クラスであると説明されています。
The :target CSS pseudo-class represents a unique element (the target element) with an id matching the URL's fragment.
拙訳)CSS の
:target
疑似クラスは,URL フラグメントの ID と一致する唯一の要素(対象要素)を表す。
また,:target
は CSS3 のセレクターであり,ほとんどのブラウザーでサポートされています。
あの IE でさえ :target
に対応している
仕組み
先ほどの脚注を アドレスバーに注目しつつ,もう一度クリックしてみてください。URL の末尾に #fn2
があったはずです。
ここで,冒頭にある Zenn の脚注部分の HTML コードと先の CSS を再度示します。
<!-- 本文 -->
<p
>脚注表示のテスト。<sup class="footnote-ref"
><a href="#fn1" id="fnref1">[1]</a></sup
></p
>
<!-- (中略) -->
<!-- 脚注 -->
<section class="footnotes">
<div class="footnotes-title">
<img
src="https://twemoji.maxcdn.com/2/svg/1f58b.svg"
class="emoji footnotes-twemoji"
loading="lazy"
width="20"
height="20"
/>脚注</div
>
<ol class="footnotes-list">
<li id="fn1" class="footnote-item">
<p
>ここに脚注の内容が入ります。
<a href="#fnref1" class="footnote-backref">↩︎</a></p
>
</li>
</ol>
</section>
/* Zenn の脚注をモーダルウィンドウ内に表示する */
.footnote-item:target {
background: var(--c-base-bg);
bottom: 0;
box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.5);
inline-size: 100%;
left: 0;
padding: 60px !important;
position: fixed;
z-index: 10;
}
.footnote-item:target .footnote-backref {
block-size: 200%;
inline-size: 100%;
left: 0;
position: fixed;
top: -50%;
z-index: -10;
}
各々の脚注項目(.footnote-item
)には固有の ID が与えられており,必ず本文へ戻るためのリンク(.footnote-backref
)を子要素として含んでいます。同様に,本文中の脚注番号とそこへのリンクを含む要素(.footnote-ref
)にも脚注側から戻れるよう,固有の ID が振られています。
本文中にある脚注番号(.footnote-ref > a
)をクリックすると,その脚注の URL フラグメントを参照します。その結果,当該の .footnote-item
が :target
疑似クラスにマッチするわけです。反対に .footnote-backref
をクリックする(脚注から本文へ戻る)際には別の ID を参照するため,脚注は :target
の対象とみなされなくなります。ゆえに,上記の CSS を適用するだけでウィンドウの表示・非表示ができたのです。
既知の問題
ただ,CSS の :target
疑似クラスを用いたモーダルウィンドウには,少しだけ厄介な点があります。自分が見つけた範囲では次の通りです。もしも妙案がありましたら,ぜひともお教えください。
- 表示中は CSS だけで
<body>
のスクロールを無効にできない。ただし,将来的に:target-within
が主要ブラウザーで実装された際は,その限りでないと思われる。 - ウィンドウを閉じるリンクの判定が,脚注の子要素以外の全域に及んでしまう。つまり,ウィンドウの余白部分をクリックしても閉じられる。
- 本文へ戻る際の挙動がややぎこちない? 一応,
scroll-behavior: smooth;
を<html>
に適用することで対応可能。
終わりに
終わりに,事の発端は,MDN の『CSS reference』を眺めていたときのことでした。そこで :target
の存在をたまたま知り,その面白さと,実は CSS3 のセレクターゆえに多くのブラウザーで対応していることへの驚きから,本記事を書いた次第です。
Discussion