裝置上的 100vh 問題與現代視窗單位
明明設定了
100vh,底部的按鈕或選單卻被瀏覽器的網址列(Address Bar)擋住。本文觀念與範例整理自
css-100vhGitHub 專案。
為什麼 100vh 在手機上會壞掉?
在桌機瀏覽器裡,100vh 通常代表「現在看到的整個視窗高度」,所以拿來做 Hero 首屏或全螢幕版面幾乎沒問題。
但在 行動裝置瀏覽器(iOS Safari、Chrome 等) 裡,畫面上方與下方會有:
- 網址列 / 工具列:會隨著使用者捲動而「伸縮、隱藏或出現」。
- 實際可視區域:會跟著網址列高度變化,而不是固定不變。
問題在於:
100vh通常等於「網址列完全縮起來時的最大高度」。- 當網址列再次顯示時,瀏覽器 不會重新計算
vh。
因此就會看到這些情況:
- Hero 區塊底部的 CTA 按鈕被網址列擋住。
- 版面多出一點點捲軸,畫面看起來「快滿版但又不是」。
- 鍵盤彈出/收起時,整個畫面高度劇烈跳動。
這些就是「100vh 在手機上壞掉」的常見症狀。
現代視窗單位:svh、dvh、lvh
為了解決行動裝置上的 vh 問題,現代瀏覽器新增了三個 Viewport Height 單位:
| 單位 | 全名 | 含意 | 適合情境 |
|---|---|---|---|
svh | Small Viewport Height | 最小 可視高度(假設網址列始終存在) | Hero、Landing 首屏:穩定、不被遮擋 |
dvh | Dynamic Viewport Height | 動態 可視高度(網址列伸縮時即時更新) | 聊天室、全螢幕 Modal、Web App 版面 |
lvh | Large Viewport Height | 最大 可視高度(網址列完全隱藏的情境) | 特殊背景、全螢幕特效,較少單獨使用 |
重點可以這樣記:
svh:最保守但最穩定,不求剛好填滿整個實際螢幕,但不會被網址列吃掉。dvh:最接近「實際看到的高度」,適合真的要「填滿剩餘畫面」的互動式 UI。lvh:頂多拿來做背景或特效,平常很少直接拿來當內容高度。
實戰情境 A:穩定的 Hero / Landing 首屏(用 svh)
Landing Page 最怕:
- 首屏底部 CTA 被網址列遮住。
- 捲一下畫面高度改變,整塊 Hero 跳來跳去。
這種情況可以這樣寫:
.hero {
/* 舊瀏覽器 fallback:至少有滿版高度 */
min-height: 100vh;
/* 現代行動瀏覽器:用不會被網址列吃掉的可視高度 */
min-height: 100svh;
display: grid;
place-items: center;
}
說明:
- 先寫
100vh,再覆蓋100svh:不支援新單位的瀏覽器會停在vh,支援的會套用svh。 min-height而不是height:內容比一頁還長時,仍可正常往下捲。
搭配 HTML 大致會長這樣:
<section class="hero">
<div class="hero-content">
<h1>行動裝置上的 100vh 問題</h1>
<p>了解為什麼會壞掉,並用 svh / dvh 解決。</p>
<button class="hero-cta">開始閱讀</button>
</div>
</section>
實戰情境 B:真正「全螢幕」的 App / Modal(用 dvh)
有些情境(例如聊天室、全螢幕側邊欄、行動版 Dashboard)真的需要「畫面剩多少就佔多少」,可以用 dvh:
.app-shell {
position: fixed;
inset: 0;
/* fallback:至少有滿版高度 */
height: 100vh;
/* 真正隨網址列、工具列變化的高度 */
height: 100dvh;
overflow: auto;
}
或是全螢幕 Modal:
.modal {
position: fixed;
inset: 0;
z-index: 1000;
height: 100vh;
height: 100dvh;
overflow: auto;
background: rgba(0, 0, 0, 0.6);
}
.modal__panel {
max-width: 640px;
margin: 0 auto;
background: #fff;
}
在支援 dvh 的行動瀏覽器中:
- 網址列縮起來 → 可視高度變高 →
100dvh也跟著增加。 - 網址列再出現 →
100dvh會變小 → 畫面不會被切掉或多出奇怪空白。
使用 lvh 的時機
lvh 表示「網址列完全隱藏時的最大高度」,實務上較少直接用在內容區塊,可以考慮用在:
- 全螢幕背景圖或漸層特效。
- 不太在意被切掉一點點的裝飾性元素。
例如:
.landing-background {
position: fixed;
inset: 0;
height: 100vh;
height: 100lvh;
background: radial-gradient(circle at top, #4dc9b0, #1e293b);
z-index: -1;
}
視覺上可以盡量鋪滿整個裝置,但文字內容區塊本身仍建議用 svh / dvh。
Fallback 心法:永遠先寫 vh,再寫 svh / dvh
現代瀏覽器在解析 CSS 時,遇到「看不懂的單位」會直接忽略那一行。
因此建議的寫法是:
.page {
height: 100vh; /* 舊瀏覽器或不支援新單位時使用 */
height: 100dvh; /* 支援新單位時,覆蓋前一行 */
}
同樣概念也適用在 min-height:
.hero {
min-height: 100vh;
min-height: 100svh;
}
這樣寫可以同時符合:
- 相容性:舊瀏覽器至少有
100vh的效果。 - 行動體驗:新瀏覽器用更正確的
svh/dvh,避免被網址列吃掉 或跳動。
本地練習與 Debug 建議
如果想實際感受差異,可以比照 css-100vh 專案 的做法:
- 做兩個頁面:
- 一個只用
100vh(例如index-100vh.html)。 - 一個用
svh/dvh+vhfallback(例如index.html)。
- 一個只用
- 用 Chrome DevTools 的裝置模擬或實機手機打開,反覆:
- 捲動頁面,讓網址列縮起來 / 再出現。
- 注意底部按鈕是否被擋住或畫面是否跳動。
實際看過一次之後,對「vh 在手機上為什麼不可靠」會有更直覺的體感。
小結
- 不要盲信
100vh:在行動裝置上,它通常是「網址列縮起來時的最大高度」,而不是你當下看到的螢幕高度。 - 穩定的首屏 / Hero 區塊:用
min-height: 100vh; min-height: 100svh;,避免底部 CTA 被遮住。 - 需要真正填滿可視區域的 UI:用
height: 100vh; height: 100dvh;,讓高度隨網址列即時變化。 - 記得 Fallback 心法:先寫
vh,再寫svh/dvh,就能兼顧舊瀏覽 器與行動端體驗。
參考 / 延伸閱讀
- CSS Viewport 新單位 vh, vw & dvh, lvh, svh…:整理傳統
vh的歷史問題,以及svh、dvh、lvh等新單位的設計背景與使用場合。
連結:CSS Viewport 新單位 vh, vw & dvh, lvh, svh… - Why 100vh breaks on mobile and what to use instead — Tushar Kanjariya:實際解釋為什麼
100vh在行動瀏覽器上會超出可視區域,以及如何用現代 viewport 單位與 CSS 寫法來修正版面。
連結:Why 100vh breaks on mobile and what to use instead