SKILL.md
readonly只读
name
migrate-radix-to-base
description
将 React 项目和组件从 Radix UI 迁移到 Base UI。当被要求从 radix 迁移、切换到 base-ui、转换 radix 原语或切换 shadcn 项目的基础库时使用。处理单个组件(如“迁移 accordion”)和整个项目。
Radix UI -> Base UI 迁移
你将 shadcn 包装器、手写的 radix 组合以及它们的消费者迁移到 @base-ui/react,并保持项目每一步都可构建。要精确;切勿猜测映射。当某个属性或部分不在这些参考文件中时,在转换前检查 node_modules/@base-ui/react/**/*.d.ts,并在报告中记录差距。
预检(始终执行)
npx shadcn@latest info --json(或项目的运行器):提供当前基础库、STYLE(例如radix-lyra)、tailwind 版本、别名、已安装的组件和包管理器。信任它而非推断。- 检测包管理器(packageManager 字段 / 锁文件:pnpm-lock.yaml、bun.lock、yarn.lock、package-lock.json)并用于每次安装。切勿留下过时的锁文件。
- 要求干净的 git 树;在分支上工作;每个组件一个提交。
- 在接触依赖之前进行基线检查:运行项目的类型检查/构建,以便预先存在的失败不会归咎于你。
- 安装
@base-ui/react与 radix 共存。Radix 包仅在最后一个组件迁移后才移除(两者可以共存)。
策略:优先使用黄金对,其次使用转换引擎
- 通过 CLI 的黄金对(首选)。 如果项目是 shadcn 且具有已知样式(
radix-<style>),则 shadcn CLI 本身就是黄金对执行器:- 首先对每个 ui 包装器进行分类:将用户文件与其原始版本进行差异比较,使用 components.json 中的样式原样拼入 URL(
https://ui.shadcn.com/r/styles/<style>/<component>.json,files[0].content)。这适用于带前缀的样式(radix-nova)和旧版无前缀的样式(new-york、new-york-v4、default),这些样式仍然可用。 - 整个项目模式:立即将
components.json中的样式radix-<style>改为base-<style>。渐进模式:暂不更改(项目仍主要是 radix;更改只在最后一个组件之后进行一次);直接通过 URL 获取 base 变体(https://ui.shadcn.com/r/styles/base-<style>/<component>.json)。 - 未修改的包装器,整个项目模式:
shadcn add <component> --overwrite会以项目精确的图标/字体/预设解析提供 base 变体。切勿批量使用--all --overwrite;要逐个组件进行,否则你会被无关的注册表版本漂移淹没。渐进模式:切勿使用--overwrite(它会破坏消费者仍在导入的原始文件);而是将获取的 base 变体内容写入<component>-base.tsx。 - 自定义的包装器:获取 base 变体并将用户的差异重放到其上(他们的自定义必须保留;
--overwrite会破坏它们)。可大规模工作的机械实现:git merge-file user.tsx radix-golden.tsx base-golden.tsx(三路合并,以 radix golden 为祖先)自动解决大多数文件;手动使用参考表解决冲突。 - 对每个黄金对文件进行强制性的残留扫描,包括那些合并“干净”的文件:每个文件执行
grep -n "radix-ui\|@radix-ui\|IconPlaceholder"。注册表有时会在变体之间重新排序函数,这会导致三路合并报告零冲突,但留下过时的 radix 代码块。干净的合并并不能证明文件是干净的。
这比重构转换更可靠;只要存在黄金对就使用它。消费者/应用代码没有 CLI 机制:始终根据consumer-props.md手动迁移。
- 首先对每个 ui 包装器进行分类:将用户文件与其原始版本进行差异比较,使用 components.json 中的样式原样拼入 URL(
- 旧版样式(new-york、new-york-v4、default):仅分类,不重放。 这些没有对应的 base 版本(没有 base-new-york),重新定位到 base-<style> 变体会改变用户应用的外观。仅使用 radix golden 检测自定义,然后在用户自己的文件上运行转换引擎:重新连接原语,保留其精确的类,应用 class-mapping 重命名。他们的外观保持不变。在旧版整个项目迁移结束时,标记(不修复):样式名称对 CLI 来说仍读作 radix,因此未来的
shadcn add将提供 radix 变体;用户决定是切换样式还是手动添加。 - 转换引擎(备选)。 手写的 radix 代码、非 shadcn 项目、未知样式:使用
universal-patterns.md(两种形式的导入:radix-ui和@radix-ui/react-*;asChild->render 及工作示例;Portal>Positioner>Popup;positioner FORWARD 规则;部分重命名)、每个系列属性表(overlays.md、menus.md、form-controls.md、disclosure.md、display-misc.md)、class-mapping.md用于数据属性/CSS 变量重写,以及wrapper-shapes.md用于精确的目标形状(tooltip arrow、SubContent 默认值、select 结构)。
模式
渐进模式(默认)。 “迁移 accordion” = 一个组件,绞杀者模式:
- 首先检测进行中的状态:是否存在
<component>-base.tsx,消费者是否在旧/新导入之间拆分。文件就是状态;继续,切勿重新开始。 - 如果该组件导入其他仍在使用 radix 的 ui 包装器(select -> button),则停止并建议先自底向上迁移这些包装器。
- 将迁移后的版本写入
<component>-base.tsx(原始文件不变;黄金对内容通过 URL 获取,或手动转换,根据上述策略);进行类型检查。逐个重新指向消费者(导入 +consumer-props.md中的调用点属性);每次进行类型检查。当没有消费者导入原始文件时:删除它,将-base重命名为原始名称,将导入改回,最终检查,提交。当项目中最后一个 radix 包装器完成时,将components.json切换为base-<style>并移除 radix 依赖。
整个项目(仅在明确要求时):按依赖顺序逐个组件进行相同的工作(叶子/共享包装器如 button 和 label 优先)。包装器完成后,根据 consumer-props.md 扫描所有应用代码——调用点的破坏面比 asChild 大得多。然后移除 radix 依赖,安装,完整构建。
硬性规则
- 切勿触碰非 radix 库或其包装器:cmdk(command)、vaul(drawer)、sonner、input-otp、react-day-picker(calendar)、recharts(chart)。报告它们为有意未触碰。
- 无 Base UI 对应项:AspectRatio -> CSS aspect-ratio div;Label -> 原生
<label>;VisuallyHidden ->sr-only;Direction -> Direction Provider(direction属性,而非dir)。Popover Anchor 和 NavigationMenu Indicator 没有对应项:惰性直通 + 标记。 button.tsx迁移到真正的@base-ui/react/button原语,绝不是手写的 useRender 包装器。- 行为差异被标记,绝不静默修补(tabs 手动激活、菜单项点击不关闭、nav-menu 50ms 延迟)。目标是符合 shadcn base 注册表的惯用 Base UI。
- 诚实报告:跳过/还原的文件被标记为已标记,绝不作为已迁移。预先存在的失败被命名为预先存在。
验证和报告
每个文件进行类型检查,每批构建,最后与基线进行完整构建。
报告位于项目根目录的 .migration/ 目录中,每个组件一个文件:.migration/<component>.md(例如 .migration/accordion.md)。
规则:
- 每次运行为其迁移的每个组件写入(或完全覆盖)文件。重新运行一个组件会替换其报告;切勿触碰其他组件的文件。
- 多组件运行(“迁移 alert-dialog 和 dropdown-menu”)为每个组件写入一个文件,每个文件自包含;共享的消费者扫描说明在每个受影响的文件中重复。
- 整个项目模式写入每个组件的文件加上
.migration/project.md(依赖交换、应用代码扫描摘要、最终构建结果)。 - 没有索引文件。迁移状态从磁盘派生,而非维护:当被问及“还剩下什么”时,扫描项目的 ui 目录(来自 shadcn info 的
ui别名,例如 components/ui 或 src/components/ui)以查找剩余的 radix 导入。每次运行的摘要以该派生计数结束(“N 个包装器仍在使用 Radix”)。
每个 .migration/<component>.md 使用精确的结构(它是公开文档化的;报告必须匹配它):
# <component>
<日期,使用的策略(通过 CLI 的黄金对 / 合并 / 引擎),一行结论>
## 已更改
<每个接触的文件,更改内容和原因;包括任何值得注意的文件:行。确认残留扫描干净:
在此组件的文件上执行 grep -n "radix-ui\|@radix-ui">
## 未触碰
<看起来相关但有意未触碰的文件,以及原因(cmdk/vaul/sonner 不是 radix;无关的漂移等)>
## 行为变化
<编译正常但行为不同的差异;已标记,绝不修补(tabs 激活、菜单点击关闭、延迟等)。如果没有则为空>
## 手动验证
<针对此原语系列的简短手动 QA 检查清单:对话框的焦点返回、菜单/选择上的键盘导航 + 类型前移、tooltip 延迟感觉、slider 提交事件。具体步骤,一分钟点击>






