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 都會影響內容寬度,不需要期待每台機器的長字串寬度相同。
檢查時看三個位置:
columns:原始右欄是否被內容撐大?改欄軌後是否回到剩餘空間?itemWidth:右欄 item 是否仍有獨立的固定寬度或最小值?preScroll與preClient:長程式碼應保留在自己的捲動區,不能只因頁面看不到捲軸就宣布修好。
只改欄軌那組仍可能讓內容溢出,是刻意保留的對照。完整處理那組的預期是欄軌不被長字串撐大,並讓程式碼在 <pre> 裡水平捲動;請實際把捲軸移到尾端,確認內容沒有被裁掉。
別把固定側欄也當成能無限縮小
上面的側欄與 gap 本身就需要 176px。更窄的畫面即使右欄縮到零,也沒有空間放下固定側欄;正式版面需要在適當斷點改成單欄:
@media (max-width: 640px) { .layout { grid-template-columns: minmax(0, 1fr); }}斷點要依側欄內容與版面容器驗收,640px 只是這個範例的選擇。若實際問題是某個區塊本來就需要單軸捲動,可參考 CSS 單軸 overflow 與捲動容器;若是捲軸出現時造成整頁位置跳動,則是 scrollbar-gutter 的版面位移問題,不應把兩者都用 min-width 修補。
參考資料: