CSS优先级与选择器
约 1837 字大约 6 分钟
cssspecificity
2025-07-30
概述
CSS 优先级(Specificity)决定了当多条规则应用于同一元素时,哪条规则的声明最终生效。理解优先级计算规则和层叠(Cascade)机制,是编写可维护 CSS 和解决样式冲突的关键。CSS Cascading Layers(@layer)是 CSS 级联机制的最新补充,为大型项目的样式管理提供了更强大的控制能力。
CSS 级联排序
完整的级联优先级排序(从高到低):
!important声明(UA > User > Author)- 普通声明的来源(Author > User > UA)
- 在同来源中,按 Specificity 排序
- Specificity 相同时,后出现的规则胜出
1. Specificity 计算
Specificity 由四个级别组成,可以理解为一个四位数:
(Inline, ID, Class/Attribute/Pseudo-class, Element/Pseudo-element)计算示例
/* (0,0,0,1) — 1 个元素选择器 */
p { color: black; }
/* (0,0,1,0) — 1 个类选择器 */
.text { color: blue; }
/* (0,0,1,1) — 1 个类 + 1 个元素 */
p.text { color: green; }
/* (0,1,0,0) — 1 个 ID 选择器 */
#main { color: red; }
/* (0,1,1,1) — 1 个 ID + 1 个类 + 1 个元素 */
#main p.text { color: purple; }
/* (0,0,2,1) — 2 个类 + 1 个元素 */
.container .text p { color: orange; }
/* (0,1,2,0) — 1 个 ID + 1 个类 + 1 个属性选择器 */
#nav .link[href] { color: teal; }
/* (1,0,0,0) — 内联样式 */
/* <p style="color: gold"> */对比排序
* → (0,0,0,0) — 通配符无权重
li → (0,0,0,1)
ul li → (0,0,0,2)
ul ol+li → (0,0,0,3)
.active → (0,0,1,0)
ul li.active → (0,0,1,2)
#navbar → (0,1,0,0)
#navbar .link → (0,1,1,0)
style="" → (1,0,0,0)2. !important 的使用与滥用
/* !important 会覆盖所有普通声明,即使 specificity 更低 */
p { color: red !important; } /* 胜出 */
#main p.text { color: blue; } /* 被覆盖 */
/* !important 之间按正常 specificity 比较 */
p { color: red !important; } /* (0,0,0,1) !important */
#main p { color: blue !important; } /* (0,1,0,1) !important → 胜出 */
/* 唯一能覆盖 Author !important 的是 User !important 或 User Agent !important */合理使用 !important 的场景
/* 1. 覆盖第三方库内联样式 */
.third-party-widget .override {
display: block !important; /* 第三方组件使用了 inline style */
}
/* 2. 工具类(Utility classes)*/
.hidden { display: none !important; }
.sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
}3. Cascade Layers(@layer)
CSS Layers 是管理大型项目样式优先级的现代方案。
/* 定义层的优先级顺序(后定义的优先级更高)*/
@layer reset, base, components, utilities;
/* 各层的样式 */
@layer reset {
* { margin: 0; padding: 0; box-sizing: border-box; }
a { color: inherit; text-decoration: none; }
}
@layer base {
body { font-family: system-ui; line-height: 1.6; }
h1 { font-size: 2rem; }
}
@layer components {
.card { padding: 1rem; border: 1px solid #ddd; border-radius: 8px; }
.button { padding: 0.5rem 1rem; background: #007bff; color: white; }
}
@layer utilities {
.mt-4 { margin-top: 1rem; }
.text-center { text-align: center; }
}Layer 的关键规则
/* 未分层的样式优先级高于所有层 */
.card { padding: 2rem; } /* 未分层 → 胜出 */
@layer components {
.card { padding: 1rem; } /* 被覆盖 */
}
/* 嵌套层 */
@layer framework {
@layer base {
.btn { color: blue; }
}
@layer theme {
.btn { color: red; } /* framework.theme 优先级高于 framework.base */
}
}
/* 导入到指定层 */
@import url('bootstrap.css') layer(framework);4. 现代伪类选择器
:where() — 零优先级
/* :where() 内的选择器不贡献任何 specificity */
:where(#main .content p) {
color: blue;
/* specificity: (0,0,0,0) 尽管包含 ID 和 class */
}
p {
color: red;
/* specificity: (0,0,0,1) → 胜出!*/
}
/* 用途:创建易于覆盖的默认样式 */
:where(h1, h2, h3, h4, h5, h6) {
margin-top: 1em;
margin-bottom: 0.5em;
/* 开发者的任何规则都能轻松覆盖 */
}:is() — 取最高优先级
/* :is() 的 specificity 等于其参数中 specificity 最高的选择器 */
:is(#header, .nav) a {
color: blue;
/* specificity: (0,1,0,1) 因为 #header 贡献 (0,1,0,0) */
}
/* :is() 也用于简化选择器 */
/* 传统写法 */
header a:hover,
nav a:hover,
footer a:hover {
color: red;
}
/* 简化为 */
:is(header, nav, footer) a:hover {
color: red;
}:has() — 父元素选择器
/* :has() 的 specificity 等于其参数的 specificity */
div:has(> .active) {
background: yellow;
/* specificity: (0,0,1,1) — div(0,0,0,1) + .active(0,0,1,0) */
}
/* 实用示例 */
/* 包含图片的卡片有不同布局 */
.card:has(img) {
grid-template-columns: 200px 1fr;
}
/* 表单验证状态 */
.form-group:has(:invalid) {
border-color: red;
}
.form-group:has(:focus) {
border-color: blue;
}
/* 暗色模式下的链接 */
:root:has(#dark-mode:checked) {
--text-color: #fff;
--bg-color: #1a1a1a;
}5. 属性选择器
/* 精确匹配 */
[type="text"] { border: 1px solid #ccc; }
/* 前缀匹配 */
[href^="https"] { color: green; }
/* 后缀匹配 */
[href$=".pdf"]::after { content: " (PDF)"; }
/* 包含匹配 */
[class*="col-"] { float: left; }
/* 大小写不敏感 */
[type="text" i] { /* 匹配 type="TEXT" 等 */ }
/* 属性选择器的 specificity 等同于 class: (0,0,1,0) */6. 伪类与伪元素
/* 伪类 (0,0,1,0) */
a:hover { }
input:focus { }
li:nth-child(2n+1) { }
p:first-of-type { }
input:checked { }
div:empty { }
:root { }
:not(.active) { /* :not() 的参数计入 specificity */ }
/* 伪元素 (0,0,0,1) */
p::first-line { }
p::first-letter { }
div::before { content: ''; }
div::after { content: ''; }
::placeholder { }
::selection { background: yellow; }
li::marker { color: red; }7. 避免优先级战争
BEM 命名法
/* Block Element Modifier — 保持所有选择器在 (0,0,1,0) 级别 */
.card { }
.card__title { }
.card__body { }
.card--featured { }
.card__title--large { }
/* 所有选择器 specificity 相同,靠源码顺序控制覆盖 */CSS 变量实现主题切换
:root {
--color-primary: #007bff;
--color-text: #333;
--spacing-md: 1rem;
}
/* 组件使用变量,无需高 specificity */
.button {
background: var(--color-primary);
color: white;
padding: var(--spacing-md);
}
/* 主题切换只需修改变量 */
[data-theme="dark"] {
--color-primary: #6c9bd2;
--color-text: #eee;
}8. 调试 Specificity
// 浏览器 DevTools 中查看 Specificity
// Elements → Styles 面板显示所有匹配规则
// 被划线的属性表示被更高优先级规则覆盖
// 计算 specificity 的工具函数
function calculateSpecificity(selector) {
let a = 0, b = 0, c = 0;
// 移除 :not() / :is() / :has() 外壳,保留参数
// ID 选择器
a = (selector.match(/#[\w-]+/g) || []).length;
// Class, 属性, 伪类
b = (selector.match(/\.[\w-]+|\[.*?\]|:[\w-]+(?:\(.*?\))?/g) || []).length;
// 元素, 伪元素
c = (selector.match(/^[\w]+|[\s>+~][\w]+|::[\w-]+/g) || []).length;
return `(0,${a},${b},${c})`;
}总结
CSS Specificity 是级联机制的核心组成部分,按照 Inline > ID > Class/Attribute/Pseudo-class > Element/Pseudo-element 四个层级计算权重。!important 应当谨慎使用,@layer 提供了更优雅的优先级管理方案。:where() 的零优先级特性适合创建可覆盖的默认样式,:is() 简化复杂选择器,:has() 实现了期待已久的父元素选择能力。在大型项目中,使用 BEM 命名、CSS 变量和 @layer 可以有效避免优先级战争,保持样式的可维护性。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于