useBrandColor
让用户在运行时换主色(色板选色、自由取色)。选中一个颜色后算出
--ui-primary / --ui-primary-fg / --ui-secondary / --ui-secondary-fg 写到 html 的内联样式上,
强调渐变、焦点环、主色阴影这些派生 token 自动跟着变。
主色固定的宿主不需要它:在 app 级入口 import tokens.css 之后覆写 token 即可(见 主题 token)。
基础
未选择,沿用 tokens.css 的主色。
返回值
| 名称 | 类型 | 说明 |
|---|---|---|
color | ComputedRef<string | undefined> | 当前主色;挂载前一律是 fallback,与服务端一致 |
select(hex) | (color: string) => boolean | 选中并立即生效、持久化;不合法或不在色板里时不动,返回 false |
reset() | () => void | 清掉已存选择回到 fallback;没有 fallback 时删掉内联变量,回到 tokens |
选项
| 选项 | 说明 |
|---|---|
storageKey | 持久化用的 localStorage 键;不给就不持久化 |
fallback | 没有已存选择时的主色;不给就沿用 tokens.css(及宿主覆写)的主色 |
presets | 可选色白名单:给了就只认这些,存储被改坏、色板改版时回落 fallback |
foreground | 写死前景色,不按对比度选(如产品要「彩色实底一律白字」) |
foregrounds | 按对比度选前景时的两个候选 { light, dark },缺省 #ffffff 与 #111827 |
shade | 深一档在 OKLCH 明度上降多少,缺省 0.06 |
storage | 换掉存储本身;首帧脚本只读 localStorage,换了就不再首帧上色 |
只认 #rgb / #rrggbb:任意 CSS 颜色要浏览器才解析得了,而首帧的变量表要在构建期算。
页面级单例,首次调用时的配置决定整页(同 useTheme)。
算法
- 前景按对比度选:在深浅两个候选里取 WCAG 2.x 对比度高的那个,副色的前景按副色自己算。
WCAG 2 的公式对中等亮度的饱和色(粉、橙)偏向深色字,观感上有人更喜欢白字——产品有这类取舍时
传
foreground写死,代价是偏亮的主色上文字可能掉出 AA,由宿主挑色板规避。 - 深一档在 OKLCH 里降明度:色相、色度不动,只降感知明度;降完落到 sRGB 外(深而艳的色常见) 就收色度拉回来,不逐通道截断——截断会把色相拧偏。不用 HSL(它的「明度」不是感知明度,同样降 10% 黄色几乎没变、蓝色一下就黑了),也不按 RGB 等比压暗(饱和色压完发灰发脏)。
算法都是导出的纯函数,宿主可以直接用:brandVariables(color, options)、contrastRatio(a, b)、
pickForeground(background, candidates)、darken(color, amount)、relativeLuminance(color)。
首帧与 SSR
已存的颜色只在浏览器里,服务端不知道。首帧靠一段 head 脚本:在样式生效前把变量写到 html 上,
SSR 与 SSG 的首帧就是对的颜色。脚本不带取色算法——色板与 fallback 在构建期预算成表,色板外的
颜色读 useBrandColor 另存的变量缓存(<storageKey>:vars)。html 的内联样式不属于 Vue 渲染的
任何节点,水合不比对它,不会有水合差异。
Nuxt 宿主在模块配置里写一次,首帧脚本与整页配置都由 @xwink/ui/nuxt 接管,组件里
useBrandColor() 不必再传参:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@xwink/ui/nuxt'],
xwinkUi: {
brand: {
storageKey: 'my-site:brand',
fallback: '#6c4de6',
presets: ['#6c4de6', '#e8589b', '#2f86c9'],
},
},
})
非 Nuxt 宿主从不牵出组件的 @xwink/ui/brand 取 brandBootstrapScript(config),在构建配置里
内联进 index.html 的 head。
不走 Cookie:明暗档是全站共享的用户偏好,放在可注册域的 Cookie 上让一处切换处处生效; 品牌色是单个站点的产品选择,不该跨站串色,首帧脚本已经覆盖 SSR 与 SSG,Cookie 多出来的只是 让服务端把「哪个色块选中」也渲染对。明暗共用一个主色:内联样式压过 tokens.css 深色档的主色, 选色时要兼顾两档底色。
界面里按 color 渲染选中态(色板高亮)时,挂载前是 fallback、挂载后才换成已存色,
与 XThemeToggle 同理;需要的话自己做挂载门控。