CSS scrollbar-gutter 怎麼防止捲軸出現造成版面跳動
頁面平常沒有捲軸,內容變多後突然出現 classic scrollbar,整個版面向左縮一點,這種微小位移在雙欄面板、置中的 modal 或固定寬度導覽列上特別明顯。讀者可能只覺得畫面「跳了一下」,但按鈕、游標和正在閱讀的位置都可能因此改變。
CSS 的 scrollbar-gutter 可以預留 scrollbar 所需的空間,讓內容從「沒有溢出」變成「需要捲動」時仍維持相同的內容寬度。最常用的起點是:
.results-panel { overflow: auto; scrollbar-gutter: stable;}先記住一個邊界:classic scrollbar 會佔 layout space,overlay scrollbar 通常覆蓋在內容上,因此 overlay 模式本來就不會因 scrollbar 出現而縮窄內容,scrollbar-gutter 也不一定看得到差異。
auto、stable 與 stable both-edges 差在哪
這個 property 的值不只是「開或關」,不同值會改變預留空間的時機:
| 值 | 行為 | 適合的場景 |
|---|---|---|
auto | 依瀏覽器預設,只在需要時出現 gutter | 不在意內容寬度隨 scrollbar 改變的區塊 |
stable | 在適用的 classic scrollbar 滾動盒預留一側空間,即使目前尚未溢出 | 清單、結果面板、置中內容,需要前後寬度一致 |
stable both-edges | 在 stable 基礎上,另一側也保留對稱空間 | 卡片或雙欄面板需要視覺中心不偏移 |
stable both-edges 不是「兩條 scrollbar」,而是讓兩側的內容邊界對稱。它很適合工具面板或卡片內的結果清單;一般頁面先從 stable 開始,避免無意中縮小可用寬度。
一個可以直接測試的雙欄範例
HTML 只需要兩個具有獨立滾動的面板:
<main class="page"> <section class="panel panel--stable"> <h2>固定一側 gutter</h2> <div class="items"> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> </div> </section>
<section class="panel panel--symmetric"> <h2>兩側對稱 gutter</h2> <div class="items"> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> <p>結果項目</p> </div> </section></main>.page { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem;}
.panel { block-size: 14rem; overflow: auto; border: 1px solid #cbd5e1; border-radius: 0.75rem; padding: 1rem;}
.panel--stable { scrollbar-gutter: stable;}
.panel--symmetric { scrollbar-gutter: stable both-edges;}
.items { display: grid; gap: 0.75rem;}讓 .items 的內容先少於面板高度,再用 JavaScript、開發者工具或測試資料增加項目。比較兩個面板的 heading、文字邊界和 scrollbar 出現前後是否維持對齊;不要只看 scrollbar 本身有沒有顯示。
全頁面與局部面板要分開決定
如果問題發生在整個文件,可以在 root 滾動盒上預留空間:
html { overflow-y: auto; scrollbar-gutter: stable;}如果只有搜尋結果、側欄或 modal 會滾動,應把設定放在那個局部容器,不要讓整個頁面平白增加 gutter。尤其是 modal 打開時常會鎖住 body 滾動;此時同時檢查 body 的 scrollbar、modal 的 scrollbar 以及開關 modal 前後的 padding,避免用兩套補償造成內容又跳一次。
不要直接把所有容器改成 overflow: scroll 來「固定」版面。它確實可能強制顯示 scrollbar,卻會帶來不必要的視覺元素、觸控體驗差異和巢狀滾動問題;scrollbar-gutter: stable 的目的正是預留空間但保留內容是否溢出的語意。
overlay scrollbar 不一定有可見效果
在使用 overlay scrollbar 的作業系統或瀏覽器設定中,scrollbar 不佔用 layout space,所以即使加上 stable,內容寬度也可能看起來完全沒變。這不是 property 失效,而是根本沒有 classic gutter 可以預留。
測試時至少比較:
- classic scrollbar 與 overlay scrollbar 的環境。
- 內容剛好不溢出、剛開始溢出,以及大量溢出的三種狀態。
- 直接載入頁面、切換 tab、開關 modal 和鍵盤 focus 後的內容位置。
- RTL 或左右都有操作工具的面板,確認
both-edges沒有破壞可用寬度。
用 feature query 留下合理退化
MDN 將 scrollbar-gutter 標為 Baseline 2024 的功能,但較舊的裝置和瀏覽器仍應檢查相容性。可以把增強樣式包在 feature query,讓不支援的瀏覽器沿用原本的 overflow 行為:
.results-panel { overflow: auto;}
@supports (scrollbar-gutter: stable) { .results-panel { scrollbar-gutter: stable; }}如果舊瀏覽器也必須完全穩定,可以設計不依賴 scrollbar 寬度的版面,例如讓主要區塊使用 max-inline-size、把動態寬度放在內層,或在 modal 開關時統一管理滾動鎖定;不要未量測就硬編碼某個 scrollbar 寬度。
這個屬性只處理 scrollbar 造成的可用寬度變化,不能代替圖片尺寸、字型載入、Skeleton 或資料請求完成前的空間預留。若跳動來自內容本身高度改變,可以再看 content-visibility 與 contain-intrinsic-size 的版面穩定做法;若是固定 header 下的錨點與捲動位置,則可參考 CSS scroll-snap 與固定 header。
結論:先找出真正的 scrolling box
scrollbar-gutter: stable 適合用來穩定 classic scrollbar 出現前後的內容寬度,stable both-edges 則適合需要左右對稱的面板。先把它放在真正負責 overflow: auto 的滾動盒,再用 classic/overlay、溢出前後和 modal 切換做驗證。
版面跳動若來自別的來源,繼續調整 gutter 不會解決問題。把 scrollbar 的空間變化和內容高度、圖片載入、字型替換分開量測,才能得到可維護的 CSS,而不是堆疊更多固定寬度補丁。
常見問題
Q: scrollbar-gutter: stable 會讓所有瀏覽器都多出一條 scrollbar 嗎?
A: 不會。它預留的是 classic scrollbar 可能使用的 layout space,不等於強制顯示捲軸;是否出現仍由 overflow 和內容是否溢出決定。overlay scrollbar 也可能看不到差異。
Q: stable both-edges 什麼時候比 stable 好?
A: 當面板內容需要左右對稱、scrollbar 出現後不能讓文字或工具列偏向一側時使用。一般單欄頁面先用 stable,因為 both-edges 會再保留另一側空間。
Q: 可以用 overflow: scroll 取代 scrollbar-gutter 嗎?
A: 不建議當成通用替代方案。overflow: scroll 會強制顯示 scrollbar,可能造成不必要的視覺和巢狀滾動問題;scrollbar-gutter 能保留穩定空間,又讓內容是否溢出的語意保持清楚。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。