# Day 63：CSS 裝飾線實作與 Margin Collapse 父子穿透問題

By [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake) · 2026-02-25

---

> 📅 2026-02-25　｜　🏷 CSS · Pseudo-elements · Box Model · Margin Collapse

* * *

一、畫水平裝飾線的兩種方式
-------------

電商網站的 Products 區常見標題下方的短裝飾線，實作上有兩個選項：

方式

語意

跨瀏覽器一致性

適用場景

`<hr>` 元素

✅ 代表段落主題轉換

❌ 預設樣式不一致，需 reset

內容層次上有分隔意義

CSS 偽元素

❌ 純裝飾

✅ 完全由 CSS 掌控

純視覺的短裝飾線

**結論**：如果裝飾線只是視覺點綴，不代表任何內容分隔語意，偽元素是更乾淨的選擇，符合「結構歸 HTML、裝飾歸 CSS」的分工原則。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/Pseudo-elements)

* * *

二、`::before` / `::after` 的佔位概念
------------------------------

`::before` 與 `::after` 插入的位置，是在**目標元素的內容裡面**的前後，不是 HTML 結構中兄弟元素的順序。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/::before)

    ┌─────────────────────────────┐
    │  .element                   │
    │  ┌──────┐ ┌───────┐ ┌─────┐│
    │  │before│ │ 內容  │ │after││
    │  └──────┘ └───────┘ └─────┘│
    └─────────────────────────────┘
    

MDN 的定義：`::before` 是目標元素的「第一個子節點」，`::after` 是「最後一個子節點」，預設都是 `display: inline`。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::after)

### ⚠ 最常見的新手陷阱

偽元素預設 `display: inline`，此時 `width` 與 `height` 設定不生效。**必須加上** `display: block` **或** `display: inline-block` 才能控制尺寸。 [bennadel](https://www.bennadel.com/blog/2445-using-css-pseudo-elements-before-and-after.htm)

❌ 看不見，因為 inline 元素忽略 width/height \*/ .element::before { content: ''; width: 40px; height: 1px; background-color: #999; } /\* ✅ 加上 display: block 才能設定尺寸 \*/ .element::before { content: ''; display: block; /\* 關鍵 \*/ width: 40px; height: 1px; background-color: #999; }

* * *

三、裝飾線用 `background-color` vs `border`
-------------------------------------

兩種方式都能畫出 1px 的線，但設計意圖不同： [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS)

考量點

`background-color`

`border`

語意

描述盒子的填色

描述盒子的邊線

圓角支援

✅ 可用 `border-radius`

❌ 線段本身不支援

`box-sizing` 影響

❌ 不受影響

✅ 影響總高度計算

適用場景

獨立裝飾線

元素邊緣框線（card、input 等）

**結論：偽元素裝飾線用** `background-color` **更直觀。** 以 `width` + `height` + `background-color` 直接描述形狀與顏色，語意清楚，也不受 `box-sizing` 影響。

### 完整實作範例

    /* Products 標題上方的短裝飾線 */
    .products__subtitle::before {
      content: '';
      display: block;
      width: 40px;
      height: 1px;
      background-color: #999;
      margin: 0 auto 16px; /* 水平置中，下方推開 16px */
    }
    

* * *

四、偽元素 Debug 技巧
--------------

開發時無法直接用 HTML 確認偽元素位置，可用以下方式：

1.  **DevTools Elements 面板**：展開元素節點，`::before` / `::after` 會列在子節點裡
    
2.  **加背景色 debug**：開發中先設 `background-color: red` 確認位置與尺寸，完成後換回正式顏色
    
3.  **DevTools Computed 面板**：查看偽元素的 box model 計算結果
    

* * *

五、偽元素撐高父層對 `vertical-align` 的影響
-------------------------------

`::before` 設為 `display: block` 後，會成為父元素內部的**區塊子元素**，進而撐高父元素的內容盒。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::before)

*   **父元素為** `display: block`：正常行為，頁面整體對齊不受影響
    
*   **父元素為** `display: inline-block`：`::before` 撐高元素後，`vertical-align` 的基線計算會偏移，造成同行兄弟元素錯位，需特別留意
    

    沒有 ::before 時：
    ┌──────────────────────┐
    │  inline-block 元素   │  基線在文字底部
    └──────────────────────┘
    
    有 ::before (display: block) 後：
    ┌──────────────────────┐
    │ ┌──────────────────┐ │
    │ │  ::before（高）  │ │  ← 這個高度把基線往下推
    │ └──────────────────┘ │
    │  文字                │  基線現在在這裡
    └──────────────────────┘
    
    

* * *

六、Margin Collapse：父子元素的外距穿透
---------------------------

### 問題現象

Footer 的兩個子容器之間出現非預期間隔，但 DevTools 確認兩個容器的 `margin` 都是 0。

### 根本原因

**Margin Collapse（外距折疊）** 不只發生在相鄰兄弟元素之間，也會發生在**父子元素之間**。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing)

當父層**同時**滿足以下條件，子元素的 `margin` 會穿透父層折疊到容器外部：

*   沒有 `border`
    
*   沒有 `padding`
    
*   沒有 inline 內容（如文字）
    

                            ← <p> 的 margin-top 穿透 .footer__bottom
    ┌──────────────────┐       向上折疊，視覺上變成容器間的空隙
    │ .footer__content │
    └──────────────────┘
      ↑ 非預期的間距（其實是 <p> 的 margin）
    ┌──────────────────┐
    │ .footer__bottom  │  ← 沒有 padding，無法阻擋
    │  ┌────────────┐  │
    │  │    <p>     │  │  ← 有預設 margin-top: 1em
    │  └────────────┘  │
    └──────────────────┘
    

**驗證方式**：DevTools → 點選 `<p>` 元素 → Computed → 確認 `margin-top` 有值。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing)

### 修正方案與語意設計

`padding` 應設定在**父層容器**，而非子層 `<p>`：

    /* ✅ 容器主動負責內部留白 */
    .footer__bottom {
      text-align: center;
      padding: 60px 0; /* padding 同時阻斷了 margin collapse */
    }
    
    .footer__copyright {
      color: rgb(200, 200, 200);
      margin: 0; /* 消除 <p> 預設 margin，語意更清晰 */
    }
    

寫法

語意

`padding` 設在父層

**容器**主動把內容往內推擠，職責清晰

`padding` 設在子層

子元素自己撐大，責任邊界模糊

這個修法有「一石二鳥」的效果：父層獲得 `padding` 後，子層的 `margin` 就無法再穿透折疊出去，Margin Collapse 問題同時消失。 [thecodingfox](https://www.thecodingfox.com/blog/collapsing-margins)

### 記憶口訣

> **父層沒有** `padding` **/** `border`**，子層的** `margin` **會「穿牆而出」。**

* * *

七、防止 Margin Collapse 的所有方式
--------------------------

方式

說明

加 `padding` 或 `border`

最常用，也最語意化

加 `overflow: hidden`

建立新的 BFC，阻斷折疊

加 `display: flow-root`

明確建立 BFC，無副作用

父層有 inline 內容（如文字）

自動阻斷折疊

[robcallaghan.co](https://www.robcallaghan.co.uk/blog/margin-collapsing-explained-with-examples/)

* * *

延伸閱讀
----

*   [MDN —](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) `::before` [偽元素](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/::before)
    
*   [MDN —](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::after) `::after` [偽元素](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::after) [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::after)
    
*   [MDN — Mastering Margin Collapsing](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing) [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing)
    
*   [MDN — CSS Pseudo-elements](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements) [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements)
    

* * *

TODO
----

*   \[ \] Add implementation examples
    
*   \[ \] Add resource links
    
*   \[ \] Proofread and format

---

*Originally published on [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake/day-63)*
