線上課程觀課進度管理小工具開發日誌
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
學習情境: 在檢視結訓學員的程式碼時,發現他們的切版作業採用了
box-sizing: border-box,但我目前的作業還沒有使用這個設定。透過這次筆記深入理解 Box Model、偽元素、偽類的運作原理與最佳實踐。
/* 我的作業沒有這段設定 */
header {
border: 5px solid black;
/* 使用預設的 content-box */
}
footer {
border: 5px solid black;
padding: 30px;
/* 實際寬度會超出設定值 */
}
/* 學員作業開頭有這段 */
* {
box-sizing: border-box;
}
/* 搜尋後發現還有另外一種常用的包含三個選擇器的設定 */
*,
*::before,
*::after {
box-sizing: border-box;
}
發現問題:為什麼大家都用這個設定?這三個選擇器分別是什麼意思?
Content-box (CSS 預設值): developer.mozilla
實際佔據寬度 = width + padding-left + padding-right + border-left + border-right
Border-box (現代推薦): developer.mozilla
實際佔據寬度 = width (已包含 padding 和 border)
內容區域寬度 = width - padding - border
<!-- HTML -->
<div class="container">
<div class="box content-box">
<h3>Content-box (預設)</h3>
<p>width: 200px<br>padding: 20px<br>border: 5px</p>
</div>
<div class="box border-box">
<h3>Border-box</h3>
<p>width: 200px<br>padding: 20px<br>border: 5px</p>
</div>
</div>
/* CSS */
.container {
display: flex;
gap: 20px;
padding: 20px;
background: #f0f0f0;
}
.box {
width: 200px;
padding: 20px;
border: 5px solid #333;
background: white;
}
.content-box {
box-sizing: content-box;
/* 實際寬度 = 200 + 40 + 10 = 250px */
}
.border-box {
box-sizing: border-box;
/* 實際寬度 = 200px */
/* 內容區 = 200 - 40 - 10 = 150px */
}
/* 視覺化實際寬度 */
.box::after {
content: '';
position: absolute;
bottom: -30px;
left: 0;
right: 0;
height: 2px;
background: red;
}
觀察重點:
Content-box 的盒子明顯比較寬
Border-box 的盒子寬度就是你設定的 200px
兩者內容區域大小不同
<!-- HTML -->
<header>
<div class="logo">
<img src="logo.png" alt="LOGO" />
<p>ZERO TYPE</p>
</div>
<nav>
<ul>
<li><span>Home</span></li>
<li>Feature</li>
<li>News</li>
</ul>
</nav>
</header>
/* CSS - 影響分析 */
header {
border: 5px solid black;
background-color: #f2f2f2;
/* content-box: 寬度 = 100% + 10px (border) */
/* border-box: 寬度 = 100% (border 內推) */
}
header .logo {
margin: 15px 80px 30px 150px;
/* margin 不受 box-sizing 影響 */
}
影響:改用 border-box 後,header 內容區會縮小 10px(左右 border 各 5px)。 developer.mozilla
<!-- HTML -->
<footer>
<div class="copyright">© 2023 Zerotype.</div>
<div class="social-media-link">
<ul>
<li><a href="#"><img src="facebook.png" alt="FB"/></a></li>
<li><a href="#"><img src="twitter.png" alt="Twitter"/></a></li>
</ul>
</div>
</footer>
/* CSS - 影響分析 */
footer {
border: 5px solid black; /* 左右共 10px */
padding: 30px; /* 左右共 60px */
background-color: #f2f2f2;
}
/* content-box 下:
實際寬度 = 100% + 60px + 10px = 超出容器!
*/
/* border-box 下:
實際寬度 = 100%
內容區域 = 100% - 60px - 10px
*/
影響:改用 border-box 後,footer 內容區會縮小 70px,需要調整內部元素的 margin。 vocus
<!-- HTML -->
<div class="try-it-btn">
<h2>TRY IT NOW!</h2>
<p>Don't worry it's for free.</p>
</div>
/* CSS */
.try-it-btn h2 {
padding: 20px 30px;
background: linear-gradient(to bottom, #ffb84d 50%, #ff9500 50%);
border-radius: 5px;
/* 沒有設定 width,影響較小 */
}
影響:按鈕內容區會稍微縮小,但因為沒有固定寬度,視覺差異不大。 vocus
<!-- HTML -->
<div class="layout-demo">
<h3>50% + 50% 佈局問題對比</h3>
<h4>❌ Content-box:會跑版</h4>
<div class="row content-box-layout">
<div class="col">左欄 (content-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div><div class="col">右欄 (content-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div>
</div>
<h4>✅ Border-box:完美並排</h4>
<div class="row border-box-layout">
<div class="col">左欄 (border-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div><div class="col">右欄 (border-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div>
</div>
</div>
/* CSS */
.layout-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
background: #f5f5f5;
}
h3 {
margin-top: 0;
color: #333;
}
h4 {
margin: 20px 0 10px;
color: #666;
}
.row {
margin-bottom: 30px;
border: 3px dashed #999;
background: white;
font-size: 0; /* ⭐ 消除 inline-block 的空白字符 */
}
.col {
width: 50%;
padding: 20px;
border: 5px solid #333;
display: inline-block;
vertical-align: top;
font-size: 14px; /* ⭐ 恢復正常字體大小 */
line-height: 1.5;
}
/* Content-box 版本:會爆版! */
.content-box-layout .col {
box-sizing: content-box;
background: #ffebee;
/* 每個欄位實際寬度 = 50% + 40px + 10px */
/* 兩欄加起來 = 100% + 100px > 100% */
}
/* Border-box 版本:完美並排 */
.border-box-layout .col {
box-sizing: border-box;
background: #e8f5e9;
/* 每個欄位實際寬度 = 50% (已包含 padding 和 border) */
/* 兩欄加起來 = 100% */
}
觀察重點:
Content-box 版本的右欄會被擠到下一行
Border-box 版本完美佔據各 50%
⚠️ 重要提醒:inline-block 空白問題
使用 display: inline-block 時,HTML 標籤之間的換行和空白會被視為空白字符,佔據空間,導致元素換行。
解決方法一:父元素設 font-size: 0
.row {
font-size: 0; /* 消除空白 */
}
.col {
font-size: 14px; /* 子元素恢復正常 */
}
解決方法二:HTML 標籤不換行
<!-- ✅ 兩個 div 緊連,不要換行 -->
<div class="col">左欄</div><div class="col">右欄</div>
<!-- ❌ 這樣會產生空白字符 -->
<div class="col">左欄</div>
<div class="col">右欄</div>
替代方案:也可以用 float 來做多欄並排(後續會學習)。
核心概念:透過 CSS 在元素內部插入虛擬內容,不需要修改 HTML。 developer.mozilla
<!-- HTML -->
<div class="demo-container">
<h3>偽元素示範</h3>
<div class="text-with-icon">重要訊息</div>
<div class="quote">
這是一段需要引號的文字內容,展示如何用偽元素自動加上裝飾性引號效果。這個方法可以保持 HTML 簡潔,所有裝飾都由 CSS 處理。
</div>
<ul class="nav-list">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
/* CSS */
.demo-container {
padding: 20px;
max-width: 600px;
margin: 0 auto;
background: #f5f5f5;
}
h3 {
margin-top: 0;
color: #333;
}
/* 範例 1: 在文字前加圖示 */
.text-with-icon {
padding: 15px;
background: #fff3e0;
border-left: 4px solid #ff9800;
margin: 20px 0;
}
.text-with-icon::before {
content: "⚠️ ";
font-size: 1.2em;
}
/* 範例 2: 自動加引號 */
.quote {
position: relative;
padding: 20px 40px;
background: white;
margin: 20px 0;
border-left: 4px solid #2196f3;
line-height: 1.6;
font-style: italic;
color: #555;
}
.quote::before {
content: "\201C"; /* 左彎引號 " (使用 Unicode 編碼) */
position: absolute;
top: -10px; /* 往上移,不蓋住文字 */
left: 10px;
font-size: 3em;
color: #2196f3;
line-height: 1;
opacity: 0.3; /* 半透明,更柔和 */
}
.quote::after {
content: "\201D"; /* 右彎引號 " */
position: absolute;
bottom: -10px; /* 往下移,不蓋住文字 */
right: 10px;
font-size: 3em;
color: #2196f3;
line-height: 1;
opacity: 0.3;
}
/* 範例 3: 導航列分隔符號 */
.nav-list {
list-style: none;
padding: 0;
background: white;
padding: 15px;
border-radius: 4px;
}
.nav-list li {
display: inline-block;
padding: 5px 10px;
}
.nav-list li:not(:last-child)::after {
content: "|";
margin-left: 10px;
color: #999;
}
觀察重點:
所有裝飾都不在 HTML 中
::before 在內容前面,::after 在內容後面
必須有 content 屬性才會顯示 ppl-ai-file-upload.s3.amazonaws
引號使用 Unicode 編碼 \201C 和 \201D 確保瀏覽器相容性
調整 top/bottom 為負值讓引號不蓋住文字
💡 常用 Unicode 引號編碼:
符號 | 名稱 | CSS content 寫法 |
|---|---|---|
" | 左彎雙引號 |
|
" | 右彎雙引號 |
|
' | 左彎單引號 |
|
' | 右彎單引號 |
|
「 | 中文左引號 |
|
」 | 中文右引號 |
|
/* ❌ 不會顯示 */
.element::before {
color: red;
font-size: 2em;
}
/* ✅ 正確寫法 */
.element::before {
content: ""; /* 就算是空的也要寫 */
color: red;
font-size: 2em;
}
/* 結構示意 */
<div class="box">
::before (第一個孩子)
原本的內容
::after (最後一個孩子)
</div>
/* ❌ 這些元素無法使用偽元素 */
img::before { } /* 無效 */
input::after { } /* 無效 */
br::before { } /* 無效 */
/* 因為它們不能有子元素 */
<!-- HTML (簡化版) -->
<header>
<nav>
<ul>
<li><span>Home</span></li>
<li>Feature</li>
<li>News</li>
<li>About</li>
</ul>
</nav>
</header>
/* CSS - 用偽元素加分隔線 */
nav li {
display: inline-block;
margin-right: 100px;
}
/* 優化:用偽元素取代 margin,視覺更清楚 */
nav li:not(:last-child)::after {
content: "·"; /* 或用 "|" */
margin: 0 20px;
color: lightgray;
font-size: 1.2em;
}
/* 現在選中狀態的底線效果 */
nav li span {
color: orange;
position: relative;
}
nav li span::after {
content: "";
display: block;
width: 100%;
height: 2px;
background-color: orange;
margin-top: 5px;
}
特徵 | 偽類 | 偽元素 |
|---|---|---|
作用 | 選擇元素的「特殊狀態」 | 創造「虛擬元素」 |
冒號數量 | 一個 | 兩個 |
是否生成盒子 | 否 | 是 |
需要 content | 不需要 |
|
數量限制 | 可多個 | 一個選擇器一組 |
常見用途 | 互動狀態、結構關係 | 裝飾性內容、特殊格式 |
<!-- HTML -->
<div class="interactive-demo">
<h3>偽類 + 偽元素組合</h3>
<ul class="styled-list">
<li>第一項</li>
<li>第二項</li>
<li>第三項</li>
</ul>
<a href="#" class="fancy-link">帶動畫效果的連結</a>
</div>
/* CSS */
.interactive-demo {
padding: 20px;
max-width: 600px;
margin: 0 auto;
}
/* 範例 1: 偽類選擇結構 */
.styled-list {
list-style: none;
padding: 0;
}
.styled-list li {
padding: 10px;
margin: 5px 0;
background: #f0f0f0;
}
/* 用偽類選擇第一個 */
.styled-list li:first-child {
background: #e3f2fd;
font-weight: bold;
}
/* 用偽元素加裝飾 */
.styled-list li:first-child::before {
content: "👑 ";
}
/* 用偽類選擇懸停狀態 */
.styled-list li:hover {
background: #fff3e0;
cursor: pointer;
}
/* 範例 2: 動態底線效果 */
.fancy-link {
display: inline-block;
padding: 10px 20px;
text-decoration: none;
color: #333;
position: relative;
}
/* 用偽元素創建底線 */
.fancy-link::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: orange;
transition: width 0.3s;
}
/* 用偽類觸發動畫 */
.fancy-link:hover::after {
width: 100%;
}
觀察重點:
偽類 :first-child 選擇「第一個元素」(狀態)
偽元素 ::before 插入圖示(新內容)
偽類 :hover 觸發「懸停狀態」
偽元素 ::after 創造底線(新元素)
可以組合使用:li:hover::before
// 如果沒有偽類,需要這樣寫 JS
button.addEventListener('mouseenter', () => {
button.classList.add('hovered');
});
button.addEventListener('mouseleave', () => {
button.classList.remove('hovered');
});
// 有了 :hover 偽類,CSS 自動處理
button:hover {
background: orange;
}
<!-- 沒有偽元素:HTML 很亂 -->
<button>
<span class="icon">★</span>
<span class="text">按鈕</span>
</button>
<!-- 有了偽元素:HTML 簡潔 -->
<button>按鈕</button>
button::before {
content: "★ ";
}
偽元素會生成「真實的盒子」,所以需要設定 box-sizing。 forum.freecodecamp 偽類只是選擇器,不生成盒子,不需要設定。
*, /* 選擇所有真實 HTML 元素 */
*::before, /* 選擇所有 ::before 偽元素 */
*::after { /* 選擇所有 ::after 偽元素 */
box-sizing: border-box;
}
因為 * 只會選到真實的 HTML 元素,不會選到偽元素。 stackoverflow
<!-- HTML -->
<div class="comparison">
<h3>偽元素 box-sizing 對比</h3>
<div class="card without-reset">
<h4>沒設定偽元素的 box-sizing</h4>
<p>這是卡片內容</p>
</div>
<div class="card with-reset">
<h4>有設定偽元素的 box-sizing</h4>
<p>這是卡片內容</p>
</div>
</div>
/* CSS */
.comparison {
padding: 20px;
}
.card {
width: 300px;
padding: 20px;
border: 2px solid #333;
margin: 20px 0;
position: relative;
background: white;
}
/* 兩張卡片都用 border-box */
.card {
box-sizing: border-box;
}
/* 共用的偽元素樣式 */
.card::before {
content: "標籤";
position: absolute;
top: -15px;
left: 20px;
width: 60px;
padding: 5px 10px;
border: 2px solid orange;
background: white;
font-size: 12px;
text-align: center;
}
/* 第一張卡片:偽元素用預設 content-box */
.without-reset::before {
box-sizing: content-box;
/* 實際寬度 = 60 + 20 + 4 = 84px */
}
/* 第二張卡片:偽元素也用 border-box */
.with-reset::before {
box-sizing: border-box;
/* 實際寬度 = 60px */
}
/* 視覺化寬度差異 */
.card::before {
outline: 2px dashed red;
outline-offset: 2px;
}
觀察重點:
第一張卡片的標籤明顯比較寬
第二張卡片的標籤寬度就是設定的 60px
如果不統一 box-sizing,版面計算會混亂 stackoverflow
/* ❌ 偽類不用設定 */
a:hover {
/* :hover 只是選擇「懸停狀態的 a 元素」
這個 a 元素本身已經有 box-sizing */
}
/* ✅ 偽元素需要設定 */
a::before {
/* ::before 生成了新的盒子
需要明確設定 box-sizing */
}
偽類只是「選擇器的條件」,不生成盒子
偽元素會生成「新的盒子」,需要設定 forum.freecodecamp
如果現在改變預設值,全世界數百萬個舊網站都會瞬間跑版。 reddit
<!-- HTML -->
<div class="simple-demo">
<h3>為什麼不能改預設值?</h3>
<p class="intro">
2005 年的網站設計師寫了這段 CSS:
</p>
<div class="code-block">
.sidebar {<br>
width: 300px;<br>
padding: 20px;<br>
border: 5px solid black;<br>
}
</div>
<div class="comparison">
<div class="col">
<h4>✅ Content-box (當年的預設)</h4>
<div class="visual-box content-box-visual">
<div class="content-area">內容區<br>300px</div>
<div class="padding-area">padding 40px</div>
<div class="border-area">border 10px</div>
</div>
<p class="total">總寬度 = <strong>350px</strong></p>
</div>
<div class="col">
<h4>❌ 如果改成 Border-box</h4>
<div class="visual-box border-box-visual">
<div class="content-area small">內容區<br>250px<br>(被壓縮)</div>
<div class="padding-area">padding 40px</div>
<div class="border-area">border 10px</div>
</div>
<p class="total">總寬度 = <strong>300px</strong></p>
</div>
</div>
<div class="note">
<strong>結果:</strong>同樣的 CSS,元素從 350px 變成 300px,所有基於「350px」的版面計算都會錯位。
這就是為什麼瀏覽器無法改變預設值——會破壞全球數百萬個舊網站。
</div>
</div>
/* CSS */
.simple-demo {
padding: 20px;
max-width: 900px;
margin: 0 auto;
background: #f5f5f5;
}
h3 {
margin-top: 0;
color: #333;
text-align: center;
}
h4 {
margin: 10px 0;
color: #666;
text-align: center;
}
.intro {
background: #e3f2fd;
padding: 15px;
border-left: 4px solid #2196f3;
line-height: 1.8;
margin-bottom: 20px;
text-align: center;
}
.code-block {
background: #263238;
color: #aed581;
padding: 15px;
border-radius: 4px;
font-family: monospace;
margin-bottom: 30px;
line-height: 1.6;
}
.comparison {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
.col {
flex: 1;
}
.visual-box {
border: 3px solid #333;
margin: 20px 0;
padding: 5px;
background: white;
}
.content-box-visual {
width: 350px;
}
.border-box-visual {
width: 300px;
}
.content-area {
background: #e8f5e9;
padding: 40px 20px;
text-align: center;
font-weight: bold;
border: 5px solid #4caf50;
}
.content-area.small {
background: #ffebee;
border-color: #f44336;
}
.padding-area,
.border-area {
text-align: center;
padding: 5px;
font-size: 12px;
color: #666;
}
.total {
text-align: center;
font-size: 18px;
background: #fff9c4;
padding: 10px;
margin: 10px 0;
border-radius: 4px;
}
.total strong {
color: #1976d2;
font-size: 24px;
}
.note {
padding: 20px;
background: #fff9c4;
border: 3px solid #fbc02d;
border-radius: 4px;
line-height: 1.8;
}
.note strong {
color: #f57c00;
}
觀察重點:
左側 (content-box):側邊欄實際佔據 350px,這是設計師當年的預期
右側 (border-box):側邊欄實際佔據 300px,內容區被壓縮 50px
同樣的 CSS 代碼,在不同 box-sizing 下,元素佔據的空間完全不同
想像一下這個情境: reddit
/* 2005 年寫的舊網站,沒有設定 box-sizing */
.sidebar {
width: 300px;
padding: 20px;
border: 5px solid black;
/* 作者預期:實際寬度 = 300 + 40 + 10 = 350px */
}
.content {
width: 650px; /* 剛好跟 sidebar 加起來是 1000px */
}
如果今天瀏覽器把預設值改成 border-box: reddit
.sidebar 實際寬度變成 300px(不是 350px)
原本設計好的版面全部跑掉
全球數百萬個沒在維護的舊網站集體崩潰
這些地方還在使用 20 年前的網頁系統: reddit
政府機關網站
醫療機構系統
大型企業內部工具
教育機構平台
銀行系統介面
它們可能永遠不會更新,但必須繼續運作。
早期有兩套盒模型標準: blog.csdn
Internet Explorer 5 (1999年): reddit
width = content + padding + border
(類似現在的 border-box)
W3C CSS 標準 (2000年): developer.mozilla
width = content only
(就是現在的 content-box)
當時 W3C 認為「寬度應該只指內容」符合邏輯,但實務上開發者發現 IE 的做法更直覺。 reddit
CSS 工作小組後來也公開承認「這是 CSS 的設計錯誤」。 reddit
Tim Berners-Lee (Web 發明者) 的原則: reddit
「1990 年的網頁,在 2026 年的瀏覽器中應該還能正常顯示」
這是 Web 能成功的關鍵原因之一 — 內容可以跨越時空存在。
不像程式語言有版本宣告:
// JavaScript 可以這樣
"use strict"; // 啟用嚴格模式
CSS 沒有類似機制能說「這個網頁用 CSS v2 標準」或「用 CSS v4 標準」。 reddit
如果改預設值,需要: reddit
所有瀏覽器廠商同時更新
全球開發者修改數百萬個網站
測試確保沒有任何舊網站被破壞
處理各種邊緣案例
這個成本遠超過「讓開發者自己加三行 CSS」。
有趣的是,某些 HTML 元素的預設值其實是 border-box: stackoverflow
/* 瀏覽器內建樣式 */
button, input[type="submit"], input[type="reset"] {
box-sizing: border-box; /* 預設就是 border-box! */
}
原因: stackoverflow
這些元素是後來才加入 HTML 的
設計時已經知道 border-box 比較好
不會破壞既有網站(因為是新元素)
這證明了「如果重新設計,大家都會選 border-box」。
既然標準無法改,社群發展出「重置樣式」的做法: freecodecamp
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit; /* 繼承父元素的設定 */
}
好處:可以在特定區域改回 content-box。 freecodecamp
現代框架都內建 border-box:
Bootstrap
Tailwind CSS
Normalize.css
typeof null // "object" (錯誤!)
這是 JavaScript 設計錯誤,但無法修正,因為會破壞全球數百萬個網站。 reddit
HTTP 418: I'm a teapot
原本是愚人節玩笑,但被寫入標準後就無法移除,因為有人真的在用。 reddit
只要元素會產生盒子並參與版面佈局,就需要統一計算方式。 dev
/* 混用兩種 box-sizing */
.left-column {
width: 50%;
padding: 20px;
border: 5px solid black;
box-sizing: content-box;
/* 實際寬度 = 50% + 40px + 10px → 爆版! */
}
.right-column {
width: 50%;
padding: 20px;
border: 5px solid black;
box-sizing: border-box;
/* 實際寬度 = 50% (已包含) → 正常 */
}
/* 結果:兩欄加起來超過 100%,右欄被擠下去 */
*,
*::before,
*::after {
box-sizing: border-box;
}
.left-column,
.right-column {
width: 50%;
padding: 20px;
border: 5px solid black;
/* 兩欄各佔 50%,完美並排 */
}
直覺化計算: crypist
設定 width: 300px,元素在版面上就佔 300px
響應式設計更容易: dev
.col {
width: 25%; /* 不用擔心 padding/border 爆版 */
padding: 15px;
border: 2px solid gray;
}
維護性提升: crypist
修改 padding 或 border 時不需要重新計算 width
/* 專案第一行 CSS */
*,
*::before,
*::after {
box-sizing: border-box;
}
評估風險後再決定是否加上:
測試所有頁面
檢查是否有地方依賴 content-box 行為
漸進式重構,不要一次全改
/* 在 style.css 最前面加上 */
*,
*::before,
*::after {
box-sizing: border-box;
}
需要檢查這些區域:
Header:
檢查內容是否太擠
logo 位置是否正常
Footer:
檢查 padding 是否需要調整
社交媒體連結排列是否正常
Main 區域:
.hero-intro 和 .hero-img 的並排
Article 的雙欄排版
/* 導航列加分隔線 */
header nav li:not(:last-child)::after {
content: "·";
margin: 0 15px;
color: #ccc;
}
/* TRY IT NOW 按鈕加箭頭 */
.try-it-btn h2::after {
content: " →";
transition: transform 0.3s;
}
.try-it-btn h2:hover::after {
transform: translateX(5px);
}
/* Footer 上方加裝飾線 */
footer::before {
content: "";
display: block;
width: 80%;
height: 2px;
background: linear-gradient(
to right,
transparent,
#ff9500,
transparent
);
margin: 0 auto 20px;
}
記住:
Border-box 讓寬度計算更直覺
現代專案一律使用 border-box
改動後要測試所有版面
易錯:
忘記 border-box 會壓縮 content 區域
只設定 *,忘記偽元素
混用兩種 box-sizing
記住:
兩個冒號 ::
必須有 content 屬性
在元素內部,不是外部
適合裝飾性內容
引號用 Unicode 編碼 \201C \201D
易錯:
忘記 content: "";
用在空元素(<img>, <input>)
誤以為在元素外面
引號位置蓋住文字
記住:
一個冒號 :
選擇元素的狀態或結構關係
不生成盒子
易錯:
混淆冒號數量
對偽類設定 box-sizing
記住:
*,
*::before,
*::after {
box-sizing: border-box;
}
❌ 易錯:
只寫 *
忘記偽元素
這個案例告訴我們: reddit
標準制定要謹慎:一旦發佈就很難改
向後兼容很重要:比「技術上正確」更重要
開發者可以自救:透過 Reset CSS 解決標準問題
歷史會評價設計:20 年後才發現當初的決策是錯的
CSS Working Group 現在每次制定新標準都會討論「這會不會成為下一個 box-sizing」。 reddit
[box-sizing] developer.mozilla
[Pseudo-elements] developer.mozilla
[Pseudo-classes] developer.mozilla
[Box Model] developer.mozilla
我會在這裡貼上自己的實驗連結:
[ ] Box Model 對比實驗 https://codepen.io/gcake119/pen/YPWRJZJ
[ ] 偽元素應用範例 https://codepen.io/gcake119/pen/EayOMje
[ ] 偽元素的 box-sizing 問題 https://codepen.io/gcake119/pen/gbMQERM
[ ] 偽分類與偽元素結合 https://codepen.io/gcake119/pen/xbOQBqB
[ ] 歷史包袱視覺化示範 https://codepen.io/gcake119/pen/YPWRgOq
標準不等於最佳實踐
Content-box 是標準,但 border-box 更實用
要理解背後的歷史脈絡
向後兼容很重要
Web 的成功建立在「永不破壞舊內容」
這也是為什麼有些設計無法改變
社群可以自救
雖然標準有問題,社群透過 CSS Reset 解決
現代專案幾乎都採用 border-box
偽元素是強大工具
保持 HTML 簡潔
裝飾性內容用 CSS 處理
減少無意義的標籤
實作中學習最有效
CodePen 實驗幫助理解抽象概念
親手重現問題才能真正掌握
[ ] 將 border-box 加入我的作業
[ ] 測試所有版面是否正常
[ ] 用偽元素優化導航列和按鈕
[ ] 在 CodePen 建立實驗範例
[ ] 整理成技術筆記並分享
筆記日期: 2026-02-10
學習情境: 對照結訓學員程式碼發現的問題
主題標籤: #CSS #BoxModel #Pseudo-elements #Pseudo-classes #BestPractice
CodePen 實驗: 連結如上文
學習情境: 在檢視結訓學員的程式碼時,發現他們的切版作業採用了
box-sizing: border-box,但我目前的作業還沒有使用這個設定。透過這次筆記深入理解 Box Model、偽元素、偽類的運作原理與最佳實踐。
/* 我的作業沒有這段設定 */
header {
border: 5px solid black;
/* 使用預設的 content-box */
}
footer {
border: 5px solid black;
padding: 30px;
/* 實際寬度會超出設定值 */
}
/* 學員作業開頭有這段 */
* {
box-sizing: border-box;
}
/* 搜尋後發現還有另外一種常用的包含三個選擇器的設定 */
*,
*::before,
*::after {
box-sizing: border-box;
}
發現問題:為什麼大家都用這個設定?這三個選擇器分別是什麼意思?
Content-box (CSS 預設值): developer.mozilla
實際佔據寬度 = width + padding-left + padding-right + border-left + border-right
Border-box (現代推薦): developer.mozilla
實際佔據寬度 = width (已包含 padding 和 border)
內容區域寬度 = width - padding - border
<!-- HTML -->
<div class="container">
<div class="box content-box">
<h3>Content-box (預設)</h3>
<p>width: 200px<br>padding: 20px<br>border: 5px</p>
</div>
<div class="box border-box">
<h3>Border-box</h3>
<p>width: 200px<br>padding: 20px<br>border: 5px</p>
</div>
</div>
/* CSS */
.container {
display: flex;
gap: 20px;
padding: 20px;
background: #f0f0f0;
}
.box {
width: 200px;
padding: 20px;
border: 5px solid #333;
background: white;
}
.content-box {
box-sizing: content-box;
/* 實際寬度 = 200 + 40 + 10 = 250px */
}
.border-box {
box-sizing: border-box;
/* 實際寬度 = 200px */
/* 內容區 = 200 - 40 - 10 = 150px */
}
/* 視覺化實際寬度 */
.box::after {
content: '';
position: absolute;
bottom: -30px;
left: 0;
right: 0;
height: 2px;
background: red;
}
觀察重點:
Content-box 的盒子明顯比較寬
Border-box 的盒子寬度就是你設定的 200px
兩者內容區域大小不同
<!-- HTML -->
<header>
<div class="logo">
<img src="logo.png" alt="LOGO" />
<p>ZERO TYPE</p>
</div>
<nav>
<ul>
<li><span>Home</span></li>
<li>Feature</li>
<li>News</li>
</ul>
</nav>
</header>
/* CSS - 影響分析 */
header {
border: 5px solid black;
background-color: #f2f2f2;
/* content-box: 寬度 = 100% + 10px (border) */
/* border-box: 寬度 = 100% (border 內推) */
}
header .logo {
margin: 15px 80px 30px 150px;
/* margin 不受 box-sizing 影響 */
}
影響:改用 border-box 後,header 內容區會縮小 10px(左右 border 各 5px)。 developer.mozilla
<!-- HTML -->
<footer>
<div class="copyright">© 2023 Zerotype.</div>
<div class="social-media-link">
<ul>
<li><a href="#"><img src="facebook.png" alt="FB"/></a></li>
<li><a href="#"><img src="twitter.png" alt="Twitter"/></a></li>
</ul>
</div>
</footer>
/* CSS - 影響分析 */
footer {
border: 5px solid black; /* 左右共 10px */
padding: 30px; /* 左右共 60px */
background-color: #f2f2f2;
}
/* content-box 下:
實際寬度 = 100% + 60px + 10px = 超出容器!
*/
/* border-box 下:
實際寬度 = 100%
內容區域 = 100% - 60px - 10px
*/
影響:改用 border-box 後,footer 內容區會縮小 70px,需要調整內部元素的 margin。 vocus
<!-- HTML -->
<div class="try-it-btn">
<h2>TRY IT NOW!</h2>
<p>Don't worry it's for free.</p>
</div>
/* CSS */
.try-it-btn h2 {
padding: 20px 30px;
background: linear-gradient(to bottom, #ffb84d 50%, #ff9500 50%);
border-radius: 5px;
/* 沒有設定 width,影響較小 */
}
影響:按鈕內容區會稍微縮小,但因為沒有固定寬度,視覺差異不大。 vocus
<!-- HTML -->
<div class="layout-demo">
<h3>50% + 50% 佈局問題對比</h3>
<h4>❌ Content-box:會跑版</h4>
<div class="row content-box-layout">
<div class="col">左欄 (content-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div><div class="col">右欄 (content-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div>
</div>
<h4>✅ Border-box:完美並排</h4>
<div class="row border-box-layout">
<div class="col">左欄 (border-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div><div class="col">右欄 (border-box)<br>width: 50%<br>padding: 20px<br>border: 5px</div>
</div>
</div>
/* CSS */
.layout-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
background: #f5f5f5;
}
h3 {
margin-top: 0;
color: #333;
}
h4 {
margin: 20px 0 10px;
color: #666;
}
.row {
margin-bottom: 30px;
border: 3px dashed #999;
background: white;
font-size: 0; /* ⭐ 消除 inline-block 的空白字符 */
}
.col {
width: 50%;
padding: 20px;
border: 5px solid #333;
display: inline-block;
vertical-align: top;
font-size: 14px; /* ⭐ 恢復正常字體大小 */
line-height: 1.5;
}
/* Content-box 版本:會爆版! */
.content-box-layout .col {
box-sizing: content-box;
background: #ffebee;
/* 每個欄位實際寬度 = 50% + 40px + 10px */
/* 兩欄加起來 = 100% + 100px > 100% */
}
/* Border-box 版本:完美並排 */
.border-box-layout .col {
box-sizing: border-box;
background: #e8f5e9;
/* 每個欄位實際寬度 = 50% (已包含 padding 和 border) */
/* 兩欄加起來 = 100% */
}
觀察重點:
Content-box 版本的右欄會被擠到下一行
Border-box 版本完美佔據各 50%
⚠️ 重要提醒:inline-block 空白問題
使用 display: inline-block 時,HTML 標籤之間的換行和空白會被視為空白字符,佔據空間,導致元素換行。
解決方法一:父元素設 font-size: 0
.row {
font-size: 0; /* 消除空白 */
}
.col {
font-size: 14px; /* 子元素恢復正常 */
}
解決方法二:HTML 標籤不換行
<!-- ✅ 兩個 div 緊連,不要換行 -->
<div class="col">左欄</div><div class="col">右欄</div>
<!-- ❌ 這樣會產生空白字符 -->
<div class="col">左欄</div>
<div class="col">右欄</div>
替代方案:也可以用 float 來做多欄並排(後續會學習)。
核心概念:透過 CSS 在元素內部插入虛擬內容,不需要修改 HTML。 developer.mozilla
<!-- HTML -->
<div class="demo-container">
<h3>偽元素示範</h3>
<div class="text-with-icon">重要訊息</div>
<div class="quote">
這是一段需要引號的文字內容,展示如何用偽元素自動加上裝飾性引號效果。這個方法可以保持 HTML 簡潔,所有裝飾都由 CSS 處理。
</div>
<ul class="nav-list">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
/* CSS */
.demo-container {
padding: 20px;
max-width: 600px;
margin: 0 auto;
background: #f5f5f5;
}
h3 {
margin-top: 0;
color: #333;
}
/* 範例 1: 在文字前加圖示 */
.text-with-icon {
padding: 15px;
background: #fff3e0;
border-left: 4px solid #ff9800;
margin: 20px 0;
}
.text-with-icon::before {
content: "⚠️ ";
font-size: 1.2em;
}
/* 範例 2: 自動加引號 */
.quote {
position: relative;
padding: 20px 40px;
background: white;
margin: 20px 0;
border-left: 4px solid #2196f3;
line-height: 1.6;
font-style: italic;
color: #555;
}
.quote::before {
content: "\201C"; /* 左彎引號 " (使用 Unicode 編碼) */
position: absolute;
top: -10px; /* 往上移,不蓋住文字 */
left: 10px;
font-size: 3em;
color: #2196f3;
line-height: 1;
opacity: 0.3; /* 半透明,更柔和 */
}
.quote::after {
content: "\201D"; /* 右彎引號 " */
position: absolute;
bottom: -10px; /* 往下移,不蓋住文字 */
right: 10px;
font-size: 3em;
color: #2196f3;
line-height: 1;
opacity: 0.3;
}
/* 範例 3: 導航列分隔符號 */
.nav-list {
list-style: none;
padding: 0;
background: white;
padding: 15px;
border-radius: 4px;
}
.nav-list li {
display: inline-block;
padding: 5px 10px;
}
.nav-list li:not(:last-child)::after {
content: "|";
margin-left: 10px;
color: #999;
}
觀察重點:
所有裝飾都不在 HTML 中
::before 在內容前面,::after 在內容後面
必須有 content 屬性才會顯示 ppl-ai-file-upload.s3.amazonaws
引號使用 Unicode 編碼 \201C 和 \201D 確保瀏覽器相容性
調整 top/bottom 為負值讓引號不蓋住文字
💡 常用 Unicode 引號編碼:
符號 | 名稱 | CSS content 寫法 |
|---|---|---|
" | 左彎雙引號 |
|
" | 右彎雙引號 |
|
' | 左彎單引號 |
|
' | 右彎單引號 |
|
「 | 中文左引號 |
|
」 | 中文右引號 |
|
/* ❌ 不會顯示 */
.element::before {
color: red;
font-size: 2em;
}
/* ✅ 正確寫法 */
.element::before {
content: ""; /* 就算是空的也要寫 */
color: red;
font-size: 2em;
}
/* 結構示意 */
<div class="box">
::before (第一個孩子)
原本的內容
::after (最後一個孩子)
</div>
/* ❌ 這些元素無法使用偽元素 */
img::before { } /* 無效 */
input::after { } /* 無效 */
br::before { } /* 無效 */
/* 因為它們不能有子元素 */
<!-- HTML (簡化版) -->
<header>
<nav>
<ul>
<li><span>Home</span></li>
<li>Feature</li>
<li>News</li>
<li>About</li>
</ul>
</nav>
</header>
/* CSS - 用偽元素加分隔線 */
nav li {
display: inline-block;
margin-right: 100px;
}
/* 優化:用偽元素取代 margin,視覺更清楚 */
nav li:not(:last-child)::after {
content: "·"; /* 或用 "|" */
margin: 0 20px;
color: lightgray;
font-size: 1.2em;
}
/* 現在選中狀態的底線效果 */
nav li span {
color: orange;
position: relative;
}
nav li span::after {
content: "";
display: block;
width: 100%;
height: 2px;
background-color: orange;
margin-top: 5px;
}
特徵 | 偽類 | 偽元素 |
|---|---|---|
作用 | 選擇元素的「特殊狀態」 | 創造「虛擬元素」 |
冒號數量 | 一個 | 兩個 |
是否生成盒子 | 否 | 是 |
需要 content | 不需要 |
|
數量限制 | 可多個 | 一個選擇器一組 |
常見用途 | 互動狀態、結構關係 | 裝飾性內容、特殊格式 |
<!-- HTML -->
<div class="interactive-demo">
<h3>偽類 + 偽元素組合</h3>
<ul class="styled-list">
<li>第一項</li>
<li>第二項</li>
<li>第三項</li>
</ul>
<a href="#" class="fancy-link">帶動畫效果的連結</a>
</div>
/* CSS */
.interactive-demo {
padding: 20px;
max-width: 600px;
margin: 0 auto;
}
/* 範例 1: 偽類選擇結構 */
.styled-list {
list-style: none;
padding: 0;
}
.styled-list li {
padding: 10px;
margin: 5px 0;
background: #f0f0f0;
}
/* 用偽類選擇第一個 */
.styled-list li:first-child {
background: #e3f2fd;
font-weight: bold;
}
/* 用偽元素加裝飾 */
.styled-list li:first-child::before {
content: "👑 ";
}
/* 用偽類選擇懸停狀態 */
.styled-list li:hover {
background: #fff3e0;
cursor: pointer;
}
/* 範例 2: 動態底線效果 */
.fancy-link {
display: inline-block;
padding: 10px 20px;
text-decoration: none;
color: #333;
position: relative;
}
/* 用偽元素創建底線 */
.fancy-link::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: orange;
transition: width 0.3s;
}
/* 用偽類觸發動畫 */
.fancy-link:hover::after {
width: 100%;
}
觀察重點:
偽類 :first-child 選擇「第一個元素」(狀態)
偽元素 ::before 插入圖示(新內容)
偽類 :hover 觸發「懸停狀態」
偽元素 ::after 創造底線(新元素)
可以組合使用:li:hover::before
// 如果沒有偽類,需要這樣寫 JS
button.addEventListener('mouseenter', () => {
button.classList.add('hovered');
});
button.addEventListener('mouseleave', () => {
button.classList.remove('hovered');
});
// 有了 :hover 偽類,CSS 自動處理
button:hover {
background: orange;
}
<!-- 沒有偽元素:HTML 很亂 -->
<button>
<span class="icon">★</span>
<span class="text">按鈕</span>
</button>
<!-- 有了偽元素:HTML 簡潔 -->
<button>按鈕</button>
button::before {
content: "★ ";
}
偽元素會生成「真實的盒子」,所以需要設定 box-sizing。 forum.freecodecamp 偽類只是選擇器,不生成盒子,不需要設定。
*, /* 選擇所有真實 HTML 元素 */
*::before, /* 選擇所有 ::before 偽元素 */
*::after { /* 選擇所有 ::after 偽元素 */
box-sizing: border-box;
}
因為 * 只會選到真實的 HTML 元素,不會選到偽元素。 stackoverflow
<!-- HTML -->
<div class="comparison">
<h3>偽元素 box-sizing 對比</h3>
<div class="card without-reset">
<h4>沒設定偽元素的 box-sizing</h4>
<p>這是卡片內容</p>
</div>
<div class="card with-reset">
<h4>有設定偽元素的 box-sizing</h4>
<p>這是卡片內容</p>
</div>
</div>
/* CSS */
.comparison {
padding: 20px;
}
.card {
width: 300px;
padding: 20px;
border: 2px solid #333;
margin: 20px 0;
position: relative;
background: white;
}
/* 兩張卡片都用 border-box */
.card {
box-sizing: border-box;
}
/* 共用的偽元素樣式 */
.card::before {
content: "標籤";
position: absolute;
top: -15px;
left: 20px;
width: 60px;
padding: 5px 10px;
border: 2px solid orange;
background: white;
font-size: 12px;
text-align: center;
}
/* 第一張卡片:偽元素用預設 content-box */
.without-reset::before {
box-sizing: content-box;
/* 實際寬度 = 60 + 20 + 4 = 84px */
}
/* 第二張卡片:偽元素也用 border-box */
.with-reset::before {
box-sizing: border-box;
/* 實際寬度 = 60px */
}
/* 視覺化寬度差異 */
.card::before {
outline: 2px dashed red;
outline-offset: 2px;
}
觀察重點:
第一張卡片的標籤明顯比較寬
第二張卡片的標籤寬度就是設定的 60px
如果不統一 box-sizing,版面計算會混亂 stackoverflow
/* ❌ 偽類不用設定 */
a:hover {
/* :hover 只是選擇「懸停狀態的 a 元素」
這個 a 元素本身已經有 box-sizing */
}
/* ✅ 偽元素需要設定 */
a::before {
/* ::before 生成了新的盒子
需要明確設定 box-sizing */
}
偽類只是「選擇器的條件」,不生成盒子
偽元素會生成「新的盒子」,需要設定 forum.freecodecamp
如果現在改變預設值,全世界數百萬個舊網站都會瞬間跑版。 reddit
<!-- HTML -->
<div class="simple-demo">
<h3>為什麼不能改預設值?</h3>
<p class="intro">
2005 年的網站設計師寫了這段 CSS:
</p>
<div class="code-block">
.sidebar {<br>
width: 300px;<br>
padding: 20px;<br>
border: 5px solid black;<br>
}
</div>
<div class="comparison">
<div class="col">
<h4>✅ Content-box (當年的預設)</h4>
<div class="visual-box content-box-visual">
<div class="content-area">內容區<br>300px</div>
<div class="padding-area">padding 40px</div>
<div class="border-area">border 10px</div>
</div>
<p class="total">總寬度 = <strong>350px</strong></p>
</div>
<div class="col">
<h4>❌ 如果改成 Border-box</h4>
<div class="visual-box border-box-visual">
<div class="content-area small">內容區<br>250px<br>(被壓縮)</div>
<div class="padding-area">padding 40px</div>
<div class="border-area">border 10px</div>
</div>
<p class="total">總寬度 = <strong>300px</strong></p>
</div>
</div>
<div class="note">
<strong>結果:</strong>同樣的 CSS,元素從 350px 變成 300px,所有基於「350px」的版面計算都會錯位。
這就是為什麼瀏覽器無法改變預設值——會破壞全球數百萬個舊網站。
</div>
</div>
/* CSS */
.simple-demo {
padding: 20px;
max-width: 900px;
margin: 0 auto;
background: #f5f5f5;
}
h3 {
margin-top: 0;
color: #333;
text-align: center;
}
h4 {
margin: 10px 0;
color: #666;
text-align: center;
}
.intro {
background: #e3f2fd;
padding: 15px;
border-left: 4px solid #2196f3;
line-height: 1.8;
margin-bottom: 20px;
text-align: center;
}
.code-block {
background: #263238;
color: #aed581;
padding: 15px;
border-radius: 4px;
font-family: monospace;
margin-bottom: 30px;
line-height: 1.6;
}
.comparison {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
.col {
flex: 1;
}
.visual-box {
border: 3px solid #333;
margin: 20px 0;
padding: 5px;
background: white;
}
.content-box-visual {
width: 350px;
}
.border-box-visual {
width: 300px;
}
.content-area {
background: #e8f5e9;
padding: 40px 20px;
text-align: center;
font-weight: bold;
border: 5px solid #4caf50;
}
.content-area.small {
background: #ffebee;
border-color: #f44336;
}
.padding-area,
.border-area {
text-align: center;
padding: 5px;
font-size: 12px;
color: #666;
}
.total {
text-align: center;
font-size: 18px;
background: #fff9c4;
padding: 10px;
margin: 10px 0;
border-radius: 4px;
}
.total strong {
color: #1976d2;
font-size: 24px;
}
.note {
padding: 20px;
background: #fff9c4;
border: 3px solid #fbc02d;
border-radius: 4px;
line-height: 1.8;
}
.note strong {
color: #f57c00;
}
觀察重點:
左側 (content-box):側邊欄實際佔據 350px,這是設計師當年的預期
右側 (border-box):側邊欄實際佔據 300px,內容區被壓縮 50px
同樣的 CSS 代碼,在不同 box-sizing 下,元素佔據的空間完全不同
想像一下這個情境: reddit
/* 2005 年寫的舊網站,沒有設定 box-sizing */
.sidebar {
width: 300px;
padding: 20px;
border: 5px solid black;
/* 作者預期:實際寬度 = 300 + 40 + 10 = 350px */
}
.content {
width: 650px; /* 剛好跟 sidebar 加起來是 1000px */
}
如果今天瀏覽器把預設值改成 border-box: reddit
.sidebar 實際寬度變成 300px(不是 350px)
原本設計好的版面全部跑掉
全球數百萬個沒在維護的舊網站集體崩潰
這些地方還在使用 20 年前的網頁系統: reddit
政府機關網站
醫療機構系統
大型企業內部工具
教育機構平台
銀行系統介面
它們可能永遠不會更新,但必須繼續運作。
早期有兩套盒模型標準: blog.csdn
Internet Explorer 5 (1999年): reddit
width = content + padding + border
(類似現在的 border-box)
W3C CSS 標準 (2000年): developer.mozilla
width = content only
(就是現在的 content-box)
當時 W3C 認為「寬度應該只指內容」符合邏輯,但實務上開發者發現 IE 的做法更直覺。 reddit
CSS 工作小組後來也公開承認「這是 CSS 的設計錯誤」。 reddit
Tim Berners-Lee (Web 發明者) 的原則: reddit
「1990 年的網頁,在 2026 年的瀏覽器中應該還能正常顯示」
這是 Web 能成功的關鍵原因之一 — 內容可以跨越時空存在。
不像程式語言有版本宣告:
// JavaScript 可以這樣
"use strict"; // 啟用嚴格模式
CSS 沒有類似機制能說「這個網頁用 CSS v2 標準」或「用 CSS v4 標準」。 reddit
如果改預設值,需要: reddit
所有瀏覽器廠商同時更新
全球開發者修改數百萬個網站
測試確保沒有任何舊網站被破壞
處理各種邊緣案例
這個成本遠超過「讓開發者自己加三行 CSS」。
有趣的是,某些 HTML 元素的預設值其實是 border-box: stackoverflow
/* 瀏覽器內建樣式 */
button, input[type="submit"], input[type="reset"] {
box-sizing: border-box; /* 預設就是 border-box! */
}
原因: stackoverflow
這些元素是後來才加入 HTML 的
設計時已經知道 border-box 比較好
不會破壞既有網站(因為是新元素)
這證明了「如果重新設計,大家都會選 border-box」。
既然標準無法改,社群發展出「重置樣式」的做法: freecodecamp
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit; /* 繼承父元素的設定 */
}
好處:可以在特定區域改回 content-box。 freecodecamp
現代框架都內建 border-box:
Bootstrap
Tailwind CSS
Normalize.css
typeof null // "object" (錯誤!)
這是 JavaScript 設計錯誤,但無法修正,因為會破壞全球數百萬個網站。 reddit
HTTP 418: I'm a teapot
原本是愚人節玩笑,但被寫入標準後就無法移除,因為有人真的在用。 reddit
只要元素會產生盒子並參與版面佈局,就需要統一計算方式。 dev
/* 混用兩種 box-sizing */
.left-column {
width: 50%;
padding: 20px;
border: 5px solid black;
box-sizing: content-box;
/* 實際寬度 = 50% + 40px + 10px → 爆版! */
}
.right-column {
width: 50%;
padding: 20px;
border: 5px solid black;
box-sizing: border-box;
/* 實際寬度 = 50% (已包含) → 正常 */
}
/* 結果:兩欄加起來超過 100%,右欄被擠下去 */
*,
*::before,
*::after {
box-sizing: border-box;
}
.left-column,
.right-column {
width: 50%;
padding: 20px;
border: 5px solid black;
/* 兩欄各佔 50%,完美並排 */
}
直覺化計算: crypist
設定 width: 300px,元素在版面上就佔 300px
響應式設計更容易: dev
.col {
width: 25%; /* 不用擔心 padding/border 爆版 */
padding: 15px;
border: 2px solid gray;
}
維護性提升: crypist
修改 padding 或 border 時不需要重新計算 width
/* 專案第一行 CSS */
*,
*::before,
*::after {
box-sizing: border-box;
}
評估風險後再決定是否加上:
測試所有頁面
檢查是否有地方依賴 content-box 行為
漸進式重構,不要一次全改
/* 在 style.css 最前面加上 */
*,
*::before,
*::after {
box-sizing: border-box;
}
需要檢查這些區域:
Header:
檢查內容是否太擠
logo 位置是否正常
Footer:
檢查 padding 是否需要調整
社交媒體連結排列是否正常
Main 區域:
.hero-intro 和 .hero-img 的並排
Article 的雙欄排版
/* 導航列加分隔線 */
header nav li:not(:last-child)::after {
content: "·";
margin: 0 15px;
color: #ccc;
}
/* TRY IT NOW 按鈕加箭頭 */
.try-it-btn h2::after {
content: " →";
transition: transform 0.3s;
}
.try-it-btn h2:hover::after {
transform: translateX(5px);
}
/* Footer 上方加裝飾線 */
footer::before {
content: "";
display: block;
width: 80%;
height: 2px;
background: linear-gradient(
to right,
transparent,
#ff9500,
transparent
);
margin: 0 auto 20px;
}
記住:
Border-box 讓寬度計算更直覺
現代專案一律使用 border-box
改動後要測試所有版面
易錯:
忘記 border-box 會壓縮 content 區域
只設定 *,忘記偽元素
混用兩種 box-sizing
記住:
兩個冒號 ::
必須有 content 屬性
在元素內部,不是外部
適合裝飾性內容
引號用 Unicode 編碼 \201C \201D
易錯:
忘記 content: "";
用在空元素(<img>, <input>)
誤以為在元素外面
引號位置蓋住文字
記住:
一個冒號 :
選擇元素的狀態或結構關係
不生成盒子
易錯:
混淆冒號數量
對偽類設定 box-sizing
記住:
*,
*::before,
*::after {
box-sizing: border-box;
}
❌ 易錯:
只寫 *
忘記偽元素
這個案例告訴我們: reddit
標準制定要謹慎:一旦發佈就很難改
向後兼容很重要:比「技術上正確」更重要
開發者可以自救:透過 Reset CSS 解決標準問題
歷史會評價設計:20 年後才發現當初的決策是錯的
CSS Working Group 現在每次制定新標準都會討論「這會不會成為下一個 box-sizing」。 reddit
[box-sizing] developer.mozilla
[Pseudo-elements] developer.mozilla
[Pseudo-classes] developer.mozilla
[Box Model] developer.mozilla
我會在這裡貼上自己的實驗連結:
[ ] Box Model 對比實驗 https://codepen.io/gcake119/pen/YPWRJZJ
[ ] 偽元素應用範例 https://codepen.io/gcake119/pen/EayOMje
[ ] 偽元素的 box-sizing 問題 https://codepen.io/gcake119/pen/gbMQERM
[ ] 偽分類與偽元素結合 https://codepen.io/gcake119/pen/xbOQBqB
[ ] 歷史包袱視覺化示範 https://codepen.io/gcake119/pen/YPWRgOq
標準不等於最佳實踐
Content-box 是標準,但 border-box 更實用
要理解背後的歷史脈絡
向後兼容很重要
Web 的成功建立在「永不破壞舊內容」
這也是為什麼有些設計無法改變
社群可以自救
雖然標準有問題,社群透過 CSS Reset 解決
現代專案幾乎都採用 border-box
偽元素是強大工具
保持 HTML 簡潔
裝飾性內容用 CSS 處理
減少無意義的標籤
實作中學習最有效
CodePen 實驗幫助理解抽象概念
親手重現問題才能真正掌握
[ ] 將 border-box 加入我的作業
[ ] 測試所有版面是否正常
[ ] 用偽元素優化導航列和按鈕
[ ] 在 CodePen 建立實驗範例
[ ] 整理成技術筆記並分享
筆記日期: 2026-02-10
學習情境: 對照結訓學員程式碼發現的問題
主題標籤: #CSS #BoxModel #Pseudo-elements #Pseudo-classes #BestPractice
CodePen 實驗: 連結如上文
Share Dialog
Share Dialog
No activity yet