📋 目錄
大多數前端工程師寫 CSS 的第一天就學會了
html { ... }這個選擇器。但你知道嗎,CSS 裡有至少六種方式可以選擇<html>元素?有些是日常實用的,有些純粹是趣味知識——比如:not(* > *)會給你一隻小鳥 🐦。這篇文章不是要顛覆你對 CSS 的理解,而是讓你在同事面前小小的炫一下。
為什麼要討論這個
在多數情況下,html { ... } 或 body { ... } 就足夠了。但這些替代方法的價值在於:
- 拓寬你對 CSS 選擇器的理解
- 應對特殊場景(如 SVG 文件、Shadow DOM)
- 純粹的樂趣——CSS 可以很有趣
方法一:直接使用標籤名
/* 最基本的方式 */
html {
scroll-behavior: smooth;
font-family: system-ui, sans-serif;
}
這是所有人都知道的方法,沒有什麼驚喜。
方法二::root——最實用的替代方案
/* :root 匹配文檔根元素 */
:root {
/* 在 HTML 文件中,:root 等於 <html> */
--color-primary: #4f46e5;
--spacing-unit: 8px;
--font-family: 'Inter', system-ui, sans-serif;
}
:root 的優勢
| 維度 | html | :root |
|---|---|---|
| 優先級 | 0-0-1(元素選擇器) | 0-1-0(偽類) |
| 支援 XML | ❌ 僅 HTML | ✅ 支援 SVG、RSS、MathML |
| 宣告 CSS 變數 | ✅ | ✅(更常見) |
:root 的優先級更高,這在某些覆蓋場景裡很有用。
實際應用
/* :root 是宣告全域 CSS 變數的最佳位置 */
:root {
/* 設計 Token */
--color-brand: #4f46e5;
--color-brand-light: #818cf8;
--color-brand-dark: #3730a3;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-4: 16px;
--space-8: 32px;
/* Typography */
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
/* Animation */
--transition-fast: 150ms ease;
--transition-normal: 300ms ease;
}
/* 在其他地方使用 */
.button {
background: var(--color-brand);
padding: var(--space-2) var(--space-4);
transition: var(--transition-fast);
}
XML 支援
:root 不只是為 HTML 設計的——它適用於所有 XML 文檔:
<!-- SVG 文件 -->
<svg xmlns="http://www.w3.org/2000/svg">
<style>
:root {
fill: #4f46e5;
}
</style>
<rect width="100" height="100"/>
</svg>
<!-- RSS/Atom feed -->
<rss version="2.0">
<style>
:root {
font-family: Arial, sans-serif;
}
</style>
<channel>
<!-- ... -->
</channel>
</rss>
方法三::scope——明確的作用域根
/* :scope 匹配當前作用域的根元素 */
:scope {
/* 在全域作用域中,:scope 等於 <html> */
font-size: 16px;
}
:scope 的設計目的是明確標記作用域的根元素,在 CSS @scope 規則中特別有用:
/* @scope 區塊內的作用域根 */
@scope (.card) {
:scope .card-header {
/* :scope 在這裡指的是 .card */
font-weight: bold;
}
}
在全域作用域中,:scope 和 :root 都能匹配 <html>,但語義不同:
:root:文檔根元素:scope:當前作用域的根元素
方法四:&——在嵌套中引用根
Sass/SCSS 或現代 CSS 嵌套語法中的 &:
/* 在非嵌套上下文中的 & */
/* 現代 CSS(Chrome 120+)*/
& {
font-size: 16px;
}
/* 在 Sass/SCSS */
& {
font-size: 16px;
}
當 & 在最外層(非嵌套)時,它等於 <html>。
嵌套中的 &
/* 嵌套中的 & */
.page {
& .header {
/* & 在這裡指的是 .page */
background: white;
}
& {
/* 這個 & 等於 .page */
color: #333;
}
}
與 :scope 的關係
& 在嵌套的根位置,與 :scope 有相似的語義——都指向某個作用域的根。
方法五::has(head) 或 :has(body)——利用 Has 偽類
/* :has() 選擇包含特定子元素的父元素 */
/* <html> 是唯一同時包含 <head> 和 <body> 的元素 */
html:has(head) {
/* 匹配有 <head> 的 <html> */
}
html:has(body) {
/* 匹配有 <body> 的 <html> */
}
/* 兩個都寫等於匹配 <html> */
html:has(head):has(body) {
/* 這個選擇器幾乎就是 <html> 本身 */
}
實際用途
老實說,這個方法沒有實際用途——<html> 永遠都有 <head> 和 <body>。但它展示了 :has() 偽類的強大之處。
/* 一個更實際的 :has() 用法 */
.card:has(.badge) {
/* 匹配包含 badge 元素的卡片 */
position: relative;
}
.card:has(.badge)::after {
content: '';
position: absolute;
top: -4px;
right: -4px;
width: 12px;
height: 12px;
background: red;
border-radius: 50%;
}
方法六::not(* *)——利用否定偽類
/* :not(* *) = 不被任何元素包含的元素 */
/* 只有 <html> 符合這個條件 */
:not(* *) {
background: #f9fafb;
}
/* :not(* > *) = 不是任何元素的直接子元素 */
:not(* > *) {
/* 這個選擇器本身沒有實際效果 */
/* 但在某些瀏覽器中可能匹配到 <html> */
}
小鳥彩蛋 🐦
CSS-Tricks 的文章提到了一個有趣的彩蛋。試試這個:
:not(* > *)::before {
content: '🐦';
}
在一些瀏覽器環境下,這可能會在 <html> 元素前面顯示一隻小鳥。
方法七:* + 祖先選擇(bonus)
/* 這個方法的極致變體 */
/* <html> 是唯一沒有祖先的元素 */
*:not(:has(:not(:has(:not(*))))) {
/* 這是一個過度複雜的選擇器 */
/* 基本上等同於選擇 <html> */
}
這個例子只是為了說明——CSS 選擇器的組合可以是無限的,但不代表你應該這樣寫。
優先級比較
| 選擇器 | 優先級 | 實用性 |
|---|---|---|
html | 0-0-1 | ⭐⭐⭐⭐⭐ |
:root | 0-1-0 | ⭐⭐⭐⭐⭐ |
:scope | 0-1-0 | ⭐⭐⭐ |
&(在最外層) | 取決於上下文 | ⭐⭐ |
:has(head) | 0-1-1(需驗證) | ⭐ |
:not(* *) | 0-1-1(需驗證) | ⭐ |
推薦:什麼情況下用哪個
宣告全域 CSS 變數
/* 推薦:使用 :root */
:root {
--color-primary: #4f46e5;
}
設定基礎字體大小
/* 推薦:使用 html */
html {
font-size: 16px;
scroll-behavior: smooth;
}
在 @scope 中使用
/* 使用 :scope */
@scope (.card) {
:scope {
/* scope 根元素 */
}
}
結語:CSS 選擇器的藝術
這六種方法,涵蓋了從「每個人都知道」到「純粹是彩蛋」的範圍。實用的建議很簡單:
- 宣告 CSS 變數:用
:root - 設定基礎樣式:用
html或:root - 在 @scope 中:用
:scope
其他的方法——:has()、:not()、嵌套中的 &——更多是展示 CSS 選擇器的靈活性和深度。了解它們可以拓寬你對 CSS 的理解,有時候也能在特殊場景派上用場。
至于那只小鳥 🐦——就當作是 CSS 給你的一個小驚喜吧。
本文是「2026 CSS 實用技術」系列文章之一。