題幹與適用場景
設計系統需要讓同一個 padding、顏色或 flex-direction 根據媒體、樣式或特性條件變化,同時避免為每個變體複製整組規則。團隊提出使用 CSS if(),但仍要支援不認得此函式的瀏覽器。請給出採用邊界、寫法與驗證計畫。
這題考察 CSS 值求值與級聯,不是把 JavaScript 條件搬進樣式表。MDN 說明 if() 會按順序回傳第一個為真的分支;CSS Values 5 草案明確指出未命中時產生空 token stream,宣告不會像 @media 那樣自動回復級聯。2026 前端面試資料仍把 CSS 版面、級聯與瀏覽器差異視為基礎訊號,因此答案要能把新語法放回相容性與除錯框架。
面試官考察點
強回答會區分三類條件:media() 描述環境、style() 讀取祖先樣式查詢、supports() 檢查瀏覽器能力。它會先寫一個明確的傳統宣告,再用第二個宣告漸進增強;不會假設解析器忽略未知值後仍保留同一宣告的舊值。
面試官也會檢查候選人是否知道分支按書寫順序匹配、else 是總為真的 fallback、沒有匹配時可能得到 guaranteed-invalid,以及多條宣告如何參與級聯。回答應包含瀏覽器矩陣、DevTools 檢查與停用新特性後的視覺回退。
回答前需要釐清的問題
條件來自哪裡
確認條件是視窗方向、容器的自訂屬性,還是瀏覽器是否支援某個值。視窗或使用者偏好用 media();元件狀態可用 style();能力偵測用 supports()。如果條件跨多個宣告,普通 @media、@container 或 @supports 可能更清楚。
fallback 必須維持什麼
確認舊瀏覽器只需可讀,還是必須維持精確間距、對比度與互動。若是無障礙或版面安全,先提供穩定宣告,再用 if() 覆蓋;不能讓未命中的分支把屬性變成初始值。
元件是否允許級聯覆寫
確認自訂屬性由哪個祖先設定、是否可能被主題層覆寫。style() 查詢讀取計算後的樣式關係;若變數來自同一屬性,可能觸發循環替換。需要明確 owner 和命名空間,避免元件內部條件讀取自己正在計算的變數。
30 秒回答框架
「我先確定條件來源,再決定用 media()、style() 還是 supports()。if() 按分支順序回傳第一個真值,else 提供穩定 fallback;沒有匹配時結果可能是 guaranteed-invalid,所以我會保留一條傳統宣告作為舊瀏覽器回退。跨多個規則的條件仍使用 @media、@container 或 @supports。最後用實際瀏覽器矩陣檢查解析、級聯、對比度與版面,不把實驗性語法當成無條件的生產依賴。」
分步驟深入解答
第一步:把條件與職責對應起來
media() 適合方向、寬度與使用者偏好;style() 適合元件祖先暴露的狀態;supports() 適合值或選擇器的能力偵測。條件只改變一個屬性值時,if() 能減少重複宣告;需要同時改變多個屬性、偽元素或子樹時,規則級查詢比較容易審查。
第二步:寫出明確的求值順序
CSS Values 5 把參數定義為按順序排列的條件與值。瀏覽器評估第一條條件,再繼續下一條,直到找到真值;else 永遠為真,因此應放在最後。示例:
.card {
padding: 1rem;
padding: if(
style(--density: compact): 0.5rem;
media(width > 60rem): 1.25rem;
else: 1rem;
);
}第一條匹配會停止後續判斷。寫多個相互重疊的條件時,先寫更具體的分支,再寫通用分支;否則後面的條件永遠不會生效。
第三步:理解未命中與舊瀏覽器回退
未命中且沒有 else 時,if() 產生 guaranteed-invalid 或空 token stream。該結果可能讓整條宣告無效或落到屬性的初始行為,不能當成「保留上一條宣告」的保證。對關鍵版面,先寫靜態宣告,再寫 if() 宣告;不認得 if() 的瀏覽器會跳過第二條,保留第一條。
.toolbar {
flex-direction: row;
flex-direction: if(media(orientation: portrait): column; else: row);
}如果新瀏覽器認得 if() 但條件不匹配,else 仍會提供值。若需要整體規則的回退,@supports (padding: if(...)) 比在每個屬性重複複雜條件更容易管理。
第四步:處理 style 查詢的級聯與循環
style() 查詢通常讀取祖先或容器暴露的自訂屬性。主題層可以設定 --density: compact,元件根據該狀態選擇值。不要讓 --size 的定義同時依賴自身的 if(style(--size: ...));CSSWG 說明這會被視為循環替換,查詢按 false 處理。把狀態變數與衍生屬性分開命名,並在元件文件寫出來源。
第五步:比較 if() 與規則級查詢
只有 color 的一段值隨支援情況變化時,if(supports(...)) 很合適;要切換多個宣告時,@supports 能包住整組規則。當版面依賴容器寬度,@container 或規則級 media() 通常比一長串內聯值更易讀。減少複製不是唯一目標,也要計入審查與除錯成本。
第六步:建立瀏覽器與視覺回歸驗證
為支援與不支援 if() 的瀏覽器分別執行頁面;檢查 CSSOM 最終值、級聯來源、對比度、焦點順序與版面溢出。覆蓋條件未命中、多個條件同時為真、變數未定義、屬性值本身無效與 if() 解析失敗。若新語法只改善裝飾而非核心功能,可限制在增強層;若改變無障礙,必須保留可接受的靜態基線。
高品質示範回答
我會先問條件來源與回退目標。視窗或偏好用 media,元件狀態用 style,能力偵測用 supports;只有一個屬性值需要條件化時才用 if(),多條規則仍用 @media、@container 或 @supports。
求值按書寫順序,第一條為真的分支獲選,else 永遠為真。沒有匹配且沒有 else 時不能假設保留舊值,可能得到 guaranteed-invalid。因此關鍵屬性先寫靜態宣告,再寫 if() 覆蓋。舊瀏覽器跳過未知宣告,現代瀏覽器用明確的 else。
我會把主題變數與衍生屬性分開,避免 style 查詢形成循環,並把更具體條件放前面。驗證覆蓋瀏覽器支援、CSSOM 最終值、級聯、對比度、溢出與未命中分支;如果功能組更清楚,就退回 @supports,不為少幾行 CSS 犧牲可維護性。
常見錯誤
- 錯誤表現 → 沒寫靜態回退 → 失敗原因 → 不支援 if() 的瀏覽器會跳過整條宣告 → 修正方法 → 先寫穩定值,再寫 if() 覆蓋。
- 錯誤表現 → 把 if() 當作規則級 @media → 失敗原因 → 它只回傳一個屬性值,不能取代多宣告版面分支 → 修正方法 → 多屬性切換使用規則級查詢。
- 錯誤表現 → 不寫 else → 失敗原因 → 未命中可能產生 guaranteed-invalid → 修正方法 → 提供明確預設值或保留前一條宣告。
- 錯誤表現 → 讓 style() 查詢讀取自己正在計算的變數 → 失敗原因 → 循環替換會使查詢失效 → 修正方法 → 分離狀態變數與衍生屬性。
- 錯誤表現 → 看到解析成功就宣稱相容 → 失敗原因 → 實驗性特性可能只有部分實作或行為不一致 → 修正方法 → 用瀏覽器矩陣與視覺回歸驗證真實結果。
追問及應對
追問一:if() 與 @supports 應該如何選擇?
若只需要決定一個屬性值,例如顏色格式,if(supports(...)) 可以讓值與屬性靠近。要同時改變多個宣告、偽元素或版面規則時,@supports 更易讀,也能集中處理不支援時的整組樣式。兩者可以組合,但不要把整頁邏輯塞進單個 if()。
追問二:兩個條件都為真時會發生什麼?
按書寫順序選擇第一條為真的分支,後續分支不再參與結果。把高優先級或更具體的條件寫在前面,並在程式碼審查標出互斥假設;否則新增分支可能被前面的通用條件遮蔽。
追問三:如果瀏覽器不支援 if(),能否依靠 CSS 解析器保留舊值?
可以依靠兩條獨立宣告實作回退:先寫穩定值,解析器跳過包含未知函式的第二條宣告。不能把舊值與新值寫在同一條宣告,也不能假設支援 if() 的瀏覽器在未命中時自動恢復上一條宣告。
追問四:style() 查詢導致元件狀態難以追蹤,怎麼辦?
建立狀態變數的命名空間與唯一 owner,記錄它由哪個祖先設定;在 DevTools 檢查計算值與級聯來源。若狀態要跨多個元件或改變很多屬性,改用明確 class、data 屬性或規則級查詢,犧牲一點簡潔換取可觀測性。