Repository navigation
Registry catalog, Field variants, form generator, and Radix → Base UI migration - #106
Merged
Merged
Conversation
Remove files no route reaches through static imports: legacy *-form docs demos, unused templates (login, register, code-viewer), components/blocks, DropdownContext, the Radix toast stack, and unused ui primitives (context-menu, progress, sonner wrapper). Drop the three Radix packages only those files used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tailwind v4 never loaded tailwind.config.js (globals.css has no @config), so the col-span safelist for runtime form layouts was dead. Move it to an @source inline() rule, delete the config file, point components.json at no config as shadcn expects for v4, drop the nonexistent "form" alias, and declare the lucide icon library. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the hand-rolled registry (registry/ui twins, registry/schema.ts, scripts/build-registry.ts, committed public/registry/*.json) with the official shadcn workflow: a root registry.json built by `shadcn build` into public/r on dev/build. - Items point at the files the app renders (components/ui/*), so what users install is what the site shows. This fixes credit-card, whose published copy had drifted to an older API. - location-input now ships its countries/states data as registry:lib files instead of asking users to copy them by hand. - Drop signature-pad's stale "progress" dependency; declare lucide-react. - lib/registry-catalog.ts is the one reader of registry.json. The docs shell, sidebar and [slug] route take title, description and install command from it; constants/components.ts is gone. - /components/[slug] is statically generated with per-item metadata and now appears in the sitemap. - /registry/* redirects to /r/* so existing install commands keep working. - __tests__/registry-catalog.spec.ts checks every item's files, npm deps and registryDependencies against its real imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shadcn projects now start on Base UI (base-*), Radix (radix-*) or the
legacy new-york style. A registry item only installs cleanly into all of
them if it uses the props every stock wrapper shares, so:
- Popover triggers are styled with buttonVariants instead of
`asChild` + <Button> (Base UI spells that `render`).
- Select triggers render an explicit label in <SelectValue>; Base UI
otherwise shows the raw value ("hex" instead of "Hex").
- onValueChange handlers tolerate Base UI's nullable value.
The catalog test now fails if an item imports a primitive library,
uses asChild/render, styles data-state attributes, or relies on types
the stock wrappers don't export.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generated forms import eight components the registry did not ship, and the playground sent users to third-party sites (or nowhere, for Rating) to find them. Add phone-input, datetime-picker, smart-datetime-input, file-upload, tags-input, multi-select, password-input and rating to registry.json, each with a docs page. Make them installable into any shadcn project: - drop InputProps/CalendarProps imports the stock wrappers don't export - replace the relative './scroll-area' import and react-icons with alias imports and lucide - trigger popovers via buttonVariants instead of asChild - use a plain <style> in color-picker instead of Next-only styled-jsx The playground notice now lists the components a form uses, links their docs pages, and shows one install command for all of them, including fields inside multi-column rows (previously skipped). Verified: all 24 items install with `shadcn add` and typecheck in fresh radix-nova, base-nova and new-york-v4 projects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Six of the seven templates showed `shadcn add <slug>` for items that existed nowhere, and shadcn-auth pointed at the unrelated official login-05 block. Copied code also failed to compile without lib/validation-schemas and the site's password-input. - registry.json gains a block per template plus the shared auth-shell and validation-schemas items they depend on (by URL), with features in meta and install notes in docs. - Templates link between flows via real routes (/sign-in, /sign-up, ...) and next/link; the preview intercepts those links to switch flows. - contact, newsletter and the Supabase/Firebase/Better Auth templates move from the deprecated <Form> (an empty item in new shadcn styles) to <Field> + form.register. - constants/templates.ts keeps only grouping and branding; titles, descriptions and features come from the catalog. The flow lists that were duplicated between the sidebar and the page are one AUTH_FLOWS. - The block viewer reads file contents from the built /r/<name>.json, so /api/file is gone. Open in v0 now opens the real registry item. Verified: all 33 items install with `shadcn add` and typecheck in fresh radix-nova, base-nova and new-york-v4 projects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FormBuilder imported components/editor but never rendered it, so the editor, its stale command list (constants/menu.tsx) and the BlockNote, react-grid-layout and lodash packages it pulled in were dead weight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What a field variant is used to be spread over ~18 switch statements keyed by an untyped string: the preview renderer (711 lines), zod schema, two default-value functions, four import lists, the legacy snippet generator and a JSON-schema generator, which had already drifted apart. components/field-variants/ now holds one module per variant with its defaults, builder settings, validation, default value, preview control and generated control. Adding a field type means adding one module. - Validation is written once with a small recorder (schema-expr.ts) that yields both the runtime zod schema and its source. This replaces zodSchemaToString, which read zod v3 internals and silently dropped .min/.max/.email under zod v4. - Preview and generated code share one default value per variant (the preview used to check checkboxes and switches the code left false). - The preview renders through Controller + Field; FieldShell arranges label, control, description and error. - Optional fields accept their empty defaults; credit card and location report one field-level message instead of empty nested errors. - The edit dialog shows settings each variant declares. - FormFieldType drops the unused runtime callbacks and value props. - Removes renderFormField, generate-code-field, json-schema-generator and the now unused components/ui/form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck adapters The four generators (RHF, TanStack, Server Actions, Bring Your Own) duplicated import collection and row layout verbatim but handled only 7 of 22 variants; everything else became a plain <Input>, sometimes without importing it. RHF spread form.register() onto Radix Checkbox, Select, Switch and Slider, which never fire onChange, and TanStack used the removed zod-form-adapter and never validated. lib/form-code/ now generates every variant through its control() template and a small adapter per library: - RHF: Controller + Field, zodResolver with input/output types. - TanStack: form.Field + Field, the zod schema as a Standard Schema onSubmit validator. Generated files start with the one shadcn install command they need (official items plus this registry's), import exactly what they render, and are formatted with prettier instead of js-beautify. The playground notice shows the same command. Server Actions (disabled in the UI) and Bring Your Own (never reachable) are dropped. __tests__/form-code.spec.ts generates a form with every variant for each library and typechecks it against the app's components. Verified separately against stock radix-nova and base-nova components. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two app modules imported Radix directly: features-vertical defined an Accordion it never rendered, and the readme page used the raw Separator primitive. Delete the former, route the latter through ui/separator, and add a test that fails when app code imports a primitive library outside components/ui. The Base UI migration then only has to change the wrappers and the props consumers pass them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Following the official migrate-radix-to-base skill: add @base-ui/react next to the Radix packages (they coexist until the last wrapper moves), apply Base UI's setup (an isolated app root and position: relative on body for iOS Safari), and alias --color-sidebar for the base sidebar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the Radix Slot-based button with the base-vega button built on
@base-ui/react/button, keeping the site's extra variants (arrow, smile,
gooey*, linkHover*, shine, ringHover, expandIcon). The unused
Icon/iconPlacement props are dropped. Consumers that rendered a link
through `asChild` now pass `render={<Link />}` with nativeButton={false}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega label from the shadcn CLI (), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega separator from the shadcn CLI (@base-ui/react/separator ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega checkbox from the shadcn CLI (@base-ui/react/checkbox ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega switch from the shadcn CLI (@base-ui/react/switch ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega slider from the shadcn CLI (@base-ui/react/slider ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega radio-group from the shadcn CLI (@base-ui/react/radio @base-ui/react/radio-group ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega scroll-area from the shadcn CLI (@base-ui/react/scroll-area ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega avatar from the shadcn CLI (@base-ui/react/avatar ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega tabs from the shadcn CLI (@base-ui/react/tabs ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega popover (@base-ui/react/popover: Portal > Positioner >
Popup). The sponsors radar hover trigger moves from asChild to
render={<div />} with nativeButton={false}. Triggers styled with
buttonVariants (combobox, date picker, registry items) need no change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega accordion (@base-ui/react/accordion, Content -> Panel). The FAQ drops type="single"/collapsible: Base UI accordions open one item at a time and collapse by default. The accordion keyframes now read --accordion-panel-height instead of the Radix variable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega dialog from the shadcn CLI (@base-ui/react/dialog ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega sheet from the shadcn CLI (@base-ui/react/dialog ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cmdk stays (it is not Radix); the wrapper's dialog now comes from the Base UI dialog and its search field from the new input-group component, which the CLI adds alongside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega select from the shadcn CLI (@base-ui/react/select ), replacing the Radix new-york wrapper. No consumer changes needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega dropdown-menu (@base-ui/react/menu). The theme switch and the column picker pass their Button through `render` instead of `asChild`, and the picker's label now sits in a DropdownMenuGroup, which Base UI's GroupLabel requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega breadcrumb: BreadcrumbLink drops the Radix Slot for useRender + mergeProps from @base-ui/react. Existing href usage is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take the base-vega sidebar and tooltip together (the tooltip's only
consumer is the sidebar). Keep this site's sidebar layout: 12rem wide,
positioned inside the page (absolute, auto height) rather than fixed to
the viewport, and no left border. Sidebar links move from asChild to
render={<Link />}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
These wrappers sit on react-day-picker and input-otp, which the migration leaves alone; their only Radix dependency was @radix-ui/react-icons. Use the lucide equivalents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- field: take the base-vega wrapper, whose choice-card styles key off
data-checked instead of Radix's data-[state=checked].
- edit dialog: give the input-type SelectValue its label; Base UI selects
render the raw value ("text") otherwise.
Left alone on purpose: the header's vaul Drawer (asChild is vaul's API,
not Radix), cmdk, sonner, input-otp and react-day-picker.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Remove every @radix-ui/* package; nothing imports them any more. - Flip components.json to base-vega (the classic look on Base UI), so `shadcn add` now delivers Base UI components; `shadcn info` reports base: "base". - Import shadcn/tailwind.css for the data-open/data-horizontal/… variants the base wrappers rely on, and replace tailwindcss-animate with tw-animate-css as current shadcn projects do. The accordion keyframes now come from shadcn/tailwind.css. shadcn moves to dependencies since the CSS is imported at build time. - .migration/project.md records what changed, what was left alone (vaul, cmdk, sonner, input-otp, react-day-picker), behavior changes and a manual check list, per the official skill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add CONTEXT.md with the domain glossary (Field variant, Form library, Schema expression, Registry item/catalog, Template, Primitive seam), and update the README: pnpm commands, how to install registry items, what the tests check, and where each concern lives. The old Components section described modules that never existed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The repo had no CI, so nothing ran the new catalog, field-variant and generated-code tests. Add a workflow for pull requests and main, and a typecheck script that generates Next's types first (next-env.d.ts is gitignored, and SVG/image imports need it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR does four things: it makes the shadcn registry complete, deepens the form builder, runs the official Radix → Base UI migration, and adds CI. It is 35 commits, one per concern and one per migrated
components/uiwrapper, so it can be reviewed or reverted commit by commit.Registry.
registry.jsonis now the single source for each registry item, and docs pages read it instead of repeating metadata. Adding a component used to mean editing 7 places; it now means editing 3 (source file, catalog entry, docs page).registry:block.asChild/render, no state-attribute classes. So one published item installs into Radix and Base UI projects alike./registry/*.jsonURLs redirect to/r/*.json.Field variants. Adding a field type is now one module.
Base UI. This follows the official
migrate-radix-to-baseskill: wrappers come from the shadcn CLI asbase-vega(shadcn's "classic look" style), with the site's customizations ported onto them. Consumers moved fromasChildtorender, and the Radix packages are removed. Details are in.migration/project.md.Evidence
Bugs fixed along the way:
.min/.max/.emailunder zod v4 (zodSchemaToStringreadcheck.kind).form schema › keeps zod checks in generated sourcepasses.<Input>, and usedregister()on Radix Switch/Select, which never fireonChange.renders a bound control for every fieldandbinds … through the library, not register()pass.zod-form-adapterand never validated.validates TanStack forms with the schema through Standard Schemapasses.location-inputinstalled without its countries/states data, andcredit-cardinstalled a 294-line older API than the site renders.shadcn add <slug>for items that didn't exist.Checks:
pnpm vitest run: 7 files, 344 tests pass. This includes typechecking a generated form that uses every variant, for both libraries.pnpm typecheck,pnpm registry:validate(33 items) andpnpm buildall pass.shadcn addand typecheck in freshradix-nova,base-novaandnew-york-v4projects. Generated forms typecheck against stockradix-novaandbase-novacomponents.Merge Danger
Door: two-way. Nothing is destructive and each commit reverts cleanly. The Base UI switch is spread across 25 small commits, one per wrapper plus setup and finishing steps.
Blast Radius:
.migration/project.md./r/<name>.json, and/registry/*308-redirects there.credit-cardnow ships the API the site renders (cvvLabelis a prop, not a value field).pnpm dev/buildrunshadcn buildfirst.public/ris gitignored, andshadcnis now a runtime dependency becauseglobals.cssimportsshadcn/tailwind.css.shadcn-ui/ui) and a{style}per-base split (not needed while items stay primitive-agnostic).🤖 Generated with Claude Code