Tokens
组件库的单一事实源是 packages/ui/core/src/styles/tokens.css:纯 :root 自定义属性 + 明暗 / 对比度 / 尺寸维度的重定义块。组件内 var(--ui-*) 不写内联 fallback,全局调整只改这里。
色彩
| Token | 默认值 | 用途 |
|---|---|---|
--ui-bg | #f9fafb | 页面底色 |
--ui-surface | #ffffff | 卡片 / 控件面 |
--ui-surface-muted | #f4f4f5 | 次级面(表头 / 行悬停 / 内联代码底) |
--ui-surface-hover | #e5e7eb | hover / 抬升面 |
--ui-fg | #1f2937 | 正文 |
--ui-fg-strong | #111827 | 标题 / 强调文字 |
--ui-fg-subtle | #4b5563 | 说明性正文(比正文轻、比次要文字重) |
--ui-fg-muted | #5b6371 | 次要文字 / placeholder |
--ui-border | #d1d5db | 常规边框 |
--ui-border-muted | #e5e7eb | 分隔线 / 弱边框 |
--ui-primary | #d9a06c | 主题色(主操作 / 选中态 / 聚焦) |
--ui-primary-fg | #ffffff | 主题色之上的文字 |
--ui-secondary | #c78d55 | 次级色(与主题色组成品牌强调) |
--ui-secondary-fg | #ffffff | 次级色之上的文字 |
--ui-primary-gradient | 自动生成 | primary → secondary 的品牌强调渐变 |
--ui-danger | #dc2626 | 危险语义 |
--ui-danger-fg | #ffffff | 危险色之上的文字 |
--ui-success | #16855a | 成功语义 |
--ui-success-fg | #ffffff | 成功色之上的文字 |
--ui-warning | #b45309 | 警告语义 |
--ui-warning-fg | #ffffff | 警告色之上的文字 |
--ui-info | #2563eb | 信息语义 |
--ui-info-fg | #ffffff | 信息色之上的文字 |
派生值
跟随 primary / danger 自动换主题,宿主一般不需要单独覆写:
| Token | 说明 |
|---|---|
--ui-ring | 聚焦环颜色(primary 70% 透明混合) |
--ui-focus-ring | 聚焦环阴影整体 |
--ui-danger-ring | 危险态聚焦环 |
--ui-shadow | 浮层阴影 |
--ui-shadow-primary | 主题色阴影(gradient 按钮悬停) |
尺寸
控件尺寸标尺(mini / small / medium(默认) / large / super)以 --ui-size-<size>-<维度> 形式集中定义(高度 / 水平内边距 / 字号 / 圆角四维度),组件只消费下面这组 --ui-control-*(默认 medium 档):
| Token | 默认值 | 用途 |
|---|---|---|
--ui-radius | 16px | 容器级圆角(卡片 / 弹窗 / 面板);紧凑档(mini / small)跟该档控件圆角 |
--ui-control-height | 36px | 控件统一高度(按钮 = 输入框 = 下拉,天然等高) |
--ui-control-padding-x | 12px | 控件水平内边距 |
--ui-control-font-size | 14px | 控件字号 |
--ui-control-radius | 10px | 控件级圆角 |
五档标尺值见
tokens.css的--ui-size-*;data-size="small"环境与组件sizeprop 共用同一套值。
叠放层级
按用途预分档,组件只引用 token、不写数字。同一档里后打开的在上面(浮层都 portal 到 body,按 DOM 顺序叠),所以可交互的层共用一档:对话框里打开的菜单、菜单里打开的对话框,两个方向的嵌套都对。
| Token | 值 | 放什么 |
|---|---|---|
--ui-z-sticky | 40 | 吸顶顶栏、侧边章节导航 |
--ui-z-float | 60 | 常驻悬浮窗(聊天浮窗) |
--ui-z-layer | 100 | 可交互层:弹层、菜单、下拉、对话框 |
--ui-z-toast | 200 | 全局提示 |
--ui-z-tooltip | 300 | 悬停提示 |
--ui-z-drag | 400 | 拖放反馈、假光标 |
用法:class="z-(--ui-z-layer)"。组件内部的局部层级(表格吸顶表头)不走这里。
四维适配
全部通过重定义 token 实现,组件代码零分支。本站「调试面板」可实时切换验证:
- 主题色:全仓默认品牌只在
tokens.css定义;强调渐变由--ui-primary → --ui-secondary自动生成。确有独立品牌的宿主才在 app 级入口覆写主题色、次级色及各自前景色(见开始使用),不在页面级 style 里写。 - 明暗:
[data-theme="dark"]下使用 zinc 基调,并把品牌色收敛为低亮度铜棕#9b6a48 → #7b482f,避免琥珀铜在暗色背景上过度抢眼;html 标属性即启用。 - 对比度:
@media (prefers-contrast: more)下加深边框,并把--ui-fg-subtle与--ui-fg-muted并成同一档——本体是媒体查询,不占 html 属性;本站调试面板以data-contrast="more"强制预览同一组值。默认档(不开高对比)本身就要达标:四档文字在--ui-bg/--ui-surface/--ui-surface-muted上都过 WCAG AA 的 4.5:1,由scripts/gate/ui-token-contrast.test.mts钉住。 - 尺寸:
[data-size="small"](全局紧凑)切换控件尺寸组;组件sizeprop 做实例级覆盖(mini / small / medium / large / super)。
换色
画大面积色块的组件都接 color,换色逻辑全库只有 colorVariables 一份:主色实底与渐变按钮(XButton)、开关(XSwitch)、勾选(XCheckbox / XRadioGroup)、滑条(XSlider)、进度(XProgress)、工具组配方的选中块(XSegmented / XToggle / XToggleGroup)。
- 取值:
primary(缺省,沿用宿主主色)/danger/success/warning/info换成对应语义 token,跟着明暗档走;十六进制走品牌色算法(与 useBrandColor 同一套:前景按对比度选、副色降一档),明暗共用。其余 CSS 写法不认、不改写:前景色要在服务端也算得出来。 - 改写的是组件根上的一族变量:
--ui-primary/-fg/--ui-secondary/-fg,外加渐变、焦点环、主色阴影这几个派生值(PRIMARY_DERIVED_VARIABLES)。自定义属性在声明它的元素上求值,:root上的派生值已按根的主色算好,子树里只改--ui-primary它们不跟着变,所以在同一处重新声明。组件内部照旧只画--ui-primary一族,没有color === 'danger'这类分支。 - 工具组配方的项缺省灰底(主色留给导航与主按钮),给了
color(含primary)才换成该颜色的主色版。 - 小面积的强调(徽标
XBadge的语义档、链接色、焦点描边)不在此列:它们是语义档位本身,不是「同一块换个颜色」。
宿主自写有主色实底的组件时同样继承 ColorProps、根上绑 colorVariables(color):
<script setup lang="ts">
import { colorVariables, type ColorProps } from '@xwink/ui'
const props = defineProps<ColorProps>()
</script>
<template>
<span :style="colorVariables(props.color)" class="bg-(--ui-primary) text-(--ui-primary-fg)">
<slot />
</span>
</template>
状态过渡
悬停、按下、选中、聚焦、展开这类状态变化全库用一个默认 TRANSITION(ui-transition duration-150 ease-out)。ui-transition 是 tailwind.css 里的 @utility:过渡颜色类、透明度、阴影、位移与滤镜;减弱动效只留颜色类——这个偏好针对的是运动,变色不晃也不移,全关反而让切换生硬。150ms ease-out 是跟手与看得出过渡之间的取值。
- 不写 Tailwind 的
transition-*原子类:它们都写transition-property,后写的整串替换前面的,叠在默认上不是追加而是互相覆盖(经cn()时 twMerge 也只留最后一个)。要多过渡的属性追加进变量:cn(TRANSITION, '[--ui-transition-extra:width]'),多个用逗号。 - 时长、缓动只写各自的属性,经
cn()后写的赢:duration-300、按下更跟手的active:duration-75。 - 入场、呼吸这类展示动效是关键帧,不走它。
消费方式
组件用 Tailwind 箭头变量原子类直接引用 token——刻意不用包级 @theme 注册,layer 归属永远跟宿主 utilities 走:
<div class="bg-(--ui-surface) text-(--ui-fg) border border-(--ui-border)">
<span class="text-(--ui-fg-muted)">次要文字</span>
</div>
宿主写自己的样式时照同样方式引用,四维适配自动继承。