组件皮肤
组件皮肤层级重新样式化内置组件。它构建在一个特定的样式栈和一个稳定的 钩子契约之上。
样式栈
内置 UI 一起使用四种技术:
- CSS Modules 用于组件作用域的样式,使用被明确视为非公共契约的哈希 类名。
- CSS 自定义属性 用于语义令牌(
--st-*)。 - 级联层 用于排列事实来源的顺序。
- 容器查询 用于适应组件自身容器的布局,尺寸以
rem表示。
主题针对钩子属性,绝不针对生成的类名。
级联层顺序
所有样式都生活在一个固定的级联层顺序中:
@layer reset, tokens, base, components, plugin-base, theme, user;
后面的层胜过前面的层,因此优先级是:
reset—— 基础重置。tokens—— 令牌定义。base—— 元素级默认值。components—— 内置组件样式。plugin-base—— 面向插件提供的基础样式的层。theme—— 活动主题的皮肤。user—— 用户自己的覆盖,最后加载。
用户覆盖样式表总是最后加载,因此损坏的或有主见的主题永远无法阻止用户
覆盖它。就 !important 而言:除属于面向用户的无障碍模式的无障碍偏好层
外,主题 CSS 中禁止该构造。
钩子契约
主题通过四个属性样式化组件,这些属性由宿主发布,并像 SDK 的其他部分一样 版本化:
<div
data-component="chat-message"
data-part="container"
data-role="assistant"
data-state="streaming"
></div>
data-component—— 组件类型。data-part—— 组件内部的结构部分。data-role—— 语义角色,例如消息角色。data-state—— 状态,例如open、closed或streaming。
主题的皮肤 CSS 随后看起来像这样:
@layer theme {
[data-component='button'][data-variant='primary'] > [data-part='icon'] {
color: var(--st-color-accent-text);
}
[data-component='action-bar'] [data-part='group'][data-role='secondary'] {
color: var(--st-color-text-secondary);
}
}
@neotavern/theme-sdk 包导出 dataHook 辅助函数来构建这些属性对象,
因此组件作者和主题作者使用相同的名称。
什么不是契约
- 生成的 CSS-module 类名 —— 哈希、不稳定,不是 SDK 的一部分。 针对它们的主题会在下一次构建时失效。
- 内部 React 层级 —— 主题不得依赖文档记录钩子之外的组件内部或 DOM 顺序。
- 数字布局值 —— 坐标、网格方案和断点不能通过令牌契约样式化;
视口断点位于注册表中,容器查询必须用
rem编写。
禁止的 CSS
主题样式表在加载前被扫描。禁止的构造在安装和校验时被拒绝:
@importjavascript:URL 和expression()。-moz-binding和behavior:。- 远程或协议相对 URL(
url(http:、url(https:、url(//)。 data:text/html。!important(无障碍偏好层除外)。
这让主题 CSS 保持纯净、本地和安全。皮肤应该引用的令牌请参阅 设计令牌;皮肤可以重新样式化的命名区域请参阅 外壳契约。