CSS 入門全攻略:從基本語法到響應式設計
CSS 入門教學,從基本語法到 RWD 響應式設計一次搞懂。帶你拆解層疊、盒模型、回應式三大核心,學會 Flexbox 與 Grid 排版,新手也能獨立調出穩定版面。
作者:褚崇名(Sliven)
本頁目錄
- CSS 到底在做什麼:把「內容」和「長相」徹底分家的那一層
- 三種把 CSS 寫進網頁的方式,以及我為什麼只推薦一種
- 選擇器與層疊:CSS 真正的權力遊戲
- CSS 架構與命名系統:從「能動」到「可維護」的關鍵
- Box Model:所有排版的地基,也是新手最常跌倒的地方
- 文字與色彩:決定網站氣質的兩個變數
- 單位系統:px、em、rem、% 與視窗單位各自該出現的場合
- 現代 CSS 功能::has()、容器查詢與 Subgrid 的革命
- 現代排版的雙主菜:Flexbox 與 Grid 該怎麼選
- 元件設計模式:從按鈕到卡片的系統化
- 響應式設計:不是「再做一個手機版」,是用同一份 CSS 適應所有螢幕
- 過場與互動:用 transition 與 transform 給網站加上呼吸感
- 進階動畫系統:從 transition 到 keyframes 的完整動態設計
- 變數、函式與預處理器:讓 CSS 從「樣式表」升級成「系統」
- CSS-in-JS 與框架選擇:何時該用工具、何時該用原生 CSS
- CSS 與 SEO 的隱形連結:速度、行動裝置與 Core Web Vitals
- CSS Debug 與 DevTools:從猜測走向精準的偵查
- 跨瀏覽策略與 @supports:優雅降級 vs 漸進增強
- 列印 CSS 與多媒體查詢:不只螢幕,還有紙張和投影
- 無障礙深化:從 :focus 到 ARIA 的完整可訪性設計
- 新手最常踩的七個地雷
- 一份給新手的三十天實戰路線圖
- 結語:CSS 是用腦袋寫的,不是用眼睛調的
想像一下你打開一個剛上線的網站,結果畫面是一整片擠在左上角的純文字、圖片大到把螢幕撐破、連一行話都斷在奇怪的地方。這不是工程師在整人,這就是「沒有 CSS」的網站真實長相。HTML 負責把內容放上去,CSS(Cascading Style Sheets,層疊樣式表)才是決定這些內容長什麼樣子、排在哪裡、在手機上會不會跑樣的那一層。這篇我要從最根本的觀念講起,一路帶到你能在實際專案裡寫出可以維護、對 SEO 又友善的樣式。
先把答案講在前面:CSS 入門的關鍵,換句話說,就三件事,層疊(cascade)、盒模型(box model)、回應式(responsive)。背幾百個屬性幫不了你太多,這三個觀念通了,你之後看到任何陌生的 CSS 屬性都查得出來、改得動、不會把整頁弄壞。屬性會過時、框架會換名字,這三個地基卻相對穩定。接著順著這三條主線,把該學的觀念和該練的手感一次串起來。
CSS 到底在做什麼:把「內容」和「長相」徹底分家的那一層
很多人以為 CSS 就是「讓網頁變漂亮」的工具,這個說法沒錯但太淺。CSS 真正在做的事,是把文件的結構和它的視覺表現拆開。HTML 純粹描述內容的層級與語意(這是一段文章、這是標題、這是清單),CSS 則獨立決定這些結構要用什麼字、多大、什麼顏色、排在畫面哪個位置。
這種拆分帶來的好處非常實際。同一份 HTML,你只要換一份 CSS,就能讓網站從清爽的極簡風變成高對比的賽博龐克風,內容一字不改。反過來說,當內容和樣式混在一起(例如把樣式直接散寫進 HTML 標籤),你要改一個全站的按鈕顏色,就可能得逐一修改許多檔案。這也是為什麼「結構與樣式分離」會被當成現代網頁設計的重要原則,它不是美學問題,是工程紀律問題。
換個方式想:HTML 是房子的鋼骨和隔間,CSS 是裝潢。你不會把水管直接焊在牆面的磁磚裡,因為哪天要修水管就得敲掉整面牆。CSS 之於 HTML,就是那層可以獨立替換、不該和結構長在一起的裝潢層。
三種把 CSS 寫進網頁的方式,以及我為什麼只推薦一種
CSS 可以用三種方式套到 HTML 上,理解它們的差異,比背任何屬性都重要,因為這直接決定你的專案好不好維護。
| 方式 | 寫法 | 適用情境 | 致命缺點 |
|---|---|---|---|
| 行內樣式(Inline) | style="color:red;" 直接寫在標籤上 | 測試、一次性的微小覆蓋 | 無法獨立快取或重用,且一般作者樣式不易覆蓋 |
| 內部樣式(Internal) | 寫在 <head> 的 <style> 裡 | 單一頁面需要特殊樣式 | 每頁都要重傳一份、無法跨頁共用 |
| 外部樣式(External) | 獨立的 .css 檔,用 <link> 引入 | 正式專案的標準做法 | 需要多一個請求(可被快取抵銷) |
只要看到整頁充斥 style="" 的程式碼,通常就能判斷這個專案前面的開發缺乏一致的樣式規範。把散落在各標籤裡的行內樣式抽進外部檔案,常常很費工,而且抽出來之後還會發現同一個顏色被重複硬寫。這就是行內樣式最麻煩的地方:它方便到會讓人偷懶,然後維護代價會在專案擴大後浮現。
所以我的建議很明確:一般多頁網站的共用樣式優先放在外部 .css 檔。行內樣式只在臨時測試或真的沒辦法改外部檔時用,用完就刪。內部樣式則適合單頁特有樣式、元件封裝或 critical CSS 等情境。
選擇器與層疊:CSS 真正的權力遊戲
很多新手以為 CSS 就是「指定某個東西、給它樣式」,這個理解只對了一半。CSS 的全名叫做層疊樣式表,那個「層疊」才是核心。當多個宣告競爭同一個屬性時,瀏覽器會依來源與重要性、cascade layer、優先級(specificity)、作用域接近程度與出現順序等規則,決定最後採用哪一條。優先級只是層疊演算法其中一步(見 MDN 的 Specificity 文件)。
選擇器是「你怎麼指到那個元素」的語法,常見的有這幾種:
- 標籤選擇器:
p選到所有段落、h2選到所有二級標題。 - 類別選擇器:
.button選到所有帶有這個 class 的元素,這是實務上最常用的一種。 - ID 選擇器:
#header選到那個唯一 ID,權重高但很少用,因為 ID 不能重複。 - 屬性選擇器:
[type="text"]選到符合某屬性的元素。 - 偽類與偽元素:
:hover是指標移到元素上的狀態、::before會建立該元素的第一個子偽元素。 - 組合選擇器:
.nav > li選的是 .nav 的「直接子層」li,空白和 > 的差別是新手最容易搞混的地方。
選擇器只是入場券,真正的重頭戲是優先級。優先級以 ID、類別/屬性/偽類、標籤/偽元素等欄位逐欄比較,不是把它們換算成可任意相加的十進位分數。在來源、重要性和 cascade layer 等條件相同時,ID 欄位高於類別欄位,類別欄位又高於標籤欄位。所以 #main .title(一個 ID 加一個類別)的優先級高於 .title(單一類別)。「為什麼我改了卻沒生效」常和層疊或優先級有關,但也可能是語法、載入順序或選擇器沒有匹配。
那如果前面的層疊條件和優先級都一樣呢?作用域接近程度也相同時,才是後寫的贏。一般的行內 style="" 會優先於同一作者來源中的一般樣式表宣告;同一作者來源的樣式表 !important 可以蓋過一般行內樣式,但行內的 !important 又有更高優先順序。這也是我前面叫你少用行內樣式的原因,它會提高覆蓋與維護的難度。
一句話記住:CSS 不是「先寫先贏」;先比較來源、重要性與層疊層,再比較優先級,前面條件都相同才輪到出現順序。
CSS 架構與命名系統:從「能動」到「可維護」的關鍵
理解選擇器和優先級之後,下一步是思考「怎麼給元素取名字」。小專案還好,但網站頁面和元件一多,命名的混亂就會成為維護噩夢。今天你給一個區塊取名 .box,下週另一個工程師做了另一個區塊也叫 .box,兩個樣式互相打架,然後你們開始在選擇器前面加父層類別來提升權重,最後整個樣式表變成一串又臭又長的巢狀選擇器,沒人讀得懂。
這個問題有成熟的解法:建立一套命名規範。實務上最常見的是 BEM(Block Element Modifier),它把每個元件拆成三個層次:
- Block(區塊):獨立、可重用的元件,例如
.card、.button。 - Element(元素):區塊內部的組成部分,用
__(雙底線)分隔,例如.card__title、.card__body。 - Modifier(修飾符):區塊或元素的變化,用
--(雙破折號)分隔,例如.button--primary、.button--large。
這套命名的好處是看名字就知道用途和關係。.card__title 一看就知道是卡片元件的標題部分,.button--primary 是按鈕的主要版本。BEM 通常使用單一 class 選擇器,避免讓樣式依賴深層 DOM 結構;你後續要覆蓋樣式直接寫那個類別就好,不用去猜它在哪個父層底下。
除了 BEM,還有其他做法像 OOCSS(Object-Oriented CSS)和 SMACSS,但核心觀念都一樣:用一致的規則管理 CSS,而不是留下一坨散亂的樣式。實務上,專案一開始就該定好命名規則,讓整個團隊遵守,後續維護會輕鬆很多。
元件化的思維也延伸到檔案結構。中型以上的專案,建議按元件拆檔,例如 components/button.css、components/card.css,再用打包工具組合。這樣你找樣式時直接到對應的元件檔,不用在一個幾千行的 style.css 裡翻半天。檔案結構本身,就是可維護性的關鍵。
Box Model:所有排版的地基,也是新手最常跌倒的地方
如果你想深入理解盒模型,我也寫了一篇完整的圖解,把 padding、margin、border 之間的關係拆得很細,建議搭配閱讀:CSS Box Model 完全圖解。這裡我先把最關鍵的觀念講清楚,因為它會決定你後面所有排版的成敗。
瀏覽器把每一個 HTML 元素都當成一個「盒子」,這個盒子從內到外有四層:最內層是內容(content)本身的寬高,往外是 padding(內距,內容到邊框的留白)、border(邊框)、margin(外距,這個盒子和別人的距離)。你設 width: 200px 的時候,問題就來了:這個 200px 到底是指內容、還是包含 padding、還是連邊框都算進去?
這正是新手最常踩的雷。在 CSS 預設的 content-box 行為裡,width 只算「內容」那一層。所以你設了 200px 的寬度,再加上左右各 20px 的 padding 和 5px 的 border,這個盒子的 border box 寬度會變成 200 + 20×2 + 5×2 = 250px。你以為排了三個一樣寬的區塊會剛好填滿容器,結果第三個被擠到下一行去。
常見的處理方式,是把元素與偽元素都改用 border-box:
*, *::before, *::after {
box-sizing: border-box;
}
這段把元素的盒模型改成「你設的 width 包含 content、padding 和 border,但不包含 margin」。下了這段之後,一般情況下 width: 200px 的 border box 就是 200px,padding 和 border 會從內容可用空間扣除。這是許多專案會採用的全域設定,能減少尺寸計算的意外。
文字與色彩:決定網站氣質的兩個變數
排版的地基打好之後,視覺氣質幾乎是由文字和色彩這兩組變數決定的。CSS 處理文字的屬性非常完整,但實務上你最常動的是這幾個:font-family(字體)、font-size(字級)、font-weight(粗細)、line-height(行高)、letter-spacing(字距)。其中行高是新手最容易忽略、卻會明顯影響閱讀舒適度的一個;合適數值要依字體、字級、欄寬與內容測試,太擠會讓人讀得很累,太鬆又會讓段落碎掉。
字體的選擇牽涉到中英文的搭配,這又是一整門學問。中文網站的字體策略和英文差很多,因為中文字數龐大、字型檔案也大,載入策略要特別處理。如果你想系統性地挑字,可以參考這篇中文字體設計全攻略;如果是要找英文字體,則看精選英文字體推薦。而決定文字怎麼排列才好看,背後是排版設計的原則,推薦讀這篇排版設計實戰技巧,裡面把行距、層次、視覺節奏講得很清楚。
色彩這邊,CSS 支援好幾種寫法:十六進位(#3366cc)、RGB(rgb(51,102,204))、HSL(hsl(220,60%,50%))。實務上偏好 HSL,因為它的三個數值分別代表色相、飽和度、明度,這個結構和你腦袋裡想「我要這個顏色再亮一點、再淡一點」的直覺是一致的,改一個數字就能微調,不像十六進位那樣改了半天還猜不出結果。背後的色彩原理,可以搭配色彩學完整指南來讀,理解了色相環,你在做網站配色時才不會瞎猜。實際要為整個網站定一套色彩計畫時,這篇網頁配色實戰指南給了很清楚的步驟。
單位系統:px、em、rem、% 與視窗單位各自該出現的場合
寫 CSS 你幾乎每一行都會碰到單位,但很多新手從頭到尾只會用一種 px,這會在兩個地方絆住你:回應式縮放,以及使用者的無障礙設定。CSS 的長度單位分成絕對和相對兩大類,搞懂它們的差別,是從「能動」走向「可適應」的關鍵一步。
| 單位 | 參照對象 | 典型用途 |
|---|---|---|
| px | CSS 參考像素(絕對長度,不一定等於裝置像素) | 邊框、陰影等需要精確固定的細節 |
| em | 自身字級;用於 font-size 時參照父元素字級 | 元件內部、要跟著元件字級縮放的間距 |
| rem | 根元素(html)的字級(相對) | 全站字級、行高、大尺度留白的主幹 |
| % | 依屬性定義的參照值 | 寬度、彈性區塊的填滿 |
| vw / vh | 視窗寬度或高度的百分之一 | 滿版橫幅、全螢幕區塊 |
這裡面最值得記住的是 rem。因為它參照的是根字級,當使用者把瀏覽器的預設字級調大時,未被固定根字級抵消的 rem 尺寸會跟著放大。用 px 指定字級通常不會跟著瀏覽器的預設字級設定改變,但仍會隨頁面縮放(zoom)放大。我的習慣是這樣分工:字級、大尺度的留白多用 rem;行高優先使用無單位數值;邊框和陰影這種需要精確細節的地方才用 px;元件內部想跟著自己字級縮放的間距才用 em。這套分工一旦內化,你寫出來的版面會自然具備彈性。
視窗單位 vw、vh 則是做滿版畫面時的好幫手,例如首頁那種佔滿整個視窗高度的橫幅,可以用 min-height: 100vh;。不過要注意,手機瀏覽器的網址列會改變可見區域:100dvh(dynamic viewport height)會動態貼合當下可見高度,但可能在捲動時跟著縮放;如果要避免介面伸縮造成的尺寸跳動,可考慮固定且較保守的 100svh。要用哪一種,取決於內容是否必須隨可見區域即時填滿。
現代 CSS 功能::has()、容器查詢與 Subgrid 的革命
CSS 在最近幾年有了一批強大的新功能,它們解決了過去需要 JavaScript 或繁複 hack 才能做到的事情。理解這些現代語法,能讓你寫出更簡潔、更穩健的樣式。
:has():父層選擇器,這可能是 CSS 歷史上最被期待的功能之一。過去 CSS 只能「往下選」(子層、後代),無法「往上選」(父層),但 :has() 打破了這個限制。例如你想讓包含圖片的段落自動調整樣式:
p:has(img) {
display: flex;
align-items: center;
}
這行 CSS 的意思是「選到所有包含 img 元素的 p」,這在過去必須靠 JavaScript 找出來再改 class,現在純 CSS 就能做到。:has() 自 Chrome 105、Safari 15.4、Firefox 121 起在主流瀏覽器可用,實務上可以放心使用(配適當的 @supports 或 feature query 更穩,瀏覽器支援詳見 MDN 的 :has() 文件)。常見應用包括:包含錯誤訊息的表單欄位變紅、有圖片的卡片自動改排版、子元素內容不同時父層顯示不同樣式。
容器查詢(Container Queries),解決的是回應式設計的一個根本限制。傳統的寬度媒體查詢看的是「視窗寬度」,但實務上你真正想知道的常是「元件容器有多大」。一個側欄卡片即使位於寬視窗,也可能只剩很窄的空間,用視窗寬度判斷就可能不合適。容器查詢讓你能根據元件所在容器的寬度來調整:
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
這樣一來,卡片在手機版的主內容區、桌機版的側欄、甚至被放到一個小彈窗裡,都能根據最近的祖先查詢容器尺寸自動調整排版,不用再猜它在哪裡。尺寸容器查詢的規則會套用到容器的後代,不能讓容器依自己的尺寸查詢直接改自己的樣式。容器查詢自 Chrome 106、Safari 16 起可用,現在也已獲主流現代瀏覽器支援(見 Can I use 的容器查詢支援表)。
Subgrid,解決的是巢狀 Grid 的對齊問題。過去你在 Grid 容器裡放一個子 Grid,子 Grid 的格線和父 Grid 是獨立的,這會讓排版不一致。Subgrid 讓子 Grid 直接繼承父 Grid 的格線定義:
.parent {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
.child {
display: grid;
grid-template-columns: subgrid;
}
當 .child 是 .parent 的 Grid item 時,它的子項目可以沿用 .child 在父 Grid 中所跨越的欄軌,和父層格線對齊。這在做複雜的版面設計(例如雜誌風格的佈局)時非常實用。Subgrid 由 Firefox 71 率先支援,Safari 16、Chrome 117 也已跟上,至此跨瀏覽器互通(見 Can I use 的 Subgrid 支援表)。
aspect-ratio,解決的是「如何讓元素保持固定比例」的問題。過去你得用 padding-top: 56.25% 這種 hack 來做 16:9 的容器,現在直接寫:
.video-container {
aspect-ratio: 16 / 9;
width: 100%;
}
瀏覽器會自動維持這個比例,不管容器寬度怎麼變。這在做圖片、影片、卡片元件時極為實用,所有主流現代瀏覽器都已支援(見 MDN 的 aspect-ratio 文件)。
:is() 與 :where(),這兩個偽類用來簡化複雜的選擇器。:is() 讓你一次列出多個可能的選擇器,權重取其中最高的;:where() 功能相同但權重永遠是 0。例如:
:is(h1, h2, h3).title {
color: blue;
}
/* 等於分別寫 h1.title, h2.title, h3.title */
這能大幅減少重複的選擇器宣告,讓樣式表更簡潔。兩者都已獲所有主流現代瀏覽器支援(見 MDN 的 :is() 文件)。
這些現代 CSS 功能有一個共同點:它們都是「為了解決真實專案痛點」而設計的,不是炫技。當你理解它們解決什麼問題,自然會在適當的時候用上,而不是為了用而用。
現代排版的雙主菜:Flexbox 與 Grid 該怎麼選
講完地基和變數,接下來是真正把東西「排到對的位置」的工具。現代 CSS 排版有兩大主力:Flexbox 和 Grid。很多人搞不清楚什麼時候用哪個,其實判斷標準很簡單,看你的版面是「一個方向」還是「兩個方向」。
| 比較項目 | Flexbox | Grid |
|---|---|---|
| 維度 | 一維(一個方向排列) | 二維(列與行同時控制) |
| 最適合 | 導覽列、按鈕群、水平置中 | 整體頁面骨架、卡片矩陣、複雜版面 |
| 排列邏輯 | 項目依內容彈性伸縮 | 你先定義格線,再把東西放進格子 |
| 反向相容 | 非常成熟,所有現代瀏覽器都支援 | 同樣成熟,subgrid 是相對新的補強 |
Flexbox 的強項是「讓一群東西在一條線上對齊和分配空間」。最經典的應用是導覽列:左邊 logo、右邊選單,中間自動撐開,你只要 display: flex; justify-content: space-between; 就能處理,不必再用 float 和清除浮動。它也擅長處理交叉軸置中;在 Flex 容器中可用 align-items: center; 對齊項目。
Grid 則適合需要同時控制列和行、像表格那樣佈局的情境。例如首頁「左邊主內容、右邊側欄」的骨架,可在 display: grid 的容器上用 grid-template-columns: 2fr 1fr; 定義兩欄;若還要安排橫幅等區域,可再搭配 grid rows 或 grid areas。它特別適合 Bento Grid 排版,如果你想做出那種質感,可以看這篇Bento Grid 排版教學。
實務上這兩個並非互斥,你會把它們搭在一起用。用 Grid 把整個頁面切成幾個大區塊,再在每個區塊裡用 Flexbox 處理內部的對齊。把 Grid 當骨架、Flexbox 當肌肉,這個心智模型一旦建立,你就不會再為「該用哪個」糾結。
元件設計模式:從按鈕到卡片的系統化
理解 Flexbox 和 Grid 之後,下一步是思考「怎麼設計可重用的元件」。一個成熟的網站會有按鈕、卡片、表單、模態框這些重複出現的元件,它們不是單一的樣式,而是「同一族」的變體。按鈕會有主要、次要、危險、大型、小型等變化,卡片會有有圖片版、純文字版、水平版、垂直版。把這些變體設計成一個系統,而不是每次都重新寫,這就是元件思維。
以按鈕為例,一個健壯的按鈕系統會把基礎樣式與變體分開,變體用獨立的修飾符類別覆蓋:
.button {
padding: 0.75rem 1.5rem;
border-radius: 0.375rem;
font-weight: 500;
cursor: pointer;
transition: background 0.2s ease, transform 0.2s ease;
}
.button--primary {
background: var(--brand-color);
color: white;
border: 2px solid var(--brand-color);
}
.button--secondary {
background: white;
color: var(--brand-color);
border: 2px solid var(--brand-color);
}
.button--danger {
background: var(--danger-color);
color: white;
border: 2px solid var(--danger-color);
}
.button--large {
padding: 1rem 2rem;
font-size: 1.125rem;
}
.button--small {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
.button:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.button:active {
transform: translateY(0);
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
這套設計的好處是:所有按鈕共享基礎樣式(padding、border-radius、transition),變體用修飾符覆蓋顏色和尺寸,狀態(hover、active、disabled)統一管理。當你要改全站按鈕的圓角,只要改 .button 的 border-radius,所有按鈕跟著變。這是「系統」的力量。這些狀態與轉場參數也可以先在設計稿裡定好,怎麼在 Figma 裡定義按鈕的狀態與轉場參數有具體做法,讓設計端和開發端講同一套規格。
卡片元件也類似,一張典型的卡片會有容器、圖片區、標題、內文、標籤、底部按鈕,用 BEM 命名就是:
.card {
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.card__image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.card__content {
padding: 1.5rem;
}
.card__title {
font-size: 1.25rem;
margin-bottom: 0.75rem;
}
.card__body {
color: var(--text-secondary);
line-height: 1.6;
}
.card--horizontal {
display: grid;
grid-template-columns: 1fr 2fr;
}
@media (max-width: 640px) {
.card--horizontal {
grid-template-columns: 1fr;
}
}
這樣設計的卡片可以垂直(預設)或水平(--horizontal 變體),圖片用 aspect-ratio 維持比例,object-fit: cover 會在必要時裁切圖片以填滿容器,而不拉伸變形。你想加一個「深色主題卡片」變體,只要再加 .card--dark 覆蓋顏色即可。
模態框(Modal)元件需要額外注意無障礙。一個合格的模態框應該:
- 讓背景內容在開啟期間不可互動,並以遮罩等視覺方式表達模態狀態
- 優先使用原生
<dialog>搭配showModal();自製容器則使用role="dialog"和aria-modal="true" - 透過
aria-labelledby或aria-label提供可存取名稱 - 開啟時把焦點移入模態框,讓 Tab 與 Shift+Tab 留在框內,關閉後把焦點移回合理位置
- 支援 Escape 關閉,並提供具有可存取名稱的可見關閉按鈕
這些不只是「好看」的問題,是不讓部分使用者無法使用的關鍵。
元件思維的核心是「一次設計,多次重用」。你把按鈕元件設計完善,接下來整個專案的按鈕都只要套 class,不用再重寫。這也是為什麼成熟的團隊常會建立設計系統(Design System),它不只為了好看,也是為了維護效率和一致性。
響應式設計:不是「再做一個手機版」,是用同一份 CSS 適應所有螢幕
這是新手最需要建立正確觀念的一環。響應式設計(Responsive Web Design,RWD)的核心想法是:同一份 HTML、同一份 CSS,根據螢幕寬度自動改變樣式。它並不等於再做一個手機版網站,重點在於讓同一個網站有能力適應從手機到桌機的各種寬度。想完整理解 RWD 的觀念與做法,可以讀這篇響應式網頁設計完整教學,而 RWD 和 AWD(自適應設計)的差異,這篇AWD vs RWD 比較講得很清楚。
為什麼響應式在 2026 年早就從「加分項」變成「基本款」?兩個硬道理。第一,Statista 的全球行動流量統計顯示,行動裝置長期占全球網站流量很大的比例。第二,Google 已在 2024 年完成行動優先索引(mobile-first indexing)的最後階段,現在主要使用 Googlebot Smartphone 抓取網站,並以行動版內容進行索引和排名(見 Google 的行動優先索引最佳做法文件)。如果內容完全無法透過行動裝置存取,就可能無法被 Google 索引。
響應式的兩個技術支柱是 viewport meta 和媒體查詢(media query)。viewport meta 是放在 HTML <head> 裡的一行宣告,告訴瀏覽器「這個頁面要按照裝置寬度來縮放,不要用預設的桌機寬度去硬縮」:
<meta name="viewport" content="width=device-width, initial-scale=1">
媒體查詢則是 CSS 裡的條件判斷,讓你能針對特定螢幕寬度寫不同的樣式。例如:
.container {
padding: 16px;
}
@media (min-width: 768px) {
.container {
padding: 40px;
}
}
這段程式碼示範的是行動優先(mobile-first)的寫法:基礎樣式(padding: 16px)先服務窄螢幕,然後在寬度大於等於 768px 時覆蓋成更大的留白。這是一種 CSS 組織策略,和 Google 採用哪個 crawler 進行行動優先索引是兩回事;Google 不要求網站必須用 min-width 媒體查詢。mobile-first 往往能減少窄版額外覆蓋規則,但仍應依專案內容和團隊慣例選擇。
關於媒體查詢的斷點,有一個常見的誤解值得澄清:斷點不要照著特定裝置的寬度來設,而要照你自己的版面在哪個寬度開始壞掉來設。正確的流程是把瀏覽器視窗從寬慢慢拉到窄,觀察版面在哪個臨界點擠成一團,那個點才是你真正該設的斷點。很多人會直覺地把斷點設成某款熱門手機或平板的解析度,但裝置解析度年年都在變,你今天設好的數字明年可能就對不上新機型;相對地,你的版面結構在什麼寬度崩潰,是你隨時可以自己看見、也相對穩定的事實。讓內容來決定斷點,不要讓斷點去遷就裝置。
過場與互動:用 transition 與 transform 給網站加上呼吸感
到目前為止講的都是「靜態」的樣式,但一個讓人覺得有質感的網站,幾乎都帶有細微的動態回饋:滑鼠移到按鈕上時顏色柔和地轉換、卡片被點擊時輕輕放大。這些效果的核心是兩個屬性:transition 和 transform。它們是 CSS 裡最划算的投資之一,用很少的程式碼就能把網站的精緻度拉高一個檔次。
transition 的作用是讓某個屬性的變化「不要瞬間完成,而是用一段時間平滑過渡」。它的語法可以很簡單:
.button {
background: #3366cc;
transition: background 0.2s ease;
}
.button:hover {
background: #4477dd;
}
這段意思是:按鈕的背景色,當它發生變化時(例如指標移上去觸發 :hover),用範例設定的 0.2 秒和 ease 緩動曲線平滑過渡,而不是啪一下跳過去。實際時間沒有適用所有介面的固定門檻,應依互動目的、移動距離與使用者測試調整;日常操作通常要避免讓過場拖慢回饋。
transform 則用來移動、縮放、旋轉元素,而且它有一個關鍵優勢:改變 transform 不會觸發 layout,瀏覽器通常也能有效率地合成,但是否建立獨立合成層或使用 GPU 仍由瀏覽器決定。想做卡片浮起的效果,通常用 transform: translateY(-4px); 比改 margin 或 top 更合適;這有助於降低動畫成本,但不保證 Core Web Vitals 分數一定改善。至於緩動曲線,ease 是常見預設,會先加速再減速;linear 是等速,常見於進度或持續旋轉等需要均勻速度的動態。
有一個現代的修養要特別提:尊重使用者作業系統裡的「降低動態」設定。有些人會因為前庭功能或暈動的問題,把系統設成減少動畫,你可以用媒體查詢 @media (prefers-reduced-motion: reduce) 偵測到這個偏好,主動移除非必要動態或改成較溫和的效果。這算不上炫技,卻是基本的數位禮貌,也正好和無障礙設計的原則一致。
進階動畫系統:從 transition 到 keyframes 的完整動態設計
掌握了 transition 和 transform 之後,下一層是 CSS 動畫(animation)。它用 @keyframes 定義一連串的關鍵影格,讓瀏覽器自動補間成流暢的動畫。這不是為了炫技,很多時候是使用者體驗的必要部分,例如載入中的旋轉圖示、捲動到視窗內元素的淡入、提醒訊息的滑出,都是動畫的實用場景。
一個基本的動畫長這樣:
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.6s ease forwards;
}
這段定義了一個淡入並向上浮現的動畫,forwards 讓動畫結束後元素保持在最終狀態(不會跳回原樣)。你可以用在「元素捲進視窗時自動觸發」的情境,用 Intersection Observer API 加上這個 class,網站就會有流暢的「進場」感覺。
動畫的效能原則是:能用 transform 和 opacity 完成時,通常優先用它們。這兩個屬性的變化不會觸發版面重排,瀏覽器也常能在合成階段處理。相反地,改動 width、height、top、left 等屬性可能觸發 layout 與 paint;成本高低仍取決於頁面結構、受影響範圍和瀏覽器實作,應用 DevTools 實測。
will-change 是一個告訴瀏覽器「這個屬性稍後可能改變」的提示,讓瀏覽器有機會提前做最佳化,但不保證一定建立合成層:
.animated-card {
will-change: transform;
}
但不要濫用 will-change,瀏覽器為最佳化保留的資源可能增加記憶體用量,用在即將發生高成本變化的元素就好。動畫結束後可以用 JavaScript 移除這個屬性,讓瀏覽器回收相關資源。
動畫和無障礙的平衡是個重要議題。剛剛提過 prefers-reduced-motion,一個完整的做法是:
.fade-in {
animation: fadeIn 0.6s ease forwards;
}
@media (prefers-reduced-motion: reduce) {
.fade-in {
animation: none;
opacity: 1;
transform: none;
}
}
這樣對開啟減少動畫的使用者,元素會直接顯示最終狀態,沒有動畫過程。這不是「降低體驗」,而是「尊重偏好」。Core Web Vitals 不直接評分動畫風格,但若動畫或狀態切換改動會影響版面的屬性,並在沒有近期使用者輸入的情況下造成非預期位移,就可能計入 CLS。設計動畫時,要預留最終狀態的空間,並優先使用不影響 layout 的屬性。
變數、函式與預處理器:讓 CSS 從「樣式表」升級成「系統」
早期的 CSS 有個讓人頭痛的限制:它沒有變數。你要全站用同一個主色,就得到處寫死那個色碼;哪天客戶說「把品牌藍改深一點」,你得全文替換幾十個地方。這個痛點催生了 Sass、SCSS 這類預處理器(preprocessor),它們讓你用變數、巢狀、函式來寫 CSS,再編譯成瀏覽器看得懂的純 CSS。預處理器的觀念很值得學(想深入了解可以讀這篇 Sass 與 SCSS 入門),因為它預演了現代 CSS 的方向。
但好消息是,原生 CSS 這幾年已經把最常用的能力補齊了。CSS 自訂屬性(custom properties,俗稱 CSS 變數)就是其中最重要的一個:
:root {
--brand-color: #3366cc;
--space-unit: 8px;
}
.button {
background: var(--brand-color);
padding: var(--space-unit);
}
這樣寫的好處是,主色和留白單位只在 :root 定義一次,之後全站用 var() 引用。改色只要改一行,全站跟著變。更強的是,CSS 變數是執行時動態的,你可以用 JavaScript 即時改它,這是預處理器變數做不到的,例如做一個讓使用者切換深色主題的功能,只要換掉 :root 裡幾個變數值就完成。
CSS 變數搭配語意化命名,還能進一步做出主題切換。把顏色變數取名叫 --bg、--text、--accent 這類跟「用途」有關的名字,而不是 --blue 這種直接描述顏色的名字。這樣一來,當你要做深色模式,只要在 [data-theme="dark"] 這個選擇器裡重新定義同一組變數的值,整個網站瞬間切換主題,完全不必動到元件本身的樣式。這個設計在十年前的純 CSS 裡幾乎做不到,現在卻是一組變數的事,這也是我說現代 CSS 已經值得被當成一個小系統來認真對待的原因。
除了變數,CSS 還內建了一些實用函式。calc() 讓你直接在樣式裡做運算(例如 width: calc(100% - 40px)),min()、max()、clamp() 則是回應式排版的利器。其中 clamp() 特別值得認識,它能讓字級隨螢幕寬度自動伸縮、同時設上下限:
h2 {
font-size: clamp(1.5rem, 4vw, 2.5rem);
}
這行的意思是:標題字級最小不小於 1.5rem、最大不超過 2.5rem,中間則跟著螢幕寬度的 4% 動態調整。這就是所謂的流體字級(fluid typography),一份規則就搞定從手機到桌機的字級縮放,完全不用寫一長串媒體查詢。這是現代 CSS 最讓人興奮的進展之一,十年前的 CSS 工程師做夢都想有這個。
CSS-in-JS 與框架選擇:何時該用工具、何時該用原生 CSS
現代前端開發有一個大分歧:該用原生 CSS 還是 CSS-in-JS(styled-components、emotion)?該用 utility-first 的 Tailwind CSS 還是傳統的 Bootstrap?這些選擇沒有絕對答案,但理解各自的設計哲學和適用場景,能幫你做出不後悔的決定。
CSS-in-JS 有多種實作。以 styled-components 這類 runtime CSS-in-JS 為例,它把樣式和元件放在一起,可根據 props 動態生成樣式,並產生 class 名稱來降低全域衝突。代價是執行時處理會增加工作量(SSR 也要正確設定)、樣式除錯路徑較複雜、團隊成員得會 JavaScript;zero-runtime CSS-in-JS 則會在建置階段抽出 CSS,不具備相同的執行時成本。它適合「高度動態、元件大量重用、團隊熟悉 React」的專案;如果你的網站是靜態的、變動少、團隊以 HTML/CSS 為主,用原生 CSS 加上 BEM 命名反而更簡單直接。
Tailwind CSS 是 utility-first 的代表,提供一大堆像 text-center、p-4、bg-blue-500 這種單一用途的 class,你在 HTML 裡組合它們排版。好處是不用離開 HTML 就能完成大部分排版、設計一致性強(顏色與間距都是預設系統)、建置時只產生偵測到的 utility。它也原生支援 hover:、focus: 等狀態變體;主要取捨是標記中的 class 可能變長、動態組合 class 時要符合掃描規則,團隊也需要熟悉 utility 名稱。它不只適合快速原型或小型專案,大型專案也能使用;是否比 BEM 易讀、好交接,取決於元件抽象和團隊慣例。
Bootstrap 這類 component framework 提供「現成的 UI 元件」,常見的按鈕、導覽列、表單都有預設樣式,內建回應式系統,新手容易上手,社群資源也多。缺點是直接沿用預設值容易做出相似的視覺,載入完整套件也可能帶入未使用的 CSS 或 JavaScript;不過 Bootstrap 可透過 Sass 變數、CSS 變數、自訂建置與正確的載入順序調整,客製化並不必然需要 !important。它適合快速驗證想法或內部管理後台;要求獨特視覺、長期維護的網站,則要比較客製框架與自訂設計系統的維護成本。
還有一個選擇:完全不依賴框架,用原生 CSS。這對小型網站來說常常是很直接的選擇,不用學額外框架,直接用 border-box、rem、Flexbox、Grid 這些現代語法。等到專案規模或團隊需求真的需要,再考慮引入工具。過早架構不是優雅,是把簡單的事變複雜。
CSS 與 SEO 的隱形連結:速度、行動裝置與 Core Web Vitals
這一節特別值得講,因為很多 CSS 教學會完全略過這塊,但它對一個會被 Google 收錄的網站來說,重要性不亞於任何視覺技巧。CSS 不只影響長相,也會影響 Google 渲染頁面時看到的內容、行動版可用性與部分頁面體驗指標。
第一個關鍵是渲染阻塞(render-blocking)。一般樣式表不會讓 HTML 解析本身完全停止,但瀏覽器通常要取得並解析會影響目前頁面的 CSS,才能完成 CSSOM 並渲染內容。代價是:關鍵 CSS 越大、下載越慢,使用者看到主要內容的時間就可能越晚。「最大內容繪製(LCP)」衡量的是視窗內最大內容元素完成渲染的時間,也是 Core Web Vitals 之一。2026 年目前的三項 Core Web Vitals 是 LCP、互動至下一次繪製(INP)與累計版面位移(CLS);INP 已在 2024 年 3 月取代 FID(見 web.dev 的 Web Vitals 說明)。
第二個關鍵是版面位移(CLS)。如果你用 HTML 的 width、height 屬性或 CSS aspect-ratio 先保留圖片空間,圖片載入完成時通常就不會推動周圍內容;反之,沒有預留位置的圖片可能在載入後把下方內容往下推。CLS 會依定義把觀察期間內非預期的版面位移彙整成分數,是衡量視覺穩定度的 Core Web Vital。想把這幾個指標徹底搞懂,這篇Core Web Vitals 完全攻略是必讀。
從這個角度看,幾個對 SEO 友善的 CSS 習慣就會自然浮現:
- 把關鍵的、影響首屏畫面的 CSS 精簡到最小,必要時放進 HTML 的
<style>元素,減少 critical rendering path 的等待(這是內部樣式,不是散落在元素style=""屬性裡的行內樣式)。 - 圖片永遠先設好寬高或用 CSS 預留容器,避免載入時版面跳動。圖片本身的檔案大小也要壓,這篇圖片壓縮工具實測給了完整清單。
- 善用延遲載入,把非必要的資源延後到需要時才載入,做法看這篇Lazy Loading 延遲載入指南。
- 靜態資源走 CDN 加速,縮短下載時間,原理和推薦服務在這篇CDN 網站加速解析。
- 整體的載入效能優化是一整套工程,這篇我們整理的速度指南把全貌講得很完整。
這些做法看似是「效能優化」,但本質上它們和 CSS 怎麼寫息息相關。一個把樣式整理得乾淨、檔案切割合理、不重複載入的 CSS,本身就是 SEO 資產。
再補一個常被看漏的點:CSS 檔案的組織方式也會影響載入效能。把所有樣式塞進一個超大檔案會增加初始下載與解析成本;拆成許多檔案則要考量請求開銷、快取命中與是否每頁都需要。實務上的折衷是按頁面區塊或元件拆分原始碼,再由建置工具依實際路由合併或切割。CSS 中巢狀的 @import 也可能形成額外的資源發現與下載依賴鏈,通常改用 HTML 的 <link> 或建置工具處理較容易最佳化。樣式表怎麼組織,看起來是整潔問題,實際上會反映在使用者看到畫面的速度上。
CSS Debug 與 DevTools:從猜測走向精準的偵查
新手遇到「樣式沒生效」的問題,常常靠猜測和試誤:加個 !important 試試、改個選擇器試試。這樣可以解決問題,但效率低,而且容易留下技術債。專業的做法是善用瀏覽器的 DevTools,讓瀏覽器告訴你「真正發生了什麼」。
Computed 面板是偵查的第一步。你在元素上按右鍵「檢查」,切到 Computed 分頁,會看到該元素最終套用的所有樣式值。這些值是瀏覽器把所有 CSS 規則疊加計算後的結果。如果你想知道某個元素實際的 font-size 是多少,不要去猜,直接看 Computed,它會告訴你精確的數字(包含繼承和預設值)。
Styles 面板則是看「哪條宣告生效」的地方。它會列出影響該元素的規則,被其他宣告覆蓋的屬性通常會被劃掉;部分未生效或無效的屬性也會提供提示。這能幫你判斷問題來自層疊、選擇器、語法,還是規則根本沒有匹配。
Specificity 計算,新版 Chrome DevTools 可在 Styles 面板把游標移到選擇器上查看 specificity 的欄位值。比較兩條規則時仍要先看來源、重要性和 cascade layer;只有這些條件相同,specificity 才決定先後,其他條件也相同時才看出現順序。
CSS Coverage 工具(Chrome 內建)能幫你找出「沒用到的 CSS」。開啟方式是 DevTools 的 More Tools → Coverage,點 Record 後讓網頁跑一圈,它會用紅色標示哪些 CSS 沒有被用到。這對於清理舊專案的冗餘樣式極為有用,但要小心:有些 CSS 可能是動態載入的內容或響應式斷點才用到,不要一刀砍。
Break on DOM modifications,當你的樣式會隨 JavaScript 動態改變,而且你想抓出「是哪段程式碼在改」的時候,可以在 Elements 面板對元素按右鍵 → Break on → Attributes modifications,這樣每次該元素的 style 被改時,JavaScript debugger 就會暫停,讓你看到是哪段程式在做。這對追蹤動態樣式的 bug 極為有效。
還有一些小技巧:
- 用
:hover偽類測試時,DevTools 的 Styles 面板右上角有個 :hov 圖示,可以強制觸發元素的狀態,不用真的把滑鼠移上去。 - 用
element.style在 Console 裡即時改樣式測試,document.querySelector('.my-element').style.color = 'red';,這比在 Styles 面板手動改快。 - 用
getComputedStyle(element)在 Console 裡印出元素的所有計算樣式,方便複製或進一步處理。
養成「用 DevTools 找答案」的習慣,會讓你的 Debug 效率提升一個檔次。猜測和試誤是新手階段無可避免的過程,但專業的成長就是從「靠猜」走向「靠工具」。
跨瀏覽策略與 @supports:優雅降級 vs 漸進增強
理想的情況是所有使用者都用最新版瀏覽器,但實務上總會有人用舊版 Chrome 或殘存的舊環境。這時該怎麼辦?兩種策略:優雅降級(Graceful Degradation)和漸進增強(Progressive Enhancement)。
優雅降級是「先做給現代瀏覽器的完整版本,再給舊瀏覽器提供備案」。例如你的網站用 Grid 做排版,對不支援 Grid 的舊瀏覽器,你就提供一個簡單的單欄版面,功能還在,只是視覺較簡單。這個策略適合「核心功能不依賴新語法」的情況。
漸進增強則是「先做最基本可用的版本,再為現代瀏覽器加強」。例如你先寫一個用 float 的基礎排版,然後用 @supports 偵測瀏覽器支援 Grid 時,再疊加 Grid 版本:
.container {
float: left;
width: 100%;
}
@supports (display: grid) {
.container {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
這樣不支援 Grid 的瀏覽器會用 float 版本,支援的會得到更強的 Grid 排版。漸進增強讓每個人都得到最好的可用體驗,是比較推薦的思維。
@supports 是 CSS 的功能偵測規則,它讓你能「針對支援某語法的瀏覽器寫特定樣式」,而不是用 JavaScript 去偵測瀏覽器版本(這個做法不可靠,因為瀏覽器版本和功能支援不是一對一)。例如:
@supports (backdrop-filter: blur(10px)) {
.modal-overlay {
backdrop-filter: blur(10px);
}
}
這樣只有支援 backdrop-filter 的瀏覽器會套用背景模糊效果,不支援的就跳過,不會報錯。
實務上,你可以用工具幫忙處理跨瀏覽差異。Autoprefixer 是常見的 PostCSS plugin,會依 Browserslist 目標自動加入需要的瀏覽器前綴,你只寫標準語法即可。PostCSS 本身是轉換 CSS 的工具平台;是否能把特定現代語法轉成相容版本,取決於你安裝、設定的 plugin,而且不是每個新功能都能完整 polyfill。
什麼時候該考慮舊瀏覽器?應依網站分析資料、受眾與組織支援政策決定。Internet Explorer 11 桌面應用程式已在多數受支援的 Windows 10 版本於 2022 年 6 月 15 日終止支援,但 Microsoft Edge 的 IE mode 仍是不同的企業相容方案。一般對外網站可依自己的瀏覽器支援矩陣使用現代語法,並視需要提供「功能可用但視覺簡化」的降級體驗(IE 11 退場的官方細節見 Microsoft 的 Internet Explorer retired 公告)。
列印 CSS 與多媒體查詢:不只螢幕,還有紙張和投影
CSS 的媒體查詢不只用在 @media (min-width: ...) 的回應式設計,還能處理不同的輸出媒體。最常見的是 @media print,讓你為「列印這件事」寫專屬樣式。
很多人會忽略列印需求,但實務上你會遇到「使用者想把網頁存成 PDF 或印出來」的情況。如果你沒有寫 print CSS,列印結果常常是災難:導覽列印出來了、按鈕印出來了、背景色吃掉墨水、網址跟著頁面內容印出來。一份合格的 print CSS 應該:
@media print {
/* 隱藏螢幕專用元素 */
.no-print,
nav,
.sidebar,
button,
a[href^="javascript:"] {
display: none;
}
/* 讓連結印出 URL */
a[href^="http"]::after {
content: " (" attr(href) ")";
}
/* 要求瀏覽器盡量不要緊接在標題後分頁 */
h1, h2, h3 {
break-after: avoid-page;
}
/* 要求瀏覽器盡量不要在圖片內分頁 */
img {
break-inside: avoid;
}
/* 移除背景色(省墨水) */
body {
background: white;
color: black;
}
/* 確保文字足夠大 */
body {
font-size: 12pt;
}
}
這段樣式做了幾件事:隱藏導覽列、側欄、按鈕等螢幕專用的元素;讓外部連結自動印出 URL,這樣紙本閱讀者能看到來源;要求瀏覽器盡量不要在標題後或圖片內分頁;移除頁面背景色;並設定紙本字級。實際分頁和背景列印仍會受瀏覽器及使用者列印設定影響。
除了 print,還有其他媒體查詢:@media speech 是給語音合成器使用的媒體類型,不等同於一般螢幕閱讀器偵測;@media (prefers-color-scheme: dark) 可回應深色配色偏好;@media (prefers-contrast: more) 可回應使用者要求更高對比的偏好。這些偏好查詢能幫助網站適應不同使用需求。
無障礙深化:從 :focus 到 ARIA 的完整可訪性設計
無障礙設計(Accessibility,a11y)不是「選配」,而是讓每個人都能使用你的網站的基本要求。CSS 在無障礙裡扮演關鍵角色,不只是「不讓人看不清」而已。
焦點管理是鍵盤操作的重要基礎。許多純鍵盤、切換控制器與螢幕閱讀器使用者會依賴鍵盤焦點;螢幕閱讀器也可能提供虛擬游標、標題導覽等其他方式,並非只靠 Tab。如果使用者看不出來「現在焦點在哪」,就很難操作。所以每個可互動元素(按鈕、連結、表單欄位)都必須有清楚的焦點樣式:
button:focus-visible {
outline: 2px solid var(--brand-color);
outline-offset: 2px;
}
注意我用了 :focus-visible 而不是 :focus。瀏覽器會依輸入方式、元素類型與其他啟發式規則,判斷何時應顯示明顯焦點;它通常會在鍵盤導覽時匹配,但不是單純等同「Tab 時顯示、滑鼠時不顯示」。現代瀏覽器支援 :focus-visible(自 Chrome 86、Firefox 85、Safari 15.4 起通用)。對不支援的瀏覽器,可保留 :focus 作為備案,判定細節見 MDN 的 :focus-visible 文件。
更完整的做法是:
button:focus {
/* 給舊瀏覽器的備案 */
outline: 2px solid var(--brand-color);
}
button:focus:not(:focus-visible) {
/* 用滑鼠點擊時,移除焦點樣式 */
outline: none;
}
支援 :focus-visible 的瀏覽器會依自己的啟發式規則決定何時保留焦點框;不支援的瀏覽器則會保留 :focus 備案。
顏色對比是 WCAG 2.2 的核心要求。要符合 AA,文字和背景的對比度至少要 4.5:1;符合 WCAG 大字定義的文字則至少 3:1,另有標誌、裝飾與非啟用元件等例外(見 WCAG 2.2 SC 1.4.3 對比度準則)。你可以用線上工具如 WebAIM Contrast Checker 檢查每個顏色組合是否符合標準。如果設計師給的顏色不達標,你可以調整前景或背景顏色,或在圖片上的文字下增加能穩定提高對比的底色。
Skip links 是讓鍵盤使用者跳過重複導覽列的設計。一個典型的 skip link 是頁面最上方的一個「跳到主內容」連結,預設隱藏,當使用者 Tab 鍵焦點落在上面時才顯示:
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--brand-color);
color: white;
padding: 8px 16px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
把這個連結放在頁面 DOM 的開頭,鍵盤使用者第一次按 Tab 時就能聚焦它,按 Enter 直接跳到主內容,不用每次都穿過整段導覽列。
ARIA 屬性和 CSS 的配合也值得理解。aria-hidden="true" 只會把元素及其子樹從輔助科技的可存取樹隱藏,不會讓畫面上的元素消失,也不該套在仍可取得焦點的元素上;若要同時從畫面與可存取樹移除,可視情境使用 HTML hidden 或 CSS display: none。role="alert" 的用途是讓重要且有時效性的動態訊息由輔助科技主動宣告,並不要求元素一定使用 position: fixed 或特定 z-index。
無障礙不是「做完 SEO 再考慮的事」。Google 並沒有把通過某個無障礙標準列為一項通用排名加分,但語意清楚的 HTML 和結構化標題也能幫助搜尋引擎理解內容;高對比、鍵盤可操作等要求則主要是為了讓使用者真正能使用網站,不應只用排名效果衡量。
新手最常踩的七個地雷
在各種專案裡,有些錯誤幾乎是每隔幾個專案就會再出現一次。把它們整理出來,目的不在嘲笑誰,單純是讓你在自己寫的時候有個避雷清單。
第一個,!important 氾濫。當你發現樣式改不動,又搞不清楚層疊規則,最偷懶的做法就是在後面加個 !important 強制覆蓋。大量使用之後,重要宣告之間仍得比較來源、layer、優先級與順序,維護會變得更難。!important 應該保留給確實需要提高重要性的情境;一般覆蓋優先用合理的來源順序、cascade layers 和低優先級選擇器管理。
第二個,行內樣式散落各處。前面已經講過,這裡只再強調一次:行內樣式是維護地獄的入口。看到 style="" 連續出現的程式碼,幾乎可以肯定這個專案沒有建立樣式規範。
第三個,過度巢狀的選擇器。新手為了「確保選得到」,會寫出 .wrapper .main .content .post .title 這種又長又深的選擇器。它不只權重高得嚇人、難以被覆蓋,而且只要 HTML 結構稍微一改,整條規則就失效。好的做法是給元素一個語意清楚的 class,直接選那個 class。
第四個,沒有先決定一致的 box-sizing 策略。這個前面提過,混淆 content-box 與 border-box 很容易造成尺寸計算錯誤。若專案採用全域 border-box,記得連 ::before 與 ::after 一起設定。
第五個,用 px 把字級和間距寫死。用 px 指定的字級通常不會隨瀏覽器的預設字級設定改變,雖然仍會隨頁面 zoom 放大。常見做法是用 rem(相對於根字級的單位)設定字級,並避免在根元素用固定 px 抵消使用者的預設值。間距則可以用前面提到的 CSS 變數統一管理,避免一堆魔術數字散落各處。
第六個,忘了 :focus 之類的鍵盤操作狀態。很多人只設計了 :hover 的滑鼠互動,完全沒想過用鍵盤 Tab 鍵導覽的使用者。對他們來說,焦點停在哪個元素上根本看不出來,等於網站不能操作。養成習慣,每次寫 :hover 時順手補上 :focus 或 :focus-visible 的樣式,這對使用螢幕閱讀器或純鍵盤操作的人,是能不能用的差別,也是無障礙檢測裡最基本的項目。
第七個,z-index 滿天飛卻不管理層級。遇到東西被蓋住,直覺就是把它 z-index 調到 9999,幾次之後整個專案的堆疊順序變成一團沒人理得清的數字。比較健康的做法是預先定義幾個層級(例如下拉選單 100、彈窗 1000、提示訊息 2000),用 CSS 變數集中管理,需要的時候引用變數而非亂填數字。這個習慣專案小的時候看不出差別,等版面長大就會救你一命。
一份給新手的三十天實戰路線圖
觀念講完了,剩下的就是動手。很多人卡在「看了很多教學卻寫不出東西」,問題多半出在沒有一條清楚的練習路徑。接下來這份三十天的節奏,是一條可供依循的練習路徑,把它當成參考、按自己的速度調整。
- 第 1 到 5 天:地基。把三種引入方式、選擇器(標籤、類別、ID、組合)和優先級徹底搞懂。這幾天不用碰任何屬性,純粹練「指到對的元素」這件事。自己出一張靜態 HTML,用不同選擇器去指定它,觀察優先級誰贏誰輸。
- 第 6 到 10 天:盒模型與文字。把 box-sizing、padding、margin、border 的關係弄透,再練 font 相關屬性。目標是能徒手排出一篇排版乾淨的文章頁面,字級、行高、留白都自己調到舒適。
- 第 11 到 17 天:Flexbox 與 Grid。先花四天在 Flexbox,把導覽列、按鈕群、垂直置中練熟;再花三天在 Grid,練習切出兩欄、三欄的頁面骨架。這週結束你應該能排出大部分常見的版型。
- 第 18 到 22 天:響應式。加上 viewport meta,開始寫媒體查詢。強迫自己用 mobile-first 的順序,把手機版排好再往上疊桌機版。找一台手機或用瀏覽器的開發者工具模擬,實際看看不同寬度下的呈現。
- 第 23 到 27 天:變數與現代語法。把 CSS 變數、
calc()、clamp()用進來,把之前寫死的色碼和數字抽出來統一管理。這幾天你會開始體會到「樣式表變成系統」的感覺。 - 第 28 到 30 天:實作一個小專案。挑一個簡單的東西,例如一頁式的個人介紹頁或產品登陸頁,從空白開始完整做出來。想找靈感,可以參考這篇網頁排版設計範例,或看網頁設計趨勢了解現在流行的視覺風格。做的過程中遇到不會的屬性,查官方文件、不要瞎猜。
想再往更全面的網頁設計領域走,這篇網頁設計完整指南把 UI/UX 原則、回應式、SEO 友善架構串在一起,是很適合接續閱讀的進階地圖。如果你是用 WordPress 之類的架站工具,了解 CSS 之後,再去碰像 Elementor、Bricks Builder 這類視覺化編輯器,會發現它們底層的觀念其實就是這些,只是包了一層圖形介面。
結語:CSS 是用腦袋寫的,不是用眼睛調的
寫到這裡,我想傳達的一個觀念是:好的 CSS 工程師和差的 CSS 工程師,差別從來不在誰背的屬性多,關鍵是誰腦袋裡有清楚的系統。層疊、盒模型、回應式,這三個觀念貫穿所有你會遇到的 CSS 問題;變數和函式則是把樣式表從「一堆設定」升級成「可維護系統」的關鍵。一旦你在腦袋裡把這套系統建立起來,之後任何新屬性、新框架,都只是往這套系統裡掛新的工具而已。
看待 CSS 的態度,和看待 SEO 是一樣的:它都是那種「慢慢累積、長期見效」的功夫。急不得,但也絕對不會白費。你今天把盒模型弄懂、把優先級的觀念建立起來,這些理解會長期受用。CSS 的語法會持續演進、新的排版方式會出現,但底層的思考結構相對穩定。把地基打穩,剩下的都是往上蓋。
現在,給自己一個小功課:打開你手邊任何一個網頁,按右鍵檢查元素,去看看別人怎麼寫選擇器、怎麼組織盒模型。光是「會看」這件事,就是學 CSS 最快的方式。剩下來的,就交給你的雙手了。