線上課程觀課進度管理小工具開發日誌
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」。
記錄雞蛋糕的每一步前端煉成過程,從小白到(也許是)前端工程師的學習與分享。
線上課程觀課進度管理小工具開發日誌
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 雞蛋糕的前端修煉屋

Subscribe to 雞蛋糕的前端修煉屋
<100 subscribers
<100 subscribers
Part 1: CSS 命名系統
Part 2: 重構過程與問題解決
核心概念:用「區塊-元素-修飾符」三層結構命名,讓 class 名稱本身就能表達元素的身份和關係。 frontendmentor
命名規則:
.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
結構清晰:一看 class 名稱就知道元素關係
避免樣式衝突:命名空間隔離
適用情境:
中大型專案
需要明確元件邊界
團隊協作開發
核心概念:把 CSS 當成「樂高積木」,強調「結構與外觀分離」和「容器與內容分離」。 developer.mozilla
實作方式:
/* 通用結構類別 */
.list { /* 列表基礎結構 */ }
.list-item { /* 列表項目結構 */ }
/* 特定外觀類別 */
.nav-list { /* 導航列表樣式 */ }
.nav-item { /* 導航項目樣式 */ }
HTML 使用:
<ul class="list nav-list">
<li class="list-item nav-item">...</li>
</ul>
適用情境:需要大量重複使用相同視覺模式時(例如 Bootstrap)。 hackmd
核心概念:不是命名規則,而是「CSS 檔案組織架構」,把樣式分成 5 大類: frontendmentor
Base (基礎):全域預設樣式 (body, a, h1)
Layout (佈局):主要版面結構 (.header, .main)
Module (模組):可重複使用的元件 (.nav, .card)
State (狀態):視覺狀態切換 (.is-active, .is-hidden)
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
核心概念:專為 React/Vue 等元件化框架 設計,用 PascalCase 命名元件。 frontendmentor
命名規則:
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
核心概念:用「倒三角形」組織 CSS 層級,從最通用到最具體。 reddit
層級結構:
Settings → 變數、配置
Tools → Mixins、函式
Generic → Reset、Normalize
Elements → 原生 HTML 標籤
Objects → 佈局模式 (OOCSS 物件)
Components → 具體元件 (BEM 命名)
Utilities → 工具類別 (!important 覆蓋)
適用情境:超大型專案需要嚴格控制 CSS 優先級 (specificity) 時。 developer.mozilla
核心概念:每個 class 只做「一件事」,像寫內聯樣式但用 class。 mastheadtechnology
實作方式:
.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
適用情境:快速原型開發、設計系統統一、不想寫自訂 CSS 時。 mastheadtechnology
方法 | 核心目標 | 命名風格 | 學習曲線 | 最適合專案類型 |
|---|---|---|---|---|
BEM | 元件獨立性 |
| 中等 | 中大型傳統網站 |
OOCSS | 樣式重用性 | 結構+外觀分離 | 中等 | 需要大量重複模式 |
SMACSS | 檔案組織 | 分類管理 | 中等 | 大型多人協作 |
SUIT CSS | 元件化框架 |
| 中等 | React/Vue 專案 |
ITCSS | 層級控制 |
業界經常混用 BEM 和 Atomic CSS(特別是 Tailwind CSS),這種組合能取各自優勢。 sitepoint
核心思想:用 BEM 定義元件結構,用 Atomic 類別處理通用樣式。 reddit
<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>
核心思想:用 Tailwind 的 @apply 指令,把 utility classes 打包成 BEM 類別。 wearecogworks
.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
保留 BEM 的結構化
享受 Tailwind 的快速開發
核心思想:主要用 Atomic CSS,只在複雜元件或需要 JavaScript 鉤子時用 BEM。 simplethread
<!-- 簡單元件: 純 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
用 BEM 的時機:
元件特有的樣式
需要 JavaScript 操作
複雜的元件變體
用 Atomic CSS 的時機:
通用間距、顏色、字體
快速原型、迭代開發
響應式變化
CSS 權重用 ID-CLASS-TYPE 三欄位表示,由左至右比較: developer.mozilla
欄位 | 包含的選擇器 | 權重值 |
|---|---|---|
ID |
| 1-0-0 |
CLASS |
| 0-1-0 |
TYPE |
| 0-0-1 |
無權重 |
| 0-0-0 |
比較規則:從左至右逐欄比較,誰的數字大誰贏。 hackmd
#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
元素 | 重構前 | 重構後 | 差異 |
|---|---|---|---|
Logo 圖片 |
|
| 降低 2 個 TYPE |
Nav 項目 |
|
| 提高但統一 |
Footer 圖示 |
|
| 降低 3 個 TYPE |
核心發現:重構後所有選擇器權重都變成 0-1-0(單一 CLASS)。 cloudfour
重構前:權重金字塔(容易失控)
#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)
優勢:
覆蓋樣式只需在 HTML 加 class,不用改 CSS 權重
後寫的自動覆蓋前寫的,符合直覺
完全避免 !important zoocha
作業目標:練習用 box model 推擠出正確的版面,將傳統 CSS 寫法重構為 BEM 風格。
技術限制:
先少用 position 和 float
練習 inline-block、text-align、vertical-align
不使用 Flexbox/Grid(未來才學)
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;
}
問題:
沒有使用 class 命名,權重不一致
依賴 position: absolute,寫死 left: 300px
權重分散(0-0-1 到 0-1-2 不等)
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 依賴
命名語意化
症狀:改用扁平化 HTML 後,Footer 社群媒體區塊視覺上偏下。
原因:<ul> 預設有 padding-left: 40px 和 margin,導致 transform: translateY(-50%) 計算的「50%」包含了 padding,所以視覺上會偏移。 developer.mozilla
解法:重置 ul 預設樣式
.footer__social-media {
margin: 0;
padding: 0;
list-style: none;
}
學習重點:
<ul> 和 <ol> 一定要重置 margin、padding、list-style developer.mozilla
absolute + transform 對盒模型很敏感,任何 padding/margin/border 都會影響計算
症狀:想用 text-align: justify 讓元素兩端對齊(左右分散),但只有一行時無效。
原因:text-align: justify 只對多行文字有效,單行時不會觸發兩端對齊。 developer.mozilla
解法:用偽元素創造「第二行」
.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
初始錯誤:用單一 <p> + <br> 分隔段落
HTML(錯誤):
<p class="content__text">
段落1<br>
<br>
段落2<br>
<br>
段落3
</p>
問題:
語意錯誤:6 個段落應該是 6 個 <p>,不是 1 個 <p> 加 5 個 <br>
無障礙問題:螢幕閱讀器會把整段當成「一個段落」
無法單獨控制樣式:無法給「第一段加粗」、「第三段換顏色」
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 應該在父容器,讓整篇文章分欄,而非每個段落內部分欄
症狀:視窗寬度稍微減少時,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 後瀏覽器會自動處理
症狀:inline 圖片容器下方有約 3-4px 的空隙。
原因:瀏覽器預設給 inline 元素留空間放英文小寫字母的下半部(如 g、y、p 的下半部)。 developer.mozilla
解法:
.header__logo-img,
.hero__img {
vertical-align: bottom; /* 對齊底線,消除空隙 */
}
學習重點:inline 或 inline-block 的圖片都要加 vertical-align: bottom 或改成 display: block。
可用寬度 = 視窗寬度 - 左右 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 會換行 ❌
解決方法:
加大可用寬度:min-width: 1400px → 可用寬度變 1200px
減少需要寬度:margin-right: 100px → Nav 寬度變 750px
問題:HTML 的換行會被瀏覽器當成「空白字符」,導致 inline-block 元素之間有約 4px 間隙。 developer.mozilla
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; /* 子元素恢復字體大小 */
}
特點:
需要手動計算每個元素寬度
對齊方式複雜(vertical-align、line-height)
響應式很難做
範例:
.header__logo-box {
width: 60px;
margin-right: 120px; /* 固定數值 */
}
.header__nav-item {
margin-right: 150px; /* 固定數值 */
}
為什麼要學:
理解寬度計算原理
知道 inline-block 的限制在哪
為 Flexbox 打基礎
特點:
不用計算寬度,瀏覽器自動處理
自動垂直置中
響應式友善
範例:
.header {
display: flex;
align-items: center; /* 自動垂直置中 */
justify-content: space-between; /* 自動兩端對齊 */
}
/* 不需要計算寬度!不需要 line-height: 6.5! */
特點:
用比例分配空間
精確控制二維佈局
適合複雜版面
範例:
.header {
display: grid;
grid-template-columns: 200px 1fr; /* Logo固定200px,Nav佔剩餘空間 */
align-items: center;
}
1fr 的意思:「佔剩餘的所有空間」,完全不用計算!
權重扁平化:所有選擇器統一為 0-1-0,避免權重衝突
語意清晰:一看 class 名稱就知道元素關係和身份
易於維護:不會因為改一個地方影響其他地方
寬度公式:可用寬度 = 視窗寬 - padding,需要寬度 = 元素寬 + 間距
inline-block 特性:會換行(整個元素掉下去),有空白字符間隙
必須精確計算:目前階段需要手動計算每個數值
<ul> 預設樣式:必須重置 margin、padding、list-style
圖片底部空隙:用 vertical-align: bottom 解決
text-align: justify 單行無效:用偽元素創造第二行
偽元素佔高度:設定 height: 0 避免撐開容器
段落分隔:用多個 <p> 而非 <br>,語意正確且對 SEO/無障礙友善
Flexbox:自動分配空間,不用計算寬度,自動垂直置中
Grid:用比例分配空間,適合複雜二維佈局
目前的痛點都會被解決:魔術數字、換行問題、對齊困難
MDN CSS 組織指南:介紹 OOCSS、BEM、SMACSS developer.mozilla
MDN CSS Specificity:權重計算規則 developer.mozilla
W3C HTML5 規範:段落與換行的正確用法
CSS Specificity Calculator:https://specificity.keegan.st/ (線上計算工具)
BEM 官方文件:http://getbem.com
Airbnb JavaScript Style Guide:包含 CSS 寫作規範
BEM vs 巢狀選擇器的效能與權重分析 zoocha
Tailwind CSS 與 BEM 混用最佳實踐 wearecogworks
CSS 架構方法論比較 sitepoint
Part 1: CSS 命名系統
Part 2: 重構過程與問題解決
核心概念:用「區塊-元素-修飾符」三層結構命名,讓 class 名稱本身就能表達元素的身份和關係。 frontendmentor
命名規則:
.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
結構清晰:一看 class 名稱就知道元素關係
避免樣式衝突:命名空間隔離
適用情境:
中大型專案
需要明確元件邊界
團隊協作開發
核心概念:把 CSS 當成「樂高積木」,強調「結構與外觀分離」和「容器與內容分離」。 developer.mozilla
實作方式:
/* 通用結構類別 */
.list { /* 列表基礎結構 */ }
.list-item { /* 列表項目結構 */ }
/* 特定外觀類別 */
.nav-list { /* 導航列表樣式 */ }
.nav-item { /* 導航項目樣式 */ }
HTML 使用:
<ul class="list nav-list">
<li class="list-item nav-item">...</li>
</ul>
適用情境:需要大量重複使用相同視覺模式時(例如 Bootstrap)。 hackmd
核心概念:不是命名規則,而是「CSS 檔案組織架構」,把樣式分成 5 大類: frontendmentor
Base (基礎):全域預設樣式 (body, a, h1)
Layout (佈局):主要版面結構 (.header, .main)
Module (模組):可重複使用的元件 (.nav, .card)
State (狀態):視覺狀態切換 (.is-active, .is-hidden)
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
核心概念:專為 React/Vue 等元件化框架 設計,用 PascalCase 命名元件。 frontendmentor
命名規則:
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
核心概念:用「倒三角形」組織 CSS 層級,從最通用到最具體。 reddit
層級結構:
Settings → 變數、配置
Tools → Mixins、函式
Generic → Reset、Normalize
Elements → 原生 HTML 標籤
Objects → 佈局模式 (OOCSS 物件)
Components → 具體元件 (BEM 命名)
Utilities → 工具類別 (!important 覆蓋)
適用情境:超大型專案需要嚴格控制 CSS 優先級 (specificity) 時。 developer.mozilla
核心概念:每個 class 只做「一件事」,像寫內聯樣式但用 class。 mastheadtechnology
實作方式:
.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
適用情境:快速原型開發、設計系統統一、不想寫自訂 CSS 時。 mastheadtechnology
方法 | 核心目標 | 命名風格 | 學習曲線 | 最適合專案類型 |
|---|---|---|---|---|
BEM | 元件獨立性 |
| 中等 | 中大型傳統網站 |
OOCSS | 樣式重用性 | 結構+外觀分離 | 中等 | 需要大量重複模式 |
SMACSS | 檔案組織 | 分類管理 | 中等 | 大型多人協作 |
SUIT CSS | 元件化框架 |
| 中等 | React/Vue 專案 |
ITCSS | 層級控制 |
業界經常混用 BEM 和 Atomic CSS(特別是 Tailwind CSS),這種組合能取各自優勢。 sitepoint
核心思想:用 BEM 定義元件結構,用 Atomic 類別處理通用樣式。 reddit
<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>
核心思想:用 Tailwind 的 @apply 指令,把 utility classes 打包成 BEM 類別。 wearecogworks
.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
保留 BEM 的結構化
享受 Tailwind 的快速開發
核心思想:主要用 Atomic CSS,只在複雜元件或需要 JavaScript 鉤子時用 BEM。 simplethread
<!-- 簡單元件: 純 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
用 BEM 的時機:
元件特有的樣式
需要 JavaScript 操作
複雜的元件變體
用 Atomic CSS 的時機:
通用間距、顏色、字體
快速原型、迭代開發
響應式變化
CSS 權重用 ID-CLASS-TYPE 三欄位表示,由左至右比較: developer.mozilla
欄位 | 包含的選擇器 | 權重值 |
|---|---|---|
ID |
| 1-0-0 |
CLASS |
| 0-1-0 |
TYPE |
| 0-0-1 |
無權重 |
| 0-0-0 |
比較規則:從左至右逐欄比較,誰的數字大誰贏。 hackmd
#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
元素 | 重構前 | 重構後 | 差異 |
|---|---|---|---|
Logo 圖片 |
|
| 降低 2 個 TYPE |
Nav 項目 |
|
| 提高但統一 |
Footer 圖示 |
|
| 降低 3 個 TYPE |
核心發現:重構後所有選擇器權重都變成 0-1-0(單一 CLASS)。 cloudfour
重構前:權重金字塔(容易失控)
#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)
優勢:
覆蓋樣式只需在 HTML 加 class,不用改 CSS 權重
後寫的自動覆蓋前寫的,符合直覺
完全避免 !important zoocha
作業目標:練習用 box model 推擠出正確的版面,將傳統 CSS 寫法重構為 BEM 風格。
技術限制:
先少用 position 和 float
練習 inline-block、text-align、vertical-align
不使用 Flexbox/Grid(未來才學)
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;
}
問題:
沒有使用 class 命名,權重不一致
依賴 position: absolute,寫死 left: 300px
權重分散(0-0-1 到 0-1-2 不等)
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 依賴
命名語意化
症狀:改用扁平化 HTML 後,Footer 社群媒體區塊視覺上偏下。
原因:<ul> 預設有 padding-left: 40px 和 margin,導致 transform: translateY(-50%) 計算的「50%」包含了 padding,所以視覺上會偏移。 developer.mozilla
解法:重置 ul 預設樣式
.footer__social-media {
margin: 0;
padding: 0;
list-style: none;
}
學習重點:
<ul> 和 <ol> 一定要重置 margin、padding、list-style developer.mozilla
absolute + transform 對盒模型很敏感,任何 padding/margin/border 都會影響計算
症狀:想用 text-align: justify 讓元素兩端對齊(左右分散),但只有一行時無效。
原因:text-align: justify 只對多行文字有效,單行時不會觸發兩端對齊。 developer.mozilla
解法:用偽元素創造「第二行」
.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
初始錯誤:用單一 <p> + <br> 分隔段落
HTML(錯誤):
<p class="content__text">
段落1<br>
<br>
段落2<br>
<br>
段落3
</p>
問題:
語意錯誤:6 個段落應該是 6 個 <p>,不是 1 個 <p> 加 5 個 <br>
無障礙問題:螢幕閱讀器會把整段當成「一個段落」
無法單獨控制樣式:無法給「第一段加粗」、「第三段換顏色」
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 應該在父容器,讓整篇文章分欄,而非每個段落內部分欄
症狀:視窗寬度稍微減少時,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 後瀏覽器會自動處理
症狀:inline 圖片容器下方有約 3-4px 的空隙。
原因:瀏覽器預設給 inline 元素留空間放英文小寫字母的下半部(如 g、y、p 的下半部)。 developer.mozilla
解法:
.header__logo-img,
.hero__img {
vertical-align: bottom; /* 對齊底線,消除空隙 */
}
學習重點:inline 或 inline-block 的圖片都要加 vertical-align: bottom 或改成 display: block。
可用寬度 = 視窗寬度 - 左右 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 會換行 ❌
解決方法:
加大可用寬度:min-width: 1400px → 可用寬度變 1200px
減少需要寬度:margin-right: 100px → Nav 寬度變 750px
問題:HTML 的換行會被瀏覽器當成「空白字符」,導致 inline-block 元素之間有約 4px 間隙。 developer.mozilla
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; /* 子元素恢復字體大小 */
}
特點:
需要手動計算每個元素寬度
對齊方式複雜(vertical-align、line-height)
響應式很難做
範例:
.header__logo-box {
width: 60px;
margin-right: 120px; /* 固定數值 */
}
.header__nav-item {
margin-right: 150px; /* 固定數值 */
}
為什麼要學:
理解寬度計算原理
知道 inline-block 的限制在哪
為 Flexbox 打基礎
特點:
不用計算寬度,瀏覽器自動處理
自動垂直置中
響應式友善
範例:
.header {
display: flex;
align-items: center; /* 自動垂直置中 */
justify-content: space-between; /* 自動兩端對齊 */
}
/* 不需要計算寬度!不需要 line-height: 6.5! */
特點:
用比例分配空間
精確控制二維佈局
適合複雜版面
範例:
.header {
display: grid;
grid-template-columns: 200px 1fr; /* Logo固定200px,Nav佔剩餘空間 */
align-items: center;
}
1fr 的意思:「佔剩餘的所有空間」,完全不用計算!
權重扁平化:所有選擇器統一為 0-1-0,避免權重衝突
語意清晰:一看 class 名稱就知道元素關係和身份
易於維護:不會因為改一個地方影響其他地方
寬度公式:可用寬度 = 視窗寬 - padding,需要寬度 = 元素寬 + 間距
inline-block 特性:會換行(整個元素掉下去),有空白字符間隙
必須精確計算:目前階段需要手動計算每個數值
<ul> 預設樣式:必須重置 margin、padding、list-style
圖片底部空隙:用 vertical-align: bottom 解決
text-align: justify 單行無效:用偽元素創造第二行
偽元素佔高度:設定 height: 0 避免撐開容器
段落分隔:用多個 <p> 而非 <br>,語意正確且對 SEO/無障礙友善
Flexbox:自動分配空間,不用計算寬度,自動垂直置中
Grid:用比例分配空間,適合複雜二維佈局
目前的痛點都會被解決:魔術數字、換行問題、對齊困難
MDN CSS 組織指南:介紹 OOCSS、BEM、SMACSS developer.mozilla
MDN CSS Specificity:權重計算規則 developer.mozilla
W3C HTML5 規範:段落與換行的正確用法
CSS Specificity Calculator:https://specificity.keegan.st/ (線上計算工具)
BEM 官方文件:http://getbem.com
Airbnb JavaScript Style Guide:包含 CSS 寫作規範
BEM vs 巢狀選擇器的效能與權重分析 zoocha
Tailwind CSS 與 BEM 混用最佳實踐 wearecogworks
CSS 架構方法論比較 sitepoint
倒三角結構
高 |
超大型專案 |
Atomic CSS | 快速開發 | 單一功能類別 | 高(需記憶大量類別) | 快速迭代專案 |
<!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">© 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>
/* 主要 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;
}
倒三角結構
高 |
超大型專案 |
Atomic CSS | 快速開發 | 單一功能類別 | 高(需記憶大量類別) | 快速迭代專案 |
<!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">© 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>
/* 主要 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;
}
Share Dialog
Share Dialog
No activity yet