伪类必须用单冒号(如:hover),伪元素必须用双冒号(如::before);混用会导致规则静默失效,且影响语义表达、调试与维护。

伪类必须用单冒号,这是CSS2就定死的语法契约
浏览器遇到 ::hover、::focus 这类写法,不会报错,但整条规则会被静默忽略——DevTools 里根本看不到它生效。原因很简单::hover 从 CSS2.1 开始就是伪类,它的语义是“对已有 DOM 元素加状态筛选”,W3C 没有、也不会改这个底层约定。哪怕你加了 !important,a::hover { color: red; } 依然没反应。
更隐蔽的问题出在 :not() 里::not(::hover) 是非法语法,整个条件直接崩掉;而 :not(:hover) 才是正确写法。ESLint 或 Stylelint 通常会把它识别为解析失败的选择器,而不是警告。
伪元素强制用双冒号,是CSS3为划清语义边界做的硬性升级
::before、::after 在 CSS2.1 里确实用单冒号(:before),但那是历史妥协。CSS3 明确把它们“升格”为伪元素,并规定必须用双冒号,目的是强调:这不是在选状态,是在造内容——::before 插入的节点不在 DOM 中,document.querySelector('::before') 查不到,也不响应事件。
新伪元素如 ::placeholder、::selection、::backdrop 从设计之初就只认双冒号。Chrome 57+、Firefox 51+、Safari 10.1+ 中,:placeholder 写成单冒号会被直接忽略,样式面板里都找不到这条规则。
构建工具也跟着标准走:cssnano、PostCSS 插件可能压缩丢弃单冒号伪元素规则,或者在同一选择器里混用 div::before 和 div:before 时报冲突。
为什么旧写法还没被废弃?兼容性不是借口,而是包袱
浏览器保留 :before 这种单冒号伪元素写法,纯粹是为了不炸掉全网存量页面——IE8 及更早版本只认这个。但注意:这不是推荐,是容忍。现代项目里继续用 :before,等于主动放弃语义清晰性,也埋下协作隐患:新人看到 :first-letter,没法立刻判断它是伪类还是伪元素;团队代码审查时,容易把 ::before 当成 :hover 去调试。
还有个实际坑点:button:hover::before 合法,但 button::before:hover 完全无效——伪元素本身没有交互状态,:hover 只能作用于宿主元素。这个顺序错误不会报错,只会让你对着空白效果抓耳挠腮。
真正容易被忽略的是层叠与可维护性成本
一旦元素设了 position: relative 和 z-index,它的 ::before 就自动成为第一个子 stacking context。此时若没显式给 ::before 设 z-index,它和内部其他子元素的层级关系就不可控——这跟冒号写法无关,但常因写法混乱被误判为“伪元素失效”。
写 ::before 而不是 :before,不只是语法更新,是把“我正在生成内容”这个意图钉在代码里。当别人接手你的 CSS,看到双冒号,就知道这儿不是状态切换,而是结构干预——这种信号,在复杂 UI 维护中比省几毫秒加载时间重要得多。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











