CSS 入門實戰:層疊判定、盒模型與響應式排查的十站路線

CSS 入門不必從背屬性開始。樣式沒反應,先照來源、重要性、層疊層、優先級與出現順序找出競爭中的贏家;爆版,先算 box-sizing 的寬度帳;跑版,先找版面失衡點再設斷點。容器查詢讓元件自帶適應力,Flexbox 與 Grid 按維度分工,開發者工具依固定順序開箱,十個站點把新手最常見的樣式故障逐站收斂。
本頁目錄
想像一個場景:你接手一個上架中的購物網站,想把結帳按鈕換成更醒目的顏色。CSS 改了,檔案存了,瀏覽器也重新整理了,按鈕一動也不動。你把宣告換個位置、再包一層 class,畫面依然故我。同一天,店長用手機打開商品頁,三張促銷卡片擠成一團,圖片壓到文字,按鈕跑出卡片邊界。你打開樣式表,幾千行宣告排在眼前,沒有一行告訴你問題出在哪。
這種卡關很少是因為 CSS 太難。屬性成百上千種,故障模式卻只有幾類;多數人缺的是判斷順序,不是更多語法。CSS 也不是逐條執行的命令清單:你寫下的每一條宣告都只是候選,畫面最終長什麼樣,由瀏覽器依一套固定判準裁決。理解這一點,「樣式沒反應」就有了結構性的解釋:宣告不是被忽略,是在競爭裡輸掉了。輸在哪一關、輸給誰,可以按順序查出來,不必碰運氣。
接下來的內容據此安排:把新手最常帶進門的十類症狀整理成十個站點,畫面壞掉時先分診,判斷問題屬於結構、套用、空間、適配或效能哪一層,再進對應站點照流程排查,每站都可以單獨跳讀。這套判斷不綁任何框架或主題,搬去別的專案一樣用。它還有個商業上的附帶好處:委外溝通時說得出「這是層疊覆蓋問題」,比只說「畫面怪怪的」少一輪來回,也少一次範圍誤判的報價。
快速重點:CSS 不是逐條執行你的指令,而是讓所有宣告競爭,再依固定判準選出贏家。樣式沒反應,先查層疊判定順序與選擇器範圍;爆版,先查 box-sizing 的寬度帳;跑版,先找版面失衡點再設斷點。帶著症狀進對站點,屬性永遠查得到。
畫面壞了,先分診:問題出在結構、套用還是空間?
直接給做法:改程式碼之前,先判斷問題屬於哪一層。內容不見、順序錯亂、標題層級跳來跳去,是結構層的問題,該修 HTML;內容都在但樣子不對,是套用層,往層疊與選擇器查;樣式都在但空間計算不對,寬度爆掉、間距失控,是空間層,往盒模型查;只有特定寬度才壞,是適配層,往回應式策略查;畫面會動但變慢或跳動,是效能層。分診錯樓層,之後的每一分鐘都是白花。
分診靠對照,不靠靈感。第一個對照:暫時停用樣式表再讀一次頁面,內容順序、標題層級、連結位置都還合理,結構層過關,問題在 CSS;停用樣式後內容本身就亂了,先修 HTML。第二個對照:縮放視窗或換裝置檢視,只在某個寬度範圍壞,屬於適配層;什麼寬度都壞,就跟適配無關。第三個對照:用無痕視窗打開,排除快取與瀏覽器擴充功能的干擾再下判斷。三個對照通常兩三分鐘做完,卻能省下一整個下午的亂試。
跳過分診的代價有固定劇本。標題看起來太小,直接調字級,真正的問題是那段文字被包在一般的 div 裡,繼承不到標題樣式,字級調了半天,語意層級還是錯的。卡片太擠,直接加 padding,真正的問題是寬度計算模式搞錯,加得越多爆得越慘。連結點了沒反應,問題在 JavaScript 或 href 寫錯,樣式動得再多也沒用。症狀是結果,樓層才是原因;先分診,就是強迫自己從原因那端開始查。
結構層值得多講兩句,因為它最常被誤當成樣式問題。HTML 的標籤本身帶著預設樣式與語意:h1 到 h6 決定層級,清單標籤決定項目關係,表格標籤決定資料結構。用 div 把一切都包起來再靠 class 硬刻樣式,短期可行,長期會同時傷到可維護性、無障礙與搜尋理解;語意對了,樣式才有穩定的掛點。反過來說,看到「樣式怎麼下都不對」的元素,先確認它的標籤是不是你以為的那種。
左右捲動檢視完整表格
| 你看到的症狀 | 先懷疑的樓層 | 對應站點 |
|---|---|---|
| 改了宣告,畫面完全不動 | 套用層:層疊與選擇器 | 第二站 |
| 寬度設了,版面還是爆、元素被擠下一行 | 空間層:盒模型與寬度計算 | 第三站 |
| 特定寬度才擠壞、手機跑版 | 適配層:回應式、單位與容器查詢 | 第四站、第五站 |
| 對不齊、等不了高、跨層級對不齊 | 排版選型:Flexbox、Grid、Subgrid | 第六站 |
| 載入時內容跳動、互動卡頓 | 效能層:重排與空間預留 | 第七站 |
| 想改不敢改,改一處壞兩處 | 結構債:命名與選擇器深度 | 第八站 |
| 鍵盤或讀屏使用者用不了 | 呈現層的無障礙 | 第九站 |
這張表也可以倒著用:當作整篇的地圖,帶著症狀直接跳到對應站點。想把分診放回更大的脈絡,網頁設計的完整觀念地圖把結構、樣式與互動的分工講得更系統;版面配置範例適合用來累積「正常版面長什麼樣」的眼力,眼力有了,失衡一眼就認得出來。
分診比背屬性划算,理由很簡單:屬性上百種,年年還在新增,背不完;故障模式就那幾類,而且可以遷移。你在主題、編輯器或任何框架上遇到的樣式問題,剝掉工具外殼之後,都會落到同一組樓層裡。判斷框架是自己的,工具換了照用。
時間軸也是現成的線索。昨天還好好的,今天就壞,問自己這中間變了什麼:主題或外掛更新、新裝的程式、上一次的修改。把懷疑範圍收窄到「變動過的東西」,比把整個網站當嫌疑犯快得多;版本更新覆蓋樣式,正是第八站要談的結構債的常見引爆點。
對委外或跨部門合作的人,分診還兌換成一種溝通貨幣。跟設計師討論時,「這個問題是欄寬加總超過容器」比起「看起來很擠」更能得到對的修改;跟廠商回報時,「行動版在特定寬度以下破版」比起「手機看起來怪怪的」更能縮短來回。詞彙精準不是耍專業,是把問題送對樓層的第一步。
樣式寫了沒反應:不是壞掉,是它在層疊裡輸了
先給答案:宣告沒生效,多半不是語法錯,而是三種情況之一:瀏覽器根本沒讀到它、讀到了但選擇器沒命中、命中了但在競爭裡輸給別條規則。前兩種查引入與拼字,第三種照固定順序找贏家:來源、重要性、層疊層、選擇器優先級、出現順序,一關一關比。優先級只是其中一關,而且是逐級比較,不是總分。
入門教學常把 CSS 講成「寫了就會生效」的命令,這個印象最害人。每一條宣告都是提案,畫面上只看得到贏家;輸掉的宣告不會消失,它還躺在程式碼裡,只是被蓋住了。所以「刪掉重打一次」通常沒有用,重打的還是同一個候選,會輸的一樣輸。越急著加宣告,候選越多,畫面越亂,因為你從頭到尾都沒找出目前的主宰者是誰。
把「有沒有被讀到、有沒有命中」先排掉,值得按順序做四件事。第一步,確認檔案真的有載入:在頁面原始碼或開發者工具的網路面板,找到你寫的那份樣式表,路徑對、狀態正常,而且引入標籤的媒體條件符合目前畫面;寫給列印用的樣式,在螢幕上本來就不會生效。第二步,確認選擇器命中:在開發者工具點選目標元素,看你的宣告有沒有出現在樣式面板;完全沒出現,通常是拼錯字、大小寫不符,或 class 掛在別的元素上。第三步,宣告有出現但被標成覆蓋,進入判定順序找贏家。第四步,宣告明明是有效宣告、結果卻不如預期,查屬性本身的繼承規則:字體顏色這類屬性會從父層繼承,邊框、間距這類屬性本來就不繼承,寫在父層期待它傳下來,是常見的誤會。
多條規則同時落在同一個元素上時,瀏覽器不會把宣告加總或取平均,而是照固定順序逐步分出勝負:先比來源,再比重要性,接著比層疊層,然後才是選擇器的優先級,每一關分出結果就不再往下一關看,全部平手才由出現順序定輸贏。這裡最常被誤解的是 specificity:它不是一個可以加總的總分,而是像分欄數字逐欄比較,id 那欄先分勝負,平手才輪到 class 與偽類,再平手才比元素標籤,所以十個 class 疊起來也贏不過一個 id。

來源這一關也解開一個老疑問:為什麼你的樣式總是贏過瀏覽器預設?因為瀏覽器自帶的使用者代理樣式表,在來源判定上就是最弱的一群,同層級比較根本輪不到。各家的預設不盡相同,也是跨瀏覽器小差異的來源之一;很多骨架在開頭做一小段重設,就是為了把起跑線拉齊,而不是什麼儀式。
MDN 的 specificity 條目把逐欄比較寫得很清楚,搭配開發者工具讀效果更好。順帶一個現代陷阱:用 :is() 把一長串選擇器合併時,整條規則的優先級以括號裡最強的選擇器計。簡化是真的,權重變高也是真的,整理樣式前先想清楚這筆交換。
MDN 對層疊的說明把行內樣式的位階也交代得很清楚:一般的行內樣式會贏過樣式表裡的一般宣告;同一位作者來源的 !important 宣告又能蓋過一般行內樣式;行內也可以寫 !important,而且位階更高。當來源、重要性、層疊層與優先級全部相同時,才輪到出現順序說話,後寫的贏。把這串位階放在心裡,樣式被蓋掉時你就有完整的嫌疑犯名單,不必再用猜的。
層疊層值得單獨認識,因為外掛與主題越來越常用它。把樣式分進具名的層,層與層的先後在宣告時就說清楚,先宣告的層輸給後宣告的層,而沒進層的樣式在一般宣告裡反而贏過有進層的。你的樣式沒進任何層、別人的樣式進了層,你照樣贏;反過來,你自己把樣式放進層,卻沒搞清楚順序,就會輸得莫名其妙。第十站的微案會再遇到它。
用一段情境把整個判定走完。商品頁的價格字色寫在主題樣式表,你又在自訂區補了一條,頁面編輯器還在元素上掛了行內樣式,某個外掛的樣式帶了 !important。四條宣告都想決定同一個字色:行內樣式先贏過樣式表裡的一般宣告,外掛的 !important 又壓過行內,除非行內也寫 !important;你的自訂宣告若在沒有層的樣式表裡,贏過所有進層的規則,卻還是輸給前面兩位。把每條宣告放進這個階梯裡跑一遍,誰該贏、為什麼,紙上就能推演出來,這正是判定順序的價值:結果算得出來,不是運氣。
範圍比你想的更常出事
選擇器的匹配範圍,是「沒反應」與「套過頭」共同的變因。.nav > li 這種用大於符號連接的子選擇器,只會命中直接放在 nav 下一層的清單項目;.nav li 這種以空格連接的後代選擇器,會命中 nav 底下任意深度的清單項目,子選單、更深層的選單全部算在內。把子選擇器寫成後代選擇器,樣式會灌進你不想動的子選單;把後代寫成子選擇器,深層項目就收不到樣式。範圍一開始就跟你以為的不同,症狀看起來卻很像層疊問題。

把這個差異放回導覽列的情境看:多數選單至少兩層,第一層的間距用子選擇器控制,第二層選單有自己的縮排與字級。若間距規則被寫成後代選擇器,子選單全部被撐開;若字級規則被寫成子選擇器,子選單又收不到調整。兩種症狀都像「樣式怪怪的」,往上追都是範圍。學會先問「這條選擇器到底命中誰」,一半的靈異現象會當場變成普通事件。
頁面編輯器則是接手者最常見的覆蓋來源。視覺化工具為了讓你按下去就生效,傾向輸出行內樣式,或一次掛上多個 class 的高權重選擇器;你之後在樣式表裡寫的一般宣告,常常輸在起跑點。這不是編輯器的錯,是它的設計目標使然,但你要知道自己天天在跟什麼等級的對手競爭。想深究這類工具的行為模式,Elementor 頁面編輯器的完整教學值得整篇讀過。查到這裡還不算結案,輸贏的最終證據在開發者工具,第十站會給固定流程。
知道位階之後,使用紀律就跟著出來。行內樣式留給真正一次性、頁面產生器輸出的場合,自己手寫時盡量別用;!important 留給覆蓋你無法改動的第三方樣式,而且要集中管理、寫註解說明原因,別讓它散落各處。這兩個都是逃生口,不是日常通道;把它們當日常,等於把每個未來的修改都升級成一場權重戰爭。
寬度設了還是爆版:從 box-sizing 重算這筆帳
先講結論:爆版的第一嫌疑人是寬度計算模式,檢查的順序是先問「這個元素的 width 到底涵蓋哪些層」,再談數字大小。多數爆版不是數字設錯,而是你在心裡用一套算法、瀏覽器用另一套,兩邊的帳從沒對上過。帳目模式確認之前,任何寬度上的試錯都是白費。
爆版的帳要從 box-sizing 算起。content-box 模式的 width 只算內容區,padding 與 border 要逐項加上去;border-box 模式的 width 包含內容、padding 與 border;無論哪種模式,margin 都不算在 width 裡。一張卡片設定寬度 300px、左右 padding 各 20px、左右 border 各 5px,content-box 在畫面上實際佔 350px,border-box 就是 300px。三張這樣的卡片排同一列,每張都以為自己是三百,畫面上各多出五十,總寬度爆掉、第三張被擠到下一行,多數爆版就從這筆帳開始。

所以看到爆版的標準動作是先問:這個元素的 box-sizing 是什麼?答案確認了,數字才有意義。許多骨架會做全站重設,把元素統一成 border-box,讓「設多少就佔多少」成立;重設本身合理,但接手舊站要小心,既有版面是在 content-box 的帳目下調出來的,一刀切過去,某些區塊會突然變窄、間距整組走位。重設與否是取捨,不是信仰;要做,就搭配完整的一輪視覺回歸檢查,而不是改完就上線。
確認的模式不用背,開發者工具查得到:選到元素,在計算後樣式裡找 box-sizing 的值,上下游的帳目當場對齊。另一個保險是限制條件:欄位設了最小與最大寬度,等於給版面上下界,內容特別長或特別短時不會無限撐開或縮扁;保險不能取代算帳,但能讓算錯的代價變小。
margin 有自己的脾氣。上下相鄰的兩個區塊,各自的 margin 不會相加,而是取較大者合併成一個;想把兩個區塊推得更開,狂加 margin 有時毫無變化,因為你加的那一側根本沒有參與摺疊之外的計算。方向不對,數字加再多也推不動。這與寬度帳目屬於同一族問題:都是「你以為的加法」與「瀏覽器的加法」不一致。
摺疊有邊界,不是到處都發生。它出現在一般區塊排版的上下方向;元素被排成彈性或格線的項目時,項目之間的 margin 不摺疊。這也帶出一個實務選擇:排版的間距與其用兩側 margin 互相推擠,不如交給現代排版的間距屬性,或用單側的 margin 設節奏,帳目單純,行為也好預測。
症狀還會變形。按鈕加了左右 padding,文字沒換行卻把按鈕撐成不合理的寬;卡片加上 border 之後剛好溢出一點點,橫向捲軸就此出現;輸入框設了百分比寬再加 padding,表單整排超過容器。看到「加一點就爆一點」的模式,直接回來查 box-sizing,不要在寬度數字上試錯。盒模型各層的互動還有更多細節,盒模型的圖解說明把它一層層拆開。
手機跑版先別急著加斷點:臨界點屬於版面,不屬於裝置
答案先講:不需要為每種手機各寫一組樣式。斷點由版面的失衡點決定,不是由裝置型號決定;把可用寬度逐步縮窄,觀察內容在什麼寬度開始撐不住,那個臨界點才是斷點。判準放錯邊,維護的帳很快會找上門:為某支當季手機硬湊的條件,換機潮一來就得重談一次;掛在自己版面臨界點上的條件,內容不改它就不變,樣式數量也守得住。
設定斷點的順序應該倒過來:不是先抄一張裝置寬度對照表,而是把視窗從寬往窄慢慢拉,盯著版面看它在哪個寬度開始擠壞;內容開始互相壓迫、欄位開始錯位的那個臨界寬度,才是你的斷點。裝置的解析度年年換,版面結構相對穩定,讓版面的失衡點決定媒體查詢的條件,比讓特定手機型號決定更耐用。

實作上建議一邊拉、一邊記。每發現一個失衡點,記下寬度數字與當下壞的性質:是欄位坍塌、字級過大、圖片撐爆,還是按鈕換行。一個版面常有兩三個臨界點,各自對應不同性質的調整;把它們混成一個「手機版」大斷點,等於強迫所有問題在同一個寬度一起修,樣式只會越疊越厚。記錄也讓團隊後續討論有共同座標,而不是各憑印象。
流量現實也站在這個做法後面。Statista 的長期統計顯示,行動裝置佔全球網站流量的比例已連續多年超過一半;Google 也早已改用行動優先的檢索方式,以行動版內容為主要評估對象。手機畫面不是桌面版的附屬品,是多數訪客看到的第一現場;在行動版被收進摺疊或隱藏的內容,重要性也會跟著打折,別把行動版當成砍內容的地方。
找到臨界點之後,媒體查詢才有東西可寫。寫法本身不複雜:條件就是你觀察到的寬度,內容是「在這個寬度以下,哪些性質要換值」。常見的調整對象依序是欄位數量、字級與行距、區塊間距、圖片尺寸;順序有講究,先讓結構降欄,再談細部尺寸,欄位沒降之前,字級調了照樣擠。圖片單獨記一條:讓它隨容器縮放並保持比例,撐爆版面的機會就少一大半。
還有一個順序上的選擇:先寫窄版,再往上疊寬版。從最受限的版面開始,你被迫早點決定內容的優先序,次要的裝飾與區塊會自然被推到較寬的畫面;反方向先寫桌面版,容易把所有東西都排好再想辦法塞回小螢幕,斷點越補越多。兩種方向都寫得出能用的樣式,差別在過程中誰逼你做取捨。
還有一類爆版來自內容本身:沒有斷點的長網址、連續的英數字串,會像一根硬棍子把容器往外撐,樣式怎麼算都救不了。處理這類內容的換行屬性存在,但下之前先想清楚斷的規則,中文逐字可斷、西文照單字斷,混排時規則會互相打架;先確認內容來源能不能先整理,再考慮樣式端的手段。
翻轉也要看:手機直拿與橫放的寬度差,足夠讓勉強過關的版面再壞一次;平板介於手機與桌面之間,別假設它一定拿到哪一邊的樣式。這也是把判準放在版面而非裝置的另一個理由,橫豎切換的瞬間,改變的從來是可用寬度。
讓一部分斷點直接消失
clamp() 的語意分三段:括號裡三個值,第一個是下限,第三個是上限,中間是偏好值。可用寬度很小、偏好值低於下限時,套用下限;寬度落在範圍內,照偏好值彈性伸縮;超過上限就封頂,不再繼續長大。

把 clamp() 用在字級與主要間距上,寬度連續變化時尺寸跟著連續調整,不少原本只為改字級而設的斷點可以刪掉,樣式表也少一組條件要維護。中文有個額外考量:筆畫密度高,字級縮小時可讀性比西文更早出問題,下限要設在還能輕鬆閱讀的位置,不能只跟著視覺稿走。字體與中文字的搭配,中文字體的選擇指南有完整討論;字級與層次的節奏可以對照排版設計技巧;中西混排時,英文字體的挑選也一起看。
左右捲動檢視完整表格
| 單位 | 相對於誰 | 適合接手的跑版症狀 |
|---|---|---|
| rem | 網站的根字級 | 按鈕與內文字在窄螢幕過小、間距跟著失控 |
| % | 父容器的寬度 | 側欄佔比把主欄擠壞、欄寬加總超過容器 |
| vw | 視窗寬度 | 全幅區塊的內距在窄螢幕爆掉,常與 clamp() 併用設上下限 |
單位挑選的本質是「相對於誰」。字級與間距錨在根字級,換主題時整套等比搬;欄寬錨在父容器,父層變化時自動跟;只有真正想隨視窗等比縮放的性質才用視窗單位,而且通常要配上下限,避免極窄的畫面上縮過頭,這也是表格裡視窗單位一欄標註要與 clamp() 併用的原因。
「要不要另做手機版」是另一個決策分支。多數內容型網站,同一份程式碼適配即可;行動版與桌面版內容差異大、或團隊維護資源充足時,自適應路線才值得考慮,自適應與響應式的比較把兩條路的成本與風險攤開。適配的完整做法,響應式設計的指南有系統性的步驟。
同一張卡片放進側欄就擠壞:容器查詢改變適配的判斷
先別為每個位置各寫一套覆蓋樣式。容器查詢讓元件依最近祖先查詢容器的尺寸自動調整排版,規則套用於該容器的後代;元件自帶適應能力,位置相關的覆蓋就能刪掉。判斷的分岔點也在這裡:症狀隨視窗寬度變化,屬媒體查詢;症狀隨放置位置變化,屬容器查詢。
行銷頁的典型情境(示意):同一支促購橫幅,放首屏是全寬,放側欄只剩三分之一,放活動彈窗更窄。用媒體查詢處理,你要為每個位置各寫覆蓋,橫幅一搬家,覆蓋全數失效;用容器查詢處理,橫幅自己知道所在容器多寬,該換排版就換排版,搬去哪裡都成立。
容器查詢把適配的主導權交回元件自己:在卡片外層的元素宣告查詢容器之後,同一張卡片放在主內容區、側欄或小彈窗裡,都會依最近祖先查詢容器的實際寬度自動換排版,規則只套用到那個容器的後代。Chrome 從 106 版、Safari 從 16 版起提供這項支援。

宣告的位置有講究。查詢容器宣告在祖先元素上,常見是卡片的外層容器;條件規則寫在元件自己的樣式裡,描述「當最近的查詢容器寬度落於某範圍時,內部如何調整」。一頁裡可以有多個查詢容器,需要區分時再給容器命名,讓規則指名要比對哪一個。這些細節與支援度,caniuse 的容器查詢頁面隨時可查,主流瀏覽器已經在位。
查詢軸也有個慣例可依:多數情境以行向寬度為查詢對象,因為高度通常隨內容生長,拿高度當條件,規則容易在內容一變時集體翻盤;要用雙軸的尺寸查詢,容器得有明確的高度,條件苛刻得多。先問內容在哪個方向失去耐性,再決定查詢軸,跟排版選型的思路是同一條。
MDN 對尺寸容器查詢的定義講得很精確:查詢依最近的祖先查詢容器而定,樣式規則套用到容器的後代。
原理回到定義就看得懂:查詢的對象是祖先容器而不是視窗,所以同一個元件放進不同容器會得到不同結果;規則套用於後代,所以元件內部的排版邏輯是自包含的,搬走也帶得走。把促購橫幅、報名表單、商品卡都往這個方向整理,之後版面再怎麼重排,元件都搬得動;頁面骨架的寬窄變化仍交給媒體查詢,兩層各司其職,覆蓋樣式自然變少。想看這類做法撐起的當代版面樣貌,網頁設計趨勢提供了不少案例方向。
導入也有順序:先把「到處搬」的元件挑出來,通常是卡片、橫幅、表單這類會被放進不同容器的;給它們的外層加上查詢容器宣告,再把手寫的位置覆蓋一段一段刪掉,刪一段驗一段。位置相關的覆蓋清乾淨之後,樣式表會少一整族只為特定頁面存在的規則,元件也才有機會真的被重用,而不是被複製貼上。
該用 Flexbox、Grid 還是 Subgrid:對齊問題的選型
按維度選。內容沿一個方向排列,用 Flexbox;頁面要切成二維區域,用 Grid;需要不同層級沿用同一組格線,加上 Subgrid。三個不是互斥選項,分層混用是常態,把問題的維度講清楚,選型其實沒有懸念。
常聽到一種說法:Flexbox 能排的 Grid 都排得動,學一套就夠。可行性沒錯,維護性是另一回事。一維的內容用二維工具去刻,規則會長出多餘的行列定義,之後每改一次,都要在二維結構裡找回那個其實只有一維的意圖,改動範圍跟著放大(示意)。工具對準問題的維度,程式碼才會跟著版面的語意走。
選型的心智模型一句話就夠:Grid 管二維,Flexbox 管一維。頁面要切成幾個區塊、欄位怎麼分、哪個區域要跨列跨欄,是二維問題,交給 Grid;一列按鈕要置中、一排導覽項目要撐開、卡片裡的圖片文字要上下排,是一維問題,交給 Flexbox。兩者不是二選一,實務上經常分層混用:外層用 Grid 切出頁面骨架,區塊內部用 Flexbox 處理對齊與分配。

用產品列表頁走一遍(示意):外層用 Grid 定義三欄與欄距,寬度不足時降為兩欄;每張卡片內部,圖片、名稱、價格、按鈕用 Flexbox 直向排,按鈕列靠外側對齊。頁面骨架是二維的,卡片內部是一維的,兩層各自用對工具,改版時各動各的。同樣的分層思考撐起了 Bento Grid 區塊式排版,那一類版面把「大區塊用格線、區塊內用彈性排列」推到更明顯的程度。
兩套工具各有常見的坑。彈性排列的項目預設不換行,塞太多就往外出,記得打開換行並用間距屬性控制列距;格線的欄寫成固定值,內容一長就溢出,改用比例或最小最大值,讓欄自己伸縮。這些坑都不是工具的缺陷,是「維度想清楚之前就動手」的代價。
再看兩個對齊場景。定價表的三欄方案,按鈕想貼齊卡片底部,欄位內容長短不齊,這是 Grid 與 Subgrid 的組合場景;表單的標籤與輸入框對齊、說明文字的縮排節奏,則是一維的事,交給彈性排列就夠。對齊問題先分維度,工具自然就選對。
Subgrid 補上跨層對齊這一塊:子網格在指定的軸上,沿用它所跨越的父層軌道,讓不同巢狀層級的內容沿同一組格線對齊。做雜誌式版面時,每張卡片的標題、摘要、按鈕高度各自不同,有了 Subgrid,它們可以在整個列表裡橫向對齊。Firefox 71 率先支援,Safari 16 與 Chrome 117 其後跟上。

跨層對齊為什麼本來很難?因為每個子容器各自定義軌道時,子層的第三條線與隔壁卡片的第三條線之間沒有任何關係,各自內容多高就多高;沿用父層軌道之後,兩邊才共享同一組線。想追最新支援數字,查 caniuse 的 Subgrid 頁面;要不要用它的判斷很單純:沒有跨層對齊需求,普通 Grid 就夠,有雜誌式排版或多層卡片對齊的講究,才值得引入。
選型三問收尾:這段內容有幾個方向要對齊?區塊需要跨列跨欄嗎?不同巢狀層級需要共用同一組格線嗎?第一問答一個方向,用 Flexbox;答兩個方向,用 Grid;第三問答是,再加 Subgrid。「全都用 Grid」的反射解,省了十分鐘思考,通常預支了之後幾個月的維護成本。
網站感覺變慢:CSS 真正的成本在執行方式,不在行數
CSS 會拖慢網站,但主因很少是行數。三個檢查點:動畫動到哪些屬性、媒體有沒有預留空間、樣式資源怎麼載入。每個點都有對應的量測方式,不必憑體感爭論。
動畫的成本分野在屬性選擇:transform 與 opacity 的變化不觸發版面重排,瀏覽器通常可以在合成階段處理;改 width、height、top、left 則可能引發版面重新計算與繪製,成本依頁面結構而異。把「改變外觀」與「改變占位」分開設計,是多數互動效果的第一條效能線。

拿懸停效果當例子(示意):按鈕被指向時想放大一點。用 transform 的縮放,按鈕的占位不變,周圍元素不需要重新排列;直接改寬高,同一列的鄰居全部要重算位置,列表一長,成本就跟著長。多數互動只想改外觀,不需要動占位,這條線畫清楚,效果與成本就能同時顧到。
重排也不是每個頁面都一樣痛。同一個懸停放大的效果,放在只有三顆按鈕的頁尾,成本小到量不出來;放在幾百列的商品牆,每列都要重算,體感就出現了。所以效能原則要看場合應用:互動密集、列表長、巢狀深的頁面先守,單純的著陸頁不必為了原則把效果全部砍掉。原則本身是免費的,判斷什麼時候用,才是價值所在。
圖片的空間要先留。在 HTML 寫上 width 與 height 屬性,或用 CSS 的 aspect-ratio 指定比例,瀏覽器會在圖片載入前先保留那塊空間,內文不會先擠在上方、等圖片下載完成再被推下去。

這正是 CLS 要抓的症狀:內容先出現,圖片載入完成後把下面的段落整塊推下去,讀者正在讀的那行字突然跳走。預留空間的成本接近零,效益直接反映在版面穩定度上。比例設定的細節,MDN 的 aspect-ratio 條目有完整說明。
量測要有座標。Core Web Vitals 把速度與穩定度整理成幾個可量測的指標,web.dev 的文件是定義與量法的出處:CLS 看版面位移,LCP 看主要內容出現的速度,INP 看互動回應。指標是體檢報告,不是軍備競賽;改善多少依網站條件而異,先量、再改、再量,比對外承諾數字誠實。
常見兇手可以排個順序:未預留空間的媒體、載入中的字型替換、觸發重排的動畫。字型替換值得單獨講:文字先用備用字體繪製,正式字體下載完成後整批換上,每行寬度都變,等於全頁重排一次。字型子集化與載入策略都能壓低這段震盪,屬於字型工程的範圍,樣式層能做的是讓替換前後的行高與字寬差異縮小,震盪自然小。
整理成可以照跑的自檢順序:
- 打開頁面上所有動畫與轉場,確認移動的性質屬於 transform 或 opacity,會改占位的效果用縮放類性質重做。
- 逐張檢查媒體:有沒有 width 與 height 屬性或比例設定,載入之前空間是否已經預留。
- 檢查字型策略:替代字體與正式字體的字寬行高差異大不大,字型檔能不能子集化。
- 看樣式載入:首屏用不到的大段樣式有沒有延後,引入標籤的媒體條件是否符合實際使用情境。
樣式資源的載入也有帳要算。瀏覽器算不出樣式就畫不出畫面,所以樣式表會擋住第一次繪製,這是機制不是意外。治理方向就兩條:讓首屏真正需要的那份小一點、早一點到,把其餘延後;以及讓用不到的晚一點來,例如條件不符合的媒體樣式,下載歸下載,不阻塞繪製。樣式表裡沒用到的規則也不是免費的,解析與比對都要時間,只是比重相對小,先抓大頭再回頭清這個。
載入治理還有幾個延伸面向:圖片壓縮工具把媒體體積壓下來,延遲載入讓首屏之外的圖片不搶頻寬,CDN 的加速原理縮短資源傳輸距離;整體策略看網站速度優化指南,指標對應的改法看Core Web Vitals 的優化實戰。CSS 在這張地圖裡通常不是最大塊的,但重排與位移這兩項,主導權確實在樣式手上。
CSS 改不動了:問題多半在命名與結構,不是語法變難
樣式表會走到沒人敢動,通常是三個病灶:沒有命名系統、深層巢狀選擇器、樣式與 DOM 位置綁死。解法是讓關係看得見:引入一套命名法,把高重疊的樣式逐步收斂成元件。這是在還結構債,不是在學新語法。
「會動就好」的命名,在第三個頁面上線後開始打架。卡片一處叫 card,另一處叫 box,第三處叫 item;紅色按鈕的覆蓋寫在頁面層,改了紅色,首頁變了、活動頁沒變,因為那邊又有一份自己的紅。維護成本這時以非線性的速度成長,幅度依專案而異;症狀是「改一處壞兩處」,根源是關係沒有寫在名字裡。
BEM 把元件名字拆成三層:Block 是元件整體,例如 .card;Element 是組成部分,用兩條底線連接,例如 .card__title;Modifier 是變體,用兩條橫線連接,例如 .card--dark。看名字就知道用途與從屬關係,樣式不再依賴深層的 DOM 位置。

命名法把關係搬進名字,還有一個紅利:選擇器可以變淺。深層巢狀選擇器把樣式綁死在 DOM 位置,結構一改全部失效,而且權重高,覆蓋要付出更高代價;名字自帶語意之後,一層 class 就夠,HTML 怎麼搬,樣式跟著名字走。搜尋也變簡單:想找深色卡片的所有使用處,搜變體名字就到齊,不必在檔案間用肉眼比對。
現代語法也在幫忙還債。傳統上「卡片裡有圖片時加上邊距」要靠 JavaScript 先加 class,現在 :has() 讓樣式直接表達這層關係,MDN 的 :has() 條目有完整語法。時代背景也變了:微軟在 2022 年 6 月讓 IE 11 退役之後,「考慮舊瀏覽器」這句老藉口失效大半,敢用新語法本身就是減債。
BEM 不是唯一解。OOCSS、SMACSS、utility-first 是同一個問題的不同答案,差別在約定放哪裡:放進名字、放進檔案結構,或交給工具產生的 class。選擇標準很實際:團隊讀得懂、新人接得上、兩年後還找得到樣式,就是好系統。
重構有順序,攤開來是四步:
- 挑最常修改的頁面當起點,那裡的債利息最高,還了立刻有感。
- 盤點那頁的元件清單:卡片、按鈕、表單、橫幅各自認列,先收邊界最清楚的。
- 每次只收一個元件:改名、刪覆蓋、驗證一氣呵成,中間不穿插別的修改。
- 上線觀察一輪,沒有回報再收下一個,有回報先處理回報。
大爆炸式重寫的失敗率比慢慢還債高得多,因為同時改動的變數一多,出問題時連兇手都指認不出來。樣式集中在外部樣式表的維護理由也在這裡:看得到全貌,才看得到重複;散在行內與各頁面的樣式,連「哪裡用了紅色」都答不出來。要再進一步,變數與 mixin 可以消掉大量重複,SCSS 的教學把取捨一起講。
用鍵盤把網站走一遍:三個無障礙初檢動作
畫面正常的網站,未必人人用得了。滑鼠看得見的問題,眼睛都找得到;鍵盤與讀屏使用者遇到的牆,坐在螢幕前看不出來,所以初檢的動作設計成「換一種輸入方式走一遍」:只靠鍵盤、只看對比、只看語意樹。三個動作做完,最常見的排除性問題會自己現形,不需要任何檢測工具或認證流程。
三個動作可以先擋掉常見問題。第一,只按 Tab 走完頁面,看焦點移到哪裡都清楚可見、每個操作都到得了;第二,檢查文字對比,WCAG 2.2 的 1.4.3 要求一般文字對比至少 4.5:1,大字至少 3:1;第三,確認鍵盤導覽能完成所有操作,不依賴滑鼠懸停。這三項是初步檢核,通過不代表通過全部 WCAG,但足以讓最常見的排除性問題現形。

焦點可見是第一條線,因為它最常被親手拔掉。許多主題為了視覺乾淨,把 focus 的外框整個拿掉,鍵盤使用者從此在頁面上失去位置;:focus-visible 提供了兩全的寫法,滑鼠點擊不出外框、鍵盤聚焦才出現,MDN 的條目有語法與適用條件。對比的那兩個數字來自 WCAG 2.2 的 1.4.3,是 AA 等級的門檻,規範對大字有明確定義,約是放大後的十八點字或十四點粗體;量法不用背,開發者工具的檢色器會直接顯示前後景的對比數值,淺灰配白底的那種優雅,常常就倒在這條線前面。
對比不足的補救通常有兩條路:加深文字色,或把背景調亮,動哪邊都行,動完重量一次就是。真正要小心的是品牌色的執念:識別色落在門檻之下時,把它用在標誌與裝飾,承載資訊的文字換成夠深的同系色,品牌與可讀性兩邊都保得住。門檻數字不是要你放棄設計,是提醒把對比當成設計條件之一,而不是被撿剩的那個。
模態框的焦點要有去有回:彈窗開啟時,焦點移進框內;按下 Tab 與 Shift 加 Tab,焦點在框內循環,不會落到被遮住的背景;關閉彈窗時,焦點回到觸發它的那顆按鈕。

訂閱彈窗是行銷頁最常見的情境(示意):折扣碼的彈窗跳出來,焦點卻還留在背景的導覽列上,鍵盤使用者按了半天 Tab,都在讀不到優惠內容的選單裡打轉;關掉彈窗,焦點又飛回頁頂,剛剛讀到哪裡全部歸零。開啟移入、框內循環、關閉返回,三步做齊,這個元件對鍵盤與讀屏使用者才算真的存在。長頁面再補一條「跳到主內容」的連結,讓鍵盤使用者不必每次逐項穿越整排導覽。
鍵盤走訪的順序也值得看一眼。原則上,順序跟著 HTML 的結構走;用樣式把區塊搬到別的位置,讀屏與鍵盤的順序不一定照視覺稿重排,而彈性與格線排版的排位設定則會連順序一起搬。自己走一遍 Tab,聽聽順序讀起來像不像一句通順的話,比對著設計稿猜可靠。
兩種隱藏服務不同目的。aria-hidden="true" 僅將該元素與其底下的子節點,自輔助科技的存取樹中移除,畫面上它還在,也因此不該套在可以聚焦的元素上,否則焦點會停在螢幕閱讀器讀不到的東西上;display: none 或 HTML 的 hidden 屬性,則是畫面與可存取樹一起移除。隱藏畫面與隱藏語意是兩件事,選錯邊,某些使用者會碰到看不見也讀不到、或看得到卻讀不到的狀態。

動態效果還有一條保險絲:系統層的「減少動態」偏好,可以用媒體查詢偵測,讓設定過的使用者拿到淡入淡出取代位移動畫的版本。效果投入之前先問一句「動不了的使用者拿到什麼」,是樣式層最便宜的體貼。對比與配色則不是審美題,是可讀性工程:色彩學的基礎先建立色相、明度、彩度的共同語言,配色計畫的實務步驟把對比檢查排進流程裡;互動回饋也要看得見,hover 與點擊的狀態差異屬於按鈕動效設計的範圍,動得輕,但要動得被看見。
DevTools 的開箱順序:先看裁決結果,再回找有效宣告
工具功能再多,排查樣式問題時的紀律只有一條:裁決出爐之前,程式碼一行都先別動。多數人卡關不是不會用工具,是把順序用反了,先盯著一堆宣告猜,越猜越亂。固定流程只有三步,做完通常就有答案。
開發者工具的開箱順序固定:先看 Computed 面板的計算後樣式,確認這個屬性由誰勝出;再回 Styles 面板找有效宣告,讀被覆蓋的競爭宣告與選擇器的 specificity 欄位;兩邊都對上了,才回到程式碼修正。順序不能倒,先看裁決結果再回找依據,就不會被一堆看似相關的宣告牽著走。

定位想清楚,工具才順手:開發者工具不是拿來即時改樣式的沙盒,而是裁決紀錄的閱卷現場。Styles 面板會標出每條宣告的輸贏,輸掉的宣告還在,只是被標為覆蓋,往上游看還找得到來源檔案與行號;Computed 面板給的是所有競爭結束後的最終值,等於裁判的判決書主文。判決先讀,卷宗再翻。盲目的 !important 是把債務往上搬一層,你贏了這一條,下一個要覆蓋你的人只能疊得更高,到頭來整份樣式表變成軍備競賽。
想像一個場景:按鈕的字級怎麼改都不動。Computed 一看,font-size 的計算值根本不是你寫的數字;回 Styles 面板,有效宣告來自一段你沒寫過的樣式,而且掛在某個層疊層裡;順著來源翻,是某個外掛的層宣告在樣式表裡畫了一層。三分鐘定位是層疊層覆蓋,不是快取,也不是瀏覽器壞掉;修正只需要把自己的樣式放進對的層,而不是疊上 !important。第二站講的判定順序,在這裡變成你親眼看到的證據。
幾個小紀律讓流程更穩。一次只改一個變數,改完就重新整理驗證,別把三個猜測同時送上線;懷疑快取就用無痕視窗加強制重新整理,排除後再往下查;查行動版就把工具切到裝置模擬,在目標寬度下重走同樣三步。滑鼠懸停才出現的樣式,工具可以強制鎖定狀態,不用把游標真的停在按鈕上猜;折疊選單、下拉選單、提示文字這類互動樣式的排查,都靠它把「看不到的狀態」固定在畫面上。同一套工具也是搜尋面向的入口:檢索器看到的內容、行動版呈現、結構資料,都在裡面查得到,用 F12 找 SEO 問題的檢查法把流程整理成了實戰清單。
自己修、套編輯器、找人做:維護樣式的三條路怎麼選
按修改頻率與風險分。一次性小改,自己修;常態改版,用主題或頁面編輯器,並懂基本覆蓋原理;牽涉品牌與轉換路徑的大改,找專業執行。判斷依據是長期維護成本,不是技術崇拜,也不是把預算當唯一變數。
左右捲動檢視完整表格
| 路線 | 時間成本 | 可控性 | 主要風險 |
|---|---|---|---|
| 自己修 | 單次低,學習曲線在前 | 高,改的是自己寫的覆蓋 | 沒備份就上線、越修越亂 |
| 主題或頁面編輯器 | 中,要學工具 | 中,受工具輸出限制 | 更新覆蓋、高權重選擇器難接手 |
| 專業執行 | 溝通與預算在前 | 高,規格可談 | 依合作方而異,驗收要有清單 |
用情境套一遍更好選。年節換個橫幅顏色、活動頁加一段樣式,一年碰不到幾次,學整套系統不划算,照著第十站的三步找到目標、寫對覆蓋,收工。每月都在調版面、上活動檔期,值得把主題或頁面編輯器學熟,讓例行改版不依賴工程資源。品牌識別翻新、結帳流程重排,牽一髮動全身,這種規模交給專業執行,你的責任是把規格與驗收講清楚:指定要測的寬度範圍、要過的對比門檻、驗收用的裝置清單,寫進去,才叫做過。
自修有底線,整理成清單是這樣:
- 改動放在子主題或佈景提供的自訂 CSS 區,不動主題本體,更新來了才不會被洗掉。
- 改前備份,改後至少在手機寬度與無痕視窗各看一次。
- 被覆蓋時回到判定順序找贏家,不是疊宣告。
- 牽涉寬度的修改,先確認 box-sizing。
- 改到停不下來,回頭檢查命名與選擇器深度。
這份清單就是前十站的收斂版,也是自修者的安全網:它擋不住所有問題,但能擋住大多數把小毛病修成大事故的路。
編輯器路線要認清雙面性。它把改版速度變快,同時把覆蓋權重變高,兩者都是真的。懂一點判定順序,你才知道工具幫你寫了什麼、自己寫的宣告為什麼輸,也才不會被工具綁架。這條路的兩個常見選擇,WordPress 網頁設計與 Bricks Builder 教學各自把能力邊界講得很清楚。
收個邊界:CSS 處理呈現,不處理內容語意、資料與商業邏輯。標題該用什麼層級、圖片該放什麼替代文字、結帳流程少一步,這些問題樣式一個都解不了,硬用樣式去修,只是把問題藏進畫面裡。入門的最小裝備也就三件:層疊的判定順序、盒模型的寬度帳、失衡點斷點。三件帶著,屬性永遠查得到。CSS 入門的終點不在會寫多少屬性,而在畫面壞掉時,你知道下一步查哪裡。