CSS变量不响应:invalid等伪类,必须用JS设置data属性驱动样式;:invalid无法区分未输入与输错,且不触发变量重计算;可靠方案是JS设data-valid,CSS用属性选择器映射var()。

不能只靠 :invalid 或 :has() 直接驱动 var() 变化——CSS 变量本身不响应校验逻辑,必须由 JS 主动设置状态,再让 CSS 基于该状态注入变量并应用样式。
为什么 :invalid 里定义 --error 往往无效
很多人写 input:invalid { --error: #e53e3e; border-color: var(--error); },结果边框没变红。这不是语法错,而是浏览器行为限制::invalid 状态本身不触发变量重计算,且它只在失焦/提交后才生效,页面一加载空 required 输入框就匹配 :invalid,但此时用户根本没输过内容。
-
:invalid不区分“未输入”和“输错”,业务上不可信 -
var(--error)若未在祖先节点(如:root或form)提前声明 fallback,会回退为initial,边框可能直接消失 - 某些 UI 框架会重置
:invalid样式权重,导致你的规则被覆盖
真正可控的链路:用 data- 属性 + :root 变量 + 属性选择器
把校验判断权交给 JS,CSS 只做映射。这是目前最稳、兼容性最好、也最容易调试的方式。
- JS 设置状态:
input.setAttribute('data-valid', 'false')(注意值是字符串'false',不是布尔false) -
:root定义基础色::root { --form-error: #ef4444; --form-success: #22c55e; } - CSS 映射样式:
input[data-valid="false"] { border-color: var(--form-error); } - 错误提示显隐也走同一条链:
.field[data-valid="false"] .error-msg { opacity: 1; height: auto; }
:has() 和 var() 能一起用,但必须绕开伪类陷阱
想用 :has() 实现父容器高亮?可以,但别让它承担状态判断。把 :has() 当作“定位器”,把变量控制权还给 data- 属性。
立即学习“前端免费学习笔记(深入)”;
- 正确写法:
.field:has(input[data-valid="false"]) { background-color: var(--form-error-bg, #fff8f8); } - 不要写:
.field:has(input:invalid) { --error: #ef4444; }——:has()内部不能定义变量,整条规则会被忽略 - 结构必须严格:
.error-msg必须是.field的后代,否则:has()查不到,data-属性也白设 - 构建工具(如 PostCSS)默认删
:has(),需确认插件配置允许保留
容易被忽略的细节:过渡、继承与 fallback
transition: border-color 在 var(--form-error) 切换时不会动画——因为变量值变化本身不可过渡。要实现平滑效果,得靠属性本身的可动画性。
- 写
border: 2px solid var(--form-error);比只写border-color更安全,避免丢失border-style - 所有
var()都加 fallback:background-color: var(--form-error-bg, #fff8f8);,否则未定义变量时样式会塌陷 -
var()不自动继承,若.error-msg在 Shadow DOM 内,必须显式从:host透传或在组件内重申变量 - 移动端 Safari 对
:has()+data-组合支持稳定,但安卓 WebView 仍可能忽略:has(),data-属性方案必须作为兜底


















