@xwink/ui

useTheme

明暗档的单一入口。读写用户选择、解析出当前生效档写进 html[data-theme],并同步 color-scheme ——滚动条、下拉、日期选择这些原生控件不吃 --ui-* token,只认它。

XThemeToggle 只是它的一个操作面,同页放几个都是同一份状态。

基础

选择、生效档与 toggle

返回值

名称类型说明
preferenceRef<'light' | 'dark' | 'auto'>用户的选择,可写;auto 如实保留
resolvedReadonly<Ref<'light' | 'dark'>>当前真正生效的档,auto 时跟随系统
isDarkComputedRef<boolean>resolved === 'dark'
systemComputedRef<'light' | 'dark'>系统外观本身
toggle()() => void在明暗间翻转,落到一个明确档位(不再是 auto
setHostTheme(theme)(theme?: 'light' | 'dark') => void嵌入协议用:宿主指定这一页显示哪档,不持久化、不改用户偏好;传 undefined 撤销

选择与生效档要分开取:跨窗口下发主题(比如 Console 把档位交给 iframe 里的管理子站)必须传 resolved,把 auto 原样传过去等于让对方回落它自己的默认档。

全站共享

缺省时偏好存在可注册域的 Cookie(xwink-ui-theme)上,而不是 localStorage:localStorage 按源隔离, console / chat / home / uc 各存一份;Cookie 按域共享,任何一个站点切换,其它站点在回到前台时跟上 (支持 cookieStore 的浏览器实时)。可注册域由逐级试写 Cookie 探测,本地 localhost 的 Cookie 不分端口, 开发环境同样全站一份。旧的 xwink-ui:theme localStorage 偏好在首次读取时迁移过去。

另存一份 xwink-ui-theme-resolved(上次生效档,只有 light / dark),SSR 宿主靠它在服务端就渲染出 正确档位,跟随系统时也一样;用户在两次访问之间改了系统设置才会有一次误差,客户端接管后纠正。

Nuxt 宿主启用 @xwink/ui/nuxt 即全部接好,不再各写首帧脚本或在 htmlAttrs 里写死档位:

  • head 注入首帧脚本,挂载前按 Cookie 写好 data-theme(全新访客按系统);
  • 客户端插件先于其它插件初始化 useTheme,地址上带宿主 theme 参数(嵌入协议)时改为宿主覆盖;
  • SSR 宿主的服务端插件读 Cookie,把 data-theme 渲染进 HTML,并让本次请求的 useTheme 按该档 计算;渲染用的档位放在 payload 的 UI_SSR_THEME_STATE_KEY 里,挂载前需要与服务端一致的组件按它出。

不需要接管时 xwinkUi: { theme: false }

选项

选项说明
storageRef已有的可写来源(如后端偏好接口响应里的 ref);给了它就不碰共享 Cookie
storageKeynull 表示不持久化;显式给了键而没给 storage 时存 localStorage(不跨站共享)
storage换掉存储本身(sessionStorage 或自定义实现)
fallback没有已存偏好时的档位,缺省 auto
ts
// 明暗档挂到账号上:值由接口响应驱动,异步到达也会自动生效
useTheme({ storageRef: preferencesFromApi.themeMode })

单例与调用时机

内部用 VueUse 的 createSharedComposable + useColorMode首次调用时的 options 决定整页配置, 之后的调用只是取用。@xwink/ui/nuxt 的插件已在最早时机按缺省配置调过一次,要换存储口的宿主关掉 模块的 theme 接管后在自己的插件里先调。服务端每次调用都是独立实例,不会串请求。

切换时抑制过渡来自 useColorMode