nalarx-ui

Contributing

How an item is added or changed, and the rules apps follow when they need UI.

Rules for apps

When a screen needs UI, go in this order:

  1. Reuse what the app already has in src/components/ui.
  2. Install it from @nalarx if the registry has it.
  3. Ask before writing a new primitive. A new pattern that more than one screen needs becomes an item here.

A change to an installed file goes upstream: change it in this repo, release a new version, and reinstall. A local edit that cannot wait is listed under overrides in the app's nalarx-ui.lock.json with a reason and removed once the upstream version lands.

Adding an item

Each item lives in its own folder, so two people adding items never edit the same file.

registry/
  items/button/
    item.json          the item definition
    CHANGELOG.md       one "## <version> (<date>)" entry per release
  nx/Button.tsx        source, laid out as it installs (src/components/ui/nx/Button.tsx)
  nx/lib/cn.ts
  styles/tokens.css    installs to src/styles/nalarx-ui/
  styles/nx.css
  1. Add the source under registry/nx/ and create registry/items/<item>/item.json. Paths in files are repo-relative; the install target comes from the path, so never write target. name must equal the folder name.
  2. Set meta.version to 0.1.0 and write the first changelog entry.
  3. Run bun run registry:build. It assembles registry.json and rebuilds public/r.
  4. Add a docs page under content/docs/components/.
  5. Run bun run check and bun run build, then bun run clean-consumer if the item changes how installation works.

Commit the generated files (registry.json, public/r). CI fails when they are stale.

registry/ui/button/item.json
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "type": "registry:ui",
  "title": "Button",
  "description": "The NalarX button: four sizes, seven variants, press feedback.",
  "meta": { "version": "0.1.0" },
  "dependencies": ["@base-ui/react@1.9.0", "class-variance-authority@0.7.1"],
  "registryDependencies": ["@nalarx/base"],
  "files": [{ "path": "registry/nx/Button.tsx", "type": "registry:ui" }]
}

The assembler checks that:

  • every dependency on another item is written @nalarx/<item> and that item exists;
  • every npm dependency is pinned as name@x.y.z;
  • every file exists, sits under registry/nx/, registry/styles/ or registry/scripts/, and belongs to one item;
  • the changelog has an entry for the current version.

Writing item source

  • Name exports without a prefix (Button, buttonVariants), one PascalCase file per item, named exports only.
  • Import relatively inside nx/: ./lib/cn, ./Icon. Installed files then never depend on the app's own paths.
  • Put "use client" only on files that use hooks or Base UI parts.
  • Text the user reads defaults to English and can be replaced by a prop or by the optional UIStringsProvider.
  • Build on @base-ui/react and class-variance-authority. No Radix.
  • Style with Tailwind utilities and the theme names (bg-bg-elev, text-body, rounded-nx, h-control-md). Inline style may only pass CSS variables; lint enforces it.
  • Keep product meaning out: no API calls, no app routes, no i18n hooks. Text that the user reads comes in through props.
  • Shared CSS that several items need is an @utility or --animate-* key in registry/styles/nx.css, with a base version bump. Item-specific class blocks from globals.css (.nx-btn, .nx-chip, .nx-seg-*) become cva variants inside the item.

Global classes still in nalarx-frontend

base carries the shared classes. These rules stay in the frontend's globals.css until the item that owns them is ported:

ClassesOwner item
.nx-btn, .nx-btn.*, .nx-btn-touch (and the .nx-btn press and transition rules)button
.nx-chip, .nx-chip-dotchip
.nx-seg-list, .nx-seg-tab, .nx-seg-tab-underline, .nx-seg-indsegmented-tabs
.nx-select-panel, .nx-select-openselect
.nx-dots-loader, @keyframes nx-pixel-ondots-loader
.nx-modal-close, .nx-icon-closemodal-close-button
.nx-fieldinput
.nx-page and screen entrance classesapp (not an item)

Releasing

  1. Bump meta.version in each changed item and add its changelog entry.
  2. Bump version in package.json and add an entry to the root CHANGELOG.md.
  3. Run bun run registry:release. It rebuilds public/r and freezes it into public/r/v<version>/, which the docs site serves as an immutable snapshot. Commit, and tag v<version> plus <item>@<version> for each changed item.

Pushing tags and deploying the docs need the repo owner's go-ahead.

On this page