Communitygithub.com

shadcn-ui/ui

將 React 專案和元件從 Radix UI 遷移至 Base UI。在要求從 radix 遷移、移至 base-ui、轉換 radix 基本元件,或切換 shadcn 專案的基礎函式庫時使用。可處理單一元件 (「遷移 accordion」) 與整個專案。

ui 是什麼?

此技能用於將基於 shadcn 或手寫 Radix UI 的 React 應用程式自動化遷移至 `@base-ui/react`。它支援漸進式遷移(一次遷移一個元件)與完整專案遷移,並且在遷移過程中始終保持專案可建置。該工具會偵測專案的樣式、套件管理器與已安裝的元件,優先使用由 `shadcn CLI` 提供的「黃金配對」機制來處理標準的 shadcn 包裝器;對於客製化包裝器則採用三向合併技術以保留使用者修改;對於手寫的 Radix 程式碼、非 shadcn 專案或舊版樣式則會使用轉換引擎逐一處理匯入、屬性與 CSS 類別映射。每個元件遷移完成後,系統會自動在 `.migration/` 目錄中生成一份詳盡的報告,記錄所有變更、未觸及的檔案、已知的行為差異以及人工驗證步驟。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/shadcn-ui/ui/tree/HEAD/skills/migrate-radix-to-base

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Radix UI -> Base UI 遷移

你需將 shadcn wrapper、手寫的 radix 組合以及其消費者遷移至 @base-ui/react,並在每一步保持專案可建置。務必精確;切勿猜測映射。當某個屬性或部件未包含在這些參考檔案中時,請先檢查 node_modules/@base-ui/react/**/*.d.ts 再進行轉換,並在報告中記錄差距。

前置檢查 (永遠執行)

  1. 執行 npx shadcn@latest info --json(或專案的執行器):取得當前基礎、STYLE(例如 radix-lyra)、tailwind 版本、別名、已安裝的元件與套件管理器。信任此輸出而非自行推斷。
  2. 偵測套件管理器(packageManager 欄位 / 鎖定檔:pnpm-lock.yaml, bun.lock, yarn.lock, package-lock.json)並使用它進行每一次安裝。絕不留過時的鎖定檔。
  3. 要求乾淨的 git 樹;在分支上作業;每個元件一個提交。
  4. 在觸碰相依套件之前進行基準檢查:執行專案的 typecheck/build,這樣預先存在的失敗就不會歸咎於你。
  5. 安裝 @base-ui/react 與 radix 並存。Radix 套件只會在最後一個元件遷移完成後才移除(兩者可以和平共處)。

策略:先找黃金配對,再使用轉換引擎

  • 透過 CLI 取得黃金配對 (首選)。 如果專案是 shadcn 且具有已知樣式 (radix-<style>),則 shadcn CLI 本身就是黃金配對的執行者:
    1. 首先對每個 ui wrapper 進行分類:將使用者的檔案與其原始範本進行差異比對,使用 components.json 中的 style 確切不變地放入 URL (https://ui.shadcn.com/r/styles/<style>/<component>.json,files[0].content)。這同時適用於有前綴的樣式 (radix-nova) 以及舊版無前綴的樣式 (new-york, new-york-v4, default),這些都仍然在服務中。
    2. 完整專案模式:現在就翻轉 components.json 中的 style radix-<style> -> base-<style>。漸進式模式:先不要翻轉(專案大部分仍是 radix;翻轉只會在最後一個元件之後進行一次);改為直接透過 URL 擷取 base 變體 (https://ui.shadcn.com/r/styles/base-<style>/<component>.json)。
    3. 原始 wrapper,完整專案模式:shadcn add <component> --overwrite 會以專案確切的圖示/字型/預設解析來交付 base 變體。絕不要使用 --all --overwrite;請逐個元件進行,否則你會淹沒在無關的註冊表版本漂移中。漸進式模式:絕不要使用 --overwrite(它會破壞消費者仍在匯入的原始檔案);改為將擷取到的 base 變體內容寫入 <component>-base.tsx。
    4. 客製化 wrapper:擷取 base 變體並將使用者的差異重新套用到其上(他們的客製化必須保留;--overwrite 會破壞它們)。可大規模運作的機械化實作:git merge-file user.tsx radix-golden.tsx base-golden.tsx(三向合併,以 radix golden 作為祖先)可自動解決大部分檔案;對於衝突則使用參考表格手動解決。
    5. 對每個黃金配對檔案(包含那些合併「乾淨」的)執行強制性的殘留掃描:grep -n "radix-ui\|@radix-ui\|IconPlaceholder" 每個檔案。註冊表有時會重新排列變體之間的函式,這會讓三向合併回報零衝突,但實際上卻留下了舊的 radix 程式碼片段。乾淨的合併並不代表檔案是乾淨的。 這比重建轉換更可靠;只要存在配對就使用它。消費者/應用程式碼沒有 CLI 機制:一律根據 consumer-props.md 手動遷移。
  • 舊版樣式 (new-york, new-york-v4, default):僅分類,不重播。 這些樣式沒有對應的 base 版本(不存在 base-new-york),而且重新定位到 base- 變體會重新設計使用者的應用程式樣式。僅使用 radix golden 來偵測客製化,然後在使用者自己的檔案上執行轉換引擎:重新接線基本元件,保留他們確切的類別,套用類別映射重新命名。他們的外觀保持不變。在舊版完整專案遷移結束時,標記(不要修正):樣式名稱對 CLI 來說仍然是 radix,因此未來的 shadcn add 將會交付 radix 變體;由使用者決定是否切換樣式或手動新增。
  • 轉換引擎 (備用)。 手寫的 radix 程式碼、非 shadcn 專案、未知的樣式:使用 universal-patterns.md(兩種形式的匯入:radix-ui 和 @radix-ui/react-*;asChild -> render 以及可運作的範例;Portal>Positioner>Popup;positioner FORWARD 規則;部件重新命名)、每個系列的 props 表格 (overlays.md, menus.md, form-controls.md, disclosure.md, display-misc.md)、class-mapping.md 用於 data-attribute/CSS-var 重寫,以及 wrapper-shapes.md 用於確切的目標形狀(tooltip arrow, SubContent 預設值, select 結構)。

模式

漸進式 (預設)。 「遷移 accordion」 = 一個元件,strangler-fig 模式:

  1. 首先偵測進行中的狀態:是否存在 <component>-base.tsx,消費者分散於舊/新匯入。檔案本身就是狀態;繼續,永遠不要重新開始。
  2. 如果該元件匯入其他仍在使用 radix 的 ui wrapper(select -> button),停止並建議先從底層開始遷移這些元件。
  3. 將遷移後的版本寫入 <component>-base.tsx(原始檔案不動;黃金配對內容透過 URL 擷取,或根據上述策略手動轉換);執行 typecheck。一次重新指向一個消費者(匯入 + consumer-props.md 中的呼叫端 props);每個都執行 typecheck。當沒有消費者匯入原始檔案時:刪除它,將 -base 重新命名回原始名稱,復原匯入,最終檢查,提交。當專案中最後一個 radix wrapper 完成最終化時,將 components.json 翻轉為 base-<style> 並移除 radix 相依套件。

完整專案 (僅在明確要求時):相同的逐個元件工作,但依照相依性順序(先處理 leaf/共享 wrapper,如 button 和 label)。在 wrapper 之後,根據 consumer-props.md 掃描所有應用程式碼——呼叫端的破壞表面比 asChild 大得多。然後移除 radix 相依套件,安裝,完整建置。

硬性規則

  • 絕對不要觸碰非 radix 的函式庫或其 wrapper: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 prop,不是 dir)。Popover Anchor 和 NavigationMenu Indicator 沒有對等物:惰性傳遞 + 標記。
  • button.tsx 遷移至真正的 @base-ui/react/button 基本元件,絕不是手寫的 useRender wrapper。
  • 行為差異要被標記,絕不靜態修補(tabs 手動啟用、menu 項目點擊不關閉、nav-menu 50ms 延遲)。目標是符合 shadcn base 註冊表的慣用 Base UI。
  • 誠實報告:跳過/復原的檔案要列為已標記,絕不能列為已遷移。預先存在的失敗要命名為預先存在。

驗證與報告

每個檔案執行 typecheck,每批執行 build,最後對比基準執行完整建置。

報告存放在專案根目錄的 .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 個 wrapper 使用 Radix」)。

每個 .migration/<component>.md 使用完全相同的結構(這是公開的文件;報告必須與之相符):

# <component>

<日期, 使用的策略 (golden pair via CLI / merge / engine), 一行判決>

## 已變更

<每個觸碰過的檔案,說明變更了什麼以及原因;對於任何值得注意的部分包含 檔案:行號。確認殘留掃描是乾淨的:
對這個元件的檔案執行 grep -n "radix-ui\|@radix-ui">

## 未觸碰

<看起來相關但刻意未觸碰的檔案,以及原因 (cmdk/vaul/sonner 不是 radix;無關的漂移;等等)>

## 行為變更

<編譯沒問題但行為不同的差異;已標記,絕不修補 (tabs 啟用、menu 點擊關閉、延遲...)。如果沒有則留空>

## 手動驗證

<針對此基本元件系列的簡短手動 QA 檢查清單:對話框的焦點返回、選單/選擇的鍵盤導航 + 類型預覽、工具提示的延遲感受、滑塊的提交事件。具體步驟,約一分鐘的點擊操作>

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相關技能