Upstream & releases
How components stay in sync with shadcn/ui, and how changes ship.
Three layers
| Layer | Lives in | Who edits it |
|---|---|---|
| Behaviour and accessibility | @base-ui/react (dependency) | Base UI, via version bumps |
| Component markup | packages/ui/src/upstream/ | Nobody. Written by pnpm sync:upstream |
| Theme structure | packages/ui/src/themes/<name>/structure.css: generated from a shadcn style, except Zynactra's | The sync (Zynactra: us) |
| Theme personality and overrides | packages/ui/src/themes/<name>/personality.css, src/styles/core.css, src/components/ | Us |
Upstream components carry semantic classes such as cn-button-variant-outline. Our stylesheet defines what those classes look like, the same way shadcn's own styles (nova, vega, maia) do. Because we never edit the component files, pulling upstream changes is a copy, not a merge.
Syncing with shadcn/ui
- Change
commitinupstream.jsonto the shadcn/ui commit you want. - Run
pnpm sync:upstream. - Review the diff in
src/upstream/and insrc/styles/reference/style-nova.css. The reference diff shows exactly whichcn-*rules upstream changed. - The sync fails if upstream now styles a class that the Zynactra structure doesn't. Add those rules, then rerun.
pnpm check, then review the docs visually.
Overrides
When the style can't express something (a new variant, the ripple), add a file in src/components/ that wraps the upstream component and mark it // ZY override. button.tsx and badge.tsx are the reference examples. Every other file there is a one-line re-export written by the sync.
Releases
Semantic versions. A token or style change that keeps every API is a minor release; a removed variant or changed prop is a major one with a migration note. Products upgrade through automatic dependency PRs.