Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
27c6821
docs: add overhaul roadmap
junwen-k Jul 9, 2026
77c5a2f
feat(v3): add version banner linking to latest docs
junwen-k Jul 9, 2026
fd5d60c
docs: update installation guide and remove stale sidebar labels
junwen-k Jul 9, 2026
7983b14
docs: add callouts recommending official shadcn/ui components
junwen-k Jul 9, 2026
9d526cd
Merge pull request #54 from junwen-k/docs/overhaul-phase-1
junwen-k Jul 9, 2026
5b152f5
feat(input-base): refresh input family styling to match latest shadcn/ui
junwen-k Jul 9, 2026
2c93af8
feat(calendar): adopt latest shadcn/ui calendar and popover styling
junwen-k Jul 9, 2026
2ff460e
feat(combobox): sync combobox and kbd styling with latest shadcn/ui
junwen-k Jul 9, 2026
8674fde
feat(dropzone): refresh dropzone styling and lucide icon imports
junwen-k Jul 9, 2026
41e3334
chore(registry): rebuild registry payloads for style refresh
junwen-k Jul 9, 2026
f937a2f
docs(file-list): add callout recommending shadcn/ui attachment component
junwen-k Jul 9, 2026
395cd17
fix(calendar): sort react-day-picker imports to satisfy lint
junwen-k Jul 10, 2026
d1360c3
fix(docs): type docs config so sidebar label badges typecheck
junwen-k Jul 10, 2026
7fc4460
Merge pull request #55 from junwen-k/feat/style-refresh
junwen-k Jul 10, 2026
9d69435
docs(roadmap): reset direction to Base UI, nova style and fumadocs
junwen-k Jul 10, 2026
2e0d617
feat(v4): re-vendor ui components from base-nova and adopt nova theme
junwen-k Jul 10, 2026
041872b
fix(v4): constrain field demo widths to match shadcn previews
junwen-k Jul 10, 2026
10fc716
fix(turbo): declare build outputs so cache replays restore .next
junwen-k Jul 11, 2026
f079350
Merge pull request #56 from junwen-k/feat/base-nova
junwen-k Jul 12, 2026
20921ce
feat(v4): migrate docs site from velite to fumadocs
junwen-k Jul 11, 2026
a48b2cd
feat(v4): overhaul docs site with shadcn's site design
junwen-k Jul 12, 2026
4361a58
fix(docs): hide framework sub-pages from sidebar and mobile nav
junwen-k Jul 12, 2026
4efbdde
fix(docs): remove doubled border on command palette search input
junwen-k Jul 12, 2026
c5e829d
docs(primitives): drop redundant "Primitive" suffix from page titles
junwen-k Jul 14, 2026
30e1376
Merge pull request #57 from junwen-k/feat/fumadocs
junwen-k Jul 14, 2026
9c4b528
feat(registry): migrate to shadcn GitHub registry, drop build step
junwen-k Jul 14, 2026
7030d4a
fix(v3): declare .contentlayer as a cached build output
junwen-k Jul 14, 2026
b8c4b81
Merge pull request #58 from junwen-k/feat/github-registry
junwen-k Jul 14, 2026
724789c
fix(docs): constrain tabs content width and silence nativeButton warn…
junwen-k Jul 14, 2026
8c723e7
fix(v4): revert tabs min-w-0 workaround to match shadcn upstream
junwen-k Jul 14, 2026
4e907e4
docs(roadmap): capture input-group migration, docs sections and UI tr…
junwen-k Jul 15, 2026
75f048e
Merge pull request #60 from junwen-k/docs/roadmap-2026-07-15
junwen-k Jul 15, 2026
7af06c9
Merge pull request #59 from junwen-k/fix/nav-button-layout
junwen-k Jul 15, 2026
24d4622
fix(v4): center header vertical separators
junwen-k Jul 15, 2026
6e8d49c
feat(registry): migrate input-base dependents to shadcn input-group
junwen-k Jul 15, 2026
6335d7f
docs(native-select): add callout recommending shadcn/ui native select
junwen-k Jul 15, 2026
50c153e
docs(calendar): add callout noting component mirrors shadcn/ui calendar
junwen-k Jul 15, 2026
ddaba50
docs(calendar): correct callout to reflect component history
junwen-k Jul 15, 2026
e052630
feat(docs): group provided-as-is components in sidebar nav
junwen-k Jul 15, 2026
c0e0cb7
docs: rename sidebar group to "In shadcn/ui"
junwen-k Jul 15, 2026
e6cc661
Merge pull request #61 from junwen-k/fix/header-separator
junwen-k Jul 15, 2026
27f8b63
Merge pull request #62 from junwen-k/feat/input-group-migration
junwen-k Jul 15, 2026
93935d8
docs(roadmap): close 4c keep/drop audit and sync completed items
junwen-k Jul 15, 2026
9de1929
docs(roadmap): record radix legacy tag strategy and reframe radix rem…
junwen-k Jul 15, 2026
5ff685b
docs: remove outdated Tailwind v4 page and announcement
junwen-k Jul 15, 2026
2f57509
docs(roadmap): decide clean cut for superseded Radix components
junwen-k Jul 15, 2026
e8e0121
feat(registry)!: remove components superseded by shadcn/ui
junwen-k Jul 15, 2026
09bdb66
fix(date-time-field): keep highlighted segment visible in dark mode
junwen-k Jul 15, 2026
f47c76d
docs(roadmap): note date/time field segment height polish for 4c ports
junwen-k Jul 15, 2026
8860c34
feat(password-input)!: port primitive to Base UI
junwen-k Jul 15, 2026
f117796
refactor(password-input): pass context and state values inline
junwen-k Jul 15, 2026
2d1d9b1
feat(registry)!: port timeline, virtualizer, and use-timescape to Bas…
junwen-k Jul 15, 2026
fbdf38e
feat(dropzone)!: port to Base UI
junwen-k Jul 16, 2026
5415c88
feat(date-time-field)!: port field primitives to Base UI
junwen-k Jul 16, 2026
1059c81
feat(phone-input)!: port primitive to Base UI
junwen-k Jul 16, 2026
67eb8ed
feat(emoji-picker)!: port toggle group to Base UI
junwen-k Jul 16, 2026
aa11108
feat(badge-group)!: port toggle group to Base UI
junwen-k Jul 16, 2026
c6f67e7
feat(date-picker)!: port popover to Base UI
junwen-k Jul 16, 2026
dab64b6
feat(sortable)!: replace Radix utilities with Base UI render pattern
junwen-k Jul 16, 2026
5ed3b3b
feat(form)!: replace form primitives with base-nova field components
junwen-k Jul 16, 2026
9ccf147
chore(deps)!: drop radix-ui dependencies
junwen-k Jul 16, 2026
533102f
docs: replace remaining radix references with base ui
junwen-k Jul 16, 2026
7abff75
fix(examples): keep timescape form demos controlled from first render
junwen-k Jul 16, 2026
2324c11
Merge pull request #63 from junwen-k/feat/port-password-input
junwen-k Jul 16, 2026
51371c9
refactor(confirmer): compose action with AlertDialogAction
junwen-k Jul 16, 2026
7157fcd
refactor(registry): align component code with shadcn idioms
junwen-k Jul 16, 2026
1e97ea1
fix(calendar): rebuild vendored calendar for react-day-picker v9
junwen-k Jul 16, 2026
49fbf40
fix(attachment): restore shimmer and scroll-fade-x utilities
junwen-k Jul 16, 2026
f77a75c
fix(registry): address internal deps via the GitHub registry
junwen-k Jul 16, 2026
d6f0d0b
docs(roadmap): sync 4b/4c status after the Base UI port
junwen-k Jul 16, 2026
c5ac40c
fix(phone-input): use static locale labels to avoid hydration mismatch
junwen-k Jul 17, 2026
6877abf
Merge pull request #64 from junwen-k/refactor/shadcn-consistency
junwen-k Jul 17, 2026
b420bee
docs(examples): decouple form demos from react-hook-form
junwen-k Jul 17, 2026
bed67d9
fix(sortable): stabilize DndContext id to prevent hydration mismatch
junwen-k Jul 17, 2026
a254aa8
docs: pair line highlights with showLineNumbers
junwen-k Jul 17, 2026
050a75f
docs: add llms.txt
junwen-k Jul 17, 2026
c63e7f6
fix(command-menu): copy GitHub registry install command
junwen-k Jul 17, 2026
f9b250b
fix(command-menu): hide framework sub-pages from command palette
junwen-k Jul 17, 2026
528e735
Merge pull request #65 from junwen-k/feat/form-docs-decoupling
junwen-k Jul 17, 2026
856c667
chore(deps): bump safe minor and patch dependencies
junwen-k Jul 17, 2026
3389fc1
fix(docs): point /docs/primitives redirect at date-time-field
junwen-k Jul 17, 2026
9a9baa1
chore(deps): upgrade react-dropzone to v17 and react-call to v2
junwen-k Jul 17, 2026
fe3f2b0
chore(tooling): update turbo, prettier, eslint stack and dev tooling
junwen-k Jul 17, 2026
73f2863
style: reformat repo with prettier 3.9
junwen-k Jul 17, 2026
fb0484d
Merge pull request #66 from junwen-k/chore/dependency-pass
junwen-k Jul 17, 2026
93579a2
chore(lint): fix import ordering and allow single-extends props inter…
junwen-k Jul 17, 2026
6dc487b
chore(pnpm): migrate to pnpm 10
junwen-k Jul 17, 2026
334365e
chore(deps): upgrade virtua, timescape, frimousse and use-local-stora…
junwen-k Jul 17, 2026
8c1a9fb
fix(virtualizer): seed faker in table example to fix hydration mismatch
junwen-k Jul 17, 2026
c6af4cf
docs: add API reference and accessibility sections
junwen-k Jul 17, 2026
4c2f29f
Merge pull request #67 from junwen-k/chore/lint-cleanup-pnpm-10
junwen-k Jul 17, 2026
04c03dd
Merge pull request #69 from junwen-k/docs/api-reference
junwen-k Jul 17, 2026
c3b856d
Merge pull request #68 from junwen-k/chore/upgrade-registry-deps
junwen-k Jul 17, 2026
1953123
docs(roadmap): sync Phase 0 deps and 4d docs sections as shipped
junwen-k Jul 18, 2026
4d3ea91
fix(date-time-field): tighten segment height to match input-group rhythm
junwen-k Jul 18, 2026
290640d
fix(phone-input): let country select popup size to its content
junwen-k Jul 18, 2026
680f501
Merge pull request #71 from junwen-k/fix/phone-input-country-select-w…
junwen-k Jul 18, 2026
4bb8eb1
chore(www): remove v3/v4 version switcher from header
junwen-k Jul 18, 2026
c4ccdf5
fix(registry): sync emoji picker and date picker popups with nova sty…
junwen-k Jul 18, 2026
9741aaa
docs(changelog): announce the Base UI overhaul
junwen-k Jul 18, 2026
1a6ce87
Merge pull request #72 from junwen-k/chore/remove-version-switcher
junwen-k Jul 18, 2026
c60ed41
Merge pull request #73 from junwen-k/fix/nova-style-drift
junwen-k Jul 18, 2026
daa9aca
docs(changelog): drop thank-you section from ship entry
junwen-k Jul 18, 2026
203f11d
Merge pull request #74 from junwen-k/docs/ship-changelog
junwen-k Jul 18, 2026
b6a18f8
Merge pull request #70 from junwen-k/fix/field-segment-height
junwen-k Jul 18, 2026
cd9891e
chore: remove apps/v3 from the monorepo
junwen-k Jul 18, 2026
89ee326
Merge pull request #75 from junwen-k/chore/remove-v3-app
junwen-k Jul 18, 2026
54d84e3
docs(roadmap): log apps/v3 removal and Vercel disconnect as done
junwen-k Jul 18, 2026
e69fc2a
Merge pull request #76 from junwen-k/docs/sync-roadmap-v3-removal
junwen-k Jul 18, 2026
394d6db
docs(changelog): drop the radix git-tag plan for a live registry pointer
junwen-k Jul 18, 2026
b9457fd
docs(changelog): drop the radix legacy callout entirely
junwen-k Jul 18, 2026
1a0d6bc
Merge pull request #77 from junwen-k/docs/drop-radix-tag-plan
junwen-k Jul 18, 2026
aeba6b8
feat(docs): add shadcn-style llms.txt support and page actions
junwen-k Jul 18, 2026
d9aa812
Merge pull request #78 from junwen-k/docs/llms-txt-page-actions
junwen-k Jul 18, 2026
d8c4cb8
docs: surface llms.txt in the docs sidebar
junwen-k Jul 18, 2026
baeb429
Merge pull request #79 from junwen-k/docs/llms-txt-sidebar-link
junwen-k Jul 18, 2026
e88fc3e
docs(primitives): style demos with shadcn Tailwind and flag unstyled …
junwen-k Jul 18, 2026
d85dcd7
docs(primitives): revert demo styling, keep unstyled callout only
junwen-k Jul 18, 2026
6ded411
Merge pull request #80 from junwen-k/docs/style-primitive-demos
junwen-k Jul 18, 2026
c3bbd85
feat(docs): add sitemap and robots routes for the v4 site
junwen-k Jul 18, 2026
9b46661
Merge pull request #81 from junwen-k/docs/seo-sitemap-robots
junwen-k Jul 18, 2026
2b6675b
fix: address audit findings from the Base UI rebuild review
junwen-k Jul 18, 2026
e067350
fix: finish staging audit-fix content changes
junwen-k Jul 18, 2026
32a1d10
revert: keep the useVirtualized() throw guard for convention consistency
junwen-k Jul 19, 2026
f43e04e
refactor(dropzone): compose visual content with shadcn Empty
junwen-k Jul 19, 2026
46e1a8e
refactor(dropzone): keep DropzoneUploadIcon for the drag-state swap
junwen-k Jul 19, 2026
5d28a5a
refactor(registry): compose cosmetic parts directly instead of re-exp…
junwen-k Jul 19, 2026
813e0b9
docs: add AGENTS.md guide and wire up the shadcn MCP
junwen-k Jul 19, 2026
4d5aa34
docs: correct AGENTS.md component model and tighten
junwen-k Jul 19, 2026
157f947
docs: add anatomy & API consistency section to AGENTS.md
junwen-k Jul 19, 2026
2c4102d
Merge pull request #83 from junwen-k/chore/final-audit-fixes
junwen-k Jul 19, 2026
8b5b917
fix(ci): pin Vercel's pnpm to the packageManager field via corepack
junwen-k Jul 19, 2026
ad09dcc
Merge pull request #84 from junwen-k/fix/vercel-pin-pnpm-via-corepack
junwen-k Jul 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 19 additions & 0 deletions .github/workflows/registry-validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Registry Validate

on:
push:
branches: [main, next]
paths:
- "registry.json"
- "apps/v4/registry.json"
pull_request:
paths:
- "registry.json"
- "apps/v4/registry.json"

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Validate registry
run: npx shadcn@latest registry validate ${{ github.repository }}#${{ github.sha }}
2 changes: 1 addition & 1 deletion .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pnpm commitlint \${1}
pnpm commitlint "$1"
2 changes: 1 addition & 1 deletion .lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"*": "true"
"*": "prettier --write --ignore-unknown"
}
8 changes: 8 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"shadcn": {
"command": "npx",
"args": ["shadcn@latest", "mcp"]
}
}
}
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# v3 is frozen and keeps its own prettier setup (@ianvs/prettier-plugin-sort-imports).
apps/v3
177 changes: 177 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# AGENTS.md

Guidance for AI agents (and humans) working in **junwen-k/ui-x**. These are
principles to apply with judgment, not boxes to tick β€” when a case doesn't fit,
reason from the "why" here and say so.

---

## What this project is

ui-x is a **shadcn-style component registry** β€” a natural extension of
[shadcn/ui](https://ui.shadcn.com), not a fork. We ship the components shadcn
doesn't (yet) have, built and styled so they feel like they came from shadcn
itself: a user should be able to `npx shadcn add` one of ours next to theirs and
not feel a seam.

Everything below follows from that stance.

---

## Principles

- **Compose shadcn, don't reinvent it.** Depend on shadcn's components; never
copy or restyle them. When shadcn ships a first-class version of something we
filled in, that component is **superseded** and removed in favor of the
official one (see `apps/v4/content/docs/changelog.mdx`).
- **Lean on great headless libraries; author behavior only when none exists.**
Reach for a popular, well-maintained headless library before writing
interaction logic yourself β€” `react-dropzone`, `react-phone-number-input`,
`timescape`, `react-day-picker`, `@dnd-kit`, `frimousse`, `virtua`. We hand-
author behavior (on [Base UI](https://base-ui.com)) only where no good library
fits β€” e.g. the password visibility toggle.
- **Match shadcn's conventions.** `data-slot` attributes, CVA variants,
`render`-prop composition, file layout, naming, prose tone. Unsure how to do
something? Find the closest shadcn/ui component and mirror it.
- **Model the API deliberately** β€” see [the wrapping principle](#the-wrapping-principle),
the one rule most worth getting right.

## Hard rules

- **Base UI, never Radix.** Base UI is the headless foundation shadcn moved to;
it's what we build on when we author unstyled behavior. Never add `@radix-ui/*`.
- **One style: `base-nova`** (set in `apps/v4/components.json`). Pull shadcn core
in via the CLI β€” `npx shadcn@latest add <name>` β€” never hand-transcribe
classes. Base-nova payloads sometimes run ahead of the released Tailwind CSS
(new `cn-*` utilities); if you must vendor a class, confirm it exists in the
released `tailwind.css` first.
- **Conventional Commits**, enforced by commitlint.
- **Never self-merge a PR**, force-push, or run destructive git without explicit
confirmation. Branch, push, open the PR, leave it for the maintainer.
- **Verify state directly** β€” read the file, check the branch β€” rather than
assuming what's frozen or already done.

---

## How components are built

There's no single mold. A component takes the simplest shape that fits:

- **Styled composition** β€” styling over shadcn core and/or a headless library,
composed directly, no separate primitive (`emoji-picker`, `sortable`,
`wheel-picker`, `confirmer`).
- **Primitive + styled** β€” an unstyled `<name>-primitive.tsx` layer plus a
styled `<name>.tsx` that composes it with shadcn core. Author a primitive when
the headless behavior is worth publishing on its own β€” usually a thin
Base UI-flavored adapter over a library (`dropzone` β†’ react-dropzone,
`phone-input` β†’ react-phone-number-input, `date-time-field` β†’ timescape), or
hand-written on Base UI when no library fits (`password-input`).

`apps/v4/src/registry/new-york/ui/date-picker.tsx` is the reference to study
before designing a new component.

**Import boundaries:**

- shadcn core β†’ `@/components/ui/*` (added via CLI; **not** part of our registry).
- ui-x components & primitives β†’ `@/registry/new-york/{ui,components}/*`.
- Demos import the styled component from `@/registry/new-york/ui/<name>`.

(The folder is named `new-york/` for historical reasons β€” the _style_ is `base-nova`.)

---

## Anatomy & API consistency

A new component's API should feel like one a consumer already knows. Before
naming anything, look at the closest shadcn primitive and the nearest ui-x
sibling, and match them.

- **Parts are `<Component><Part>`**, PascalCase, exported root-first in
composition order β€” the same tree the docs "Usage" block shows
(`PhoneInput` β†’ `PhoneInputCountrySelect` β†’ `PhoneInputCountrySelectContent`).
- **`data-slot` is the kebab-case of the exported name** β€” nothing invented
(`PhoneInputCountrySelectValue` β†’ `data-slot="phone-input-country-select-value"`).
- **Reuse the established part vocabulary.** A surface is `*Content`, an opener
`*Trigger`, an option `*Item`, the selected display `*Value`, the field
`*Input`. Don't coin a new noun for a role that already has one β€” a consumer
should be able to guess a part's name from what it does.
- **Mirror the underlying anatomy so composition transfers.** When a part wraps
a shadcn primitive, keep the same shape and let it compose with that
primitive's own parts β€” `PhoneInputCountrySelect` sits inside Select's own
`SelectTrigger`, so anyone who knows Select already knows this.

When a new component and an existing one solve the same sub-problem, they should
read the same way. Consistency across the set beats a locally clever API.

---

## The wrapping principle

When you expose a sub-component, choose deliberately between **wrapping and
re-exporting** a part vs. letting the consumer **compose it from the outside**.

> Wrap + re-export only when the wrapper earns it β€” behavior, styling, or a
> composed default. A wrapper that only renames a `data-slot` on a part that
> belongs to something else (shadcn core, another ui-x component, a library) is
> cosmetic indirection; drop it and let the consumer use the part directly.

**The test:** remove the wrapper and inline the part. Does anything change
besides the `data-slot` string? No β†’ delete it. Yes β†’ keep it.

Worth keeping β€” earns its place by:

- reading your primitive's context/hook (`useDatePicker`, `usePhoneInput`, drag state);
- binding a primitive part via `render` (`<Primitive.Input render={<Input />} />`);
- adding real styling (`w-auto`, heavy item styling) or a composed default (a
placeholder, a state-swapping icon).

Precedents: `DropzoneUploadIcon` (swaps icon on drag state) and
`PhoneInputCountrySelect*` (wire `usePhoneInput`, add a flag placeholder) stay;
`PasswordInputAdornment` (was `<InputGroupAddon data-slot=… />`) and
`PhoneInputCountrySelectTrigger` (was `<SelectTrigger data-slot=… />`) were
removed β€” consumers compose `InputGroupAddon` / `SelectTrigger` directly.

When you add or drop a part, update the demos, docs (Usage / API Reference /
Accessibility), and β€” if a part disappears β€” the changelog, in lockstep.

---

## Registry & docs conventions

Registry items live in `apps/v4/registry.json`:

- `dependencies` β€” npm packages; `registryDependencies` β€” **shadcn core as plain
strings** (`"input-group"`, `"select"`), **ui-x-internal as
`"junwen-k/ui-x/<name>"`**. Validate with `cd apps/v4 && pnpm registry:validate`.

Docs are fumadocs MDX under `apps/v4/content/docs/`:

- Component pages carry Installation, Usage, Examples
(`<ComponentPreview name="…" />` β†’ `src/components/examples/`), Accessibility,
API Reference. Primitive pages document the unstyled layer with full prop
tables; the styled API Reference links back rather than repeating them.
- **Primitive demos stay unstyled** β€” plain markup plus the "Unstyled" callout.
- **Forms are library-agnostic**: show Base UI `Field`/`Form` markup and link to
shadcn's [forms guides](https://ui.shadcn.com/docs/forms) for RHF/TanStack
wiring. In form demos pass `field.value ?? null` so Base UI's `useControlled`
never flips modes.
- **Tone:** no maintenance promises, no "as-is" laundry lists, clean-cut over
keeping the superseded around; changelog tracks meaningful releases, not
per-part churn.

---

## Workflow & tooling

Understand first (nearest shadcn and in-repo precedent) β†’ design the API against
the wrapping principle β†’ implement (library or primitive as needed, then the
styled layer, `registry.json`, demos, docs in lockstep) β†’ verify β†’ commit and
open a PR.

Verify from `apps/v4/`: `npx tsc --noEmit`, `pnpm lint` (0 errors; a few known
warnings pre-exist), `pnpm registry:validate`, then eyeball the affected pages on
`pnpm dev` (:3000).

The **shadcn MCP** (`.mcp.json`) gives live registry access β€” prefer it over
guessing a component's shape or classes. The **shadcn CLI** adds core components
(`npx shadcn@latest add <name>`) and fetches docs (`npx shadcn@latest docs <name>`).
Loading