在前端網頁切版中,新手最常遇到的一個破版問題就是:「明明將區塊或輸入框設為 width: 100%(或兩個 width: 50% 並排),為什麼只要一加上 padding 內距或 border 邊框,畫面就會立刻超出父容器或掉到下一行?」
這個問題的根源,就在於瀏覽器預設的盒模型(Box Model)計算方式。只要搞懂 CSS box-sizing 屬性,將預設的 content-box 改為 border-box,就能徹底告別加減內距與邊框的煩惱。
一張表看懂:content-box 與 border-box 的寬高計算差別
假設我們在一個 div 上寫了完全相同的 CSS 尺寸設定:width: 200px; padding: 20px; border: 5px solid #000;,這兩種模式算出來的「實際佔用總寬度」會完全不同:
box-sizing 設定值 | width: 200px 代表的是誰的寬度? | 實際在畫面上佔用的總寬度計算公式 | 範例最終總寬度 |
|---|---|---|---|
content-box(瀏覽器預設值) | 僅代表「純文字內容區(Content)」的寬度 | width + 左右 padding + 左右 border | 200 + 20×2 + 5×2 = 250px(往外膨脹) |
border-box(現代切版標準) | 代表「內容 + 內距 + 邊框」加總後的最終外框寬度 | 直接等於你設定的 width(內距與邊框會往內縮減內容區) | 200px(外框固定不變!) |
注意:無論是
content-box還是border-box,margin(外距)都永遠不包含在width裡面,因為margin是元素外框與外部鄰居之間的推擠距離。
content-box是box-sizing在 CSS 規格中的初始值,但瀏覽器內建樣式會替部分表單元素另外指定。以 Chrome 為例,button、select、input type="submit"、checkbox、radio、search預設就是border-box;input type="text"與textarea則維持content-box,這也是文字輸入框最容易被padding撐破的原因。
1. 為什麼預設的 box-sizing: content-box 容易導致破版?
在瀏覽器預設的 content-box 模式下,你寫的 width 只是最裡面的內容寬度。任何你後來補上的 padding 跟 border 都會像「穿厚外套」一樣往外疊加:
/* 預設模式:content-box */
.input-default {
box-sizing: content-box;
width: 100%; /* 已經佔滿父容器 100% 寬度 */
padding: 12px 16px; /* 左右又額外往外撐開 32px */
border: 1px solid #cbd5e1; /* 左右再往外撐開 2px */
/* 結果:總寬度變成 100% + 34px,直接刺穿父容器右邊界! */
}
2. 現代切版標準寫法:box-sizing: border-box
當你將元素設為 box-sizing: border-box 時,你設定的 width: 200px 或 width: 100% 就會直接鎖定「包含 border 與 padding 在內的最外圍總寬度」。就算你隨時調整 padding 從 10px 改成 24px,元素的外框依然穩穩維持在 100%,不會被內距撐破版面。唯一的例外是 padding 加 border 的總和已經大於 width 本身,這時內容區最小只會縮到 0,元素仍會變寬(例如 width: 20px; padding: 20px; border: 5px 的實際寬度是 50px):
/* 推薦模式:border-box */
.input-modern {
box-sizing: border-box;
width: 100%;
padding: 12px 16px;
border: 1px solid #cbd5e1;
/* 結果:總寬度精準維持在 100%,內距與邊框自動向內分配! */
}
實作必備:全站統一開啟 border-box 的初始化寫法
為了不用在每個 Class 重複寫 box-sizing: border-box,現代前端專案通常會在全域 CSS 的最開頭(搭配 CSS Reset / 樣式初始化 -使用 normalize.css)加入通用規則,將全站所有元素以及 ::before / ::after 偽元素一次統一為 border-box。Bootstrap 5 的 Reboot 與 Tailwind CSS 的 Preflight 都是直接寫 *, *::before, *::after { box-sizing: border-box; };下面這個 inherit 版本則是 CSS-Tricks 推廣的寫法:
/* 全站盒模型初始化最佳實踐 */
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit; /* 或直接寫 box-sizing: border-box; */
}
兩種寫法的差別在於:box-sizing 本身不會繼承,inherit 版本是讓每個元素明確跟著父元素走。當你需要把某個第三方元件整塊改回 content-box 時,只要在它的外層容器寫一次 box-sizing: content-box;,裡面的子元素就會一起跟著切換;直接寫 border-box 的版本則必須逐一覆寫每個子元素。
為什麼這裡要把
::before與::after也一起寫進去?因為通用選擇器*預設不會選中偽元素,如果不一併指定,當你在偽元素上寫width: 100%; padding: 10px;時還是會退回預設的content-box。
參考資料與延伸閱讀
- MDN Web Docs: box-sizing
- MDN Web Docs: Introduction to the CSS basic box model
- W3C CSS Box Sizing Module Level 3:box-sizing 屬性
- CSS-Tricks: Inheriting box-sizing Probably Slightly Better Best-Practice
- Bootstrap 5.3 文件:Reboot
- Tailwind CSS 文件:Preflight
- CSS Reset / Normalize.css 樣式初始化 – 差異比較與新專案怎麼選
- 前端 Input 語法教學:inputmode 鍵盤對應、CSS 跑版除錯與常見錯誤解決方法
發佈留言