# Day 57：CSS Box Model 與偽元素、偽類

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

---

> **學習情境**: 在檢視結訓學員的程式碼時,發現他們的切版作業採用了 `box-sizing: border-box`,但我目前的作業還沒有使用這個設定。透過這次筆記深入理解 Box Model、偽元素、偽類的運作原理與最佳實踐。

* * *

一、問題發現:我的作業與學員程式碼的差異
--------------------

### 我目前的 CSS 狀態

    /* 我的作業沒有這段設定 */
    header {
      border: 5px solid black;
      /* 使用預設的 content-box */
    }
    
    footer {
      border: 5px solid black;
      padding: 30px;
      /* 實際寬度會超出設定值 */
    }
    

### 學員程式碼的常見寫法

    /* 學員作業開頭有這段 */
    * {
      box-sizing: border-box;
    }
    
    /* 搜尋後發現還有另外一種常用的包含三個選擇器的設定 */
    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
    

**發現問題**:為什麼大家都用這個設定?這三個選擇器分別是什麼意思?

* * *

二、Box Model 基礎概念
----------------

### Content-box vs Border-box

**Content-box (CSS 預設值)**: [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing)

    實際佔據寬度 = width + padding-left + padding-right + border-left + border-right
    

**Border-box (現代推薦)**: [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS/box-sizing)

    實際佔據寬度 = width (已包含 padding 和 border)
    內容區域寬度 = width - padding - border
    

### CodePen 實驗:兩種模型對比

    <!-- HTML -->
    <div class="container">
      <div class="box content-box">
        <h3>Content-box (預設)</h3>
        <p>width: 200px<br>padding: 20px<br>border: 5px</p>
      </div>
      
      <div class="box border-box">
        <h3>Border-box</h3>
        <p>width: 200px<br>padding: 20px<br>border: 5px</p>
      </div>
    </div>
    

    /* CSS */
    .container {
      display: flex;
      gap: 20px;
      padding: 20px;
      background: #f0f0f0;
    }
    
    .box {
      width: 200px;
      padding: 20px;
      border: 5px solid #333;
      background: white;
    }
    
    .content-box {
      box-sizing: content-box;
      /* 實際寬度 = 200 + 40 + 10 = 250px */
    }
    
    .border-box {
      box-sizing: border-box;
      /* 實際寬度 = 200px */
      /* 內容區 = 200 - 40 - 10 = 150px */
    }
    
    /* 視覺化實際寬度 */
    .box::after {
      content: '';
      position: absolute;
      bottom: -30px;
      left: 0;
      right: 0;
      height: 2px;
      background: red;
    }
    

**觀察重點**:

*   Content-box 的盒子明顯比較寬
    
*   Border-box 的盒子寬度就是你設定的 200px
    
*   兩者內容區域大小不同
    

* * *

三、改用 Border-box 的影響分析
---------------------

### 我的作業需要調整的地方

#### 1\. Header 區域

    <!-- HTML -->
    <header>
      <div class="logo">
        <img src="logo.png" alt="LOGO" />
        <p>ZERO TYPE</p>
      </div>
      <nav>
        <ul>
          <li><span>Home</span></li>
          <li>Feature</li>
          <li>News</li>
        </ul>
      </nav>
    </header>
    

    /* CSS - 影響分析 */
    header {
      border: 5px solid black;
      background-color: #f2f2f2;
      /* content-box: 寬度 = 100% + 10px (border) */
      /* border-box: 寬度 = 100% (border 內推) */
    }
    
    header .logo {
      margin: 15px 80px 30px 150px;
      /* margin 不受 box-sizing 影響 */
    }
    

**影響**:改用 border-box 後,header 內容區會縮小 10px(左右 border 各 5px)。 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS/box-sizing)

#### 2\. Footer 區域(影響最大)

    <!-- HTML -->
    <footer>
      <div class="copyright">&copy; 2023 Zerotype.</div>
      <div class="social-media-link">
        <ul>
          <li><a href="#"><img src="facebook.png" alt="FB"/></a></li>
          <li><a href="#"><img src="twitter.png" alt="Twitter"/></a></li>
        </ul>
      </div>
    </footer>
    

    /* CSS - 影響分析 */
    footer {
      border: 5px solid black;  /* 左右共 10px */
      padding: 30px;             /* 左右共 60px */
      background-color: #f2f2f2;
    }
    
    /* content-box 下:
       實際寬度 = 100% + 60px + 10px = 超出容器!
    */
    
    /* border-box 下:
       實際寬度 = 100%
       內容區域 = 100% - 60px - 10px
    */
    

**影響**:改用 border-box 後,footer 內容區會縮小 70px,需要調整內部元素的 margin。 [vocus](https://vocus.cc/article/649bbb8afd897800010e97b6)

#### 3\. 按鈕區域

    <!-- HTML -->
    <div class="try-it-btn">
      <h2>TRY IT NOW!</h2>
      <p>Don't worry it's for free.</p>
    </div>
    

    /* CSS */
    .try-it-btn h2 {
      padding: 20px 30px;
      background: linear-gradient(to bottom, #ffb84d 50%, #ff9500 50%);
      border-radius: 5px;
      /* 沒有設定 width,影響較小 */
    }
    

**影響**:按鈕內容區會稍微縮小,但因為沒有固定寬度,視覺差異不大。 [vocus](https://vocus.cc/article/649bbb8afd897800010e97b6)

### CodePen 實驗:版面跑版問題

    <!-- HTML -->
    <div class="layout-demo">
      <h3>50% + 50% 佈局問題對比</h3>
      
      <h4>❌ Content-box:會跑版</h4>
      <div class="row content-box-layout">
        <div class="col">左欄 (content-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div><div class="col">右欄 (content-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div>
      </div>
      
      <h4>✅ Border-box:完美並排</h4>
      <div class="row border-box-layout">
        <div class="col">左欄 (border-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div><div class="col">右欄 (border-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div>
      </div>
    </div>
    

    /* CSS */
    .layout-demo {
      padding: 20px;
      max-width: 800px;
      margin: 0 auto;
      background: #f5f5f5;
    }
    
    h3 {
      margin-top: 0;
      color: #333;
    }
    
    h4 {
      margin: 20px 0 10px;
      color: #666;
    }
    
    .row {
      margin-bottom: 30px;
      border: 3px dashed #999;
      background: white;
      font-size: 0; /* ⭐ 消除 inline-block 的空白字符 */
    }
    
    .col {
      width: 50%;
      padding: 20px;
      border: 5px solid #333;
      display: inline-block;
      vertical-align: top;
      font-size: 14px; /* ⭐ 恢復正常字體大小 */
      line-height: 1.5;
    }
    
    /* Content-box 版本:會爆版! */
    .content-box-layout .col {
      box-sizing: content-box;
      background: #ffebee;
      /* 每個欄位實際寬度 = 50% + 40px + 10px */
      /* 兩欄加起來 = 100% + 100px > 100% */
    }
    
    /* Border-box 版本:完美並排 */
    .border-box-layout .col {
      box-sizing: border-box;
      background: #e8f5e9;
      /* 每個欄位實際寬度 = 50% (已包含 padding 和 border) */
      /* 兩欄加起來 = 100% */
    }
    

**觀察重點**:

*   Content-box 版本的右欄會被擠到下一行
    
*   Border-box 版本完美佔據各 50%
    

⚠ **重要提醒:inline-block 空白問題**

使用 `display: inline-block` 時,HTML 標籤之間的**換行和空白**會被視為空白字符,佔據空間,導致元素換行。

**解決方法一:父元素設 font-size: 0**

    .row {
      font-size: 0; /* 消除空白 */
    }
    
    .col {
      font-size: 14px; /* 子元素恢復正常 */
    }
    

**解決方法二:HTML 標籤不換行**

    <!-- ✅ 兩個 div 緊連,不要換行 -->
    <div class="col">左欄</div><div class="col">右欄</div>
    
    <!-- ❌ 這樣會產生空白字符 -->
    <div class="col">左欄</div>
    <div class="col">右欄</div>
    

**替代方案**:也可以用 `float` 來做多欄並排(後續會學習)。

* * *

四、偽元素 (Pseudo-elements) 深入理解
----------------------------

### ::before 和 ::after 是什麼?

**核心概念**:透過 CSS 在元素內部插入虛擬內容,不需要修改 HTML。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements)

### CodePen 實驗:偽元素基礎

    <!-- HTML -->
    <div class="demo-container">
      <h3>偽元素示範</h3>
      
      <div class="text-with-icon">重要訊息</div>
      
      <div class="quote">
        這是一段需要引號的文字內容，展示如何用偽元素自動加上裝飾性引號效果。這個方法可以保持 HTML 簡潔，所有裝飾都由 CSS 處理。
      </div>
      
      <ul class="nav-list">
        <li>Home</li>
        <li>About</li>
        <li>Contact</li>
      </ul>
    </div>
    

    /* CSS */
    .demo-container {
      padding: 20px;
      max-width: 600px;
      margin: 0 auto;
      background: #f5f5f5;
    }
    
    h3 {
      margin-top: 0;
      color: #333;
    }
    
    /* 範例 1: 在文字前加圖示 */
    .text-with-icon {
      padding: 15px;
      background: #fff3e0;
      border-left: 4px solid #ff9800;
      margin: 20px 0;
    }
    
    .text-with-icon::before {
      content: "⚠️ ";
      font-size: 1.2em;
    }
    
    /* 範例 2: 自動加引號 */
    .quote {
      position: relative;
      padding: 20px 40px;
      background: white;
      margin: 20px 0;
      border-left: 4px solid #2196f3;
      line-height: 1.6;
      font-style: italic;
      color: #555;
    }
    
    .quote::before {
      content: "\201C";  /* 左彎引號 " (使用 Unicode 編碼) */
      position: absolute;
      top: -10px;        /* 往上移,不蓋住文字 */
      left: 10px;
      font-size: 3em;
      color: #2196f3;
      line-height: 1;
      opacity: 0.3;      /* 半透明,更柔和 */
    }
    
    .quote::after {
      content: "\201D";  /* 右彎引號 " */
      position: absolute;
      bottom: -10px;     /* 往下移,不蓋住文字 */
      right: 10px;
      font-size: 3em;
      color: #2196f3;
      line-height: 1;
      opacity: 0.3;
    }
    
    /* 範例 3: 導航列分隔符號 */
    .nav-list {
      list-style: none;
      padding: 0;
      background: white;
      padding: 15px;
      border-radius: 4px;
    }
    
    .nav-list li {
      display: inline-block;
      padding: 5px 10px;
    }
    
    .nav-list li:not(:last-child)::after {
      content: "|";
      margin-left: 10px;
      color: #999;
    }
    

**觀察重點**:

*   所有裝飾都不在 HTML 中
    
*   `::before` 在內容前面,`::after` 在內容後面
    
*   必須有 `content` 屬性才會顯示 [ppl-ai-file-upload.s3.amazonaws](https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/collection_92a0e6f8-76f3-4f42-97ca-a7dc3f18a08d/83b51e54-f68c-427d-87ca-bad08770d3ca/Xue-Xi-Lu-Jing-Gui-Hua-_v2.3_2026-01-23.md)
    
*   引號使用 Unicode 編碼 `\201C` 和 `\201D` 確保瀏覽器相容性
    
*   調整 `top/bottom` 為負值讓引號不蓋住文字
    

💡 **常用 Unicode 引號編碼**:

符號

名稱

CSS content 寫法

"

左彎雙引號

`content: "\201C";`

"

右彎雙引號

`content: "\201D";`

'

左彎單引號

`content: "\2018";`

'

右彎單引號

`content: "\2019";`

「

中文左引號

`content: "「";` 或 `content: "\300C";`

」

中文右引號

`content: "」";` 或 `content: "\300D";`

### 偽元素的關鍵特性

#### 1\. 必須有 content 屬性

    /* ❌ 不會顯示 */
    .element::before {
      color: red;
      font-size: 2em;
    }
    
    /* ✅ 正確寫法 */
    .element::before {
      content: "";  /* 就算是空的也要寫 */
      color: red;
      font-size: 2em;
    }
    

#### 2\. 在元素內部,不是外部

    /* 結構示意 */
    <div class="box">
      ::before (第一個孩子)
      原本的內容
      ::after (最後一個孩子)
    </div>
    

#### 3\. 無法用於空元素

    /* ❌ 這些元素無法使用偽元素 */
    img::before { }   /* 無效 */
    input::after { }  /* 無效 */
    br::before { }    /* 無效 */
    
    /* 因為它們不能有子元素 */
    

### 我的作業可以用偽元素優化的地方

    <!-- HTML (簡化版) -->
    <header>
      <nav>
        <ul>
          <li><span>Home</span></li>
          <li>Feature</li>
          <li>News</li>
          <li>About</li>
        </ul>
      </nav>
    </header>
    

    /* CSS - 用偽元素加分隔線 */
    nav li {
      display: inline-block;
      margin-right: 100px;
    }
    
    /* 優化:用偽元素取代 margin,視覺更清楚 */
    nav li:not(:last-child)::after {
      content: "·";  /* 或用 "|" */
      margin: 0 20px;
      color: lightgray;
      font-size: 1.2em;
    }
    
    /* 現在選中狀態的底線效果 */
    nav li span {
      color: orange;
      position: relative;
    }
    
    nav li span::after {
      content: "";
      display: block;
      width: 100%;
      height: 2px;
      background-color: orange;
      margin-top: 5px;
    }
    

* * *

五、偽類 vs 偽元素:核心差異
----------------

### 概念對比表

特徵

偽類 `:`

偽元素 `::`

**作用**

選擇元素的「特殊狀態」

創造「虛擬元素」

**冒號數量**

一個 `:`

兩個 `::`

**是否生成盒子**

否

是

**需要 content**

不需要

`::before/::after` 必須有

**數量限制**

可多個

一個選擇器一組

**常見用途**

互動狀態、結構關係

裝飾性內容、特殊格式

### CodePen 實驗:偽類與偽元素的組合

    <!-- HTML -->
    <div class="interactive-demo">
      <h3>偽類 + 偽元素組合</h3>
      
      <ul class="styled-list">
        <li>第一項</li>
        <li>第二項</li>
        <li>第三項</li>
      </ul>
      
      <a href="#" class="fancy-link">帶動畫效果的連結</a>
    </div>
    

    /* CSS */
    .interactive-demo {
      padding: 20px;
      max-width: 600px;
      margin: 0 auto;
    }
    
    /* 範例 1: 偽類選擇結構 */
    .styled-list {
      list-style: none;
      padding: 0;
    }
    
    .styled-list li {
      padding: 10px;
      margin: 5px 0;
      background: #f0f0f0;
    }
    
    /* 用偽類選擇第一個 */
    .styled-list li:first-child {
      background: #e3f2fd;
      font-weight: bold;
    }
    
    /* 用偽元素加裝飾 */
    .styled-list li:first-child::before {
      content: "👑 ";
    }
    
    /* 用偽類選擇懸停狀態 */
    .styled-list li:hover {
      background: #fff3e0;
      cursor: pointer;
    }
    
    /* 範例 2: 動態底線效果 */
    .fancy-link {
      display: inline-block;
      padding: 10px 20px;
      text-decoration: none;
      color: #333;
      position: relative;
    }
    
    /* 用偽元素創建底線 */
    .fancy-link::after {
      content: "";
      position: absolute;
      bottom: 0;
      left: 0;
      width: 0;
      height: 2px;
      background: orange;
      transition: width 0.3s;
    }
    
    /* 用偽類觸發動畫 */
    .fancy-link:hover::after {
      width: 100%;
    }
    

**觀察重點**:

*   偽類 `:first-child` 選擇「第一個元素」(狀態)
    
*   偽元素 `::before` 插入圖示(新內容)
    
*   偽類 `:hover` 觸發「懸停狀態」
    
*   偽元素 `::after` 創造底線(新元素)
    
*   可以組合使用:`li:hover::before`
    

### 設計理念差異

#### 偽類:避免手動管理狀態

    // 如果沒有偽類,需要這樣寫 JS
    button.addEventListener('mouseenter', () => {
      button.classList.add('hovered');
    });
    button.addEventListener('mouseleave', () => {
      button.classList.remove('hovered');
    });
    
    // 有了 :hover 偽類,CSS 自動處理
    

    button:hover {
      background: orange;
    }
    

#### 偽元素:避免污染 HTML 語意

    <!-- 沒有偽元素:HTML 很亂 -->
    <button>
      <span class="icon">★</span>
      <span class="text">按鈕</span>
    </button>
    
    <!-- 有了偽元素:HTML 簡潔 -->
    <button>按鈕</button>
    

    button::before {
      content: "★ ";
    }
    

* * *

六、Box-sizing 與偽元素的關係
--------------------

### 為什麼要設定偽元素的 box-sizing?

偽元素會生成「真實的盒子」,所以需要設定 `box-sizing`。 [forum.freecodecamp](https://forum.freecodecamp.org/t/why-are-pseudo-elements-used-in-this-css/73317) 偽類只是選擇器,不生成盒子,不需要設定。

### 完整設定

    *,           /* 選擇所有真實 HTML 元素 */
    *::before,   /* 選擇所有 ::before 偽元素 */
    *::after {   /* 選擇所有 ::after 偽元素 */
      box-sizing: border-box;
    }
    

### 為什麼不能只寫 `*`?

因為 `*` 只會選到真實的 HTML 元素,**不會選到偽元素**。 [stackoverflow](https://stackoverflow.com/questions/31317238/why-use-selector-in-combination-with-before-and-after)

### CodePen 實驗:偽元素的 box-sizing 問題

    <!-- HTML -->
    <div class="comparison">
      <h3>偽元素 box-sizing 對比</h3>
      
      <div class="card without-reset">
        <h4>沒設定偽元素的 box-sizing</h4>
        <p>這是卡片內容</p>
      </div>
      
      <div class="card with-reset">
        <h4>有設定偽元素的 box-sizing</h4>
        <p>這是卡片內容</p>
      </div>
    </div>
    

    /* CSS */
    .comparison {
      padding: 20px;
    }
    
    .card {
      width: 300px;
      padding: 20px;
      border: 2px solid #333;
      margin: 20px 0;
      position: relative;
      background: white;
    }
    
    /* 兩張卡片都用 border-box */
    .card {
      box-sizing: border-box;
    }
    
    /* 共用的偽元素樣式 */
    .card::before {
      content: "標籤";
      position: absolute;
      top: -15px;
      left: 20px;
      width: 60px;
      padding: 5px 10px;
      border: 2px solid orange;
      background: white;
      font-size: 12px;
      text-align: center;
    }
    
    /* 第一張卡片:偽元素用預設 content-box */
    .without-reset::before {
      box-sizing: content-box;
      /* 實際寬度 = 60 + 20 + 4 = 84px */
    }
    
    /* 第二張卡片:偽元素也用 border-box */
    .with-reset::before {
      box-sizing: border-box;
      /* 實際寬度 = 60px */
    }
    
    /* 視覺化寬度差異 */
    .card::before {
      outline: 2px dashed red;
      outline-offset: 2px;
    }
    

**觀察重點**:

*   第一張卡片的標籤明顯比較寬
    
*   第二張卡片的標籤寬度就是設定的 60px
    
*   如果不統一 box-sizing,版面計算會混亂 [stackoverflow](https://stackoverflow.com/questions/54389658/pseudo-element-not-full-container-width-when-border-used)
    

### 偽類不需要設定 box-sizing

    /* ❌ 偽類不用設定 */
    a:hover {
      /* :hover 只是選擇「懸停狀態的 a 元素」
         這個 a 元素本身已經有 box-sizing */
    }
    
    /* ✅ 偽元素需要設定 */
    a::before {
      /* ::before 生成了新的盒子
         需要明確設定 box-sizing */
    }
    

**原因**: [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes)

*   偽類只是「選擇器的條件」,不生成盒子
    
*   偽元素會生成「新的盒子」,需要設定 [forum.freecodecamp](https://forum.freecodecamp.org/t/why-are-pseudo-elements-used-in-this-css/73317)
    

* * *

七、為什麼預設值還是 Content-box?
-----------------------

### 核心原因:向後兼容性 (Backward Compatibility)

如果現在改變預設值,全世界數百萬個舊網站都會瞬間跑版。 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

### CodePen 實驗:視覺化寬度差異

    <!-- HTML -->
    <div class="simple-demo">
      <h3>為什麼不能改預設值？</h3>
      
      <p class="intro">
        2005 年的網站設計師寫了這段 CSS：
      </p>
      
      <div class="code-block">
        .sidebar {<br>
        &nbsp;&nbsp;width: 300px;<br>
        &nbsp;&nbsp;padding: 20px;<br>
        &nbsp;&nbsp;border: 5px solid black;<br>
        }
      </div>
      
      <div class="comparison">
        <div class="col">
          <h4>✅ Content-box (當年的預設)</h4>
          <div class="visual-box content-box-visual">
            <div class="content-area">內容區<br>300px</div>
            <div class="padding-area">padding 40px</div>
            <div class="border-area">border 10px</div>
          </div>
          <p class="total">總寬度 = <strong>350px</strong></p>
        </div>
        
        <div class="col">
          <h4>❌ 如果改成 Border-box</h4>
          <div class="visual-box border-box-visual">
            <div class="content-area small">內容區<br>250px<br>(被壓縮)</div>
            <div class="padding-area">padding 40px</div>
            <div class="border-area">border 10px</div>
          </div>
          <p class="total">總寬度 = <strong>300px</strong></p>
        </div>
      </div>
      
      <div class="note">
        <strong>結果：</strong>同樣的 CSS，元素從 350px 變成 300px，所有基於「350px」的版面計算都會錯位。
        這就是為什麼瀏覽器無法改變預設值——會破壞全球數百萬個舊網站。
      </div>
    </div>
    

    /* CSS */
    .simple-demo {
      padding: 20px;
      max-width: 900px;
      margin: 0 auto;
      background: #f5f5f5;
    }
    
    h3 {
      margin-top: 0;
      color: #333;
      text-align: center;
    }
    
    h4 {
      margin: 10px 0;
      color: #666;
      text-align: center;
    }
    
    .intro {
      background: #e3f2fd;
      padding: 15px;
      border-left: 4px solid #2196f3;
      line-height: 1.8;
      margin-bottom: 20px;
      text-align: center;
    }
    
    .code-block {
      background: #263238;
      color: #aed581;
      padding: 15px;
      border-radius: 4px;
      font-family: monospace;
      margin-bottom: 30px;
      line-height: 1.6;
    }
    
    .comparison {
      display: flex;
      gap: 20px;
      margin-bottom: 30px;
    }
    
    .col {
      flex: 1;
    }
    
    .visual-box {
      border: 3px solid #333;
      margin: 20px 0;
      padding: 5px;
      background: white;
    }
    
    .content-box-visual {
      width: 350px;
    }
    
    .border-box-visual {
      width: 300px;
    }
    
    .content-area {
      background: #e8f5e9;
      padding: 40px 20px;
      text-align: center;
      font-weight: bold;
      border: 5px solid #4caf50;
    }
    
    .content-area.small {
      background: #ffebee;
      border-color: #f44336;
    }
    
    .padding-area,
    .border-area {
      text-align: center;
      padding: 5px;
      font-size: 12px;
      color: #666;
    }
    
    .total {
      text-align: center;
      font-size: 18px;
      background: #fff9c4;
      padding: 10px;
      margin: 10px 0;
      border-radius: 4px;
    }
    
    .total strong {
      color: #1976d2;
      font-size: 24px;
    }
    
    .note {
      padding: 20px;
      background: #fff9c4;
      border: 3px solid #fbc02d;
      border-radius: 4px;
      line-height: 1.8;
    }
    
    .note strong {
      color: #f57c00;
    }
    

**觀察重點**:

*   **左側** (content-box):側邊欄實際佔據 350px,這是設計師當年的預期
    
*   **右側** (border-box):側邊欄實際佔據 300px,內容區被壓縮 50px
    
*   同樣的 CSS 代碼,在不同 box-sizing 下,元素佔據的空間完全不同
    

### Web 標準的兩難

想像一下這個情境: [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

    /* 2005 年寫的舊網站,沒有設定 box-sizing */
    .sidebar {
      width: 300px;
      padding: 20px;
      border: 5px solid black;
      /* 作者預期:實際寬度 = 300 + 40 + 10 = 350px */
    }
    
    .content {
      width: 650px;  /* 剛好跟 sidebar 加起來是 1000px */
    }
    

**如果今天瀏覽器把預設值改成** `border-box`: [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

*   `.sidebar` 實際寬度變成 300px(不是 350px)
    
*   原本設計好的版面全部跑掉
    
*   全球數百萬個沒在維護的舊網站集體崩潰
    

### 受影響的規模

這些地方還在使用 20 年前的網頁系統: [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

*   政府機關網站
    
*   醫療機構系統
    
*   大型企業內部工具
    
*   教育機構平台
    
*   銀行系統介面
    

它們可能永遠不會更新,但必須繼續運作。

* * *

八、歷史趣聞:IE 反而做對了
---------------

### W3C vs Internet Explorer

早期有兩套盒模型標準: [blog.csdn](https://blog.csdn.net/u011546421/article/details/53293106)

**Internet Explorer 5 (1999年)**: [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

    width = content + padding + border
    (類似現在的 border-box)
    

**W3C CSS 標準 (2000年)**: [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing)

    width = content only
    (就是現在的 content-box)
    

當時 W3C 認為「寬度應該只指內容」符合邏輯,但實務上開發者發現 IE 的做法更直覺。 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

### 諷刺的結果

*   **當時**:大家罵 IE 不遵守標準 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)
    
*   **現在**:大家承認 IE 的設計比較合理,W3C 標準是錯誤決策 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)
    

CSS 工作小組後來也公開承認「這是 CSS 的設計錯誤」。 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

* * *

九、為什麼不能「現在就改」?
--------------

### 1\. Web 的核心哲學:永不破壞舊內容

Tim Berners-Lee (Web 發明者) 的原則: [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

> 「1990 年的網頁,在 2026 年的瀏覽器中應該還能正常顯示」

這是 Web 能成功的關鍵原因之一 — 內容可以跨越時空存在。

### 2\. 沒有「版本隔離」機制

不像程式語言有版本宣告:

    // JavaScript 可以這樣
    "use strict";  // 啟用嚴格模式
    

CSS 沒有類似機制能說「這個網頁用 CSS v2 標準」或「用 CSS v4 標準」。 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

### 3\. 改動成本太高

如果改預設值,需要: [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

*   所有瀏覽器廠商同時更新
    
*   全球開發者修改數百萬個網站
    
*   測試確保沒有任何舊網站被破壞
    
*   處理各種邊緣案例
    

這個成本遠超過「讓開發者自己加三行 CSS」。

* * *

十、特殊案例:`<button>` 預設是 border-box
--------------------------------

有趣的是,某些 HTML 元素的預設值其實是 `border-box`: [stackoverflow](https://stackoverflow.com/questions/70278121/why-is-box-sizing-for-button-border-box-by-default)

    /* 瀏覽器內建樣式 */
    button, input[type="submit"], input[type="reset"] {
      box-sizing: border-box;  /* 預設就是 border-box! */
    }
    

**原因**: [stackoverflow](https://stackoverflow.com/questions/70278121/why-is-box-sizing-for-button-border-box-by-default)

*   這些元素是後來才加入 HTML 的
    
*   設計時已經知道 border-box 比較好
    
*   不會破壞既有網站(因為是新元素)
    

這證明了「如果重新設計,大家都會選 border-box」。

* * *

十一、現代解決方案:CSS Reset
-------------------

既然標準無法改,社群發展出「重置樣式」的做法: [freecodecamp](https://www.freecodecamp.org/news/what-is-box-sizing-border-box-css/)

### 方案一:全域重置(最常見)

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

### 方案二:繼承式重置

    html {
      box-sizing: border-box;
    }
    
    *,
    *::before,
    *::after {
      box-sizing: inherit;  /* 繼承父元素的設定 */
    }
    

好處:可以在特定區域改回 content-box。 [freecodecamp](https://www.freecodecamp.org/news/what-is-box-sizing-border-box-css/)

### 方案三:使用 CSS 框架

現代框架都內建 border-box:

*   Bootstrap
    
*   Tailwind CSS
    
*   Normalize.css
    

* * *

十二、類似的「歷史包袱」案例
--------------

### JavaScript 的 `typeof null`

    typeof null  // "object" (錯誤!)
    

這是 JavaScript 設計錯誤,但無法修正,因為會破壞全球數百萬個網站。 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

### HTTP 狀態碼 418

    HTTP 418: I'm a teapot
    

原本是愚人節玩笑,但被寫入標準後就無法移除,因為有人真的在用。 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

* * *

十三、統一 Box-sizing 的重要性
---------------------

### 核心原則

**只要元素會產生盒子並參與版面佈局,就需要統一計算方式**。 [dev](https://dev.to/magsimuhammadessa/a-beginners-guide-to-the-css-box-model-4hc5)

### 問題情境:兩欄佈局

    /* 混用兩種 box-sizing */
    .left-column {
      width: 50%;
      padding: 20px;
      border: 5px solid black;
      box-sizing: content-box;
      /* 實際寬度 = 50% + 40px + 10px → 爆版! */
    }
    
    .right-column {
      width: 50%;
      padding: 20px;
      border: 5px solid black;
      box-sizing: border-box;
      /* 實際寬度 = 50% (已包含) → 正常 */
    }
    /* 結果:兩欄加起來超過 100%,右欄被擠下去 */
    

### 統一後的效果

    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
    
    .left-column,
    .right-column {
      width: 50%;
      padding: 20px;
      border: 5px solid black;
      /* 兩欄各佔 50%,完美並排 */
    }
    

### 實務價值

1.  **直覺化計算**: [crypist](https://www.crypist.com/post/css/basics/why-set-box-sizing-to-border-box/)
    
    *   設定 `width: 300px`,元素在版面上就佔 300px
        
2.  **響應式設計更容易**: [dev](https://dev.to/magsimuhammadessa/a-beginners-guide-to-the-css-box-model-4hc5)
    
        .col {
          width: 25%;  /* 不用擔心 padding/border 爆版 */
          padding: 15px;
          border: 2px solid gray;
        }
        
    
3.  **維護性提升**: [crypist](https://www.crypist.com/post/css/basics/why-set-box-sizing-to-border-box/)
    
    *   修改 padding 或 border 時不需要重新計算 width
        

* * *

十四、現代最佳實踐
---------

### 新專案必做

    /* 專案第一行 CSS */
    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
    

### 維護舊專案時

評估風險後再決定是否加上:

*   測試所有頁面
    
*   檢查是否有地方依賴 content-box 行為
    
*   漸進式重構,不要一次全改
    

* * *

十五、我的作業改版計劃
-----------

### 第一步:加入全域設定

    /* 在 style.css 最前面加上 */
    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
    

### 第二步:測試版面影響

需要檢查這些區域:

1.  **Header**:
    
    *   檢查內容是否太擠
        
    *   logo 位置是否正常
        
2.  **Footer**:
    
    *   檢查 padding 是否需要調整
        
    *   社交媒體連結排列是否正常
        
3.  **Main 區域**:
    
    *   `.hero-intro` 和 `.hero-img` 的並排
        
    *   Article 的雙欄排版
        

### 第三步:用偽元素優化

    /* 導航列加分隔線 */
    header nav li:not(:last-child)::after {
      content: "·";
      margin: 0 15px;
      color: #ccc;
    }
    
    /* TRY IT NOW 按鈕加箭頭 */
    .try-it-btn h2::after {
      content: " →";
      transition: transform 0.3s;
    }
    
    .try-it-btn h2:hover::after {
      transform: translateX(5px);
    }
    
    /* Footer 上方加裝飾線 */
    footer::before {
      content: "";
      display: block;
      width: 80%;
      height: 2px;
      background: linear-gradient(
        to right,
        transparent,
        #ff9500,
        transparent
      );
      margin: 0 auto 20px;
    }
    

* * *

十六、重點整理與易錯提醒
------------

### Box Model

✅ **記住**:

*   Border-box 讓寬度計算更直覺
    
*   現代專案一律使用 border-box
    
*   改動後要測試所有版面
    

❌ **易錯**:

*   忘記 border-box 會壓縮 content 區域
    
*   只設定 `*`,忘記偽元素
    
*   混用兩種 box-sizing
    

### 偽元素

✅ **記住**:

*   兩個冒號 `::`
    
*   必須有 `content` 屬性
    
*   在元素內部,不是外部
    
*   適合裝飾性內容
    
*   引號用 Unicode 編碼 `\201C` `\201D`
    

❌ **易錯**:

*   忘記 `content: "";`
    
*   用在空元素(`<img>`, `<input>`)
    
*   誤以為在元素外面
    
*   引號位置蓋住文字
    

### 偽類

✅ **記住**:

*   一個冒號 `:`
    
*   選擇元素的狀態或結構關係
    
*   不生成盒子
    

❌ **易錯**:

*   混淆冒號數量
    
*   對偽類設定 box-sizing
    

### 統一 box-sizing

✅ **記住**:

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

❌ **易錯**:

*   只寫 `*`
    
*   忘記偽元素
    

* * *

十七、關鍵啟示
-------

這個案例告訴我們: [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

1.  **標準制定要謹慎**:一旦發佈就很難改
    
2.  **向後兼容很重要**:比「技術上正確」更重要
    
3.  **開發者可以自救**:透過 Reset CSS 解決標準問題
    
4.  **歷史會評價設計**:20 年後才發現當初的決策是錯的
    

CSS Working Group 現在每次制定新標準都會討論「這會不會成為下一個 box-sizing」。 [reddit](https://www.reddit.com/r/css/comments/12a602n/why_is_the_default_for_boxsizing_contentbox/)

* * *

十八、延伸學習資源
---------

### MDN 官方文件

*   \[box-sizing\] [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS/box-sizing)
    
*   \[Pseudo-elements\] [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements)
    
*   \[Pseudo-classes\] [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes)
    
*   \[Box Model\] [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Box_model)
    

### CodePen 實驗

我會在這裡貼上自己的實驗連結:

*   \[ \] Box Model 對比實驗 https://codepen.io/gcake119/pen/YPWRJZJ
    
*   \[ \] 偽元素應用範例 https://codepen.io/gcake119/pen/EayOMje
    
*   \[ \] 偽元素的 box-sizing 問題 https://codepen.io/gcake119/pen/gbMQERM
    
*   \[ \] 偽分類與偽元素結合 https://codepen.io/gcake119/pen/xbOQBqB
    
*   \[ \] 歷史包袱視覺化示範 https://codepen.io/gcake119/pen/YPWRgOq
    

* * *

十九、學習心得
-------

### 關鍵啟示

1.  **標準不等於最佳實踐**
    
    *   Content-box 是標準,但 border-box 更實用
        
    *   要理解背後的歷史脈絡
        
2.  **向後兼容很重要**
    
    *   Web 的成功建立在「永不破壞舊內容」
        
    *   這也是為什麼有些設計無法改變
        
3.  **社群可以自救**
    
    *   雖然標準有問題,社群透過 CSS Reset 解決
        
    *   現代專案幾乎都採用 border-box
        
4.  **偽元素是強大工具**
    
    *   保持 HTML 簡潔
        
    *   裝飾性內容用 CSS 處理
        
    *   減少無意義的標籤
        
5.  **實作中學習最有效**
    
    *   CodePen 實驗幫助理解抽象概念
        
    *   親手重現問題才能真正掌握
        

### 下一步行動

*   \[ \] 將 border-box 加入我的作業
    
*   \[ \] 測試所有版面是否正常
    
*   \[ \] 用偽元素優化導航列和按鈕
    
*   \[ \] 在 CodePen 建立實驗範例
    
*   \[ \] 整理成技術筆記並分享
    

* * *

**筆記日期**: 2026-02-10  
**學習情境**: 對照結訓學員程式碼發現的問題  
**主題標籤**: #CSS #BoxModel #Pseudo-elements #Pseudo-classes #BestPractice  
**CodePen 實驗**: 連結如上文

---

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