# Day 62：BEM 命名、定位技巧與 Hero Section 完整實作

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

---

> 📅 2026-02-24　｜　🏷 CSS · BEM · Layout · Positioning

* * *

一、版面結構拆解
--------

電商首頁的切版練習，目標是**不使用 flex/grid**，僅用傳統 CSS 佈局完成設計稿還原。

由上而下拆成四個水平區塊：Header、Hero、Products、Footer。傳統佈局的三個核心工具分工明確：

*   **Block 元素**：四大區塊天生垂直堆疊，不需要額外設定
    
*   `inline-block`：讓選單項目、產品卡片水平並排（需注意空白字元問題）
    
*   `float`：Header 左右分離、Footer 三欄排列（需搭配 clearfix 清除浮動）
    

外層容器寬度控制慣例：

    /* 外層撐滿螢幕，內層置中限寬 */
    .section {
      width: 100%;
    }
    .section__inner {
      max-width: 1200px;
      margin: 0 auto;
    }
    

* * *

二、語意化 HTML 結構規劃
---------------

### 標籤選用原則

    <header>   <!-- 頁首導航 -->
    <nav>      <!-- 導航連結集合 -->
    <main>     <!-- 頁面主要內容 -->
    <section>  <!-- 獨立內容區塊 -->
    <footer>   <!-- 頁腳 -->
    

`<h1>` **只給頁面主標題**，例如 Hero 的 "Fall & Winter"，Logo 文字改用 `<div>` 或 `<a>`。 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Getting_started/Your_first_website/Styling_the_content)

### 容器規劃判斷流程

情境

結論

需要定位基準（absolute 的父層）

需要容器

需要清除內部浮動（clearfix）

需要容器

`<li>` 本身可當容器

不需要再包 `<div>`

沒有 class 的無名 `<div>`

移除，避免巢狀過深

* * *

三、BEM 命名規範實踐
------------

BEM 是 **Block（區塊）\_\_Element（元素）--Modifier（修飾符）** 的命名方法論，目標是讓 CSS class 名稱具備自解釋能力，並避免樣式衝突。 [sparkbox](https://sparkbox.com/foundry/bem_by_example)

    .block {}              /* 獨立元件本身 */
    .block__element {}     /* 元件的組成部分，雙底線 __ */
    .block--modifier {}    /* 元件的變體狀態，雙橫線 -- */
    

### Header 命名示範

    .header
      .header__logo          ← Block 的一級 Element
      .header__nav           ← Block 的一級 Element
        .header__nav-list    ← <ul> 列表容器
          .header__nav-item  ← <li> 單項
            .header__nav-link ← <a> 連結
    

### Products 命名示範

    .products
      .products__list        ← <ul>
        .products__item      ← <li>
          .products__item-link ← <a>
            .products__item-img ← <img>
    

### ⚠️ 常見命名錯誤

❌ <ul class="header\_\_nav-item"> <!-- ul 不是 item --> ✅ <ul class="header\_\_nav-list"> ❌ <a href="#">Home</a> <!-- 缺少 class，無法精確控制樣式 --> ✅ <a href="#" class="header\_\_nav-link">Home</a> ❌ <li><div class="products\_\_item">...</div></li> <!-- 多餘容器 --> ✅ <li class="products\_\_item">...</li>

**BEM 一致性原則**：`products` 不能在某個地方寫成 `product`（少一個 s），Block 名稱必須在整個元件內保持統一。 [hackmd](https://hackmd.io/@Fabherbie/HkN0SE-e1g)

* * *

四、CSS 選擇器語法錯誤導致樣式失效
-------------------

### 問題根源

    /* ❌ 選擇器沒有 { }，破壞後續 CSS 的解析 */
    .header
    .header__logo {
        display: inline-block;
    }
    

當 CSS 選擇器缺少配對的 `{}` 時，瀏覽器無法正確解析，導致後續的規則失效或行為異常。 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS)

### 解決習慣：未完成的選擇器先註解

    /* 先把架構列出，未完成的用註解佔位 */
    /* .header */
    .header__logo {
        display: inline-block;
    }
    
    /* .header__nav-link */
    .header__cart {
        float: right; /* 靠右對齊 */
    }
    

**口訣：選擇器若沒有規則，就先註解起來！**

此外，`display: inline-block` 只能讓元素並排，**不會改變對齊位置**；要讓元素靠右需要用 `float: right`（傳統佈局下）。

* * *

五、`vertical-align` 的正確使用方式
--------------------------

### 核心概念

`vertical-align` 有三個容易混淆的特性： [w3docs](https://www.w3docs.com/snippets/css/how-to-vertically-align-inline-inline-block-elements.html)

1.  **設定在子元素上**，不是父元素
    
2.  只對 `inline` 或 `inline-block` 元素有效
    
3.  對齊基準是**行盒（Line Box）**，不是父元素的邊界
    

    行盒（Line Box）
    ┌─────────────────────────────────┐  ← line-height 決定高度
    │  [logo]  [nav]  [cart]          │  ← 三個 inline-block 元素
    └─────────────────────────────────┘
    

### 實作：Header 垂直置中

**步驟一**：在所有並排子元素加上 `vertical-align: middle`

    .header__logo,
    .header__nav,
    .header__cart {
        display: inline-block;
        vertical-align: middle; /* 設定在子元素，不是父元素 */
    }
    

**步驟二**：在父元素設定 `line-height` 等於 `height`，撐高行盒

    .header {
        height: 100px;
        line-height: 100px; /* 行盒高度 = 容器高度 → 子元素垂直置中 */
    }
    

### ⚠ 邊界情況：子元素高度大於 `line-height`

子元素過高時行盒會被撐大，對齊基準也跟著偏移，視覺上不在預期位置。防禦措施：

    .header__cart-img {
        max-height: 35px; /* 限制最大高度，防止撐破版面 */
        height: auto;
        width: auto;
    }
    

* * *

六、`inline-block` 子元素的水平置中
-------------------------

### 兩種置中方法的適用場景

目標元素

正確方法

`inline` / `inline-block` 元素

在**父層**設 `text-align: center`

`block` 元素（有明確 `width`）

在**自身**設 `margin: 0 auto`

`margin: 0 auto` 對 `inline-block` 無效，因為 `margin: auto` 只在 block formatting context 中計算。`text-align: center` 控制的是 inline formatting context 中的物件，而 `inline-block` 正是讓元素「偽裝」成行內物件，所以能被父層的 `text-align` 控制。 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Getting_started/Your_first_website/Styling_the_content)

    /* ✅ 正確：三張產品卡片水平置中 */
    .products {
        text-align: center;
    }
    
    .products__item {
        display: inline-block;
    }
    

### `<ul>` 預設樣式造成視覺偏移

`<ul>` 有瀏覽器預設的 `padding-left: 40px`，需要 reset：

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

* * *

七、Hero Section 完整實作
-------------------

### `background-image` vs `<img>` 的選擇

  

`<img>` 標籤

`background-image`

層級

HTML 內容層

CSS 裝飾層

撐開容器

✅

❌（需手動設 `height`）

SEO/無障礙

有 `alt` 屬性

無語義資訊

疊加文字

較複雜

直覺（搭配 `position`）

Hero banner 使用 `background-image`，方便直接在容器內定位文字與按鈕。 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS/Guides/Backgrounds_and_borders/Using_multiple_backgrounds)

### 背景圖關鍵 CSS

    .hero {
      background-image: url("img/cover.jpg");
      background-size: cover;           /* 覆蓋容器，等比例裁切 */
      background-position: center center;
      background-repeat: no-repeat;
      position: relative;               /* 作為內部絕對定位元素的基準 */
      height: 710px;                    /* background-image 不撐高，必須手動設定 */
    }
    

### 遮罩層 + 內容層的 HTML 架構

    <section class="hero">
      <div class="hero__overlay"></div>    <!-- 半透明遮罩層 -->
      <div class="hero__content">          <!-- 文字與按鈕 -->
        <h1 class="hero__title">Fall & Winter</h1>
        <a href="#" class="hero__btn">Shop Now</a>
      </div>
      <p class="hero__shipping">FREE SHIPPING WORLDWIDE</p>
    </section>
    

### 水平垂直置中：`transform` 方法

    /* 遮罩層 */
    .hero__overlay {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%); /* 補償自身寬高的 50% */
      width: 605px;
      height: 190px;
      background-color: rgba(255, 255, 255, 0.6);
      z-index: 1; /* 遮罩在背景圖上方 */
    }
    
    /* 內容層，z-index 高於遮罩 */
    .hero__content {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      z-index: 2; /* 文字在遮罩上方 */
      text-align: center;
    }
    
    /* 底部橫幅 */
    .hero__shipping {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      background-color: rgb(222, 222, 222);
      margin: 0;
      padding: 10px 0;
      text-align: center;
    }
    

`transform: translate(-50%, -50%)` **原理**：`top: 50%` 讓元素頂部對齊容器中線，但元素本身有寬高，所以要再往左上移動自身寬高的 50% 才會真正置中。 [stackoverflow](https://stackoverflow.com/questions/9670469/css-vertical-alignment-of-inline-inline-block-elements)

### `z-index` 層級示意

    .hero（position: relative）
      ├── background-image  ← 最底層
      ├── .hero__overlay    ← z-index: 1
      ├── .hero__content    ← z-index: 2
      └── .hero__shipping   ← z-index: auto（最上層）
    

`z-index` 只對 `position` 不是 `static` 的元素生效。 [w3schools](https://www.w3schools.com/css/css_z-index.asp)

* * *

八、偽元素 vs 真實 DOM 的選擇判斷
---------------------

偽元素（`::before`/`::after`）與偽類（`:hover`/`:focus`）的核心差異： [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS)

  

偽元素 `::`

偽類 `:`

功能

創造新的虛擬節點

描述元素的狀態

常見例子

`::before`, `::after`

`:hover`, `:focus`

可被 JS 操作

❌

—

### 選擇判斷流程

    這個層需要被 JavaScript 操作嗎？
    ├── 是 → 真實 DOM 元素
    └── 否 → 是純視覺裝飾嗎？
              ├── 是 → 偽元素 ✅（::before / ::after）
              └── 否（例如需要 ARIA / 點擊事件） → 真實 DOM 元素
    

本次遮罩選用真實 DOM，理由是尺寸為局部區域（不是全滿背景），且未來可能需要 JS 控制動態效果。

* * *

九、本次學到的 CSS 屬性整理
----------------

屬性

用途

`background-size: cover`

圖片覆蓋容器，等比例裁切

`background-position`

控制背景圖位置

`position: absolute`

相對於 `relative` 父層定位

`transform: translate()`

位移，用於水平垂直置中

`rgba()`

含透明度的顏色，製作半透明遮罩

`z-index`

控制堆疊層級，只對非 `static` 元素有效

`vertical-align`

控制 inline-block 兄弟元素的對齊基準

`line-height`

設定行盒高度，間接控制垂直置中範圍

`text-align: center`

使父層內的 inline-block 子元素水平置中

* * *

延伸資源
----

*   [MDN — CSS Backgrounds and Borders](https://developer.mozilla.org/zh-TW/docs/Web/CSS/Guides/Backgrounds_and_borders/Using_multiple_backgrounds) [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS/Guides/Backgrounds_and_borders/Using_multiple_backgrounds)
    
*   [MDN — CSS 佈局](https://developer.mozilla.org/zh-TW/docs/Web/CSS) [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/CSS)
    
*   [BEM 官方方法論](https://getbem.com) [getbem](https://getbem.com)
    
*   [BEM by Example — Sparkbox](https://sparkbox.com/foundry/bem_by_example) [sparkbox](https://sparkbox.com/foundry/bem_by_example)

---

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