1300 字
7 分鐘

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,在你的目標瀏覽器開啟。範例有原始欄軌、只改欄軌,以及完整處理三組;點按按鈕即可看到實際尺寸。這份範例已做程式碼檢閱與 JavaScript 語法檢查,尚未在本次流程執行瀏覽器版面驗證,數值要以你的瀏覽器輸出為準。

<!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 的條件下,固定欄 160px 加間距 16px,右欄的目標空間是 424px;這是尺寸計算,並非本次量測結果。實際字型、縮放與 viewport 都會影響內容寬度,不需要期待每台機器的長字串寬度相同。

檢查時看三個位置:

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

只改欄軌那組仍可能讓內容溢出,是刻意保留的對照。完整處理那組的預期是欄軌不被長字串撐大,並讓程式碼在 <pre> 裡水平捲動;請實際把捲軸移到尾端,確認內容沒有被裁掉。

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

上面的側欄與 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