Web IMKit 主题变量
IMKit 的主题不是一堆散落 CSS,而是一个 Theme 对象:mode(light/dark)+ 9 个颜色令牌。调用 apply(element) 会把令牌写成 --imkit-* CSS 变量,并设置 data-imkit-theme;组件内部只消费这些变量,因此换肤不需要改组件代码。
概念
| 令牌 | CSS 变量 | 亮色默认 | 暗色默认 |
|---|---|---|---|
primary | --imkit-primary | #4f46e5 | #818cf8 |
background | --imkit-background | #f5f6fa | #0f172a |
surface | --imkit-surface | #ffffff | #1e293b |
text | --imkit-text | #1f2430 | #e2e8f0 |
textSecondary | --imkit-text-secondary | #7a8194 | #94a3b8 |
bubbleMine | --imkit-bubble-mine | #4f46e5 | #4f46e5 |
bubbleOther | --imkit-bubble-other | #ffffff | #1e293b |
danger | --imkit-danger | #dc2626 | #f87171 |
mention | --imkit-mention | #4f46e5 | #a5b4fc |
new Theme({mode,colors})会补齐缺省令牌;非法 mode 回退到matchMedia('(prefers-color-scheme: dark)')。Theme.light()/Theme.dark()/Theme.default()是构造快捷方式。withColors(partial)返回新 Theme(不可变),适合在切换品牌色时避免污染原对象。apply是幂等的;同一元素重复应用只覆盖同名变量。
作用域
把主题应用到聊天容器而不是 document.body,可以在一页内同时存在多个不同主题的区域(例如浅色主站 + 暗色聊天侧栏)。
前置条件
- 已按 快速开始 完成
init与connect,页面能收发消息。 - 使用
@ni-paas/im-web/imkit(浏览器原生 ESM,无第三方依赖);自定义代码只调用 IMKit/IMLib 公开入口。 - 已引入默认样式:调用
ensureStyles(),或自行加载 IMKit 样式表。 - 自托管版无“UI 主题商店/付费皮肤”;所有自定义均为本地代码(不适用项)。
步骤
- 选择模式
明确走亮色、暗色还是跟随系统;跟随系统只在构造 Theme 时读取一次,系统切换后需重建 Theme 并重新 apply。
- 覆盖品牌令牌
用
Theme.light({primary,bubbleMine,...})或withColors只改需要的键,其余继承默认值。 - 应用到容器
在 mount 前/后调用
theme.apply(container);IMKit 组件 props 也支持theme,会在渲染时应用。 - 持久化选择
用
IMLocalStore.savePreference('theme', mode)保存用户选择,下次启动恢复。
参数表
| API | 参数 | 说明 |
|---|---|---|
new Theme(options) | {mode?: "light"|"dark", colors?: Partial | 构造并补齐令牌 |
Theme.light(colors?) | 部分 colors | 亮色快捷方式 |
Theme.dark(colors?) | 部分 colors | 暗色快捷方式 |
theme.withColors(partial) | 部分 colors | 返回新 Theme |
theme.toCSSVars() | — | 返回 {"--imkit-primary": "..."} 映射 |
theme.apply(el) | HTMLElement | 写入 CSS 变量 + data-imkit-theme |
示例
品牌换肤 + 暗色 + 局部作用域
JavaScript
import { Theme, IMLocalStore, ensureStyles } from '@ni-paas/im-web/imkit';
ensureStyles();
const store = new IMLocalStore('demo');
const saved = store.loadPreference('theme', 'light');
const theme = new Theme({
mode: saved === 'dark' ? 'dark' : 'light',
colors: {
primary: '#0f766e',
bubbleMine: '#0f766e',
mention: '#14b8a6',
},
});
// 只作用于聊天容器,不影响页面其它区域
const chatRoot = document.getElementById('imkit-chat');
theme.apply(chatRoot);
// 运行时切换暗色:构造新 Theme 后重新应用
function setDark(on) {
const next = on ? Theme.dark(theme.colors) : Theme.light(theme.colors);
next.apply(chatRoot);
store.savePreference('theme', on ? 'dark' : 'light');
}在自定义 CSS 中消费变量
JavaScript
/* 任何在主题容器内的元素都能读取这些变量 */
.my-badge {
color: var(--imkit-text);
background: var(--imkit-surface);
border: 1px solid var(--imkit-primary);
}
.my-danger {
color: var(--imkit-danger);
}常见问题
切换暗色后组件没变?
Theme 是不可变对象;必须构造新 Theme 并重新 apply,或通过组件 update({theme}) 传入新对象。
能只改一个气泡颜色吗?
能。用 bubbleMine/bubbleOther 令牌,或在容器内针对 .bubble 写更具体的 CSS。
为什么在 body 上应用会影响全局?
CSS 变量会继承;这是特性也是风险。需要局部主题时请应用到聊天容器。
支持 CSS-in-JS 吗?
支持。组件只读 CSS 变量,任何能写入变量的方案都可用。