Skip to content

Commit 96c073d

Browse files
authored
feat(shadcn-ui): add shadcn/ui plugin bundling official skills (#255)
* feat(shadcn-ui): add shadcn/ui plugin bundling official skills Add a new shadcn-ui Claude Code plugin bundling the official shadcn/ui skills.sh skills (shadcn + migrate-radix-to-base) from github.com/shadcn/ui, installed via bunx skills add into plugins/shadcn-ui/.agents/skills/. - plugins/shadcn-ui/: new plugin dir with .claude-plugin/plugin.json, generated Codex/Antigravity/Cursor manifests, vendored skills, and skills-lock.json - .claude-plugin/marketplace.json: add shadcn-ui entry with relevance signals (cli "shadcn", components.json) - .agents/plugins/marketplace.json + .cursor-plugin/marketplace.json: regenerated via bun scripts/cli.ts multi-format (Codex regen also reconciled two pre-existing entries already present in the Claude source-of-truth marketplace: claude-code-docs, run-verify) - release-please-config.json + .release-please-manifest.json: add plugins/shadcn-ui package + version 1.0.0 - README.md: add shadcn/ui entry under Built-in Plugins * chore(shadcn-ui): apply AI review — ui category, keywords/tags, manifestDeps signal
1 parent fc9b50b commit 96c073d

36 files changed

Lines changed: 5359 additions & 0 deletions

.agents/plugins/marketplace.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,18 @@
556556
},
557557
"category": "Tooling"
558558
},
559+
{
560+
"name": "claude-code-docs",
561+
"source": {
562+
"source": "url",
563+
"url": "https://github.com/pleaseai/claude-code-docs.git"
564+
},
565+
"policy": {
566+
"installation": "AVAILABLE",
567+
"authentication": "ON_INSTALL"
568+
},
569+
"category": "Tooling"
570+
},
559571
{
560572
"name": "semble",
561573
"source": {
@@ -1114,6 +1126,30 @@
11141126
},
11151127
"category": "Productivity"
11161128
},
1129+
{
1130+
"name": "run-verify",
1131+
"source": {
1132+
"source": "url",
1133+
"url": "https://github.com/pleaseai/run-verify-plugin.git"
1134+
},
1135+
"policy": {
1136+
"installation": "AVAILABLE",
1137+
"authentication": "ON_INSTALL"
1138+
},
1139+
"category": "Development"
1140+
},
1141+
{
1142+
"name": "shadcn-ui",
1143+
"source": {
1144+
"source": "local",
1145+
"path": "./plugins/shadcn-ui"
1146+
},
1147+
"policy": {
1148+
"installation": "AVAILABLE",
1149+
"authentication": "ON_INSTALL"
1150+
},
1151+
"category": "UI"
1152+
},
11171153
{
11181154
"name": "tanstack",
11191155
"source": {

.claude-plugin/marketplace.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,24 @@
15301530
"source": "github",
15311531
"repo": "pleaseai/run-verify-plugin"
15321532
}
1533+
},
1534+
{
1535+
"name": "shadcn-ui",
1536+
"description": "Manages shadcn/ui components and projects — adding, searching, fixing, styling, and composing UI — plus migrating from Radix UI to Base UI.",
1537+
"category": "ui",
1538+
"keywords": ["shadcn", "shadcn-ui", "components", "radix", "base-ui", "ui"],
1539+
"tags": ["ui", "skill"],
1540+
"source": "./plugins/shadcn-ui",
1541+
"relevance": {
1542+
"topic": "shadcn/ui",
1543+
"signals": {
1544+
"cli": ["shadcn"],
1545+
"filesRead": ["**/components.json"],
1546+
"manifestDeps": [
1547+
{ "file": "[/\\\\]package\\.json$", "pattern": "\"shadcn\"\\s*:" }
1548+
]
1549+
}
1550+
}
15331551
}
15341552
]
15351553
}

.cursor-plugin/marketplace.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,11 @@
364364
"source": "./plugins/greptile",
365365
"description": "Greptile agent skills for PR quality: check PR review comments and status checks, run Greptile CLI reviews on local branches, and iteratively improve PRs until they score 5/5"
366366
},
367+
{
368+
"name": "shadcn-ui",
369+
"source": "./plugins/shadcn-ui",
370+
"description": "Manages shadcn/ui components and projects — adding, searching, fixing, styling, and composing UI — plus migrating from Radix UI to Base UI."
371+
},
367372
{
368373
"name": "tanstack",
369374
"source": "./plugins/tanstack",

.release-please-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,6 @@
6767
"plugins/axi": "1.2.0",
6868
"plugins/playwright-cli": "1.0.0",
6969
"plugins/greptile": "1.1.0",
70+
"plugins/shadcn-ui": "1.0.0",
7071
"plugins/tanstack": "0.1.0"
7172
}

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,12 @@ Greptile agent skills for PR quality: check PR review comments and status checks
463463

464464
**Install:** `/plugin install greptile@pleaseai` | **Source:** [plugins/greptile](https://github.com/pleaseai/claude-code-plugins/tree/main/plugins/greptile)
465465

466+
#### shadcn/ui
467+
468+
Manages shadcn/ui components and projects — adding, searching, fixing, styling, and composing UI (`shadcn`) — plus migrating a project from Radix UI to Base UI (`migrate-radix-to-base`).
469+
470+
**Install:** `/plugin install shadcn-ui@pleaseai` | **Source:** [plugins/shadcn-ui](https://github.com/pleaseai/claude-code-plugins/tree/main/plugins/shadcn-ui)
471+
466472
## Quick Start
467473

468474
The fastest way to get started — install the marketplace and let the plugin recommender auto-detect what you need:
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
---
2+
name: migrate-radix-to-base
3+
description: Migrates React projects and components from Radix UI to Base UI. Use when asked to migrate from radix, move to base-ui, convert radix primitives, or switch a shadcn project's base library. Handles single components ("migrate accordion") and whole projects.
4+
---
5+
6+
# Radix UI -> Base UI migration
7+
8+
You migrate shadcn wrappers, hand-rolled radix compositions, and their
9+
consumers to `@base-ui/react`, keeping the project buildable at every step.
10+
Be precise; never guess a mapping. When a prop or part is not in these
11+
reference files, check `node_modules/@base-ui/react/**/*.d.ts` before
12+
transforming, and record gaps in the report.
13+
14+
## Preflight (always)
15+
16+
1. `npx shadcn@latest info --json` (or the project's runner): gives the
17+
current base, STYLE (e.g. `radix-lyra`), tailwind version, aliases,
18+
installed components, and package manager. Trust it over inference.
19+
2. Detect the package manager (packageManager field / lockfile:
20+
pnpm-lock.yaml, bun.lock, yarn.lock, package-lock.json) and use IT for
21+
every install. Never leave a stale lockfile.
22+
3. Require a clean git tree; work on a branch; one commit per component.
23+
4. Baseline check BEFORE touching dependencies: run the project's
24+
typecheck/build so pre-existing failures are never attributed to you.
25+
5. Install `@base-ui/react` alongside radix. Radix packages are removed only
26+
after the LAST component is migrated (both coexist fine).
27+
28+
## Strategy: golden pair first, transformation engine second
29+
30+
- **Golden pair via the CLI (preferred).** If the project is shadcn with a
31+
known style (`radix-<style>`), the shadcn CLI itself is the golden-pair
32+
executor:
33+
1. Classify each ui wrapper FIRST: diff the user's file against its stock
34+
origin, using the components.json style VERBATIM in the URL
35+
(`https://ui.shadcn.com/r/styles/<style>/<component>.json`,
36+
files[0].content). This works for prefixed styles (radix-nova) AND
37+
legacy unprefixed ones (new-york, new-york-v4, default), which are all
38+
still served.
39+
2. WHOLE-PROJECT mode: flip `components.json` style `radix-<style>` ->
40+
`base-<style>` now. PROGRESSIVE mode: do NOT flip yet (the project is
41+
still mostly radix; the flip happens once, after the last component);
42+
fetch base variants directly by URL instead
43+
(`https://ui.shadcn.com/r/styles/base-<style>/<component>.json`).
44+
3. PRISTINE wrappers, whole-project mode: `shadcn add <component>
45+
--overwrite` delivers the base variant with the project's exact
46+
icon/font/preset resolution. Never bulk `--all --overwrite`; go
47+
component by component, or you drown in unrelated registry version
48+
drift. PROGRESSIVE mode: never use `--overwrite` (it destroys the
49+
original that consumers still import); write the fetched base variant
50+
content to `<component>-base.tsx` instead.
51+
4. CUSTOMIZED wrappers: fetch the base variant and replay the user's diff
52+
onto it (their customizations must SURVIVE; `--overwrite` would destroy
53+
them). Mechanical implementation that works at scale:
54+
`git merge-file user.tsx radix-golden.tsx base-golden.tsx` (three-way
55+
merge, radix golden as ancestor) auto-resolves most files; hand-resolve
56+
conflicts with the reference tables.
57+
5. MANDATORY leftover sweep on EVERY golden-pair file, including ones that
58+
merged "clean": `grep -n "radix-ui\|@radix-ui\|IconPlaceholder"` per
59+
file. The registry sometimes reorders functions between variants, which
60+
makes three-way merges report zero conflicts while leaving stale radix
61+
hunks in place. A clean merge is NOT proof of a clean file.
62+
This is more reliable than reconstructing transforms; use it whenever the
63+
pair exists. Consumer/app code has no CLI mechanism: always hand-migrate it
64+
against `consumer-props.md`.
65+
- **Legacy styles (new-york, new-york-v4, default): classification only, no
66+
replay.** These have no base counterpart (there is no base-new-york), and
67+
retargeting onto a base-<style> variant would restyle the user's app. Use
68+
the radix golden ONLY to detect customizations, then run the transformation
69+
engine on the user's OWN file: rewire primitives, keep their exact classes,
70+
apply class-mapping renames. Their look stays theirs. At the end of a
71+
legacy whole-project migration, FLAG (do not fix): the style name still
72+
reads as radix to the CLI, so future `shadcn add` will deliver radix
73+
variants; the user decides whether to switch style or add manually.
74+
- **Transformation engine (fallback).** Hand-rolled radix code, non-shadcn
75+
projects, unknown styles: transform using `universal-patterns.md` (imports
76+
in BOTH forms: `radix-ui` and `@radix-ui/react-*`; asChild->render with the
77+
worked example; Portal>Positioner>Popup; the positioner FORWARD rule; part
78+
renames), the per-family props tables (`overlays.md`, `menus.md`,
79+
`form-controls.md`, `disclosure.md`, `display-misc.md`), `class-mapping.md`
80+
for data-attribute/CSS-var rewrites, and `wrapper-shapes.md` for exact
81+
target shapes (tooltip arrow, SubContent defaults, select anatomy).
82+
83+
## Modes
84+
85+
**Progressive (default).** "Migrate accordion" = one component, strangler-fig:
86+
1. Detect in-progress state first: an existing `<component>-base.tsx`,
87+
consumers split between old/new imports. The files ARE the state; resume,
88+
never restart.
89+
2. If the component imports other ui wrappers still on radix (select ->
90+
button), STOP and recommend migrating those first, bottom-up.
91+
3. Write the migrated version to `<component>-base.tsx` (original untouched;
92+
golden-pair content fetched by URL, or transformed by hand, per the
93+
strategy above); typecheck. Repoint consumers ONE AT A TIME (imports + the
94+
call-site props in `consumer-props.md`); typecheck each. When no consumer
95+
imports the original: delete it, rename `-base` -> original, flip imports
96+
back, final check, commit. When the LAST radix wrapper in the project is
97+
finalized, flip `components.json` to `base-<style>` and remove radix deps.
98+
99+
**Whole project** (only when explicitly asked): same per-component work in
100+
dependency order (leaf/shared wrappers like button and label first). After
101+
wrappers, sweep ALL app code against `consumer-props.md` — the call-site
102+
break surface is much larger than asChild. Then remove radix deps, install,
103+
full build.
104+
105+
## Hard rules
106+
107+
- NEVER touch non-radix libraries or their wrappers: cmdk (command), vaul
108+
(drawer), sonner, input-otp, react-day-picker (calendar), recharts (chart).
109+
Report them as intentionally untouched.
110+
- No Base UI counterpart: AspectRatio -> CSS aspect-ratio div; Label ->
111+
native `<label>`; VisuallyHidden -> `sr-only`; Direction -> Direction
112+
Provider (`direction` prop, not `dir`). Popover Anchor and NavigationMenu
113+
Indicator have no equivalent: inert passthrough + flag.
114+
- `button.tsx` migrates to the REAL `@base-ui/react/button` primitive, never
115+
a hand-rolled useRender wrapper.
116+
- Behavior deltas are FLAGGED, never silently patched (tabs manual
117+
activation, menu items not closing on click, nav-menu 50ms delay). The
118+
target is idiomatic Base UI matching the shadcn base registry.
119+
- Honest reporting: skipped/reverted files are listed as flagged, never as
120+
migrated. Pre-existing failures are named as pre-existing.
121+
122+
## Verify and report
123+
124+
Typecheck per file, build per batch, full build at the end vs the baseline.
125+
126+
Reports live in a `.migration/` directory at the project root, ONE FILE PER
127+
COMPONENT: `.migration/<component>.md` (e.g. `.migration/accordion.md`).
128+
Rules:
129+
- Each run writes (or fully overwrites) the file for each component it
130+
migrated. Re-running a component replaces its report; never touch other
131+
components' files.
132+
- A multi-component run ("migrate alert-dialog and dropdown-menu") writes one
133+
file per component, each self-contained; shared consumer-sweep notes are
134+
repeated in every affected file.
135+
- Whole-project mode writes the per-component files plus
136+
`.migration/project.md` (dependency swap, app-code sweep summary, final
137+
build result).
138+
- There is NO index file. Migration status is derived from disk, not
139+
maintained: scan the project's ui directory (the `ui` alias from shadcn
140+
info, e.g. components/ui or src/components/ui) for remaining radix imports
141+
when asked "what's left". End every run's summary with that derived count
142+
("N wrappers remain on Radix").
143+
144+
Each `.migration/<component>.md` uses EXACTLY this structure (it is
145+
documented publicly; reports must match it):
146+
147+
```md
148+
# <component>
149+
150+
<date, strategy used (golden pair via CLI / merge / engine), one-line verdict>
151+
152+
## Changed
153+
154+
<every file touched, with what changed and why; include file:line for
155+
anything notable. Confirm the leftover scan is clean:
156+
grep -n "radix-ui\|@radix-ui" on this component's files>
157+
158+
## Left alone
159+
160+
<files that look related but were intentionally not touched, with the reason
161+
(cmdk/vaul/sonner are not radix; unrelated drift; etc.)>
162+
163+
## Behavior changes
164+
165+
<differences that compile fine but act differently; flagged, never patched
166+
(tabs activation, menu close-on-click, delays...). Empty section if none>
167+
168+
## Verify by hand
169+
170+
<short manual QA checklist for this primitive family: focus return on
171+
dialogs, keyboard nav + typeahead on menus/select, tooltip delay feel,
172+
slider commit events. Concrete steps, one minute of clicking>
173+
```
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Class-string rewrites (layer 2)
2+
3+
Apply these across ALL class strings (className, cva definitions, cn calls),
4+
including app code. They are safe, mechanical rewrites.
5+
6+
## Data-attribute selectors
7+
8+
| Radix pattern | Base UI pattern |
9+
|---|---|
10+
| `data-[state=open]:` | `data-open:` |
11+
| `data-[state=closed]:` | `data-closed:` |
12+
| `data-[state=checked]:` | `data-checked:` |
13+
| `data-[state=unchecked]:` | `data-unchecked:` |
14+
| `data-[state=active]:` (tabs) | `data-active:` |
15+
| `data-[state=on]:` (toggle) | `data-pressed:` |
16+
| `data-[highlighted]:` | `data-highlighted:` (unchanged) |
17+
| `data-[disabled]:` | `data-disabled:` (unchanged) |
18+
| `data-[side=...]:` | `data-[side=...]:` (unchanged, still parameterized) |
19+
| `group-data-[state=open]` / `peer-data-[state=open]` | `group-data-open` / `peer-data-open` |
20+
| submenu trigger open marker `data-[state=open]:` | `data-popup-open:` |
21+
22+
## Animation idiom
23+
24+
Radix (tw-animate/keyframes):
25+
`data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0`
26+
27+
Base UI (transition + starting/ending styles):
28+
`transition-[opacity,transform] data-starting-style:opacity-0 data-ending-style:opacity-0` (plus translate/scale equivalents).
29+
30+
Do not translate animate-in/out utilities 1:1; restate the intent with
31+
`data-starting-style:` / `data-ending-style:` transitions. When the original
32+
uses per-side slide classes, keep the `data-[side=...]` or
33+
`data-[swipe-direction=...]` parameterization.
34+
35+
## CSS variables
36+
37+
| Radix var | Base UI var |
38+
|---|---|
39+
| `--radix-<comp>-content-transform-origin` | `--transform-origin` |
40+
| `--radix-<comp>-content-available-height` | `--available-height` |
41+
| `--radix-<comp>-content-available-width` | `--available-width` |
42+
| `--radix-<comp>-trigger-width` | `--anchor-width` |
43+
| `--radix-<comp>-trigger-height` | `--anchor-height` |
44+
| `--radix-accordion-content-height` | `--accordion-panel-height` |
45+
| `--radix-collapsible-content-height` | `--collapsible-panel-height` |
46+
| `--radix-navigation-menu-viewport-height/width` | `--positioner-height` / `--positioner-width` |
47+
48+
## Element changes kill pseudo-class variants
49+
50+
When a part's rendered element changes from a form control to a generic
51+
element (checkbox/switch/radio Roots render `<span>` in Base UI), `disabled:`
52+
and `:disabled` Tailwind variants become dead code. Replace them with
53+
`data-disabled:` equivalents. (Note: the shadcn base registry's checkbox
54+
still carries the dead `disabled:*` classes; treat that as an upstream quirk,
55+
not a pattern to copy.)
56+
57+
## Disabled-state hooks
58+
59+
Some Base UI triggers surface disabled state as `aria-disabled` rather than
60+
the `disabled` attribute (accordion trigger, tabs tab). Where the radix code
61+
used `disabled:opacity-50`, add or substitute `aria-disabled:opacity-50`
62+
according to the wrapper's reference file.

0 commit comments

Comments
 (0)