TL;DR
Astro 的 scoped CSS 通过给元素添加 data-astro-cid-xxx 属性实现隔离。动态生成 DOM 没有这个属性,所以无法匹配 scoped 样式。解决方案:<style is:inline> 或全局样式。
编译过程
编译前:
<style>
.post-title { color: var(--color-text); font-size: 2rem; }
</style>
<h1 class="post-title">标题</h1>
编译后:
<h1 class="post-title" data-astro-cid-abc123>标题</h1>
<style>
.post-title[data-astro-cid-abc123] { color: var(--color-text); font-size: 2rem; }
</style>
关键变化:元素添加 data-astro-cid-abc123 属性,CSS 选择器变成属性选择器。
为什么动态 DOM 无法匹配
JS 动态创建元素没有 data-astro-cid-abc123 属性,选择器无法匹配:
const el = document.createElement('h1');
el.className = 'post-title'; // 有 class 但没有 data 属性
// .post-title[data-astro-cid-abc123] 无法匹配!
解决方案
方案 1:<style is:inline>
不处理 CSS,原样输出,没有属性选择器。代价:样式变成全局。
方案 2:全局 CSS 文件
在 global.css 里定义。代价:没有隔离,类名冲突风险更高。
方案 3:JS 动态添加 data 属性(不推荐)
硬编码编译后属性名,维护困难。
实际踩坑案例
搜索弹窗组件,JS 动态创建搜索结果列表,scoped CSS 全部失效。最终方案:用 <style is:inline> + 类名加前缀避免冲突。
CSS 变量不受 scoped 影响
<style> 里引用 CSS 变量(var(--color-primary))不受限制,因为变量定义在 :root 上。
写在
记住一条规则:如果样式需要匹配 JS 动态创建元素,用 <style is:inline> 或全局 CSS。
延伸阅读
COMMENTS
种下你的想法
在花园里留下一条评论,和这篇文章一起生长。