CSS变量在Shadow DOM中不自动穿透,需显式设在:host上并通过:host选择器使用,否则var()回退fallback;all:initial会重置变量,slot内容也不继承变量。

Shadow DOM里CSS变量不是自动穿透的
CSS变量在Shadow DOM中“能用”不等于“自动穿透”,它依赖继承链,而Shadow边界会截断这条链。外部写在:root或body里的--primary,Shadow内部var(--primary)读到的永远是fallback值——浏览器根本不会往上跨过shadow host去查。
真正生效的前提只有一个:--primary必须显式设在宿主元素本身上,比如<my-button style="--primary: #007bff"></my-button>,或者用CSS规则绑定:my-button { --primary: #007bff; }。
- 小程序(如Vant Weapp)更严格:必须靠class触发,
<van-button class="theme-dark"></van-button>+.theme-dark { --btn-bg: #333; } - JS动态设置要用
element.style.setProperty('--primary', '#007bff'),不能改document.documentElement - 拼错变量名(如
--priamry)或漏写var(--primary, #007bff)里的fallback,都会静默失效,DevTools里显示invalid而不是报错
:host是变量落地的唯一合法位置
:host选择器出现在Shadow内部的<style>里,是把变量转成实际样式的必经通道。它不负责声明变量,只负责“接收并使用”。没它,变量就只是挂在宿主上的一堆字符串,对内部样式毫无影响。
例如这段代码必须写在Shadow内部的<style>中:
立即学习“前端免费学习笔记(深入)”;
:host { --button-radius: 4px; }
button { border-radius: var(--button-radius, 4px); }
外部只需控制宿主,就能联动内部:
-
<my-button size="large"></my-button>→:host([size="large"]) { --button-radius: 8px; } -
<my-button hoverable></my-button>→:host(:hover) { --shadow: 0 2px 6px rgba(0,0,0,0.1); } -
:host-context(.dark)可用于响应祖先类名,但性能略低,且Chrome 99+已废弃,不建议用于新项目
all: initial会直接杀死CSS变量
想彻底阻断外部字体、颜色等继承?很多人第一反应是给Shadow根加all: initial。但这是个致命陷阱:all会重置所有自定义属性为initial,导致var(--x)永远得不到值,直接回退到fallback,甚至在Computed面板里显示invalid。
正确做法是显式重置可继承属性,而不是一刀切:
- 用
*:not(style) { font: initial; color: initial; line-height: initial; }代替all: initial - 如果真要用
all,只能选all: unset(保留继承性),但依然可能干扰变量级联 - 检查DevTools的Computed面板:若
var(--x)旁显示invalid,八成是all惹的祸
slot内容不受变量影响
传进<slot>的HTML节点(比如<p>Hello</p>)完全拿不到宿主上的CSS变量。变量不会“穿透”到slot内容里,哪怕你在:host里设了--text-color,<p>也不会自动变色。
可行方案只有两个:
- 要求使用者主动给slot内容加class:
<my-card><p class="text-primary">Hello</p></my-card>,再配::slotted(.text-primary) { color: var(--text-color); } - 用
::slotted(*)做通用重置,但无法深入子元素内部(比如<p><span>inner</span></p>里的<span>不会被影响) - 不要指望用变量驱动slot内第三方组件的样式,得靠透传属性或事件通知


















