100vhでスマホの下が切れる原因|トップページの写真の下だけ、いつも指で押さないと見えなかった
トップページの写真をheight:100vhで画面いっぱいにしたら、スマホでだけ下が切れて見えた。
アドレスバーの出し引きで、100vhの「100%」の中身がそもそも違っていた。
トップページの一枚目、キャンプ場の写真を画面いっぱいに出したくて height: 100vh を書いた。
パソコンで見るぶんには狙いどおりで、写真の下端がちょうど画面の下端に合う。
満足してスマホで確認したら、様子が違った。
写真の下、数十ピクセルぶんだけ次のセクションの白地が見えている。
指で軽くスクロールすると写真は画面いっぱいに収まるのに、ページを開いた直後だけ下が切れて見える。
再現するたびに条件が違う気がして、最初はキャッシュのせいだと思っていた。
職業柄、こういうときはブラウザのせいにする前に自分のCSSを疑う癖がついている。
それでも見直した数値に間違いはなく、パソコンでは何度確認しても正しく表示される。
スマホでだけ起きる、というところが手がかりだった。
100vhの「100%」は、実は毎回違う数字だった
vh は viewport height の略で、画面の高さの1%を表す単位だと覚えていた。
スマホの場合、その「画面の高さ」の中に、アドレスバーやタブバーといったブラウザ自身のUIが含まれるかどうかで数字が変わる。
ページを開いた直後は、上にアドレスバー、下にツールバーが表示された状態が初期値になっていることが多い。
その状態から下にスクロールすると、ブラウザが「読むのを邪魔しない」と判断してアドレスバーを画面外へ隠す。
隠れた分だけ、実際に見えている領域は広がる。
ここで問題になるのが、100vh がどちらの状態を基準に計算されているかだ。
ブラウザによっては、アドレスバーが隠れた「いちばん広い状態」を基準に100vhを固定していた。
ページを開いた直後、アドレスバーがまだ表示されている狭い状態では、100vh分の高さが画面からはみ出す。
はみ出した分だけ、写真の下が見えなくなっていた。
直すために新しく増えた単位
この不一致は僕だけが踏んでいたものではなく、CSSの仕様を決めている団体(W3CのCSS Working Group)でも問題として扱われ、新しい単位が追加されていた。
svh(small viewport height)は、アドレスバーが出ている「いちばん狭い状態」を基準にする単位。lvh(large viewport height)は逆に、アドレスバーが隠れた「いちばん広い状態」を基準にする単位。
そして dvh(dynamic viewport height)は、アドレスバーの出し引きに合わせてその場で数字が変わる単位だった。
いちばん自分の状況に合っていたのは dvh のほうだ。
ページを開いた瞬間の狭い状態でも、スクロール後の広い状態でも、常にそのときの実際の表示領域に合わせて高さが決まる。
アドレスバーが隠れた瞬間、写真の高さもふわっと追従して伸びる。
書き換えたのは1行、でも順番が要る
該当のCSSを、こう書き換えた。
height: 100vh;height: 100dvh;
同じプロパティを2回書くのは無駄に見えるけれど、これには理由がある。dvh をまだ知らない古いブラウザは、知らない単位の行をそのまま読み飛ばす仕様になっている。
先に書いた 100vh が生きたまま残るので、古い環境でも最低限の高さは確保される。
新しい環境では後から書いた 100dvh が上書きして効く。
新旧どちらの見え方も一気に切り捨てずに済む、費用対効果のいい直し方だった。
直したあと、もう一度スマホで開き直した。
ページを開いた瞬間から写真は画面ぴったりに収まり、スクロールでアドレスバーが隠れても下に不自然な隙間はできなかった。
数値をいじって力業で調整していた頃の自分に、単位を変えるだけでいいと教えてやりたい。
いま思えば、100vhでレイアウトが崩れる相談を過去にも何度か見かけていた。
そのたびに「スマホのブラウザは癖が強い」くらいに片付けていたけれど、癖ではなく仕様の解釈違いだったらしい。
画面いっぱいに何かを表示したいときは、最初から vh ではなく dvh を書く。
いまはそれを自分の既定値にしている。
ほかにも書いています
note・スタンプ※noteは運営者が個人で書いているものです。
