组件样式隔离
组件库最怕的事:样式泄漏到宿主应用,或者宿主应用的样式污染组件库。样式与响应式都做完后,这是样式体系的最后一个缺口——业务系统引用我们的基础组件时,组件库的样式不能覆盖业务系统自己的样式。
传统做法把组件库样式全量加载进系统,设置在根元素(
:root、*)上的规则会直接改写宿主的每个元素。这一节讲清楚隔离的完整策略,以及它最终在 ai-chat-ui 里如何落地为一条条可执行的规则。
一、为什么需要样式隔离
组件库的使用场景:用户在自己的项目中引入聊天组件,组件渲染在用户的页面里。两个方向的问题:
- 样式泄漏:组件库的
.ai-chat__header { font-size: 16px }影响了用户页面中其他.header元素; - 样式污染(反向):用户的全局样式
button { border-radius: 0 }或 Tailwind preflight 把组件库的圆角按钮搞成方的。
一个容易忽视的思考起点:React/Vue 这类框架的组件内部本来就有样式隔离(scoped、CSS Modules),真正会打穿到宿主全局的,恰恰是组件库放出去的那些"全局规则"——:root 上的变量定义、* 通配 reset、裸元素选择器。所以设计原则第一条:能不动全局就不动全局,必须在全局的(令牌),要放进宿主"天然能覆盖"的机制里(见第二层的 @layer 方案)。
这些考量不能全交给大模型——由传统开发时代切到 AI 时代,"哪里会泄漏"的敏感度是你的经验,AI 负责按你的方向落地。靠什么积累:多看、多学、多跟 AI 对话。
二、三层隔离策略
第一层:BEM 命名空间(前缀纪律)
所有类名以 ai-chat- 为前缀,用 BEM 规范扩展:
.ai-chat-window { } /* Block */
.ai-chat-window__body { } /* Element */
.ai-chat-window__body--sticky { } /* Modifier */
css
用户页面中几乎不会出现 ai-chat 前缀的类名,这一层就能挡掉绝大多数撞名。工具类(如 .ai-chat-scrollbar)同样遵守前缀。
第二层:令牌作用域——@layer 方案(落地选择)
CSS Variables 默认继承,定义在 :root 上就是全局的。设计阶段有过两个候选:
- 候选一(草案):把变量从
:root挪到组件根元素.ai-chat { --ai-chat-xxx: ... },作用域物理隔离; - 候选二(落地):变量留在
:root,但整份令牌文件包进 CSS 级联层@layer ai-chat-tokens。
ai-chat-ui 最终选择候选二。原因:宿主覆盖体验更好——CSS 规范中层内样式的优先级低于任何未分层样式,宿主应用(通常不使用 @layer)写一行普通 CSS 就能覆盖组件库的任意令牌或规则,不需要 !important,不需要更高特异性:
/* 宿主覆盖组件库主色:一行未分层 CSS 直接生效 */
:root {
--ai-chat-color-accent-500: #8b5cf6;
}
css
代价是必须守住一条纪律:组件库自己严禁写未分层规则(见第三节铁律),否则反过来压过宿主。
候选一并没有被否定——多实例、严格隔离的场景(组件级暗色
target: 'component')里,作用域收敛同样在用。两层思路对应不同需求:令牌要"宿主易覆盖",实例要"互不干扰"。
第三层:Shadow DOM(可选,实际用得少)
完全隔离的场景可以上 Shadow DOM:attachShadow({ mode: 'open' }) 把组件渲染进 shadow root,宿主样式进不来、组件样式出不去。但代价是全局字体、图标不生效,Vue Teleport 不能穿透,调试更困难。实际项目里第一层 + 第二层已经足够,ai-chat-ui 未采用。
三、@layer 铁律(仓库落地规则)
这套规则实施时让 AI 给方案、人做决策后,最终沉淀成了仓库的 .claude/rules/style-isolation.md——AI 以后每次写样式都会被这条规则约束。核心条款:
/* 层顺序只在 packages/vue/src/styles/tokens.css 顶部声明一次 */
@layer ai-chat-base, ai-chat-tokens, ai-chat-components, ai-chat-animations;
css
- 归层:重置 →
ai-chat-base;令牌 →ai-chat-tokens;组件样式 →ai-chat-components;动画 keyframes →ai-chat-animations。新增层必须先在 tokens.css 顶部按依赖顺序补声明。 - 严禁未分层规则:未分层规则优先级高于所有 @layer,比宿主样式还强,等于反向污染宿主,宿主只能用
!important压制。例外:组件里<style scoped>的块可不包层——data-v-xxx属性选择器天然只命中本组件。 - 禁入区:
:root、*、裸body/html、裸元素选择器(div/p/a)只允许出现在tokens.css的既定位置(令牌定义与组件子树 box-sizing)。组件内一律禁止。 - 禁止
!important:宿主靠层顺序就能覆盖我们,我们也不该用它压宿主。 - 第三方 CSS 走可选子路径导出:如
@toimc/markdown/katex.css由宿主显式 import;禁止库代码里import 'katex/dist/katex.min.css'隐式注入——宿主无法 opt-out,也无法 tree-shake。 - z-index 令牌化:组件内部层叠 ≤ 10 自由使用;Teleport 弹层等脱离文档流的 UI 必须先在 tokens.css 定义令牌(
--ai-chat-z-popup等)再引用,禁止裸写z-index: 9999与宿主弹层打架。
@layer 浏览器兼容(2026-10 核实):Chrome/Edge 99+、Firefox 97+、Safari 15.4+,2022 年 3 月四大引擎全部落地,现为 Baseline Widely Available(Baseline Newly Available 30 个月后自动晋级),生产环境可放心全量使用。
四、宿主全局样式的防御性重置
即使做了隔离,宿主的全局 * { margin: 0; padding: 0 } 或 Tailwind preflight 仍可能渗进来。ai-chat-ui 的落地做法是把 reset 收敛到组件子树(tokens.css 真实代码):
@layer ai-chat-base {
.ai-chat-conversation,
.ai-chat-conversation *,
.ai-chat-conversation *::before,
.ai-chat-conversation *::after {
box-sizing: border-box;
}
}
css
只声明 box-sizing 这一条最容易被宿主 reset 改写的属性,不做更激进的重置。更重的手段是组件根元素 all: initial——把宿主所有继承样式(字体、颜色、行高)全部重置后组件再逐项自定义:
.ai-chat {
all: initial;
display: block;
font-family: var(--ai-chat-font-sans, sans-serif);
/* ...逐项恢复 */
}
css
代价是"恢复清单"很长且容易漏,仓库没有采用——防御强度与维护成本之间选了收敛版。
五、用 AI 落地样式隔离:提示词与工程判断
实施时的实际提示词:
减少组件对全局应用的样式污染,处理样式隔离的问题,
并形成后续前端主题及样式相关的 rules。
text
AI 返回了业界主流方案清单(类选择器前缀纪律、CSS 变量作用域——并诚实标注了"变量挪出
开始执行,使用 subagent 多人团队共同完成。
text
为什么这个任务没走 Plan 模式、没写需求文档? 工程判断:功能不复杂、业界方案成熟、前面主题系统已经把样式体系定型——此时再走完整流程是空转。复杂度评估永远是你自己的活,简单任务直接批准方案让 subagent 团队执行(实测一分多钟交付:隔离落地 + 主题能力整合 + 文档更新)。
交付结果核对(仓库可验证):
packages/docs/guide/theming.md新增"样式隔离与宿主覆盖(@layer)"小节,写明何时用变量覆盖、何时直接写未分层 CSS;.claude/rules/style-isolation.md沉淀为团队规则(第三节即源于此);- 全部组件样式归层,
tokens.css顶部一次性声明层序。
AI 审查提示词(日常复查用):
审查 ai-chat-ui 所有组件样式,检查:
1. 类名是否都有 ai-chat- 前缀?
2. 是否有未分层(@layer 外)的规则?
3. 是否用了 :root / * / 裸元素选择器(tokens.css 既定位置除外)?
4. 是否存在 !important?
5. 是否有硬编码色值未走 --ai-chat-* 变量?
输出问题清单和修复建议。
text
小结
- BEM 前缀是第一道防线,
ai-chat-前缀挡掉绝大多数撞名 - 令牌收进
@layer:变量留在:root但处于层内,宿主一行未分层 CSS 天然覆盖、无需!important——这是"草案的变量挪窝"与"落地的 @layer"之间的最终取舍 - @layer 铁律:层序声明一次、严禁未分层规则、禁入区、禁
!important、第三方 CSS 子路径导出、z-index 令牌化——并已沉淀为仓库 rules,持续约束 AI 产出 - Shadow DOM 完全隔离但限制多,实际项目用得少;宿主污染用组件子树收敛 reset 防御(
all: initial备而不用) - @layer 兼容:Chrome/Edge 99+、Firefox 97+、Safari 15.4+(2022 年初全线,Baseline Widely Available)
- 复杂度判断是人做:这种成熟小任务,问 AI 要方案 → 确认 → subagent 执行,不必全流程
第11章到此结束。样式与主题体系完整落地:三层令牌、暗色模式、主题预设与配置器、容器查询响应式、@layer 样式隔离。本章其余几节(环境升级、工具切换与跨会话记忆、章小结)见 11-04 至 11-06。下一章进入高级组件开发。
↑