線上課程觀課進度管理小工具開發日誌
Day 6:JavaScript 程式碼執行排序:遞迴函數、Call Stack、Task Queue
Call Stack;遞迴函數與 call stack 的關係;Task Queue(非同步的概念再釐清)
Day 9:「修煉圈圈 Practice Rings」Web app 開發日誌 - 2025 六角 AI Vibe Coding 體驗營期末大魔王作業
這份筆記旨在記錄 2025 六角 Vibe Coding 體驗營每日作業 Day 21 的期末專案成果,從一個簡單的個人痛點出發,透過與 AI(在我這個情境中是 Perplexity)協作,在一天內完成了一個包含前後端的全端網頁小工具:「修煉圈圈 Practice Rings」。
記錄雞蛋糕的每一步前端煉成過程,從小白到(也許是)前端工程師的學習與分享。

Subscribe to 雞蛋糕的前端修煉屋
線上課程觀課進度管理小工具開發日誌
Day 6:JavaScript 程式碼執行排序:遞迴函數、Call Stack、Task Queue
Call Stack;遞迴函數與 call stack 的關係;Task Queue(非同步的概念再釐清)
Day 9:「修煉圈圈 Practice Rings」Web app 開發日誌 - 2025 六角 AI Vibe Coding 體驗營期末大魔王作業
這份筆記旨在記錄 2025 六角 Vibe Coding 體驗營每日作業 Day 21 的期末專案成果,從一個簡單的個人痛點出發,透過與 AI(在我這個情境中是 Perplexity)協作,在一天內完成了一個包含前後端的全端網頁小工具:「修煉圈圈 Practice Rings」。
<100 subscribers
<100 subscribers
重構時間:2026 年 2 月
重構範圍:ZERO TYPE Landing Page (index.html + style.css)
技術限制:尚未使用 Flexbox/Grid,僅使用傳統 Box Model + Position 定位
學習目標:建立可維護、可預測的版面結構與命名系統
當使用傳統 Box Model 建立版面時,常見問題:
圖片載入前後版面高度跳動
圖片破圖或尺寸過大導致版面崩潰
修改內容後其他元素位置連帶跑掉
不同瀏覽器或載入速度下版面不一致
核心問題:沒有明確定義「誰負責版面空間」、「誰負責填充內容」。
<!-- ❌ 錯誤:沒有定義父層高度 -->
<div class="hero">
<img src="box.png" />
</div>
.hero {
position: relative;
/* height 沒設定,由子元素決定 */
}
.hero img {
width: 600px;
}
執行流程問題:
瀏覽器遇到 .hero,不知道高度,先畫 0px
遇到 <img>,等待圖片下載
圖片下載完成 400px
.hero 被撐高到 400px,整個版面重排(reflow)
結果:版面跳動,使用者體驗差
<!-- ✅ 正確:用容器切出空間 -->
<section class="hero">
<div class="hero__img-box">
<img class="hero__img" src="box.png" />
</div>
</section>
/* 父層明確定義版面高度 */
.hero {
position: relative;
height: 450px; /* 明確告訴瀏覽器:這區塊佔 450px */
margin-bottom: 250px;
}
/* 圖片容器:定義它在父層中的位置和寬度 */
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* 圖片:填滿容器即可 */
.hero__img {
width: 100%;
height: 100%;
}
改善後的執行流程:
瀏覽器遇到 .hero,立刻畫出 450px 高的區塊
遇到 .hero__img-box,用 absolute 定位在內部
圖片下載完成,填入容器
不需要 reflow,版面穩定
<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>
/* 父層:定義整個 logo 區域的大小和位置 */
.header__logo-zone {
display: inline-block;
margin: 15px 80px 30px 150px;
text-align: center;
}
/* 圖片容器:切出 60px 寬的空間給 logo */
.header__logo {
width: 60px;
margin: 0 auto;
}
/* 圖片:填滿固定空間 */
.header__logo-img {
width: 100%;
height: auto;
}
空間結構圖:
┌─ .header__logo-zone ──────────────────┐
│ [上 padding 15px] │
│ │
│ ┌─ .header__logo (60px) ───────┐ │
│ │ <img> 填滿這個 60px 的空間 │ │
│ └────────────────────────────────┘ │
│ │
│ ZERO TYPE (文字) │
└────────────────────────────────────────┘
關鍵思考:
logo-zone:控制整個區域的 margin 和對齊
logo:切出圖片固定的 60px 空間
img:只負責填滿那 60px
<div class="container">
<img src="large-image.png" />
</div>
.container {
width: 300px;
}
.container img {
/* 沒有設定 width,圖片以原始尺寸顯示 */
/* 假設原始尺寸是 800px,會超出 container */
}
問題視覺化:
┌─ .container (300px) ──┐
│ │
│ ┌─ <img> 800px ────────────────────────┐
│ │ │ │
│ └──────────────────────┘────────────────┘
└────────────────────────┘
↑
超出 500px
.container {
width: 300px;
}
.container img {
width: 100%; /* 永遠不會超過 300px */
height: auto; /* 保持圖片比例 */
}
執行結果:
┌─ .container (300px) ──┐
│ ┌─ <img> (300px) ───┐ │
│ │ 圖片被縮小到 │ │
│ │ 符合容器寬度 │ │
│ └────────────────────┘ │
└────────────────────────┘
版面需求:
Hero 區塊總高度 450px
左側文字佔 50% 寬度
右側圖片佔 45% 寬度
圖片垂直置中
/* 第一層:父層定義整體空間 */
.hero {
position: relative;
height: 450px;
margin-bottom: 250px;
}
/* 第二層:圖片容器定義圖片應佔的空間 */
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* 第三層:圖片填滿容器 */
.hero__img {
width: 100%;
height: 100%;
}
空間結構圖:
┌─ .hero (height: 450px) ────────────────────────┐
│ │
│ ┌─ .hero__intro ──┐ ┌─ .hero__img-box ────┐│
│ │ (width: 50%) │ │ (width: 45%) ││
│ │ IDEAS? │ │ ┌─ <img> ────────┐ ││
│ │ This is just... │ │ │ 填滿容器 │ ││
│ │ [TRY IT NOW!] │ │ │ 100% × 100% │ ││
│ └─────────────────┘ │ └─────────────────┘ ││
│ └──────────────────────┘│
└─────────────────────────────────────────────────┘
<img> 是 replaced element(置換元素),它的特性: developer.mozilla
內容由外部資源決定(圖片檔案)
尺寸預設由圖片原始大小決定
載入狀態會影響版面(載入前/後/失敗)
其他 replaced elements:<video>, <iframe>, <embed>, <object> github
<!-- ❌ 錯誤:直接對 img 使用 absolute -->
<div class="hero">
<img class="hero__img" src="box.png" />
</div>
.hero {
position: relative;
/* 沒有定義高度 */
}
.hero__img {
position: absolute;
width: 45%;
right: 0;
top: 50%;
transform: translateY(-50%);
}
問題 1:圖片載入前
┌─ .hero (高度 0px) ─┐
│ │ ← 沒設定高度,且 img 還沒載入
└─────────────────────┘
下方內容緊貼著
問題 2:圖片載入後
┌─ .hero (被撐高到 300px) ─────┐
│ ┌─ img (300px) ─────┐│
│ │ ││
│ └────────────────────┘│
└───────────────────────────────┘
下方內容被推下 300px (版面跳動!)
問題 3:圖片載入失敗
┌─ .hero (高度又變回 0px) ─┐
│ [破圖 icon] │
└──────────────────────────┘
版面又跳回去 (再次跳動!)
<!-- ✅ 正確:三層結構 -->
<div class="hero"> <!-- 1. 版面骨架 -->
<div class="hero__img-box"> <!-- 2. 圖片空間 -->
<img class="hero__img" src="box.png" /> <!-- 3. 圖片內容 -->
</div>
</div>
/* 第一層:版面骨架 */
.hero {
position: relative;
height: 450px;
}
/* 第二層:圖片空間切割 */
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* 第三層:圖片填充 */
.hero__img {
width: 100%;
height: 100%;
}
載入流程分析:
圖片載入前/後/失敗都穩定:
┌─ .hero (height: 450px) ───────────────┐
│ │
│ ┌─ .hero__img-box ─────────────────┐│
│ │ [載入中 / 顯示圖片 / 破圖 icon] ││
│ │ 但 .hero 始終保持 450px 高度 ││
│ └───────────────────────────────────┘│
│ │
└────────────────────────────────────────┘
版面大小不變,沒有跳動
責任:定義整個區塊在頁面中的空間
.hero {
position: relative; /* 成為子元素定位的參考點 */
height: 450px; /* 佔據固定高度,穩定版面 */
margin-bottom: 250px;
}
責任:在骨架內部切出「圖片應該佔的位置和大小」
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
責任:把圖片內容填入預留的空間
.hero__img {
width: 100%; /* 填滿父層 */
height: 100%;
}
BEM = Block__Element--Modifier getbem
Block__Element--Modifier
Block(區塊):獨立的頁面元件
例:.header, .hero, .footer
Element(元素):Block 的組成部分,用 __ 連接 hackmd
例:.header__logo, .header__nav, .footer__copyright
Modifier(修飾符):Block 或 Element 的不同狀態,用 -- 連接 hackmd
例:.button--disabled, .nav-item--active
這個元素需要 CSS 樣式嗎?
├─ 是 → 加 class
└─ 否
└─ 這個元素需要 JS 操作嗎?
├─ 是 → 加 class
└─ 否 → 不用加 class(純語意容器)
這個元件會在多個地方重用嗎?
├─ 是 → 獨立成 Block (.button, .card, .modal)
└─ 否 → 當作 Element (.hero__button, .footer__social-links)
Before:
<header>
<div class="logo">
<div class="logo-img"><img src="..." /></div>
<p class="logo-text">ZERO TYPE</p>
</div>
<nav>
<ul class="header-nav">
<li><span class="home-btn">Home</span></li>
header { ... } /* ❌ tag selector */
.logo { ... } /* ❌ 沒有明確從屬關係 */
header nav { ... } /* ❌ 後代選擇器 */
.header-nav li { ... } /* ❌ 選擇器層級過深 */
After:
<header class="header">
<div class="header__logo-zone">
<div class="header__logo">
<img class="header__logo-img" src="..." />
</div>
<p class="header__logo-text">ZERO TYPE</p>
</div>
<nav>
<ul class="header__nav">
<li class="header__nav-item">Home</li>
.header { ... }
.header__logo-zone { ... }
.header__logo { ... }
.header__logo-img { ... }
.header__nav-item { ... }
.header__nav-item:hover { color: orange; }
改善重點:
移除 tag selector,全部用 class
移除後代選擇器 .logo-img img
用 __ 明確表達從屬關係 hackmd
錯誤寫法:
<li class="header__nav-home">Home</li>
<li class="header__nav-list">Feature</li>
這樣看起來像兩個不同的元素,而非同一元素的不同狀態。
正確寫法:
<li class="header__nav-item header__nav-item--active">Home</li>
<li class="header__nav-item">Feature</li>
.header__nav-item {
color: gray;
list-style-type: none;
display: inline-block;
margin-right: 100px;
}
.header__nav-item--active {
color: orange;
}
為什麼要這樣寫: hackmd
所有選單項目共用 .header__nav-item 的基礎樣式
只有被選中的項目額外加上 --active 的顏色
未來修改「所有選單」或「選中狀態」都只改一個 class
Before:
<section class="hero">
<div class="hero-intro">
<h1>IDEAS?</h1>
<h2>This is just a placeholder.</h2>
<button class="try-it-btn">
<h2>TRY IT NOW!</h2>
</button>
</div>
<div class="hero-img">
<img src="..." />
</div>
</section>
section.hero .hero-intro h1 { ... } /* ❌ 4層選擇器 */
.hero-img img { ... } /* ❌ 後代選擇器 */
After:
<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">...</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="..." />
</div>
</section>
.hero__title { font-size: 50px; ... }
.hero__subtitle { font-size: 30px; ... }
.hero__try-it-btn { ... }
.hero__img { width: 100%; height: 100%; }
改善重點:
所有標題標籤都加上語意化 class
按鈕從獨立 Block 改為 .hero__try-it
圖片容器和圖片分別命名
移除所有 4 層以上的選擇器 hackmd
重構前 | 重構後 |
|---|---|
|
|
|
|
|
|
權重降低、可讀性提升 valoremreply
在 footer 的社群媒體圖示區塊,<img> 被 <a> 標籤包住,但 <a> 容器的高度比 <img> 還小,導致圖片超出容器範圍。
<img> 預設是 inline 元素,會受文字排版規則影響 developer.mozilla
Inline 元素的對齊基準是 baseline,下方會有預留空隙 cnblogs
當 <img> 設定了 width 但 <a> 容器沒有明確高度時,容器可能無法正確被撐開
視覺問題:
┌─ <a> 容器 ──────┐
│ │
│ ┌─ <img> ─────┐ │
│ │ │ │
│ │ │ │ ← 圖片底部超出
│ └──────────────┘ │
│ ↓ baseline 預留空隙
└──────────────────┘
.footer__social-media-icon {
width: 30px;
display: block; /* 將圖片改為 block 元素 */
}
display: block 讓 <img> 從 inline 變成 block 元素 developer.mozilla
Block 元素會正常撐開父容器的高度
不再受 baseline 和 line-height 等文字排版規則干擾 cnblogs
<a> 容器的尺寸會完全由 <img> 決定
修正後:
┌─ <a> 容器 ──────┐
│ ┌─ <img> ─────┐ │
│ │ │ │
│ │ │ │
│ └──────────────┘ │ ← 完美包住
└──────────────────┘
圖示按鈕(<a> 或 <button> 裡只有圖片)
純圖片連結,需要容器完全包住圖片
任何「父容器應該被子元素圖片完整撐開」的情境
必須用三層(骨架 > 容器 > 圖片):
圖片是外部資源(網路圖片、使用者上傳)
圖片尺寸不確定
需要複雜定位(absolute, transform)
需要響應式調整
可以簡化成兩層:
圖片尺寸固定且可控(如 icon)
簡單的 block 排列
不需要複雜定位
因為 .hero__img 設定了 height: 100%,形成:
.hero__img-box 的高度由內部 .hero__img 決定
.hero__img 的高度是 100%(相對於 .hero__img-box)
這看起來是「循環參照」,但實際上:
.hero__img 的原始高度(由圖片檔案決定)會先算出
.hero__img-box 被撐開到這個高度
.hero__img 再填滿 .hero__img-box
結果:圖片保持原始比例,同時填滿容器
不建議超過兩層 __: hackmd
/* ❌ 不要這樣 */
.header__nav__item__link { ... }
/* ✅ 應該這樣 */
.header__nav-item { ... }
.header__nav-link { ... }
BEM 建議扁平化命名,即使 HTML 結構有多層嵌套,CSS class 也應該保持簡單。
如果省略中間層:
.hero__img {
position: absolute;
width: 45%;
height: 100%; /* = 450px,固定寬高比 */
right: 0;
}
問題:
width: 45% + height: 100% = 寬高比固定
如果圖片原始比例不是 45:100,會變形
加上 .hero__img-box 中間層:
圖片比例自適應
不會變形
版面骨架穩定
[ ] 每個需要樣式的元素都有 class
[ ] Block 名稱獨立且語意清楚
[ ] Element 用 __ 連接
[ ] Modifier 用 -- 連接
[ ] 沒有混用 - 和 __
[ ] 沒有 tag selector(除了 reset)
[ ] 沒有後代選擇器超過 2 層
[ ] 每個 class 可以獨立解讀
[ ] HTML 和 CSS 的 class 名稱完全對應
[ ] 同一 Block 的所有 Element 都用相同前綴
從依賴子元素推擠 → 父層明確定義空間
從子元素可能超出 → 子元素相對父層尺寸
從直接定位圖片 → 三層結構穩定版面
從 4 層選擇器 → 單一 class
從混亂命名 → 系統化 BEM 命名
從難以重用 → 模組化結構
版面穩定,不會因圖片載入而跳動
命名可預測,看 class 就知道位置
選擇器權重降低,避免樣式衝突 valoremreply
複習三個原則,找出你目前程式碼中還沒套用的地方
試著改寫一個區塊,用三層結構重構
學習 Flexbox,從 MDN 的 Flexbox Guide 開始
記錄「為什麼」,不只是「怎麼做」
筆記日期:2026-02-12
學習情境:ZERO TYPE Landing Page 完整重構
技術標籤:#CSS #BoxModel #BEM #ReplacedElements #LayoutManagement
重構結果: 完整符合 BEM 規範,版面穩定可維護
重構時間:2026 年 2 月
重構範圍:ZERO TYPE Landing Page (index.html + style.css)
技術限制:尚未使用 Flexbox/Grid,僅使用傳統 Box Model + Position 定位
學習目標:建立可維護、可預測的版面結構與命名系統
當使用傳統 Box Model 建立版面時,常見問題:
圖片載入前後版面高度跳動
圖片破圖或尺寸過大導致版面崩潰
修改內容後其他元素位置連帶跑掉
不同瀏覽器或載入速度下版面不一致
核心問題:沒有明確定義「誰負責版面空間」、「誰負責填充內容」。
<!-- ❌ 錯誤:沒有定義父層高度 -->
<div class="hero">
<img src="box.png" />
</div>
.hero {
position: relative;
/* height 沒設定,由子元素決定 */
}
.hero img {
width: 600px;
}
執行流程問題:
瀏覽器遇到 .hero,不知道高度,先畫 0px
遇到 <img>,等待圖片下載
圖片下載完成 400px
.hero 被撐高到 400px,整個版面重排(reflow)
結果:版面跳動,使用者體驗差
<!-- ✅ 正確:用容器切出空間 -->
<section class="hero">
<div class="hero__img-box">
<img class="hero__img" src="box.png" />
</div>
</section>
/* 父層明確定義版面高度 */
.hero {
position: relative;
height: 450px; /* 明確告訴瀏覽器:這區塊佔 450px */
margin-bottom: 250px;
}
/* 圖片容器:定義它在父層中的位置和寬度 */
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* 圖片:填滿容器即可 */
.hero__img {
width: 100%;
height: 100%;
}
改善後的執行流程:
瀏覽器遇到 .hero,立刻畫出 450px 高的區塊
遇到 .hero__img-box,用 absolute 定位在內部
圖片下載完成,填入容器
不需要 reflow,版面穩定
<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>
/* 父層:定義整個 logo 區域的大小和位置 */
.header__logo-zone {
display: inline-block;
margin: 15px 80px 30px 150px;
text-align: center;
}
/* 圖片容器:切出 60px 寬的空間給 logo */
.header__logo {
width: 60px;
margin: 0 auto;
}
/* 圖片:填滿固定空間 */
.header__logo-img {
width: 100%;
height: auto;
}
空間結構圖:
┌─ .header__logo-zone ──────────────────┐
│ [上 padding 15px] │
│ │
│ ┌─ .header__logo (60px) ───────┐ │
│ │ <img> 填滿這個 60px 的空間 │ │
│ └────────────────────────────────┘ │
│ │
│ ZERO TYPE (文字) │
└────────────────────────────────────────┘
關鍵思考:
logo-zone:控制整個區域的 margin 和對齊
logo:切出圖片固定的 60px 空間
img:只負責填滿那 60px
<div class="container">
<img src="large-image.png" />
</div>
.container {
width: 300px;
}
.container img {
/* 沒有設定 width,圖片以原始尺寸顯示 */
/* 假設原始尺寸是 800px,會超出 container */
}
問題視覺化:
┌─ .container (300px) ──┐
│ │
│ ┌─ <img> 800px ────────────────────────┐
│ │ │ │
│ └──────────────────────┘────────────────┘
└────────────────────────┘
↑
超出 500px
.container {
width: 300px;
}
.container img {
width: 100%; /* 永遠不會超過 300px */
height: auto; /* 保持圖片比例 */
}
執行結果:
┌─ .container (300px) ──┐
│ ┌─ <img> (300px) ───┐ │
│ │ 圖片被縮小到 │ │
│ │ 符合容器寬度 │ │
│ └────────────────────┘ │
└────────────────────────┘
版面需求:
Hero 區塊總高度 450px
左側文字佔 50% 寬度
右側圖片佔 45% 寬度
圖片垂直置中
/* 第一層:父層定義整體空間 */
.hero {
position: relative;
height: 450px;
margin-bottom: 250px;
}
/* 第二層:圖片容器定義圖片應佔的空間 */
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* 第三層:圖片填滿容器 */
.hero__img {
width: 100%;
height: 100%;
}
空間結構圖:
┌─ .hero (height: 450px) ────────────────────────┐
│ │
│ ┌─ .hero__intro ──┐ ┌─ .hero__img-box ────┐│
│ │ (width: 50%) │ │ (width: 45%) ││
│ │ IDEAS? │ │ ┌─ <img> ────────┐ ││
│ │ This is just... │ │ │ 填滿容器 │ ││
│ │ [TRY IT NOW!] │ │ │ 100% × 100% │ ││
│ └─────────────────┘ │ └─────────────────┘ ││
│ └──────────────────────┘│
└─────────────────────────────────────────────────┘
<img> 是 replaced element(置換元素),它的特性: developer.mozilla
內容由外部資源決定(圖片檔案)
尺寸預設由圖片原始大小決定
載入狀態會影響版面(載入前/後/失敗)
其他 replaced elements:<video>, <iframe>, <embed>, <object> github
<!-- ❌ 錯誤:直接對 img 使用 absolute -->
<div class="hero">
<img class="hero__img" src="box.png" />
</div>
.hero {
position: relative;
/* 沒有定義高度 */
}
.hero__img {
position: absolute;
width: 45%;
right: 0;
top: 50%;
transform: translateY(-50%);
}
問題 1:圖片載入前
┌─ .hero (高度 0px) ─┐
│ │ ← 沒設定高度,且 img 還沒載入
└─────────────────────┘
下方內容緊貼著
問題 2:圖片載入後
┌─ .hero (被撐高到 300px) ─────┐
│ ┌─ img (300px) ─────┐│
│ │ ││
│ └────────────────────┘│
└───────────────────────────────┘
下方內容被推下 300px (版面跳動!)
問題 3:圖片載入失敗
┌─ .hero (高度又變回 0px) ─┐
│ [破圖 icon] │
└──────────────────────────┘
版面又跳回去 (再次跳動!)
<!-- ✅ 正確:三層結構 -->
<div class="hero"> <!-- 1. 版面骨架 -->
<div class="hero__img-box"> <!-- 2. 圖片空間 -->
<img class="hero__img" src="box.png" /> <!-- 3. 圖片內容 -->
</div>
</div>
/* 第一層:版面骨架 */
.hero {
position: relative;
height: 450px;
}
/* 第二層:圖片空間切割 */
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* 第三層:圖片填充 */
.hero__img {
width: 100%;
height: 100%;
}
載入流程分析:
圖片載入前/後/失敗都穩定:
┌─ .hero (height: 450px) ───────────────┐
│ │
│ ┌─ .hero__img-box ─────────────────┐│
│ │ [載入中 / 顯示圖片 / 破圖 icon] ││
│ │ 但 .hero 始終保持 450px 高度 ││
│ └───────────────────────────────────┘│
│ │
└────────────────────────────────────────┘
版面大小不變,沒有跳動
責任:定義整個區塊在頁面中的空間
.hero {
position: relative; /* 成為子元素定位的參考點 */
height: 450px; /* 佔據固定高度,穩定版面 */
margin-bottom: 250px;
}
責任:在骨架內部切出「圖片應該佔的位置和大小」
.hero__img-box {
position: absolute;
width: 45%;
top: 50%;
right: 0;
transform: translateY(-50%);
}
責任:把圖片內容填入預留的空間
.hero__img {
width: 100%; /* 填滿父層 */
height: 100%;
}
BEM = Block__Element--Modifier getbem
Block__Element--Modifier
Block(區塊):獨立的頁面元件
例:.header, .hero, .footer
Element(元素):Block 的組成部分,用 __ 連接 hackmd
例:.header__logo, .header__nav, .footer__copyright
Modifier(修飾符):Block 或 Element 的不同狀態,用 -- 連接 hackmd
例:.button--disabled, .nav-item--active
這個元素需要 CSS 樣式嗎?
├─ 是 → 加 class
└─ 否
└─ 這個元素需要 JS 操作嗎?
├─ 是 → 加 class
└─ 否 → 不用加 class(純語意容器)
這個元件會在多個地方重用嗎?
├─ 是 → 獨立成 Block (.button, .card, .modal)
└─ 否 → 當作 Element (.hero__button, .footer__social-links)
Before:
<header>
<div class="logo">
<div class="logo-img"><img src="..." /></div>
<p class="logo-text">ZERO TYPE</p>
</div>
<nav>
<ul class="header-nav">
<li><span class="home-btn">Home</span></li>
header { ... } /* ❌ tag selector */
.logo { ... } /* ❌ 沒有明確從屬關係 */
header nav { ... } /* ❌ 後代選擇器 */
.header-nav li { ... } /* ❌ 選擇器層級過深 */
After:
<header class="header">
<div class="header__logo-zone">
<div class="header__logo">
<img class="header__logo-img" src="..." />
</div>
<p class="header__logo-text">ZERO TYPE</p>
</div>
<nav>
<ul class="header__nav">
<li class="header__nav-item">Home</li>
.header { ... }
.header__logo-zone { ... }
.header__logo { ... }
.header__logo-img { ... }
.header__nav-item { ... }
.header__nav-item:hover { color: orange; }
改善重點:
移除 tag selector,全部用 class
移除後代選擇器 .logo-img img
用 __ 明確表達從屬關係 hackmd
錯誤寫法:
<li class="header__nav-home">Home</li>
<li class="header__nav-list">Feature</li>
這樣看起來像兩個不同的元素,而非同一元素的不同狀態。
正確寫法:
<li class="header__nav-item header__nav-item--active">Home</li>
<li class="header__nav-item">Feature</li>
.header__nav-item {
color: gray;
list-style-type: none;
display: inline-block;
margin-right: 100px;
}
.header__nav-item--active {
color: orange;
}
為什麼要這樣寫: hackmd
所有選單項目共用 .header__nav-item 的基礎樣式
只有被選中的項目額外加上 --active 的顏色
未來修改「所有選單」或「選中狀態」都只改一個 class
Before:
<section class="hero">
<div class="hero-intro">
<h1>IDEAS?</h1>
<h2>This is just a placeholder.</h2>
<button class="try-it-btn">
<h2>TRY IT NOW!</h2>
</button>
</div>
<div class="hero-img">
<img src="..." />
</div>
</section>
section.hero .hero-intro h1 { ... } /* ❌ 4層選擇器 */
.hero-img img { ... } /* ❌ 後代選擇器 */
After:
<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">...</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="..." />
</div>
</section>
.hero__title { font-size: 50px; ... }
.hero__subtitle { font-size: 30px; ... }
.hero__try-it-btn { ... }
.hero__img { width: 100%; height: 100%; }
改善重點:
所有標題標籤都加上語意化 class
按鈕從獨立 Block 改為 .hero__try-it
圖片容器和圖片分別命名
移除所有 4 層以上的選擇器 hackmd
重構前 | 重構後 |
|---|---|
|
|
|
|
|
|
權重降低、可讀性提升 valoremreply
在 footer 的社群媒體圖示區塊,<img> 被 <a> 標籤包住,但 <a> 容器的高度比 <img> 還小,導致圖片超出容器範圍。
<img> 預設是 inline 元素,會受文字排版規則影響 developer.mozilla
Inline 元素的對齊基準是 baseline,下方會有預留空隙 cnblogs
當 <img> 設定了 width 但 <a> 容器沒有明確高度時,容器可能無法正確被撐開
視覺問題:
┌─ <a> 容器 ──────┐
│ │
│ ┌─ <img> ─────┐ │
│ │ │ │
│ │ │ │ ← 圖片底部超出
│ └──────────────┘ │
│ ↓ baseline 預留空隙
└──────────────────┘
.footer__social-media-icon {
width: 30px;
display: block; /* 將圖片改為 block 元素 */
}
display: block 讓 <img> 從 inline 變成 block 元素 developer.mozilla
Block 元素會正常撐開父容器的高度
不再受 baseline 和 line-height 等文字排版規則干擾 cnblogs
<a> 容器的尺寸會完全由 <img> 決定
修正後:
┌─ <a> 容器 ──────┐
│ ┌─ <img> ─────┐ │
│ │ │ │
│ │ │ │
│ └──────────────┘ │ ← 完美包住
└──────────────────┘
圖示按鈕(<a> 或 <button> 裡只有圖片)
純圖片連結,需要容器完全包住圖片
任何「父容器應該被子元素圖片完整撐開」的情境
必須用三層(骨架 > 容器 > 圖片):
圖片是外部資源(網路圖片、使用者上傳)
圖片尺寸不確定
需要複雜定位(absolute, transform)
需要響應式調整
可以簡化成兩層:
圖片尺寸固定且可控(如 icon)
簡單的 block 排列
不需要複雜定位
因為 .hero__img 設定了 height: 100%,形成:
.hero__img-box 的高度由內部 .hero__img 決定
.hero__img 的高度是 100%(相對於 .hero__img-box)
這看起來是「循環參照」,但實際上:
.hero__img 的原始高度(由圖片檔案決定)會先算出
.hero__img-box 被撐開到這個高度
.hero__img 再填滿 .hero__img-box
結果:圖片保持原始比例,同時填滿容器
不建議超過兩層 __: hackmd
/* ❌ 不要這樣 */
.header__nav__item__link { ... }
/* ✅ 應該這樣 */
.header__nav-item { ... }
.header__nav-link { ... }
BEM 建議扁平化命名,即使 HTML 結構有多層嵌套,CSS class 也應該保持簡單。
如果省略中間層:
.hero__img {
position: absolute;
width: 45%;
height: 100%; /* = 450px,固定寬高比 */
right: 0;
}
問題:
width: 45% + height: 100% = 寬高比固定
如果圖片原始比例不是 45:100,會變形
加上 .hero__img-box 中間層:
圖片比例自適應
不會變形
版面骨架穩定
[ ] 每個需要樣式的元素都有 class
[ ] Block 名稱獨立且語意清楚
[ ] Element 用 __ 連接
[ ] Modifier 用 -- 連接
[ ] 沒有混用 - 和 __
[ ] 沒有 tag selector(除了 reset)
[ ] 沒有後代選擇器超過 2 層
[ ] 每個 class 可以獨立解讀
[ ] HTML 和 CSS 的 class 名稱完全對應
[ ] 同一 Block 的所有 Element 都用相同前綴
從依賴子元素推擠 → 父層明確定義空間
從子元素可能超出 → 子元素相對父層尺寸
從直接定位圖片 → 三層結構穩定版面
從 4 層選擇器 → 單一 class
從混亂命名 → 系統化 BEM 命名
從難以重用 → 模組化結構
版面穩定,不會因圖片載入而跳動
命名可預測,看 class 就知道位置
選擇器權重降低,避免樣式衝突 valoremreply
複習三個原則,找出你目前程式碼中還沒套用的地方
試著改寫一個區塊,用三層結構重構
學習 Flexbox,從 MDN 的 Flexbox Guide 開始
記錄「為什麼」,不只是「怎麼做」
筆記日期:2026-02-12
學習情境:ZERO TYPE Landing Page 完整重構
技術標籤:#CSS #BoxModel #BEM #ReplacedElements #LayoutManagement
重構結果: 完整符合 BEM 規範,版面穩定可維護
Share Dialog
Share Dialog
No activity yet