読む邪魔をしない
スマートフォンで自分の記事を読んでいて、気になることがありました。
画面の上に、ヘッダーと目次がずっと貼り付いたままです。どちらも便利なつもりで置いたものですが、読んでいるあいだは、ただ場所を取っています。
常に出しておくものと、必要なときだけ出すものの分け方。「消す」ではなく「薄くする」を選んだ理由。そして、2つの追従する要素が重なったときに起きた、位置の食い違いです。
貼り付いたものは、面積を占め続ける
スマートフォンの画面の高さは、だいたい本文15〜20行ぶんです。そこにヘッダーが1行、目次のバーが1行居座ると、実際に読める範囲は1割ほど減ります。
減るだけならまだしも、目次のバーはいま読もうとしている行の上に半透明でかぶさります。スクロールしている最中がいちばん邪魔です。
3つの変更
| 対象 | 前 | 後 |
|---|---|---|
| ヘッダー | 常に表示 | 下へ読み進む間は引っ込む |
| 目次(スマホ) | 常に同じ濃さ | スクロール中だけ薄くなる |
| 先頭へ戻る | 手段なし | ボタンを出す |
1. ヘッダーは「量」ではなく「向き」で判断する
最初に考えたのは「300pxスクロールしたら隠す」でした。単純ですが、これだと読み進めるほど隠れっぱなしになります。メニューを開きたくなったとき、ページの先頭まで戻ることになります。
採用したのは、スクロールの向きで判断する方式です。
下へ読み進んでいる → 隠す(読むのに要らない)
上へ戻し始めた → 出す(何かしたいはず)
ページの先頭付近 → 必ず出す
上へのスクロールは、たいてい「前に戻りたい」「別の場所へ行きたい」という合図です。その動作を、ヘッダーを呼び出す操作として兼ねさせています。新しい操作を覚えてもらう必要がありません。
細かいが要る配慮が3つあった
ひとつめはわずかな揺れを無視すること。指の震えや慣性の揺り戻しで数px動くたびに出し入れすると、ヘッダーが点滅します。6px未満の移動は無視しています。
ふたつめは画面の端で引っ張ったとき。iPhoneでは画面の一番上でさらに引っ張ると、スクロール位置がマイナスになります。そのまま計算すると「上に動いた」と誤判定します。0で止めるようにしました。
みっつめはメニューを開いているあいだは隠さないこと。開いた本人の操作でヘッダーごと消えたら、何が起きたのか分かりません。
2. 目次は「消す」のではなく「薄くする」
目次も同じように隠す案がありました。やめました。
この目次のバーには読了の進捗も出しています。消すと、どこまで読んだかが分からなくなります。
困っていたのは、文字の上に別の文字が重なって読めないことでした。薄くすれば下の文字は読めます。位置と進捗は視界の端に残ります。
指を止めれば元の濃さに戻ります。スクロール中しか邪魔にならないものを、常時消す必要はありません。
開いているときは薄くしません。開いているのは読むためではなく選ぶためなので、そこで薄くすると目的の見出しを探せなくなります。
3. 2つの追従が、食い違った
ここで問題が出ました。
目次は「ヘッダーの高さ(56px)ぶん下」に貼り付く指定になっています。ヘッダーがそこにある前提です。
ところがヘッダーは引っ込むようになりました。すると——
画面の上端
↓
(56pxの空白) ← ヘッダーが居たはずの場所
↓
目次のバー ← 宙に浮いて見える
目次だけが、意味のない隙間を空けて浮きます。
状態を、渡さずに共有する
ヘッダーが隠れているかどうかを、目次に伝える必要があります。素直に考えると「上から順に受け渡す」ですが、この構成では間にあるページ全体を作り替えることになります。
選んだのは、ページ全体に1つの値を置く方法です。
ヘッダー: --header-offset = 3.5rem(表示中)/ 0rem(隠れている)
目次 : 貼り付く位置 = --header-offset
ヘッダーは自分の状態を書くだけ、目次はその値を見るだけです。お互いを直接知らないまま、位置だけが揃います。
あえてTailwindを使わなかった箇所
このサイトのスタイルはTailwind CSSで書いています。今回の位置指定も書けますが、1か所だけ普通のCSSにしました。
理由は、壊れ方です。Tailwindのクラス名を書き間違えると、CSSが生成されないだけで、ビルドは成功します。
位置指定が消えると、目次は貼り付くのをやめて一緒に流れて消えます。エラーは出ません。実際に記事を開いてスクロールするまで分かりません。
この部分は今回いちばん確かめにくい場所だったので、綴り間違いで静かに消えない書き方を選びました。
4. 先頭へ戻るボタン
最後に、よくあるボタンを置きました。ここでも1つだけ気を付けた点があります。
薄くして消したボタンは、見えないのに押せます。透明度を0にしただけでは、そこにある扱いのままです。キーボードで操作している人には、見えない項目に順番が回ってきます。
見えないあいだは押せない・順番が回ってこない・読み上げられないの3つを揃えました。見た目と実体を一致させています。
あわせて、端末の「動きを減らす」設定を尊重しています。滑らかに移動するのが心地よい人ばかりではありません。
今回の学び
まず、常に出しておくものと、必要なときだけ出すものを分けること。便利だから常に出す、を積み重ねると、本文が読める面積だけが減っていきます。何のために画面を開いているのかに戻って決めるべきです。
次に、「隠す」の前に「薄くする」を検討すること。困っているのが重なりなら、濃さを変えるだけで解決することがあります。消してしまうと、その要素が持っていた別の役割まで一緒に失います。
そして、動かすものが増えたら、位置の前提を疑うこと。固定されている前提で書いた数値は、片方が動き出した瞬間に嘘になります。今回は「ヘッダーの高さ」がそれでした。
最後に、確かめにくい場所ほど、壊れたら気づく書き方を選ぶこと。同じ結果になる書き方が2つあるなら、間違えたときに黙って消えないほうを選ぶ価値があります。
意味を確認したい言葉があれば、用語集にまとめています。
sticky / z-index / ビューポート / CSS変数 / requestAnimationFrame / prefers-reduced-motion / Tailwind CSS / ビルド
関連記事

届かない問い合わせ
問い合わせフォームで決めることは3つあった。メールアドレスを載せるか、送信先が未設定のときフォームを出すか、失敗をどう伝えるか。共通していたのは「うまくいったように見えて、実は何も起きていない」状態をどう避けるかだった。
追従する目次と、画面の取り合い
PCで右側に追従させていた目次を、スマホでは上部に貼り付けた。ところが貼り付くものは画面を占め続けるため、本文を読む場所が足りなくなる。情報を消さずに縦を横へ並べ替えた話と、「飛んだ先がバーの裏に隠れる」という副作用への対処。
読む体験をつくる — 目次の自動生成から読了状態の保存まで
記事を5本公開したあと、読む側の体験を作り込みました。目次の自動生成、追従する目次、読了状況の表示と保存、スマートフォンのメニュー、読了目安の時間。あわせて、トップページの見た目も見直しています。 この記事は、その実装記 […]