1590 字
8 分鐘

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 也不一定看得到差異。

autostablestable both-edges 差在哪#

這個 property 的值不只是「開或關」,不同值會改變預留空間的時機:

行為適合的場景
auto依瀏覽器預設,只在需要時出現 gutter不在意內容寬度隨 scrollbar 改變的區塊
stable在適用的 classic scrollbar 滾動盒預留一側空間,即使目前尚未溢出清單、結果面板、置中內容,需要前後寬度一致
stable both-edgesstable 基礎上,另一側也保留對稱空間卡片或雙欄面板需要視覺中心不偏移

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 能保留穩定空間,又讓內容是否溢出的語意保持清楚。

參考資料:

MDN:scrollbar-gutter

W3C:CSS Overflow Module Level 3

CSS scrollbar-gutter 怎麼防止捲軸出現造成版面跳動
https://laplusda.com/posts/css-scrollbar-gutter-layout-shift/
作者
Zero
發佈於
2026-09-01
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。