1656 字
8 分鐘

CSS Grid 被長文字撐開?minmax(0, 1fr) 與 min-width: 0 怎麼分

側欄固定 160px,右欄設成 1fr,看起來應該只拿剩餘寬度。但右欄出現一行不能換行的程式碼,整個 Grid 卻比容器還寬。先把可縮小的欄軌改成 minmax(0, 1fr),確認直接子元素可縮小,再決定內容要換行還是局部捲動。

這三件事不能混為一談:欄軌負責分配空間,item 決定自身尺寸限制,文字與 <pre> 則決定內容超出空間後如何顯示。只在頁面根節點加 overflow-x: hidden,可能藏住水平捲軸,卻讓內容繼續超出版面。

1fr 的最小值並不是零#

CSS Grid 規格說明,單獨出現的 flex 軌道尺寸隱含自動最小值,因此 1fr 可理解為 minmax(auto, 1fr)。在特定條件下,長內容的最小尺寸會影響欄軌,讓它無法縮到你以為的剩餘寬度。

/* 內容可能影響右欄的自動最小值 */
.layout {
display: grid;
grid-template-columns: 160px 1fr;
gap: 16px;
}
/* 右欄可以縮到零,最大值仍用 fr 分配 */
.layout {
grid-template-columns: 160px minmax(0, 1fr);
}

minmax(0, 1fr) 沒有把長字串變短,也沒有保證圖片、表格或固定寬度子元素會自動縮小。它先移除欄軌的自動最小值;內容仍要有顯示策略。

min-width: 0 要放在會卡住的 Grid item#

Grid item 的自動最小尺寸取決於 overflow、所跨欄軌的最小函式等條件,不能簡化成「每個 Grid 子元素永遠都有 min-content 最小寬度」。對需要承載長內容、並且允許縮小的直接 item,明確設定 min-width: 0 可以表達你的尺寸需求。

.layout > .content {
min-width: 0;
}
.content pre {
max-width: 100%;
overflow-x: auto;
}
.content .long-url {
overflow-wrap: anywhere;
}

程式碼保留原始換行,所以讓 <pre> 局部水平捲動;一般文字或網址則可允許斷行。若 <pre> 的外層還有另一層 Flex 或 Grid,沿著祖先往上找真正有自動最小值的 item,只改最裡面的 <pre> 未必能解除外層限制。

min-width: 0 也不會覆蓋子元素明確寫出的 width: 900px,更不會處理內容本身的錯誤尺寸。要先確認是 min-size 還是固定寬度造成問題,再選擇移除固定值、設 responsive 尺寸或保留捲動。

用三組相同內容比較,而不是只看捲軸有沒有消失#

以下存成 grid-check.html,在你的目標瀏覽器開啟。範例有原始欄軌、只改欄軌,以及完整處理三組;點按按鈕即可看到實際尺寸。這份原始範例於 2026-10-13 由維護流程在 macOS Chrome 155.0.8059.39 開啟,實際點按量測按鈕;後面列出兩種寬度的結果。這是隔離 HTML 的驗證,不代表作者的正式專案或其他瀏覽器也有相同數值。

<!doctype html>
<html lang="zh-Hant">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Grid 溢位比較</title>
<style>
body { margin: 24px; font: 16px system-ui; }
.layout {
display: grid;
width: 600px;
max-width: 100%;
grid-template-columns: 160px 1fr;
gap: 16px;
border: 1px solid;
}
pre { margin: 0; font: 16px monospace; }
.track { grid-template-columns: 160px minmax(0, 1fr); }
.complete > article { min-width: 0; }
.complete pre { overflow-x: auto; }
</style>
<h1>Grid 溢位比較</h1>
<h2>原始</h2>
<div class="layout" id="original">
<aside>側欄</aside><article><pre></pre></article>
</div>
<h2>只改欄軌</h2>
<div class="layout track" id="track">
<aside>側欄</aside><article><pre></pre></article>
</div>
<h2>完整處理</h2>
<div class="layout track complete" id="complete">
<aside>側欄</aside><article><pre></pre></article>
</div>
<button id="measure">量測三組寬度</button>
<pre id="result"></pre>
<script>
document.querySelectorAll('.layout pre').forEach(pre => {
pre.textContent = 'W'.repeat(200);
});
document.getElementById('measure').addEventListener('click', () => {
const rows = ['original', 'track', 'complete'].map(id => {
const grid = document.getElementById(id);
const item = grid.querySelector('article');
const pre = grid.querySelector('pre');
return {
id,
columns: getComputedStyle(grid).gridTemplateColumns,
containerClient: grid.clientWidth,
containerScroll: grid.scrollWidth,
itemWidth: item.getBoundingClientRect().width,
preClient: pre.clientWidth,
preScroll: pre.scrollWidth,
};
});
document.getElementById('result').textContent =
JSON.stringify(rows, null, 2);
});
</script>
</html>

實際量測:欄軌縮小,溢位仍可能留在外層#

這次 600px 容器的讀值如下。長字串寬度由範例的 16px monospace 產生,scrollWidth 是整數,與 bounding rect 的小數可能略有差異。

對照組右欄 item 寬度Grid client/scroll widthpre client/scroll width
原始 1fr1926.5625px600/2103px1927/1927px
只改 minmax(0, 1fr)424px600/2103px424/1927px
可縮 item 加 <pre> 局部捲動424px600/600px424/1927px

只改欄軌那組已縮回 424px,但 Grid 的 scroll width 仍是 2103px。 這時繼續修改 fr 比例沒有處理剩下的問題:沒有捲動策略的 <pre> 仍把長內容畫到欄位外。完整處理那組才讓 Grid 自己的 scroll width 回到 600px,同時保留 <pre> 的 1927px 內容。

維護流程另在完整處理的程式碼區域實際水平捲動,scrollLeft 從 0 到約 1502.5px,確認尾端能抵達。不能只看 scrollWidth > clientWidth 就宣稱已操作過捲軸;那只是尺寸證據。

將 viewport 設為 320px 後,body 的兩側 margin 各 24px,三組 Grid 的 client width 是 272px。只改欄軌與完整處理的右欄都縮成 96px;前者的 Grid scroll width 仍是 2103px,後者則是 272px。長內容的 preScroll 仍為 1927px,顯示局部捲動保留了程式碼,而不是把字串刪短。

這些數值對應這份範例及該 Chrome 版本;Safari、Firefox、直書模式、縮放、不同字型與正式頁面的巢狀版面沒有測試。特別是這份 fixture 的原始/只改欄軌對照組刻意保留溢位,整張測試頁仍會有水平溢位,不能用整頁捲軸判斷第三組是否修好。

檢查時看三個位置:

  1. columns:原始右欄是否被內容撐大?改欄軌後是否回到剩餘空間?
  2. itemWidth:右欄 item 是否仍有獨立的固定寬度或最小值?
  3. preScroll 與 preClient:長程式碼應保留在自己的捲動區,不能只因頁面看不到捲軸就宣布修好。

這個範例中,只改欄軌就已讓直接 item 縮小;不能因此推論每個 Grid 都必須再加 min-width: 0 才有效。它在其他巢狀 Flex/Grid 祖先上仍可能必要,應逐層量測,而不是一次對所有後代套用。最後還要實際把局部捲軸移到尾端,確認內容沒有被裁掉。

別把固定側欄也當成能無限縮小#

上面的側欄與 gap 本身就需要 176px。更窄的畫面即使右欄縮到零,也沒有空間放下固定側欄;正式版面需要在適當斷點改成單欄:

@media (max-width: 640px) {
.layout {
grid-template-columns: minmax(0, 1fr);
}
}

斷點要依側欄內容與版面容器驗收,640px 只是這個範例的選擇。若實際問題是某個區塊本來就需要單軸捲動,可參考 CSS 單軸 overflow 與捲動容器;若是捲軸出現時造成整頁位置跳動,則是 scrollbar-gutter 的版面位移問題,不應把兩者都用 min-width 修補。

參考資料:

CSS Grid Level 1:Track sizes

CSS Grid Level 1:Automatic Minimum Size of Grid Items

MDN:grid-template-columns

CSS Grid 被長文字撐開?minmax(0, 1fr) 與 min-width: 0 怎麼分
https://laplusda.com/posts/css-grid-minmax-zero-overflow/
作者
Zero
發佈於
2026-10-07
許可協議
CC BY-NC-SA 4.0