核心组件
VS-10 组件库的 Web 实例化。每个原子组件给出变体 · 尺寸 · 状态 · 无障碍 · 令牌消费,并把项目里两套并存的 button/badge 实现(shadcn 与手写)收敛到同一份契约。组件是证据对象,不是通用 SaaS 控件。
证据对象,不是控件
| ① 一份契约 | 同一组件无论用 shadcn 还是手写 CSS,变体名/尺寸名/状态都一致。调用者无需关心实现。 |
| ② 状态先行 | 每个交互组件必须定义 hover/focus/disabled/loading/error/active——而非只画默认态。 |
| ③ 令牌消费 | 颜色/圆角/间距全走语义令牌(WS-01),禁裸 hex、禁 primitive 直引。 |
shadcn 与 bespoke 并存
项目里组件有两套实现,本规范不强制合并,而是统一契约:
| 实现 | 位置 | 令牌方式 | 用在 |
|---|---|---|---|
| shadcn/ui | components/ui/* | Tailwind 语义类 + CVA 变体 | 新组件 / theme-toggle |
| bespoke | globals.css .button/.badge | var() 直引 | 营销 / dashboard / 表单 |
两套都必须暴露相同枚举:button variant = default · secondary · ghost · destructive · outline · link,size = sm · default · lg · icon;badge intent = neutral · success · warning · danger · info · authority。新可复用组件优先进 components/ui/。
Button · 全变体全状态
| 状态 | 表现 | 令牌 |
|---|---|---|
| hover | accent → accent-hover | --accent-hover |
| focus-visible | 古铜环 | var(--focus) |
| disabled | opacity .5 + 不可点 | aria-disabled |
| loading | spinner + aria-busy | aria-busy="true" |
六意图,两实现合一
StatusBadge 把 40+ 状态枚举映射到这六意图。shadcn Badge 与 bespoke .badge-* 都落到同一组意图。
| 意图 | 语义令牌 | 典型状态枚举 |
|---|---|---|
| success | --signal-success | cleared · settled · active · approved |
| warning | --signal-warning | pending · review · expiring |
| danger | --signal-danger | flagged · failed · suspended |
| info | --signal-info | draft · queued · info |
| authority | --authority | verified · sealed(带 data-authority) |
Input · 字段全状态
当前只有表单级 .form-error,缺字段级 <FieldError>。规则:错误信息与字段用 aria-describedby 关联,屏幕阅读器随焦点播报。
全局 input/textarea/select 样式在 globals.css(高风险区,改动全站生效)。新增字段优先用既有类,勿覆盖全局。
Card · 默认线条,权威古铜
默认顶条 = line。绝大多数卡片用这个。
仅当承载已验证/封存证明,顶条才是古铜。
卡片默认顶条是 --line,不是古铜。只有带 data-authority(verified/sealed)的证明卡才用 --authority 古铜条。每个视觉组古铜 ≤1。
结构:Card · CardHeader · CardTitle · CardDescription · CardContent · CardFooter(shadcn)或 .card(bespoke)。
模态 · 抽屉 · 下拉 · 提示
全部基于 Radix(components/ui/),已用逻辑属性(start/end · ps/pe)支持 RTL。
| 组件 | 关键 a11y / RTL |
|---|---|
| Dialog / AlertDialog | role=dialog · aria-modal · 焦点陷阱;start-[50%] 居中 |
| Sheet | side top/bottom/left/right;border-s/-e 逻辑边 |
| DropdownMenu | ps-8/pe-2/start-2 逻辑内距;方向键 + Esc |
| Tooltip / Popover | 悬浮/聚焦触发;align start/center/end |
浮层用 --radius-lg/-xl + --shadow-lg;AlertDialog 的动作按钮复用 buttonVariants(),保持与 §03 同契约。
头像 · 提示 · 待补组件
| 缺口 | 现状 / 规则 |
|---|---|
| Toaster | sonner 已存在但未挂载 → layout 需渲染 <Toaster/> |
| Select / Combobox | 仅原生 <select> → 需共享组件 |
| Pagination | DataTable 有 footer 槽但无分页 → 需共享 |
| Skeleton | 只有 spinner → 需骨架占位 |
| avatar size | inline style 覆写 → 改修饰类 .ava-sm/-lg |
填补缺口时优先加进 components/ui/(shadcn 风格)并落到统一契约,而非又在 globals.css 追加一套。详见 WS-05(模式)与 WS-11(治理)。
组件的机器可执行
| 规则 | 说明 | 可检查 |
|---|---|---|
| comp.semantic-tokens | 只用语义令牌,无裸 hex | ✓ grep |
| comp.variants-unified | button/badge 变体名两实现一致 | ✓ types |
| comp.focus-visible | 每个交互元素有焦点环 | ✓ DOM |
| comp.touch | 触达 ≥44px | ✓ DOM |
| comp.states | hover/focus/disabled/loading/error 齐 | ✓ review |
| comp.status-symbol | 状态带符号,不只颜色 | ✓ review |
| comp.card-rail | 古铜条仅 data-authority 卡 | ✓ DOM |
发布前自检
- ☐ 变体/尺寸/意图名与契约一致
- ☐ 全状态都画了(含 loading/error/disabled)
- ☐ 焦点环 + 44px 触达
- ☐ 状态带符号;古铜仅 data-authority
- ☐ 新组件进 components/ui/,不追加 globals.css
组件哲学 + 双实现契约 + Button + Badge + Input + Card + 浮层 + 头像/缺口 + Lint 九项齐全。WS-04 ✓ 完成。