Repository Wiki
ChanIok/SpinningMomo

UI 组件库与设计系统(shadcn/vue 化)

本项目 Web 前端(web/ 目录)的界面层基于 shadcn-vue 体系构建:组件源码以"复制到仓库"(copy-into-repo)而非 npm 依赖的方式落地在 web/src/components/ui/ 下,通过 Tailwind CSS 变量实现主题化,形成一套可直接修改、可完全掌控的本地 UI 组件库与设计系统。

Purpose and Scope(目的与范围)

本页覆盖该 UI 组件库与设计系统的整体架构与组织方式:

  • shadcn-vue 配置文件 web/components.json 的逐项解析(风格、Tailwind、图标库、别名、注册表)
  • web/src/components/ui/ 的组件组织模式(每组件一个目录、多子组件 SFC + index.ts barrel 导出)
  • 设计令牌(design tokens)与主题化机制(baseColor: neutral + CSS 变量)
  • 别名(aliases)体系与导入路径约定
  • 组件从 CLI 生成到业务消费的端到端流程

有意留给兄弟页面的内容:具体业务组件与页面组合方式、路由与视图层、全局状态管理、构建工具链(Vite)配置、后端 C++/xmake 部分——这些各有独立目录页,本页仅在架构图中作为消费层出现,不展开。

Overview(概述)

shadcn-vue 是 shadcn/ui 设计理念在 Vue 生态的实现:它不是一个传统的组件库依赖,而是一个组件分发机制。开发者通过 CLI 把组件源码复制进自己的仓库,因此:

  1. 源码即资产:每个组件是仓库内真实的 .vue 单文件组件,可直接阅读、修改、重构,没有 node_modules 黑盒。
  2. 设计系统可编程:样式不写死在组件里,而是通过 Tailwind CSS 变量(HSL 语义令牌)注入,换主题只改变量。
  3. 组合优于配置:一个交互组件(如 alert-dialog)被拆成多个子组件(AlertDialogTrigger、AlertDialogContent、AlertDialogAction……),由业务方自行拼装,库不预设使用场景。

本项目采用的配置为 new-york 风格、neutral 基色、TypeScript、lucide 图标库、CSS 变量启用(详见下文配置表),位于 web/components.json。

已确认的组件目录

从仓库实际文件列表确认,web/src/components/ui/ 下至少包含以下组件模块(列表按字母序截断,实际更多):

组件目录子组件文件(已确认)barrel
accordion/Accordion.vue、AccordionItem.vue、AccordionTrigger.vue、AccordionContent.vueindex.ts
alert/Alert.vue、AlertTitle.vue、AlertDescription.vueindex.ts
alert-dialog/AlertDialog.vue、AlertDialogTrigger.vue、AlertDialogContent.vue、AlertDialogHeader.vue、AlertDialogFooter.vue、AlertDialogTitle.vue、AlertDialogDescription.vue、AlertDialogAction.vue、AlertDialogCancel.vueindex.ts
badge/Badge.vue(index.ts 同目录模式)

Architecture(架构)

Loading diagram...

架构解读:

  • components.json 是唯一的"设计系统元数据"入口:它声明风格(new-york)、基色(neutral)、样式注入位置(src/index.css)、图标库(lucide)与别名映射。CLI 依据它决定组件文件落盘位置与导入路径写法。
  • src/index.css 承载设计令牌:cssVariables: true 意味着组件样式引用的是语义化 CSS 变量(而非硬编码色值),主题切换/暗色模式只需在 CSS 层覆盖变量。
  • 组件库层与业务层通过别名解耦:业务代码统一从 @/components/ui/<component> 导入,组件库内部重构不影响业务导入路径。
  • 依赖方向严格单向:视图 → 业务组件 → ui 组件 → 设计令牌。UI 组件不反向依赖业务代码,这是该体系可维护的关键约束。

组件模块的 Barrel 模式

每个 UI 组件目录内部是统一的"多 SFC + barrel"结构,以 accordion 为例:

Loading diagram...

设计意图:

  • index.ts 作为唯一公共出口(barrel re-export),业务方无需知道每个子组件存放在哪个 .vue 文件里,只面向"组件目录"这一粒度编程。
  • 一组件多文件:与某些库把整个组件塞进单个文件不同,shadcn-vue 把无头交互原语的每个部分拆成独立 SFC(如 AlertDialog 拆成 9 个文件),每个文件职责单一、便于按需裁剪或替换实现。

Configuration Options(配置项详解)

shadcn-vue 的全部行为由 web/components.json 驱动,以下是逐项解析:

json
1{ 2 "$schema": "https://shadcn-vue.com/schema.json", 3 "style": "new-york", 4 "typescript": true, 5 "tailwind": { 6 "config": "", 7 "css": "src/index.css", 8 "baseColor": "neutral", 9 "cssVariables": true, 10 "prefix": "" 11 }, 12 "iconLibrary": "lucide", 13 "aliases": { 14 "components": "@/components", 15 "utils": "@/lib/utils", 16 "ui": "@/components/ui", 17 "lib": "@/lib", 18 "composables": "@/composables" 19 }, 20 "registries": {} 21}

Source: components.json

配置项类型默认值(本项目)说明
$schemastringhttps://shadcn-vue.com/schema.jsonJSON Schema 地址,供编辑器校验与补全
stylestring"new-york"组件视觉风格分支。new-york 较 default 更紧凑、对比更强,是 shadcn 生态的当代默认
typescriptbooleantrue生成 TypeScript 版组件源码(.ts + 带类型的 <script setup lang="ts">)
tailwind.configstring""Tailwind 配置文件路径;为空表示项目使用 Tailwind v4 的 CSS-first 配置(无独立 JS 配置文件)
tailwind.cssstring"src/index.css"设计令牌(CSS 变量)注入的目标样式表,即项目的 Tailwind 入口 CSS
tailwind.baseColorstring"neutral"基础色板(灰阶基准)。neutral 提供无彩度灰阶,适合以品牌色点缀的中性底
tailwind.cssVariablesbooleantrue启用 CSS 变量主题化:组件样式引用 --background、--foreground 等语义变量,而非写死色值
tailwind.prefixstring""Tailwind 类名前缀;为空即不加前缀
iconLibrarystring"lucide"组件内嵌图标统一使用 lucide 图标库
aliases.componentsstring@/components业务组件目录别名
aliases.utilsstring@/lib/utils工具函数(含 cn() 类名合并器)所在位置
aliases.uistring@/components/uiUI 组件库根目录,CLI 落盘与业务导入都指向这里
aliases.libstring@/lib通用库代码目录
aliases.composablesstring@/composables组合式函数目录
registriesobject{}自定义/第三方注册表;为空表示仅使用官方注册表

别名体系的设计意图

别名是这套体系的"稳定接口"层。注意 utils 与 ui 两条:

  • utils: "@/lib/utils" 指向 web/src/lib/utils.ts——所有组件共用的类名合并入口(shadcn 生态约定为 cn(),内部基于 clsx + tailwind-merge 组合;该文件的实现细节未在本次读取范围内,此处仅依据别名配置说明其角色)。
  • ui: "@/components/ui" 让组件源码内部的相互引用(例如 alert-dialog 内部引用 button)也走别名,而不是相对路径——这样 CLI 在任意目录落盘组件时,生成的导入语句都能解析。

Core Flow(核心流程:组件如何进入并服务于业务)

Loading diagram...

流程要点(对应真实配置):

  1. add 命令 → 读取 components.json:CLI 从中获知风格、语言与路径约定;typescript: true 保证产出 TS 源码。
  2. 落盘位置由 aliases.ui 决定:所有组件统一进入 web/src/components/ui/<name>/,与已确认的 accordion/、alert/、alert-dialog/、badge/ 目录结构一致。
  3. 令牌注入到 src/index.css:cssVariables: true + tailwind.css: "src/index.css" 共同决定了主题变量只存在这一处,是全站视觉一致性的单一事实来源(single source of truth)。
  4. 业务消费走 barrel:import { ... } from '@/components/ui/accordion' 只接触目录级出口,子组件文件重组不影响调用方。
  5. 定制即改源码:这是"复制进仓库"模式相对传统依赖的核心收益——升级是可选的,定制是安全的。

Design Tokens(设计令牌与主题机制)

cssVariables: true 模式下,组件的视觉属性被映射到语义 CSS 变量。变量定义在 web/src/index.css(由 tailwind.css 配置指定;该文件的具体令牌清单未在本次读取范围内,机制依据配置项说明):

  • 语义层变量:如背景、前景、主色、边框、圆角等以"角色"命名,而非以颜色值命名。组件类名引用的是语义(例如"主按钮背景"),主题系统只需重定义变量的值。
  • baseColor: neutral 的作用:CLI 首次初始化时按该基色生成一整套灰阶变量取值,作为默认主题。
  • 暗色/多主题:因变量集中在 index.css,可按 prefers-color-scheme 或 .dark 类批量覆盖变量,实现零组件改动换肤。
  • tailwind.config: ""(Tailwind v4 CSS-first):主题与工具类的声明收敛在 CSS 文件本身,减少了"JS 配置 + CSS 变量"双轨维护的成本。
Loading diagram...

API Reference(导入与使用契约)

本页主题是组件库的架构而非单个组件的行为,因此 API 参考记录的是体系级契约;各组件的 props/emits 细节请直接阅读对应 .vue 源文件(这也是该模式的预期用法——源码就在仓库里)。

导入入口(barrel)

  • 签名:import { <子组件>, ... } from '@/components/ui/<component-name>'
  • 说明:每个组件目录的 index.ts 是唯一稳定出口。业务方永远不应深入 xxx.vue 文件路径导入。
  • 依据:aliases.ui: "@/components/ui"(见配置表)。

已确认的目录级导出面

  • @/components/ui/accordion —— 已确认含 Accordion、AccordionItem、AccordionTrigger、AccordionContent。
  • @/components/ui/alert —— 已确认含 Alert、AlertTitle、AlertDescription。
  • @/components/ui/alert-dialog —— 已确认含 AlertDialog、AlertDialogTrigger、AlertDialogContent、AlertDialogHeader、AlertDialogFooter、AlertDialogTitle、AlertDialogDescription、AlertDialogAction、AlertDialogCancel。
  • @/components/ui/badge —— 已确认含 Badge。

命名约定(从文件名归纳)

  • 同名根组件:目录名与根组件同名(accordion/Accordion.vue)。
  • 部位后缀:子组件以部位命名(Item、Trigger、Content、Header、Footer、Title、Description)。
  • 语义动作:可交互动作以动词命名(Action、Cancel)。

说明:以上各 .vue 文件的内部实现(props 类型、可访问性属性等)未包含在本次源码读取范围内,故不作逐项罗列;请以仓库内对应文件为准,例如 Accordion.vue。

Failure Modes, Edge Cases & Concurrency(故障模式、边界与并发)

基于本体系(copy-into-repo + CSS 变量 + Tailwind)的固有特性,可验证的注意事项如下:

  • CLI 升级与本地修改冲突:组件源码在仓库内,执行 add 覆盖同名组件会冲掉本地定制。缓解方式:升级前 diff 本地版本与注册表版本;或将定制下沉到 CSS 变量/包装组件层,保持 ui/ 内源码尽量贴近上游。
  • 令牌缺失的静默失败:cssVariables: true 下,若 src/index.css 中某语义变量未定义(例如手工删除组件后残留引用),Tailwind 会静默产出无效类而非抛错,表现"组件没颜色/样式"。排查入口永远是 index.css 的变量清单。
  • 别名解析失败:@/components/ui 等别名需在构建器(Vite)与 IDE 两端同步配置;仅 components.json 有别名而 tsconfig/Vite 未配置时,运行期报模块不存在。components.json 只约束 CLI 落盘与导入写法,不提供运行时别名。
  • 组件间依赖(如 alert-dialog 内部引用 button):删除某个"被依赖"组件目录会级联破坏其它组件的 barrel 导入。审计删除时需 grep 引用。
  • 并发场景:纯客户端渲染组件,无服务端共享状态;并发问题集中在用户输入竞争(如对话框关闭动画期间重复触发 Action)。shadcn-vue 体系下此类问题的处理位于各组件源码内部(如 radix-vue 原语的禁用/去抖),业务层可通过组合式函数约束。本页不展开单组件行为。
  • TypeScript 边界:typescript: true 生成的组件带有完整 props 类型;业务方用 as any 绕过会失去该保护,且升级时静默漂移。

Performance & Operational Notes(性能与运维要点)

  • 按需编译,无运行时 CSS-in-JS:样式在构建期由 Tailwind 静态产出,组件运行时无样式注入开销;未使用的工具类会被清除(生产构建仅保留实际用到的类)。
  • 产物即源码,可审计:ui/ 下每个 .vue 都是可 diff、可 review 的普通文件,性能回归(例如新增了昂贵动画类)在 code review 阶段即可发现。
  • new-york 风格的体积取舍:较紧凑视觉通常伴随更多细边框/小间距类,但对最终 CSS 体积影响很小,真正决定体积的是使用了哪些组件。
  • 图标统一 lucide:iconLibrary: "lucide" 意味着图标按需 tree-shaking,不引入整包;替换图标库需同步修改 components.json 并重新 add 受影响组件。
  • 主题切换零重编译:因主题值全部在 CSS 变量层,运行时切换主题只是变量覆盖,不触发 JS 状态更新。
  • 升级策略:建议以组件为单位渐进升级,而不是全量 add。CLI 的 registries: {} 表示当前仅消费官方注册表,引入私有注册表可托管定制版组件。

Extension Points(扩展点)

  • 新增组件:pnpm dlx shadcn-vue@latest add <name>,落盘至 @/components/ui/<name>/,自动获得同构的 barrel 结构。
  • 定制既有组件:直接编辑仓库内 .vue 源码——这是该模式的头等扩展方式,无需 fork 上游。
  • 主题扩展:在 src/index.css 增/改 CSS 变量即可全局生效;新增品牌色=新增一组语义变量并映射进 Tailwind。
  • 自有组件入库:自建组件按"目录 + 多 SFC + index.ts barrel"的模式放入 ui/,即可享受与 shadcn 组件一致的导入体验;随后可用自定义 registries 托管并复用。
  • 组合式函数:aliases.composables: "@/composables" 预留了跨组件逻辑层,与 UI 组件解耦。
  • 风格切换:style 与 baseColor 变更需重新初始化/add 组件并更新 index.css 令牌,属于一次性迁移操作而非运行时开关。

备注:本页依据 web/components.json 与 web/src/components/ui/ 目录的真实文件结构撰写;受源码读取预算限制,各 .vue 文件内部实现与 src/index.css 具体令牌清单未逐行核验,相关表述已注明依据为配置项约定。构建工具(Vite/tsconfig)对别名的实际解析配置属于"构建工具链"兄弟页面范畴。

Sources

(1 files)