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 主题商店/付费皮肤”;所有自定义均为本地代码(不适用项)。

步骤​

  1. 选择模式

    明确走亮色、暗色还是跟随系统;跟随系统只在构造 Theme 时读取一次,系统切换后需重建 Theme 并重新 apply。

  2. 覆盖品牌令牌

    用 Theme.light({primary,bubbleMine,...}) 或 withColors 只改需要的键,其余继承默认值。

  3. 应用到容器

    在 mount 前/后调用 theme.apply(container);IMKit 组件 props 也支持 theme,会在渲染时应用。

  4. 持久化选择

    用 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 变量,任何能写入变量的方案都可用。

相关链接​