mcpField
U
ui-component-architecture

ui-component-architecture

github.com/darkmatter开发0263 次安装6 天前更新
代码仓库

关于 ui-component-architecture

面向使用共享 UI 包的团队,指导编码 Agent 优先复用现有组件,并将合适的展示组件提取到共享包,同时保持页面清晰。

主要功能

  1. 01盘点并复用共享 UI 导出
  2. 02让路由专注于组件组合
  3. 03划分共享组件边界
  4. 04使用主题令牌和变体
  5. 05检查包边界与交互行为

适用场景

  1. 01为含共享 UI 包的应用构建页面
  2. 02重构含大量内联标记的页面
  3. 03添加可复用 UI 基础组件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/darkmatter/skills --skill ui-component-architecture

安装后在 Claude Code 中输入/ui-component-architecture确认已加载。

附带文件reference/agents-md-rule.mdreference/eslint-guardrails.md

详细介绍

UI 组件架构

要纠正的失败模式:Agent 用几百行 <div className="..."> Tailwind 代码拼出一个屏幕,重新实现共享 UI 包中已有的 Button、Card 或 Badge,却从未将真正可复用的部分沉淀回该包。结果是样式重复、视觉语言逐渐不一致,屏幕代码也无人能读懂。

解决方法有两个习惯:先复用,再编写,并且通过将可复用单元提取到共享 UI 包中,让屏幕保持精简——但不要过度提取一次性的布局胶合代码。该包 MUST 是独立的包。它的路径和导入别名由各个代码库自行决定(ADR-0013)。从工作区中查找它们(package.json workspaces、packages/*、供应商目录)。不要臆造一个必需的名称。

选择组件边界时,请应用 codebase-design。如果状态、处理器、私有组件和布局共同说明一项能力,就让它们保持在一起。提取应当隐藏行为或实现有用的复用;仅凭 JSX 长度或第二个调用位置,都不足以说明应该提取。

适用场景

  • 在包含共享 UI 包的应用中编写新页面/路由/屏幕。
  • 为内联 div 较多的现有屏幕重新设置样式或重构。
  • 构建看起来像可复用原语的组件(按钮、卡片、徽章、输入框、对话框、空状态、统计信息、骨架屏、头像)。
  • 用户说“构建 X 页面”“添加 Y 视图”“创建 Z 组件”。

不适用场景

  • 非 React 代码,或没有可复用共享 UI 包的单包应用。
  • 没有独立提取职责的一次性布局包装器。
  • 编辑现有组件的内部实现,而不是添加新的界面。

原则

先复用,再编写

编写 JSX 之前,先盘点已有内容,避免重复造轮子:

  • 共享 UI 包——阅读它的 barrel 或 package.json exports。搜索所需内容。
  • 外部注册表——对于共享包中尚不存在的区块,请参阅 shadcn-registry-first 技能。流程为:注册表 → 应用屏幕 → 将稳定的部分沉淀到 UI 包中。

不要重新实现已有的 Button、Card、Badge、Input、Dialog 或 EmptyState。

保持屏幕精简

路由应让人容易理解其数据和主要视觉能力。组合现有原语,并提取具有明确接口的组件。让连贯的状态和事件处理与其控制的 UI 保持在一起。只转发屏幕属性和标记的组件只会增加跳转,却没有隐藏复杂性。私有组件可以与其所属组件共用一个文件。

将可复用单元沉淀到 UI 包中

判断哪些内容应移入共享包的关键经验法则:

  • **沉淀到共享包:**具有独立接口的可复用展示单元,或自包含的视觉原语——Button、Card、Badge、Avatar、EmptyState、Stat、Skeleton、Input、Dialog。在第二处复用是值得检查的证据;仅凭视觉相似并不能证明它们是同一个单元。
  • **留在应用中:**应用特定的组合(某个特定仪表盘的布局)、一次性胶合代码,以及任何包含数据接线或业务逻辑的内容。
  • **不确定时,就留在本地。**单次使用的组件也可能隐藏大量行为,但应用特定的属性应归属应用。第二处用法是重新审视边界的提示,而不是把应用状态移入共享包的理由。
使用令牌和变体设置样式,不要散落使用魔法值
  • 使用主题令牌(CSS 变量、Tailwind 主题),不要在 className 中散布原始十六进制字面值。
  • 对于有变体的组件,使用 cn + 变体映射(或 cva),而不是堆积条件类名字符串。
  • 将可复用视觉单元的样式集中管理;偶发性的布局样式留在本地。

工作流

1. 清点共享 UI 包

查找该包(工作区列表、packages/*、vendor 目录)。列出它已有的导出项。记下要复用的基础组件,避免重复构建。

2. 将屏幕勾勒为组合结构

在编写细节之前,先将页面列为命名组件树。为每个节点标注:UI 包中已有 / 应用专属组合 / 新建但看起来可复用。这样就能确定要导入什么、内联什么,以及提取什么。

3. 精简地构建屏幕

组合现有基础组件,并将每项交互放在一个易于理解的位置。如果文件超过配置的限制,就按有意义的职责拆分,或记录一个范围明确的限制上调;文件高度本身并不能决定是否选择某个组件。

4. 提取可复用部分

对于每个符合晋升启发式的部分:

  • 将其放入 UI 包中,与同类组件放在一起。
  • 从 barrel 或 package.json exports 导出。
  • 保持属性通用——不要让应用专属类型、路由、状态存储或 API 客户端泄漏到 UI 基础组件中。共享 UI 组件应保持无状态且只负责呈现;通过属性传入数据。
  • 使用 cn/cva 将变体放在一起。
5. 验证
  • 屏幕从 UI 包导入基础组件,而不是重新定义它们。
  • UI 包中的组件均不包含应用专属导入(@/...、应用路由器、状态存储、API 客户端)。
  • 类型检查和构建均通过,使用仓库提供的命令。
  • 交互通过组件的公共接口完成;测试应观察用户行为,而不是组件的私有 hook 或辅助函数调用。

哪些内容应晋升,哪些应保留

保留在应用中晋升到 UI 包中
/dashboard 页面布局StatCard、Badge、EmptyState
特定路由的数据获取和接线Button、Input、Dialog、Skeleton
仅在一个营销页面使用一次的 hero在 3 个以上页面复用的 hero 变体
业务逻辑、功能开关、应用状态接收属性的呈现型基础组件

避免过度提取

过度提取是相反的错误,代价同样高昂。过于急切地提取有以下迹象:

  • UI 包组件的属性名称与某个特定屏幕绑定。
  • 组件只是转发很长的属性列表,却没有封装交互或呈现决策。
  • UI 基础组件导入应用的状态存储、路由器或 API 客户端。

解决办法:将应用专属行为保留在职责清晰的应用组件中。只有在共享基础组件具备实用的呈现接口时才进行提取。即使本地辅助函数和私有组件只用一次,也应将它们放在附近。

参考资料

  • reference/agents-md-rule.md — 一条简短、可直接粘贴到使用方仓库 AGENTS.md 中的常驻规则。这是杠杆效益最高的做法:技能按需加载,在“每次 UI 编辑”时可能不会触发;而 AGENTS.md 规则始终处于上下文中。将其添加到每个包含共享 UI 包的仓库,并填写该仓库实际的包路径和导入别名。
  • reference/eslint-guardrails.md — 通过机械方式强制执行边界的 lint 规则(禁止在应用 JSX 中使用原始十六进制值、限制 JSX 深度,以及最重要的:禁止在 UI 包中导入应用代码的边界规则)。

与其他技能的关系

  • shadcn-registry-first — 从注册表获取_外部_区块。本技能负责_内部_复用和提取。二者可以配合使用:注册表 → 应用屏幕 → 将稳定部分晋升到 UI 包。
  • darkmatter-design-system — 当应用使用该系统时,负责视觉语言和设计令牌。这些令牌仍存放在 UI 包中;本技能负责包边界。
  • repository-organization — 负责目录和包的组织位置。本技能关注更细一层的组件复用粒度。
  • vercel-react-best-practices — 适用于你编写和组合的组件的性能模式。

评论

支持 Markdown