🎨 CSS 樣式與排版
📌 什麼是 CSS?
比喻:CSS 就像房子的裝潢設計 🎨
如果 HTML 是房子的骨架(鋼筋水泥),那 CSS 就是室內設計師。 CSS 決定了牆壁要什麼顏色、家具怎麼擺放、窗簾用什麼材質。 同一棟房子(HTML),換一套裝潢(CSS)就能變成完全不同的風格。
CSS(Cascading Style Sheets)用來控制 HTML 元素的外觀和排版方式。
📌 CSS 選擇器(Selectors)
選擇器決定了「你要裝潢哪個房間」。
/* 元素選擇器:選中所有 p 標籤 */
p {
color: #333; /* 設定文字顏色為深灰色 */
line-height: 1.6; /* 設定行高為字體大小的 1.6 倍 */
}
/* Class 選擇器:用 . 開頭,可重複使用在多個元素上 */
.highlight {
background-color: yellow; /* 設定背景顏色為黃色 */
padding: 4px 8px; /* 設定內距:上下 4px,左右 8px */
}
/* ID 選擇器:用 # 開頭,一個頁面中每個 ID 只能出現一次 */
#main-title {
font-size: 2rem; /* 設定字體大小為 2rem(相對於根元素) */
font-weight: bold; /* 設定字體粗細為粗體 */
}
/* 後代選擇器:選中 nav 裡面所有的 a 標籤 */
nav a {
text-decoration: none; /* 移除底線 */
color: #0066cc; /* 設定連結顏色為藍色 */
}
/* 子元素選擇器:只選中 ul 的「直接」子元素 li */
ul > li {
list-style: disc; /* 設定列表樣式為實心圓點 */
margin-bottom: 8px; /* 設定下方外距為 8px */
}
/* 相鄰兄弟選擇器:選中緊跟在 h2 後面的第一個 p */
h2 + p {
font-size: 1.1rem; /* 第一段比其他段落稍大 */
color: #555; /* 設定為較淺的灰色 */
}
/* 偽類選擇器:滑鼠懸停時的樣式 */
a:hover {
color: #ff6600; /* 懸停時連結變成橘色 */
text-decoration: underline; /* 懸停時顯示底線 */
}
/* 偽類選擇器:聚焦時的樣式(鍵盤 Tab 或點擊輸入框) */
input:focus {
border-color: #0066cc; /* 聚焦時邊框變藍色 */
outline: 2px solid rgba(0, 102, 204, 0.3); /* 加上半透明的外框 */
}
/* 偽元素選擇器:在元素「之前」插入內容 */
.required::before {
content: "* "; /* 在必填欄位前面加上星號 */
color: red; /* 星號顯示為紅色 */
}
/* 屬性選擇器:選中所有 type="email" 的 input */
input[type="email"] {
width: 100%; /* 寬度佔滿父容器 */
padding: 8px; /* 內距 8px */
}
/* nth-child 選擇器:選中奇數列(表格斑馬紋) */
tr:nth-child(odd) {
background-color: #f9f9f9; /* 奇數列背景設為淺灰色 */
}
📌 Box Model(盒模型)
每個 HTML 元素都是一個「盒子」,就像每個房間都有牆壁、地板、傢俱的空間配置。
/* Box Model 完整示範 */
.card {
/* Content:內容區域,放文字和圖片的空間 */
width: 300px; /* 設定內容寬度為 300px */
height: auto; /* 高度自動根據內容調整 */
/* Padding:內距,內容和邊框之間的空間(像房間裡牆壁到傢俱的距離) */
padding: 20px; /* 四個方向都是 20px */
padding-top: 10px; /* 也可以單獨設定某一邊的內距 */
/* Border:邊框,盒子的外框(像房間的牆壁) */
border: 1px solid #ddd; /* 1px 寬、實線、淺灰色邊框 */
border-radius: 8px; /* 設定圓角半徑為 8px */
/* Margin:外距,盒子和其他盒子之間的空間(像房間和房間之間的走廊) */
margin: 16px; /* 四個方向都是 16px */
margin-bottom: 24px; /* 下方外距設大一點 */
/* box-sizing 很重要! */
box-sizing: border-box; /* 讓 width 包含 padding 和 border,計算更直覺 */
}
/* 建議全域設定 box-sizing */
*, *::before, *::after {
box-sizing: border-box; /* 所有元素都用 border-box,避免計算寬度時出錯 */
}
📌 Flexbox 完整指南
Flexbox 就像一條彈性的置物架,可以自動調整裡面物品的排列方式。
/* === 容器屬性(Container Properties) === */
.flex-container {
display: flex; /* 啟用 Flexbox 排版模式 */
flex-direction: row; /* 主軸方向:row(水平)| column(垂直) */
flex-wrap: wrap; /* 允許子元素換行(預設 nowrap 不換行) */
justify-content: space-between; /* 主軸對齊:均勻分布,頭尾貼邊 */
align-items: center; /* 交叉軸對齊:垂直置中 */
gap: 16px; /* 子元素之間的間距 */
}
/* justify-content 常用值 */
.demo-justify {
justify-content: flex-start; /* 靠主軸起點(預設值) */
justify-content: flex-end; /* 靠主軸終點 */
justify-content: center; /* 主軸置中 */
justify-content: space-between; /* 均勻分布,第一和最後一個貼邊 */
justify-content: space-around; /* 均勻分布,兩側有半個間距 */
justify-content: space-evenly; /* 完全均勻分布 */
}
/* align-items 常用值 */
.demo-align {
align-items: stretch; /* 拉伸填滿交叉軸(預設值) */
align-items: flex-start; /* 靠交叉軸起點(頂部) */
align-items: flex-end; /* 靠交叉軸終點(底部) */
align-items: center; /* 交叉軸置中 */
align-items: baseline; /* 以文字基線對齊 */
}
/* === 子元素屬性(Item Properties) === */
.flex-item {
flex-grow: 1; /* 允許元素放大,數字越大佔越多空間 */
flex-shrink: 0; /* 不允許元素縮小(0 表示不縮小) */
flex-basis: 200px; /* 元素的初始大小 */
flex: 1 0 200px; /* 簡寫:grow shrink basis */
align-self: flex-end; /* 覆蓋容器的 align-items,單獨設定對齊 */
order: 2; /* 改變排列順序,數字越小越前面(預設 0) */
}
/* 實用範例:導覽列 */
.navbar {
display: flex; /* 啟用 Flex 排版 */
justify-content: space-between; /* Logo 和選單分散在兩端 */
align-items: center; /* 垂直置中 */
padding: 0 24px; /* 左右內距 24px */
background-color: #1a1a2e; /* 深色背景 */
height: 60px; /* 固定高度 60px */
}
/* 實用範例:卡片網格 */
.card-grid {
display: flex; /* 啟用 Flex 排版 */
flex-wrap: wrap; /* 允許卡片換行 */
gap: 20px; /* 卡片之間的間距 */
}
.card-grid .card {
flex: 1 1 300px; /* 可放大、可縮小、基礎寬度 300px */
max-width: 400px; /* 限制最大寬度 */
}
/* 實用範例:垂直水平完美置中 */
.center-content {
display: flex; /* 啟用 Flex 排版 */
justify-content: center; /* 水平置中 */
align-items: center; /* 垂直置中 */
min-height: 100vh; /* 最小高度佔滿整個視窗 */
}
📌 CSS Grid 基礎
/* Grid 容器設定 */
.grid-container {
display: grid; /* 啟用 Grid 排版模式 */
grid-template-columns: repeat(3, 1fr); /* 建立 3 欄,每欄等寬(1fr = 1 份) */
grid-template-rows: auto; /* 列高自動根據內容調整 */
gap: 20px; /* 網格之間的間距 */
padding: 20px; /* 容器內距 */
}
/* 讓某個子元素跨多欄 */
.full-width {
grid-column: 1 / -1; /* 從第 1 條線到最後一條線,佔滿所有欄 */
}
/* 讓某個子元素佔兩欄 */
.span-two {
grid-column: span 2; /* 跨越 2 個欄位 */
}
/* 經典聖杯佈局 */
.holy-grail {
display: grid; /* 啟用 Grid */
grid-template-areas: /* 用命名區域定義版面 */
"header header header" /* 第一列:header 橫跨全部 */
"nav main aside" /* 第二列:左側導覽、中間內容、右側邊欄 */
"footer footer footer"; /* 第三列:footer 橫跨全部 */
grid-template-columns: 200px 1fr 200px; /* 三欄:左 200px、中間彈性、右 200px */
grid-template-rows: 60px 1fr 40px; /* 三列:頂 60px、中間彈性、底 40px */
min-height: 100vh; /* 最小高度佔滿視窗 */
}
/* 指定子元素到對應區域 */
.holy-grail header { grid-area: header; } /* header 放到 header 區域 */
.holy-grail nav { grid-area: nav; } /* nav 放到 nav 區域 */
.holy-grail main { grid-area: main; } /* main 放到 main 區域 */
.holy-grail aside { grid-area: aside; } /* aside 放到 aside 區域 */
.holy-grail footer { grid-area: footer; } /* footer 放到 footer 區域 */
📌 響應式設計(Responsive Design)
/* 手機優先(Mobile First):先寫手機樣式,再用 media query 擴展 */
.container {
width: 100%; /* 手機上佔滿寬度 */
padding: 0 16px; /* 左右留 16px 的呼吸空間 */
}
/* 平板以上(768px 以上)*/
@media (min-width: 768px) { /* 當螢幕寬度 >= 768px 時套用 */
.container {
max-width: 720px; /* 限制最大寬度 */
margin: 0 auto; /* 水平置中 */
}
.grid-container {
grid-template-columns: repeat(2, 1fr); /* 平板上顯示 2 欄 */
}
}
/* 桌機以上(1024px 以上)*/
@media (min-width: 1024px) { /* 當螢幕寬度 >= 1024px 時套用 */
.container {
max-width: 960px; /* 桌機最大寬度 960px */
}
.grid-container {
grid-template-columns: repeat(3, 1fr); /* 桌機上顯示 3 欄 */
}
}
/* rem 和 em 的差別 */
html {
font-size: 16px; /* 根元素字體大小(1rem = 16px) */
}
.title {
font-size: 2rem; /* 2 × 16px = 32px,基於根元素計算 */
margin-bottom: 1rem; /* 1 × 16px = 16px */
}
.subtitle {
font-size: 1.5em; /* 1.5 × 父元素字體大小,基於父元素計算 */
}
📌 CSS 變數(Custom Properties)
/* 在 :root 定義全域變數(像是設計系統的調色盤) */
:root {
--primary-color: #0066cc; /* 主色:藍色 */
--secondary-color: #ff6600; /* 輔助色:橘色 */
--text-color: #333333; /* 文字顏色:深灰 */
--bg-color: #ffffff; /* 背景顏色:白色 */
--spacing-sm: 8px; /* 小間距 */
--spacing-md: 16px; /* 中間距 */
--spacing-lg: 24px; /* 大間距 */
--border-radius: 8px; /* 統一圓角大小 */
--font-family: 'Noto Sans TC', sans-serif; /* 設定字體為思源黑體 */
}
/* 使用 var() 引用變數 */
.button {
background-color: var(--primary-color); /* 使用主色作為按鈕背景 */
color: white; /* 按鈕文字為白色 */
padding: var(--spacing-sm) var(--spacing-md); /* 使用間距變數 */
border: none; /* 移除邊框 */
border-radius: var(--border-radius); /* 使用統一圓角 */
font-family: var(--font-family); /* 使用統一字體 */
cursor: pointer; /* 滑鼠游標變成手指 */
}
/* 深色模式:只需要修改變數值 */
@media (prefers-color-scheme: dark) { /* 偵測系統深色模式 */
:root {
--text-color: #e0e0e0; /* 深色模式:文字改為淺灰 */
--bg-color: #1a1a1a; /* 深色模式:背景改為深色 */
}
}
🤔 我這樣寫為什麼會錯?
❌ 錯誤 1:忘記設定 box-sizing 導致寬度計算錯誤
/* 錯誤:width 300px + padding 20px×2 + border 1px×2 = 實際佔 342px */
.card {
width: 300px; /* 以為寬度是 300px */
padding: 20px; /* 但 padding 會額外增加寬度 */
border: 1px solid #ccc; /* border 也會額外增加寬度 */
}
/* 正確:加上 box-sizing 讓 width 包含 padding 和 border */
.card {
width: 300px; /* 設定寬度為 300px */
padding: 20px; /* padding 包含在 300px 裡面 */
border: 1px solid #ccc; /* border 也包含在 300px 裡面 */
box-sizing: border-box; /* 重點!讓 300px 是最終寬度 */
}
❌ 錯誤 2:margin 塌陷(Margin Collapse)
/* 錯誤:以為上下兩個元素的間距是 40px + 30px = 70px */
.box-a {
margin-bottom: 40px; /* 下方外距 40px */
}
.box-b {
margin-top: 30px; /* 上方外距 30px */
}
/* 實際間距只有 40px(取較大值),這就是 margin 塌陷 */
/* 解法:只在一個方向設定 margin */
.box-a {
margin-bottom: 40px; /* 統一只用 margin-bottom */
}
.box-b {
margin-top: 0; /* 不設定 margin-top,避免塌陷困擾 */
}
❌ 錯誤 3:Flexbox 子元素不換行導致擠壓
/* 錯誤:沒有設定 flex-wrap,子元素擠在一行被壓扁 */
.container {
display: flex; /* 啟用 Flex 但預設不換行 */
}
.item {
width: 300px; /* 設定了寬度,但會被壓縮 */
}
/* 正確:設定 flex-wrap 允許換行 */
.container {
display: flex; /* 啟用 Flex 排版 */
flex-wrap: wrap; /* 重點!允許子元素換行 */
gap: 16px; /* 設定間距 */
}
.item {
flex: 1 1 300px; /* 可放大、可縮小、基礎寬度 300px */
}