1. :root 和 html 选择器有什么区别?
简单说: 在HTML网页里,它们俩指的都是同一个东西——<html>标签。但就像“总统”和“国家主席”的区别一样,称呼不同,身份和权重略有差异。
:root:这是CSS里的一个“伪类”,专门用来选中整个文档的根元素。在HTML里根就是<html>,所以它俩选中的是同一个标签。html:这就是直接通过标签名来选择<html>元素。
区别主要在两点:
-
权重不一样(特异性)
:root的权重比html高。你可以把CSS规则想象成衣服::root是“高级定制”,html是“普通成衣”。如果同时给<html>设置样式,两条规则都写了同一个属性,:root的样式会“覆盖”html的样式(除非html后面加了!important)。比如:css
:root { color: red; } html { color: blue; }最终文字颜色是红色,因为
:root更“强势”。 -
适用范围不同
:root是CSS标准里的“根元素”代名词,不光可以用在HTML,还可以用在SVG、XML等文档里。而html只针对HTML文档。所以写全局样式时,用:root更“通用”,也更符合“根”的语义。
通常用法: 我们一般用:root来定义全局CSS变量,因为它权重高,且一看就知道是根级别。
2. CSS变量的作用域是怎样的?
一句话:CSS变量的作用域跟普通CSS属性一样——在哪个选择器里定义,就作用在那个选择器选中的元素及其后代元素上。
你可以把它想象成 “家族遗传” :
- 全局变量(定义在
:root上) :就像写在族谱最顶上的家规,所有子孙后代(所有元素)都能继承和使用。 - 局部变量(定义在某个具体元素上) :就像某个小家庭自己定的家规,只在这个家庭(该元素)及其后代里有效,不影响其他分支。
- 覆盖(重写) :如果后代自己也定义了同名变量,就会“覆盖”祖先的变量,在自己和更后代里用新值,就像“不听祖宗的话,自己另搞一套”。
举个例子:
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS变量作用域 · 经典示例</title>
<style>
/* 基础样式,仅为了让例子更清晰 */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, 'Segoe UI', Roboto, sans-serif;
background: #f8fafc;
padding: 2rem;
line-height: 1.5;
color: #0f172a;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 24px;
box-shadow: 0 20px 35px -8px rgba(0,0,0,0.1);
padding: 2rem;
}
h1 {
font-size: 1.9rem;
font-weight: 600;
margin-bottom: 0.25rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
h1 small {
font-size: 0.9rem;
font-weight: 400;
background: #e2e8f0;
color: #334155;
padding: 0.2rem 0.8rem;
border-radius: 30px;
}
.desc {
background: #f1f5f9;
padding: 1.2rem;
border-radius: 18px;
margin: 1.5rem 0;
border-left: 6px solid #3b82f6;
font-size: 1rem;
}
code {
background: #e2e8f0;
padding: 0.2rem 0.5rem;
border-radius: 8px;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.9rem;
color: #0f172a;
}
pre {
background: #0f172a;
color: #e2e8f0;
padding: 1rem 1.5rem;
border-radius: 16px;
overflow-x: auto;
font-size: 0.95rem;
margin: 1.5rem 0;
border: 1px solid #334155;
}
/* 核心变量定义 */
:root {
--main-color: red; /* 全局红色 */
}
.sidebar {
--main-color: blue; /* 只在 sidebar 内部有效 */
background: #f0f9ff;
border-radius: 20px;
padding: 1.2rem;
border: 2px solid #b7e0ff;
margin: 1rem 0;
}
/* 盒子样式 */
.box {
color: var(--main-color);
border: 2px solid var(--main-color);
background: white;
padding: 1rem 1.5rem;
border-radius: 40px;
font-weight: 600;
margin: 0.8rem 0;
transition: all 0.1s;
box-shadow: 0 4px 6px -2px rgba(0,0,0,0.05);
}
.box span {
display: inline-block;
background: #f1f5f9;
padding: 0.2rem 1rem;
border-radius: 30px;
font-size: 0.85rem;
margin-left: 0.8rem;
color: #1e293b;
}
.highlight {
background: #fffbeb;
border: 2px dashed #f59e0b;
border-radius: 16px;
padding: 1rem;
}
hr {
margin: 2rem 0;
border: none;
border-top: 3px dotted #cbd5e1;
}
</style>
</head>
<body>
<div class="container">
<h1>
🎨 CSS变量作用域 · 经典例
<small>全局 vs 局部</small>
</h1>
<div class="desc">
⚡ <code>:root { --main-color: red; }</code> 全局红色<br>
⚡ <code>.sidebar { --main-color: blue; }</code> 只在侧边栏内部覆盖为蓝色<br>
⚡ <code>.box { color: var(--main-color); }</code> 根据所在位置不同呈现不同颜色
</div>
<pre><code>:root {
--main-color: red; /* 全局变量 */
}
.sidebar {
--main-color: blue; /* 只在 sidebar 及其内部有效 */
}
.box {
color: var(--main-color); /* 如果 .box 在 sidebar 内就是蓝色,否则红色 */
}</code></pre>
<!-- 全局区域的两个盒子 -->
<div class="box">
📦 我在全局作用域(没有 .sidebar 包裹)<span>颜色: 红色 (全局变量)</span>
</div>
<div class="box">
📦 我也是全局盒子 <span>红色</span>
</div>
<!-- 侧边栏区域,内部变量被覆盖为蓝色 -->
<div class="sidebar">
<p style="margin-top: 0; margin-bottom: 0.5rem; font-weight: 500; color: #0369a1;">🔷 sidebar 区域 (--main-color: blue)</p>
<div class="box">
📦 我在 .sidebar 内部 <span>颜色: 蓝色 (局部变量覆盖)</span>
</div>
<div class="box">
📦 我也是 sidebar 内的盒子 <span>蓝色</span>
</div>
</div>
<!-- 再放一个全局盒子,确认红色未受影响 -->
<div class="box">
📦 我又回到了全局 <span>红色 (不受 sidebar 影响)</span>
</div>
<hr>
<!-- 额外的说明:多层嵌套 -->
<div style="background: #f9fafb; border-radius: 20px; padding: 1.2rem;">
<p style="font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem;">🧩 作用域规则验证:</p>
<div class="box" style="border-color: gray; color: var(--main-color);">
当前这个盒子在根级别 → <span id="demoColor"></span>
</div>
<div class="sidebar" style="margin-top: 1rem;">
<div class="box" style="border-color: gray;">
而在 sidebar 内部 → <span id="demoColor2"></span>
</div>
</div>
</div>
<p style="margin-top: 2rem; font-size: 0.95rem; color: #475569; background: #ecfdf5; padding: 0.8rem; border-radius: 40px; text-align: center;">
✅ 结论:CSS变量作用域由定义的选择器决定,后代继承最近祖先的定义。
</p>
</div>
<!-- 简单脚本来显示计算后的颜色值(辅助说明) -->
<script>
(function() {
const boxes = document.querySelectorAll('.box');
const colorSpans = document.querySelectorAll('#demoColor, #demoColor2');
// 动态提取颜色文本
function rgbToHex(rgb) {
if (!rgb) return '';
const match = rgb.match(/^rgb((\d+),\s*(\d+),\s*(\d+))$/);
if (!match) return rgb;
const r = parseInt(match[1]).toString(16).padStart(2,'0');
const g = parseInt(match[2]).toString(16).padStart(2,'0');
const b = parseInt(match[3]).toString(16).padStart(2,'0');
return `#${r}${g}${b}`.toUpperCase();
}
const rootBox = document.querySelector('.box:first-child');
const sidebarBox = document.querySelector('.sidebar .box');
if (rootBox && document.getElementById('demoColor')) {
const color = window.getComputedStyle(rootBox).color;
document.getElementById('demoColor').innerText = rgbToHex(color) + ' (红)';
}
if (sidebarBox && document.getElementById('demoColor2')) {
const color = window.getComputedStyle(sidebarBox).color;
document.getElementById('demoColor2').innerText = rgbToHex(color) + ' (蓝)';
}
})();
</script>
</body>
</html>
作用域规则总结:
- 变量从当前元素往上找,找到哪个定义就用哪个(最近祖先优先)。
- 变量不跨兄弟元素,也不跨没有继承关系的元素。
- 定义在媒体查询或伪类里的变量,只在对应条件下生效。
所以,CSS变量天然支持“作用域”,让我们可以像写JavaScript一样管理样式主题,非常方便。