# Day 60：前端切版重構技術筆記

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

---

目錄
--

*   Part 1: CSS 命名系統
    
*   Part 2: 重構過程與問題解決
    

* * *

Part 1: CSS 命名系統
================

業界常見的 CSS 命名方式
--------------

### 1\. BEM (Block Element Modifier)

**核心概念**：用「區塊-元素-修飾符」三層結構命名，讓 class 名稱本身就能表達元素的身份和關係。 [frontendmentor](https://www.frontendmentor.io/articles/understanding-css-naming-conventions-bem-oocss-smacss-and-suit-css-V6ZZUYs1xz)

**命名規則**：

    .block              → 獨立元件
    .block__element     → 元件內的子元素
    .block--modifier    → 元件的變化版本
    

**實例**：

    <div class="card">
      <h2 class="card__title">標題</h2>
      <p class="card__text">內容</p>
      <button class="card__btn card__btn--primary">按鈕</button>
    </div>
    

**優勢**：

*   權重扁平化：所有選擇器權重統一為 `0-1-0` [zoocha](https://www.zoocha.com/news/nesting-css-good-bad-and-bem)
    
*   結構清晰：一看 class 名稱就知道元素關係
    
*   避免樣式衝突：命名空間隔離
    

**適用情境**：

*   中大型專案
    
*   需要明確元件邊界
    
*   團隊協作開發
    

* * *

### 2\. OOCSS (Object-Oriented CSS)

**核心概念**：把 CSS 當成「樂高積木」，強調「結構與外觀分離」和「容器與內容分離」。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)

**實作方式**：

    /* 通用結構類別 */
    .list { /* 列表基礎結構 */ }
    .list-item { /* 列表項目結構 */ }
    
    /* 特定外觀類別 */
    .nav-list { /* 導航列表樣式 */ }
    .nav-item { /* 導航項目樣式 */ }
    

**HTML 使用**：

    <ul class="list nav-list">
      <li class="list-item nav-item">...</li>
    </ul>
    

**適用情境**：需要大量重複使用相同視覺模式時（例如 Bootstrap）。 [hackmd](https://hackmd.io/@Yo0/Hyo9pAi3h)

* * *

### 3\. SMACSS (Scalable and Modular Architecture for CSS)

**核心概念**：不是命名規則，而是「CSS 檔案組織架構」，把樣式分成 5 大類： [frontendmentor](https://www.frontendmentor.io/articles/understanding-css-naming-conventions-bem-oocss-smacss-and-suit-css-V6ZZUYs1xz)

1.  **Base** (基礎)：全域預設樣式 (`body`, `a`, `h1`)
    
2.  **Layout** (佈局)：主要版面結構 (`.header`, `.main`)
    
3.  **Module** (模組)：可重複使用的元件 (`.nav`, `.card`)
    
4.  **State** (狀態)：視覺狀態切換 (`.is-active`, `.is-hidden`)
    
5.  **Theme** (主題)：視覺風格變化 (顏色、字型)
    

**實作方式**：

    /* base.css */
    body { font-family: Arial; }
    
    /* layout.css */
    .nav { background-color: #f0f0f0; }
    
    /* module.css */
    .nav-link { padding: 10px; }
    
    /* state.css */
    .nav-link.is-active { font-weight: bold; }
    

**適用情境**：大型專案需要多個 CSS 檔案分工管理時。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)

* * *

### 4\. SUIT CSS

**核心概念**：專為 **React/Vue 等元件化框架** 設計，用 **PascalCase** 命名元件。 [frontendmentor](https://www.frontendmentor.io/articles/understanding-css-naming-conventions-bem-oocss-smacss-and-suit-css-V6ZZUYs1xz)

**命名規則**：

    ComponentName              → .Nav (元件)
    ComponentName-descendent   → .Nav-item (子元素, camelCase)
    ComponentName--modifier    → .Nav--primary (變化版本)
    ComponentName.is-state     → .Nav-link.is-active (狀態)
    

**實作範例**：

    <nav class="Nav Nav--primary">
      <ul class="Nav-list">
        <li class="Nav-item">
          <a class="Nav-link is-active">About</a>
        </li>
      </ul>
    </nav>
    

**適用情境**：使用 React/Vue 等元件框架，希望 CSS 命名與 JS 元件命名風格一致時。 [frontendmentor](https://www.frontendmentor.io/articles/understanding-css-naming-conventions-bem-oocss-smacss-and-suit-css-V6ZZUYs1xz)

* * *

### 5\. ITCSS (Inverted Triangle CSS)

**核心概念**：用「倒三角形」組織 CSS 層級，從最通用到最具體。 [reddit](https://www.reddit.com/r/css/comments/1doepb1/which_css_naming_convention_do_you_typically_use/)

**層級結構**：

    Settings    → 變數、配置
    Tools       → Mixins、函式
    Generic     → Reset、Normalize
    Elements    → 原生 HTML 標籤
    Objects     → 佈局模式 (OOCSS 物件)
    Components  → 具體元件 (BEM 命名)
    Utilities   → 工具類別 (!important 覆蓋)
    

**適用情境**：超大型專案需要嚴格控制 CSS 優先級 (specificity) 時。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)

* * *

### 6\. Atomic CSS (原子化 CSS)

**核心概念**：每個 class 只做「一件事」，像寫內聯樣式但用 class。 [mastheadtechnology](https://mastheadtechnology.com/blog/css-class-naming-conventions-best-practices/)

**實作方式**：

    .mt-10 { margin-top: 10px; }
    .flex { display: flex; }
    .text-center { text-align: center; }
    

**HTML 使用**：

    <div class="flex mt-10 text-center">...</div>
    

**代表框架**：Tailwind CSS、Tachyons。 [samsontobiy.hashnode](https://samsontobiy.hashnode.dev/css-methodologies-bem-oocss-smacss-and-others-compared)

**適用情境**：快速原型開發、設計系統統一、不想寫自訂 CSS 時。 [mastheadtechnology](https://mastheadtechnology.com/blog/css-class-naming-conventions-best-practices/)

* * *

CSS 命名方式比較表
-----------

方法

核心目標

命名風格

學習曲線

最適合專案類型

**BEM**

元件獨立性

`block__element--modifier`

中等

中大型傳統網站

**OOCSS**

樣式重用性

結構+外觀分離

中等

需要大量重複模式

**SMACSS**

檔案組織

分類管理

中等

大型多人協作

**SUIT CSS**

元件化框架

`ComponentName-element`

中等

React/Vue 專案

**ITCSS**

層級控制

倒三角結構

高

超大型專案

**Atomic CSS**

快速開發

單一功能類別

高(需記憶大量類別)

快速迭代專案

* * *

BEM 與 Atomic CSS 混用
-------------------

業界**經常混用 BEM 和 Atomic CSS（特別是 Tailwind CSS）**，這種組合能取各自優勢。 [sitepoint](https://www.sitepoint.com/css-architecture-block-element-modifier-bem-atomic-css/)

### 模式 1: BEM 主體 + Utility 輔助（最常見）

**核心思想**：用 BEM 定義元件結構，用 Atomic 類別處理通用樣式。 [reddit](https://www.reddit.com/r/css/comments/1gn2yet/what_do_you_think_about_combining_bem_with/)

    <div class="card">
      <h2 class="card__title">Title</h2>
      <p class="card__text mt-4 text-center">Content</p>
      <button class="card__btn card__btn--primary px-6 py-2">Click me</button>
    </div>
    

* * *

### 模式 2: Tailwind @apply + BEM 命名

**核心思想**：用 Tailwind 的 `@apply` 指令，把 utility classes 打包成 BEM 類別。 [wearecogworks](https://www.wearecogworks.com/innerworks/cogworks-blog-archive/how-to-use-tailwind-with-bem)

    .card {
      @apply bg-white rounded-lg shadow-md p-6;
    }
    
    .card__title {
      @apply text-2xl font-bold mb-4;
    }
    
    .card__btn--primary {
      @apply bg-blue-500 text-white hover:bg-blue-600;
    }
    

**優勢**：

*   HTML 保持乾淨 [wearecogworks](https://www.wearecogworks.com/innerworks/cogworks-blog-archive/how-to-use-tailwind-with-bem)
    
*   保留 BEM 的結構化
    
*   享受 Tailwind 的快速開發
    

* * *

### 模式 3: Utility-first + BEM 前綴

**核心思想**：主要用 Atomic CSS，只在**複雜元件**或**需要 JavaScript 鉤子**時用 BEM。 [simplethread](https://www.simplethread.com/comparing-bem-and-tailwind/)

    <!-- 簡單元件: 純 utility classes -->
    <div class="bg-white p-6 rounded-lg shadow">
      <h2 class="text-2xl font-bold mb-4">Title</h2>
    </div>
    
    <!-- 複雜元件: BEM + utilities -->
    <div class="dropdown" data-component="dropdown">
      <button class="dropdown__trigger px-4 py-2 bg-blue-500 text-white">
        Open Menu
      </button>
      <ul class="dropdown__menu hidden absolute bg-white shadow-lg">
        <li class="dropdown__item px-4 py-2 hover:bg-gray-100">Item 1</li>
      </ul>
    </div>
    

**為什麼這樣做**：

*   Utility classes 可能會變（從 `mt-4` 改成 `mt-6`）
    
*   BEM 類別是「語意化的 JavaScript 鉤子」，不會因樣式調整而改動 [simplethread](https://www.simplethread.com/comparing-bem-and-tailwind/)
    

* * *

### 何時用 BEM？何時用 Atomic CSS？

**用 BEM 的時機**：

*   ✅ 元件特有的樣式
    
*   ✅ 需要 JavaScript 操作
    
*   ✅ 複雜的元件變體
    

**用 Atomic CSS 的時機**：

*   ✅ 通用間距、顏色、字體
    
*   ✅ 快速原型、迭代開發
    
*   ✅ 響應式變化
    

* * *

CSS 權重計算規則
----------

CSS 權重用 **ID-CLASS-TYPE** 三欄位表示，由左至右比較： [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity.)

欄位

包含的選擇器

權重值

**ID**

`#header`

1-0-0

**CLASS**

`.logo`、`[type="text"]`、`:hover`

0-1-0

**TYPE**

`header`、`p`、`::before`

0-0-1

**無權重**

`*`、`+`、`>`

0-0-0

**比較規則**：從左至右逐欄比較，誰的數字大誰贏。 [hackmd](https://hackmd.io/@hatiblog/Byb9WhEKY)

### 範例

    #myId .myClass p { /* 1-1-1 */
      color: red;
    }
    
    .myClass .myClass .myClass p { /* 0-3-1 */
      color: blue;
    }
    

雖然藍色有 3 個 CLASS，但紅色有 1 個 ID，所以**紅色贏**(ID 欄位 1 > 0)。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity.)

### 重構前後權重對比

元素

重構前

重構後

差異

Logo 圖片

`header .logo img` = `0-1-2`

`.header__logo-img` = `0-1-0`

降低 2 個 TYPE

Nav 項目

`header nav li` = `0-0-3`

`.header__nav-item` = `0-1-0`

提高但統一

Footer 圖示

`footer .social-media li img` = `0-1-3`

`.footer__social-media-icon` = `0-1-0`

降低 3 個 TYPE

**核心發現**：重構後**所有選擇器權重都變成** `0-1-0`（單一 CLASS）。 [cloudfour](https://cloudfour.com/thinks/when-to-nest-css/)

### BEM 權重扁平化的優勢

**重構前：權重金字塔（容易失控）**

    #header nav li.active     (1-1-2) ← 要用很複雜的選擇器
    footer .social li         (0-1-2)
    nav li                    (0-0-2)
    .button                   (0-1-0) ← 很難覆蓋上面的樣式
    

**重構後：權重扁平化（完全可控）**

    .header__nav-item--active  (0-1-0)
    .header__nav-item          (0-1-0)
    .footer__social-icon       (0-1-0)
    .hero__title               (0-1-0)
    

**優勢**：

1.  覆蓋樣式只需在 HTML 加 class，不用改 CSS 權重
    
2.  後寫的自動覆蓋前寫的，符合直覺
    
3.  完全避免 `!important` [zoocha](https://www.zoocha.com/news/nesting-css-good-bad-and-bem)
    

* * *

Part 2: 重構過程與問題解決
=================

專案背景
----

**作業目標**：練習用 box model 推擠出正確的版面，將傳統 CSS 寫法重構為 BEM 風格。

**技術限制**：

*   先少用 `position` 和 `float`
    
*   練習 inline-block、text-align、vertical-align
    
*   不使用 Flexbox/Grid（未來才學）
    

* * *

重構前的 HTML/CSS 結構
----------------

### Header 部分（重構前）

**HTML**：

    <header>
      <div class="logo">
        <img src="./public/logo.png" alt="網站 logo" />
        <p>ZERO TYPE</p>
      </div>
      <nav>
        <ul>
          <li><span>Home</span></li>
          <li>Feature</li>
          ...
        </ul>
      </nav>
    </header>
    

**CSS**：

    header {
      position: relative;
      border: 5px solid black;
      background-color: #f2f2f2;
    }
    
    header nav {
      position: absolute;
      top: 50%;
      left: 300px;
      transform: translateY(-50%);
    }
    
    header .logo img {
      width: 60px;
    }
    

**問題**：

1.  沒有使用 class 命名，權重不一致
    
2.  依賴 `position: absolute`，寫死 `left: 300px`
    
3.  權重分散（`0-0-1` 到 `0-1-2` 不等）
    

* * *

重構後的結構（第一版）
-----------

### 改用 BEM 命名

**HTML**：

    <header class="header">
      <div class="header__logo-zone">
        <div class="header__logo">
          <img class="header__logo-img" src="./public/logo.png" alt="網站 logo" />
        </div>
        <p class="header__logo-text">ZERO TYPE</p>
      </div>
      <nav>
        <ul class="header__nav">
          <li class="header__nav-item">Home</li>
          <li class="header__nav-item">Feature</li>
          ...
        </ul>
      </nav>
    </header>
    

**CSS**：

    .header {
      border: 5px solid black;
      background-color: #f2f2f2;
    }
    
    .header__logo-img {
      width: 100%;
      height: auto;
    }
    
    .header__nav-item {
      color: gray;
      display: inline-block;
      margin-right: 100px;
    }
    

**改進**：

*   所有選擇器權重統一為 `0-1-0`
    
*   移除 `position: absolute` 依賴
    
*   命名語意化
    

* * *

遇到的問題與解決方案
----------

### 問題 1: Footer 扁平化後無法正確垂直置中

**症狀**：改用扁平化 HTML 後，Footer 社群媒體區塊視覺上偏下。

**原因**：`<ul>` 預設有 `padding-left: 40px` 和 `margin`，導致 `transform: translateY(-50%)` 計算的「50%」包含了 padding，所以視覺上會偏移。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)

**解法**：重置 `ul` 預設樣式

    .footer__social-media {
      margin: 0;
      padding: 0;
      list-style: none;
    }
    

**學習重點**：

*   `<ul>` 和 `<ol>` 一定要重置 margin、padding、list-style [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)
    
*   `absolute` + `transform` 對盒模型很敏感，任何 padding/margin/border 都會影響計算
    

* * *

### 問題 2: text-align: justify 單行無效

**症狀**：想用 `text-align: justify` 讓元素兩端對齊（左右分散），但只有一行時無效。

**原因**：`text-align: justify` 只對**多行文字**有效，單行時不會觸發兩端對齊。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity.)

**解法**：用偽元素創造「第二行」

    .hero {
      text-align: justify;
    }
    
    .hero::after {
      content: '';
      display: inline-block;
      width: 100%;  /* 撐滿整行，讓前一行「兩端對齊」*/
      height: 0;    /* 不佔高度 */
      overflow: hidden;
    }
    

**原理**：

    沒有偽元素（單行）：
    [Intro ················ ImgBox]
    → justify 無效
    
    有偽元素（兩行）：
    [Intro ················ ImgBox]  ← 變成「第一行」，兩端對齊生效
    [::after 撐滿第二行]  ← 偽元素佔位
    

**副作用**：偽元素會撐開容器高度，需要設定 `height: 0` 避免。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity.)

* * *

### 問題 3: column-count 位置錯誤

**初始錯誤**：用單一 `<p>` + `<br>` 分隔段落

**HTML（錯誤）**：

    <p class="content__text">
      段落1<br>
      <br>
      段落2<br>
      <br>
      段落3
    </p>
    

**問題**：

1.  語意錯誤：6 個段落應該是 6 個 `<p>`，不是 1 個 `<p>` 加 5 個 `<br>`
    
2.  無障礙問題：螢幕閱讀器會把整段當成「一個段落」
    
3.  無法單獨控制樣式：無法給「第一段加粗」、「第三段換顏色」
    
4.  SEO 不友善：搜尋引擎無法正確解析段落結構
    

**正確寫法**：

    <article class="content">
      <h2 class="content__title">This is just a placeholder.</h2>
      <p class="content__text">段落1</p>
      <p class="content__text">段落2</p>
      <p class="content__text">段落3</p>
      <p class="content__text">段落4</p>
      <p class="content__text">段落5</p>
      <p class="content__text">段落6</p>
    </article>
    

**CSS**：

    .content {
      column-count: 2;      /* 父容器設定分欄 */
      column-gap: 20px;
    }
    
    .content__title {
      column-span: all;     /* 標題橫跨兩欄 */
    }
    
    .content__text {
      color: gray;
      line-height: 1.2;
      break-inside: avoid;  /* 避免段落被切斷 */
    }
    

**學習重點**：

*   `<br>` 應該用於詩歌、地址等「同段落內的換行」，不該用於分隔不同段落
    
*   `column-count` 應該在父容器，讓整篇文章分欄，而非每個段落內部分欄
    

* * *

### 問題 4: Header 視窗縮小時會換行

**症狀**：視窗寬度稍微減少時，Nav 會被推到第二行。

**原因分析**：

    視窗最小寬度 = 1200px（body min-width）
    Header 可用寬度 = 1200px - 150px（左padding）- 50px（右padding）= 1000px
    
    Logo 區佔用 = 60px + 120px（margin-right）= 180px
    Nav 區需要 = 5個項目 × 150px（間距）+ 文字寬度（約350px）= 950px
    
    總共需要 = 180px + 950px = 1130px
    可用寬度 = 1000px
    
    1130px > 1000px → 放不下！
    

**解法 1：加大視窗**

    body {
      min-width: 1400px;  /* 從 1200px 改成 1400px */
    }
    
    /* 新的可用寬度 = 1400px - 200px = 1200px > 1130px ✅ */
    

**解法 2：減少間距**

    .header__nav-item {
      margin-right: 100px;  /* 從 150px 改成 100px */
    }
    
    /* Nav 寬度 = 4個 × 100px + 350px = 750px */
    /* 總需要 = 180px + 750px = 930px < 1000px ✅ */
    

**學習重點**：

*   inline-block 元素換行時，整個元素會掉到下一行（不是內容換行）
    
*   寬度計算公式：`可用寬度 = 視窗寬 - 左右 padding`，`需要寬度 = 元素1 + 間距 + 元素2`
    
*   目前階段需要「精確計算每個數值」，未來學 Flexbox 後瀏覽器會自動處理
    

* * *

### 問題 5: 圖片底部有空隙

**症狀**：inline 圖片容器下方有約 3-4px 的空隙。

**原因**：瀏覽器預設給 inline 元素留空間放英文小寫字母的下半部（如 g、y、p 的下半部）。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)

**解法**：

    .header__logo-img,
    .hero__img {
      vertical-align: bottom;  /* 對齊底線，消除空隙 */
    }
    

**學習重點**：inline 或 inline-block 的圖片都要加 `vertical-align: bottom` 或改成 `display: block`。

* * *

Box Model 寬度計算原理
----------------

### 基本公式

    可用寬度 = 視窗寬度 - 左右 padding
    需要寬度 = 元素1寬度 + 間距 + 元素2寬度
    

**如果** `需要寬度 > 可用寬度`，第二個元素會掉到下一行。

### 實際範例

**Header 計算**：

    視窗寬度 = 1200px
    Header padding = 150px（左）+ 50px（右）= 200px
    Header 可用寬度 = 1200px - 200px = 1000px
    
    Logo 寬度 = 60px + 120px（margin-right）= 180px
    Nav 寬度 = (5個項目 × 150px) + 文字寬度（約350px）= 950px
    
    總共需要 = 180px + 950px = 1130px
    
    1130px > 1000px → Nav 會換行 ❌
    

**解決方法**：

1.  加大可用寬度：`min-width: 1400px` → 可用寬度變 1200px
    
2.  減少需要寬度：`margin-right: 100px` → Nav 寬度變 750px
    

* * *

### inline-block 的間隙問題

**問題**：HTML 的換行會被瀏覽器當成「空白字符」，導致 inline-block 元素之間有約 4px 間隙。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)

**HTML**：

    <div class="container">
      <div class="left">Left</div>  ← 這個換行變成空白
      <div class="right">Right</div>
    </div>
    

**視覺效果**：

    [Left] [4px間隙] [Right]  ← 導致寬度計算錯誤
    

**解法（推薦）**：

    .container {
      font-size: 0;  /* 消除空白字符 */
    }
    
    .left, .right {
      font-size: 16px;  /* 子元素恢復字體大小 */
    }
    

* * *

學習階段對比
------

### 目前階段：Box Model（精確計算）

**特點**：

*   需要手動計算每個元素寬度
    
*   對齊方式複雜（`vertical-align`、`line-height`）
    
*   響應式很難做
    

**範例**：

    .header__logo-box {
      width: 60px;
      margin-right: 120px;  /* 固定數值 */
    }
    
    .header__nav-item {
      margin-right: 150px;  /* 固定數值 */
    }
    

**為什麼要學**：

*   理解寬度計算原理
    
*   知道 inline-block 的限制在哪
    
*   為 Flexbox 打基礎
    

* * *

### 未來階段：Flexbox（自動分配）

**特點**：

*   不用計算寬度，瀏覽器自動處理
    
*   自動垂直置中
    
*   響應式友善
    

**範例**：

    .header {
      display: flex;
      align-items: center;      /* 自動垂直置中 */
      justify-content: space-between;  /* 自動兩端對齊 */
    }
    
    /* 不需要計算寬度！不需要 line-height: 6.5！ */
    

* * *

### 更未來：Grid（二維佈局）

**特點**：

*   用比例分配空間
    
*   精確控制二維佈局
    
*   適合複雜版面
    

**範例**：

    .header {
      display: grid;
      grid-template-columns: 200px 1fr;  /* Logo固定200px，Nav佔剩餘空間 */
      align-items: center;
    }
    

`1fr` **的意思**：「佔剩餘的所有空間」，完全不用計算！

* * *

最終完整程式碼
-------

### HTML

    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="stylesheet" href="style.css" />
        <title>ZERO TYPE</title>
      </head>
      <body>
        <header class="header">
          <div class="header__logo-box">
            <div class="header__logo">
              <img class="header__logo-img" src="./public/logo.png" alt="網站 logo" />
            </div>
            <p class="header__logo-text">ZERO TYPE</p>
          </div>
          <nav class="header__nav">
            <ul class="header__nav-list">
              <li class="header__nav-item">Home</li>
              <li class="header__nav-item">Feature</li>
              <li class="header__nav-item">News</li>
              <li class="header__nav-item">About</li>
              <li class="header__nav-item">Contact</li>
            </ul>
          </nav>
        </header>
    
        <main class="main">
          <section class="hero">
            <div class="hero__intro">
              <h1 class="hero__title">IDEAS?</h1>
              <h2 class="hero__subtitle">This is just a placeholder.</h2>
              <p class="hero__text">
                You can remove any link to our website from this website template,
                you're free to use this website template without linking back to us.
              </p>
              <div class="hero__try-it">
                <button class="hero__try-it-btn">TRY IT NOW!</button>
                <p class="hero__try-it-hint">Don't worry it's for free.</p>
              </div>
            </div>
            <div class="hero__img-box">
              <img class="hero__img" src="./public/box.png" alt="網站主視覺圖片，一個裝滿各種不同類型檔案圖示的箱子" />
            </div>
          </section>
    
          <article class="content">
            <h2 class="content__title">This is just a placeholder.</h2>
            <p class="content__text">You can remove any link to our website from this website template, you're free to use this website template without linking back to us.</p>
            <p class="content__text">This is just a place holder, so you can see what the site would look like.</p>
            <p class="content__text">You can remove any link to our website from this website template, you're free to use this website template without linking back to us. This is just a place holder, so you can see what the site would look like.</p>
            <p class="content__text">You can remove any link to our website from this website template, you're free to use this website template without linking back to us.</p>
            <p class="content__text">This is just a place holder, so you can see what the site would look like.</p>
            <p class="content__text">You can remove any link to our website from this website template, you're free to use this website template without linking back to us. This is just a place holder, so you can see what the site would look like.</p>
          </article>
        </main>
    
        <footer class="footer">
          <div class="footer__copyright">&copy; 2023 Zerotype. All Rights Reserved.</div>
          <ul class="footer__social-media">
            <li class="footer__social-media-item">
              <a href="##" target="_blank" rel="noopener noreferrer">
                <img class="footer__social-media-icon" src="./public/facebook.png" alt="Facebook" />
              </a>
            </li>
            <li class="footer__social-media-item">
              <a href="##" target="_blank" rel="noopener noreferrer">
                <img class="footer__social-media-icon" src="./public/googleplus.png" alt="Google Plus" />
              </a>
            </li>
            <li class="footer__social-media-item">
              <a href="##" target="_blank" rel="noopener noreferrer">
                <img class="footer__social-media-icon" src="./public/twitter.png" alt="Twitter" />
              </a>
            </li>
            <li class="footer__social-media-item">
              <a href="##" target="_blank" rel="noopener noreferrer">
                <img class="footer__social-media-icon" src="./public/pinterest.png" alt="Pinterest" />
              </a>
            </li>
          </ul>
        </footer>
      </body>
    </html>
    

### CSS

    /* 主要 Layout 版面架構要用父層結構完成 */
    /* BEM 命名方式 */
    /* 注意 tag 和 class 的權重，儘量用 class 命名各個元素 */
    /* 要讓第一層內容直接撐開容器 */
    
    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
    
    body {
      min-width: 1300px;  /* 確保 Header 不會換行 */
    }
    
    /* ===== Header ===== */
    .header {
      border: 5px solid black;
      margin-bottom: 30px;
      padding: 15px 50px 50px 150px;
      background-color: #f2f2f2;
    }
    
    .header__logo-box {
      display: inline-block;
      margin-right: 120px;
      text-align: center;
    }
    
    .header__logo {
      width: 60px;
      margin: 0 auto;
    }
    
    .header__logo-img {
      width: 100%;
      height: auto;
      vertical-align: bottom;  /* 消除圖片底部空隙 */
    }
    
    .header__logo-text {
      color: black;
      font-weight: bold;
      margin-top: -10px;
    }
    
    .header__nav {
      margin: 0;
      padding: 0;
      display: inline-block;
      text-align: right;
      line-height: 6.5;  /* 用行高做垂直對齊（魔術數字，未來用 Flexbox 改進）*/
      vertical-align: top;
    }
    
    .header__nav-list {
      padding: 0;
      margin: 0;
      list-style-type: none;
      display: inline-block;
      text-align: left;
    }
    
    .header__nav-item {
      color: gray;
      display: inline-block;
      margin-right: 120px;
    }
    
    .header__nav-item:last-child {
      margin-right: 0;
    }
    
    .header__nav-item:hover {
      color: orange;
      cursor: pointer;
    }
    
    /* ===== Main ===== */
    .main {
      width: 70%;
      margin: 0 auto;
      margin-top: 30px;
      margin-bottom: 400px;
    }
    
    /* ===== Hero ===== */
    .hero {
      background-color: #fcfcfc;
      height: 450px;
      margin-bottom: 250px;
      text-align: justify;  /* 兩端對齊 */
    }
    
    /* 用偽元素創造「第二行」讓 justify 生效 */
    .hero::after {
      content: '';
      display: inline-block;
      width: 100%;
      height: 0;         /* 不佔高度 */
      overflow: hidden;
    }
    
    .hero__intro {
      display: inline-block;
      width: 50%;
      vertical-align: top;
      text-align: center;
    }
    
    .hero__title {
      font-size: 50px;
      line-height: 1.2;
      text-align: left;
      margin: 0;
    }
    
    .hero__subtitle {
      font-size: 30px;
      line-height: 1.2;
      text-align: left;
      margin: 0;
    }
    
    .hero__text {
      color: gray;
      font-size: 20px;
      line-height: 1.2;
      text-align: left;
      margin: 0;
    }
    
    .hero__try-it {
      display: inline-block;
      margin-top: 30px;
      text-align: left;
    }
    
    .hero__try-it-btn {
      font-size: 28px;
      display: block;
      padding: 20px 40px;
      background: linear-gradient(to bottom, #ffb84d 50%, #ff9500 50%);
      color: white;
      font-weight: bold;
      border: 0;
      border-radius: 5px;
      cursor: pointer;
    }
    
    .hero__try-it-hint {
      display: block;
      font-size: 16px;
      margin: 0;
      color: gray;
    }
    
    .hero__img-box {
      width: 45%;
      display: inline-block;
    }
    
    .hero__img {
      width: 100%;
      height: auto;
      vertical-align: bottom;  /* 消除圖片底部空隙 */
    }
    
    /* ===== Content ===== */
    .content {
      column-count: 2;     /* 整個 article 分成兩欄 */
      column-gap: 20px;
    }
    
    .content__title {
      column-span: all;    /* 標題橫跨兩欄 */
      margin-bottom: 20px;
    }
    
    .content__text {
      color: gray;
      line-height: 1.2;
      break-inside: avoid;  /* 避免段落被切斷 */
      margin-top: 0;
      margin-bottom: 20px;
    }
    
    /* ===== Footer ===== */
    .footer {
      border: 5px solid black;
      padding: 40px 150px 20px 100px;
      background-color: #f2f2f2;
      text-align: justify;
    }
    
    /* 用偽元素創造「第二行」讓 justify 生效 */
    .footer::after {
      content: '';
      display: inline-block;
      width: 100%;
      height: 0;         /* 不佔高度 */
      overflow: hidden;
    }
    
    .footer__copyright {
      display: inline-block;
      vertical-align: middle;
    }
    
    .footer__social-media {
      margin: 0;         /* 重置 ul 預設樣式 */
      padding: 0;
      list-style-type: none;
      display: inline-block;
      vertical-align: middle;
    }
    
    .footer__social-media-item {
      display: inline-block;
      margin-right: 20px;
    }
    
    .footer__social-media-icon {
      width: 30px;
      display: block;
    }
    

* * *

核心學習重點
------

### 1\. BEM 命名帶來的價值

*   **權重扁平化**：所有選擇器統一為 `0-1-0`，避免權重衝突
    
*   **語意清晰**：一看 class 名稱就知道元素關係和身份
    
*   **易於維護**：不會因為改一個地方影響其他地方
    

### 2\. Box Model 的計算原理

*   **寬度公式**：`可用寬度 = 視窗寬 - padding`，`需要寬度 = 元素寬 + 間距`
    
*   **inline-block 特性**：會換行（整個元素掉下去），有空白字符間隙
    
*   **必須精確計算**：目前階段需要手動計算每個數值
    

### 3\. 常見陷阱與解決方案

*   `<ul>` **預設樣式**：必須重置 margin、padding、list-style
    
*   **圖片底部空隙**：用 `vertical-align: bottom` 解決
    
*   **text-align: justify 單行無效**：用偽元素創造第二行
    
*   **偽元素佔高度**：設定 `height: 0` 避免撐開容器
    
*   **段落分隔**：用多個 `<p>` 而非 `<br>`，語意正確且對 SEO/無障礙友善
    

### 4\. 未來的改進方向

*   **Flexbox**：自動分配空間，不用計算寬度，自動垂直置中
    
*   **Grid**：用比例分配空間，適合複雜二維佈局
    
*   **目前的痛點都會被解決**：魔術數字、換行問題、對齊困難
    

* * *

參考資源
----

### 官方文件

*   **MDN CSS 組織指南**：介紹 OOCSS、BEM、SMACSS [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Organizing)
    
*   **MDN CSS Specificity**：權重計算規則 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity.)
    
*   **W3C HTML5 規範**：段落與換行的正確用法
    

### 社群資源

*   **CSS Specificity Calculator**：https://specificity.keegan.st/ （線上計算工具）
    
*   **BEM 官方文件**：http://getbem.com
    
*   **Airbnb JavaScript Style Guide**：包含 CSS 寫作規範
    

### 延伸閱讀

*   **BEM vs 巢狀選擇器的效能與權重分析** [zoocha](https://www.zoocha.com/news/nesting-css-good-bad-and-bem)
    
*   **Tailwind CSS 與 BEM 混用最佳實踐** [wearecogworks](https://www.wearecogworks.com/innerworks/cogworks-blog-archive/how-to-use-tailwind-with-bem)
    
*   **CSS 架構方法論比較** [sitepoint](https://www.sitepoint.com/css-architecture-block-element-modifier-bem-atomic-css/)

---

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