cover

結論先講

Box Model 是 CSS 所有佈局問題的起點。搞不懂 Box Model 的結果是:

  • width: 100% 加上 padding 就爆版
  • margin 為什麼有時候會「合併」不相加
  • widthmin-width / max-width 衝突時誰贏
  • Inline 跟 Block 元素為什麼寬度行為不同

這些不是 bug,是 Box Model 本來就是這樣設計的。這篇把核心模型講清楚,以後看到詭異佈局你會有 mental model 可以推理。

最重要的一條:永遠設 * { box-sizing: border-box; }。下面會講為什麼。


一個 box 有四層

┌─────────────────────────────────┐
│  margin(外邊距,透明)          │
│  ┌───────────────────────────┐  │
│  │  border(邊框)             │  │
│  │  ┌─────────────────────┐  │  │
│  │  │  padding(內邊距)    │  │  │
│  │  │  ┌───────────────┐  │  │  │
│  │  │  │  content       │  │  │  │
│  │  │  │  (內容)      │  │  │  │
│  │  │  └───────────────┘  │  │  │
│  │  └─────────────────────┘  │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘

從外到內:

  • margin — 跟其他元素的間距
  • border — 邊框(有寬度、有顏色)
  • padding — 內容跟邊框之間的空白
  • content — 文字、圖片、子元素

一個常見誤解:margin 是 box 的一部分嗎?

不是。 margin 不佔 box 的「實際大小」,它只是說「我希望跟鄰居留多少距離」。背景色不會染到 margin 上。


box-sizing:瀏覽器最坑的預設值

這是 CSS 最重要的一個屬性。分兩種模式:

content-box(預設值,坑)

.box {
  box-sizing: content-box; /* 預設 */
  width: 200px;
  padding: 20px;
  border: 2px solid black;
}

實際寬度:200px + 40px(左右 padding)+ 4px(左右 border)= 244px

你設 width: 200px 結果真的占 244px。每加 padding 都要重算 width。

border-box(你要的)

.box {
  box-sizing: border-box;
  width: 200px;
  padding: 20px;
  border: 2px solid black;
}

實際寬度:200px(padding 跟 border 被「吸收」進去,content 被擠小)

width: 200px 就是 200px。

全域改掉它

每個現代專案都會寫這一條:

*, *::before, *::after {
  box-sizing: border-box;
}

這不是個人喜好,是業界共識。content-box 是 CSS1 的歷史遺產。


Margin Collapse(最容易被 debug 到懷疑人生的行為)

垂直方向的 margin 會合併不相加:

<h1 style="margin-bottom: 30px;">標題</h1>
<p style="margin-top: 20px;">段落</p>

你以為間距是 30 + 20 = 50px? 實際是 30px(取較大值)。

這叫 margin collapse,只發生在垂直方向block 元素之間

什麼時候會觸發

  • 兩個相鄰 block 元素的 top/bottom margin
  • 父元素跟第一個/最後一個子元素(如果父元素沒有 padding、border、非 auto 的 overflow)
  • 空的 block 元素的 top 跟 bottom margin

怎麼避免

  • 父元素加 padding: 1px(不優雅但有效)
  • 父元素加 overflow: hiddenauto
  • 父元素改用 Flexbox 或 Grid(flex/grid item 之間不會 collapse)

實戰:用 Flexbox / Grid 統一處理間距(下一篇會講),不要用 margin 亂噴。


width vs min-width vs max-width

三個寬度屬性同時設時的優先順序:

.box {
  width: 500px;
  min-width: 300px;
  max-width: 800px;
}

行為:

  1. 先套 width: 500px
  2. 如果 width < min-width,用 min-width
  3. 如果 width > max-width,用 max-width

min/max 永遠勝過 width。

實戰用法:容器寬度限制

.container {
  width: 100%;
  max-width: 1200px; /* 再大也不超過 1200 */
  margin-left: auto;
  margin-right: auto; /* 左右 auto = 置中 */
}

這是經典的「內容寬度 + 左右空白」排版。

避免 width: 100vw 的坑

100vw 是「視窗寬度」,不扣掉捲軸。如果頁面有垂直捲軸,width: 100vw 會比 width: 100% 寬一點點,產生水平捲軸。

width: 100%width: 100dvw(dynamic viewport width)。


Inline vs Block 尺寸行為

不是所有元素都乖乖吃 width

displaywidthheightmargin
block全吃
inline只吃左右
inline-block全吃
flex / grid item全吃

<span>width 沒用

<span style="width: 200px;">文字</span>

<span> 是 inline,width 被忽略。想吃 width 要:

span { display: inline-block; }
/* 或 */
span { display: block; }

實戰 Checklist

  • 專案首要:全域 box-sizing: border-box;
  • 知道 margin 垂直方向會合併
  • 用 Flexbox/Grid 管間距,不用一堆 margin 噴
  • 知道 max-width 勝過 width,容器用 width: 100%; max-width: Xpx;
  • 避免 100vw,用 100%100dvw
  • Inline 元素要 display: inline-block 才吃 width

工具:DevTools 看 Box Model

Chrome DevTools 的 Elements 面板右邊有個圖示化的 Box Model:

┌──────── margin ────────┐
│  ┌──── border ────┐    │
│  │  ┌── padding ──┐ │   │
│  │  │   content    │ │   │
│  │  └─────────────┘ │   │
│  └────────────────┘    │
└─────────────────────┘

每一層的數字直接改可以即時預覽效果。debug 佈局必備。


相關文章