最简洁匹配所有标题元素的方式是 :is(h1, h2, h3, h4, h5, h6),它支持现代浏览器,仅匹配真实标题标签,不穿透 shadow dom,也不匹配伪元素。

用 :is() 匹配所有标题元素最简洁
直接写 :is(h1, h2, h3, h4, h5, h6) 就能选中任意层级、任意位置的标题元素,无需重复写六遍,也不用依赖 class 或 id。
它不是“万能通配”,而是语法糖式的逻辑或匹配——只要当前元素是这六个之一,就命中。浏览器兼容性已覆盖 Chrome 88+、Firefox 90+、Safari 15.4+ 和 Edge 91+,现代项目可放心用。
- 别写
*:is(h1, h2, ...)——*是多余的,:is()本身不改变选择器权重或范围 - 不能省略具体标签:只写
:is(h*)无效,CSS 不支持通配符在标签名里 - 如果要排除某个标题(比如跳过
h1),得显式列出其余五个::is(h2, h3, h4, h5, h6)
为什么不用 [role="heading"] 替代?
有人想靠 ARIA 属性兜底,比如 [role="heading"],但它不可靠:
- 原生
h1–h6元素默认有role="heading",但很多开发者不会手动加;没加就不匹配 - 第三方组件或 CMS 输出的标题可能用
div+role="heading"模拟,这时它会被选中,但语义和 SEO 行为跟真实标题不一致 -
[role="heading"]还会误中aria-level错误的元素(比如<div role="heading" aria-level="0">),而 <code>:is(h1...h6)只认真实标签:is()和*[role~="heading"]性能与维护差异从执行角度看,
:is(h1, h2, h3, h4, h5, h6)是静态标签匹配,引擎可快速索引;而带属性值的匹配(如[role~="heading"])需逐个检查属性内容,开销略高,尤其在长列表页中更明显。维护上也更清晰:
- 改标题结构时,只需确认是否仍用
h1–h6标签,样式逻辑不变 - 若混用
role,后续接手的人得同时查 HTML 结构、JS 注入逻辑、ARIA 规范,容易漏判 - 构建工具(如 PostCSS)对
:is()支持良好,旧浏览器降级方案也明确(可展开为六个独立选择器)
容易被忽略的边界点:嵌套 shadow DOM 和伪元素
:is(h1, h2, h3, h4, h5, h6)默认不穿透 shadow DOM。如果你的标题在自定义组件内部(比如<my-card></my-card>里有h3),外部样式表无法直接命中——得在组件内样式或用::slotted(h3)配合 slot 分发处理。另外,
:is()不匹配伪元素(如::before),也不影响伪类(如:hover),它只是“选中谁”,不是“怎么渲染”。别指望靠它触发伪元素样式,那得另配规则。 - 改标题结构时,只需确认是否仍用











