@evelinka-ai/ui
v0.6.4
Published
The Evelinka UI kit: tokens, themes, Base UI primitives, compositions and patterns for the Eve console and any React 19 + Tailwind 4 app. Layers and import rules: README.md; every public module: CATALOGUE.md.
Readme
@evelinka-ai/ui
The Evelinka UI kit: tokens and themes, Base UI primitives, compositions and patterns for the Eve
console and any React 19 + Tailwind 4 app. The contract is docs/ui-kit/KIT.md (layout, names,
props, packaging) with the binding names of docs/ui-kit/PLAN.md §1; token values live in the
repo's DESIGN.md. Every public module is listed in CATALOGUE.md (generated).
Publishing and consuming the package (install, CSS choice, import rule, theme attribute):
docs/RELEASE.md, section "@evelinka-ai/ui package contract".
Layers
A layer imports itself and the layers below it, never above. scripts/check-layers.ts enforces it.
| Layer | Folder | Holds | May import |
|---|---|---|---|
| L0 theme | src/styles/, src/fonts/ | tokens.css (custom properties; imports the generated themes.css), styles.css (the Tailwind 4 entry) | nothing |
| shared | src/lib/, src/hooks/, src/theme/ | .ts only: cn, tones, motion values, breakpoints, KitLink types, the error-describer seam, hooks, the theme manifest | shared |
| L1 primitives | src/components/ | shadcn base-nova primitives, CLI-managed, flat kebab-case files | shared, L1 |
| L2 compositions | src/compositions/<name>/, src/forms/ | Eve compositions (kit-provider, settings-section, data-table, …) | shared, L1, L2 (no cycle between folders) |
| L3 patterns | src/patterns/<name>/ | page and app frames (app-shell, page-shell, …) | everything below |
| experimental | src/experimental/<name>/ | new APIs before promotion; never published | everything below L3; only the gallery imports it |
Import rules
- Import every module from its own subpath:
@evelinka-ai/ui/components/button,@evelinka-ai/ui/compositions/kit-provider,@evelinka-ai/ui/lib/utils. There is no root barrel, and kit files import each other by the same specifiers. - Nothing under
src/imports a router, TanStack Query, zod,@eve-ai-layer/*or an app. Links go throughKitLink; data states take plain props; errors are described through the app's describer (KitProvider describeError). - Compositions and patterns take colours from tokens, never a hex,
rgb()orhsl()literal. - Apps never import
@base-ui/react,radix-ui,cmdk,vaulorrechartsdirectly, and never re-vendor a primitive: the console has nocomponents.json. - The app wraps its router in one
KitProvider(link, resolved motion, material, scheme, error describer).
Adding or changing a component
The shared-component protocol is KIT §6 (and PLAN §5 during the program). In short:
- Look first:
CATALOGUE.md, the gallery,rginpackages/ui/src. Extend before adding. - Domain-free and used by two surfaces (or listed in KIT) → the kit; otherwise a console
composition in
apps/web/src/components/<domain>/built from kit parts. - Claim it: one vitrinka task
kit: <name>under the kit epic. - Build it in its own folder (KIT §5.2):
index.ts(public exports only),<name>.tsx,<name>.lib.ts(pure logic),<name>.test.tsx,<name>.gallery.tsx(every state) and aREADME.mdfrom templates/README.md. New APIs start insrc/experimental/<name>/unless KIT specifies them fully. - Never hand-edit a generated file: run the generator (below). Conflicts in generated files are resolved by re-running it.
- Conventions (KIT §5):
data-sloton every part, state as data attributes (neverdata-state),refas a prop,cn(base, variants, className)with the caller's class last, controlled and uncontrolled throughuseControllableState, noany.cn(lib/utils) knows the kit's own utilities, so a caller'stype-*role,glass-*level, namedduration-*orshadow-highlight/shadow-float/shadow-glow-attentionreplaces the kit's instead of both staying.
Commands
Everything runs in Devbox from the worktree root, never on the Mac.
devbox run --no-up -- 'bun run --cwd packages/ui typecheck' # src + tests, gallery, scripts
devbox run --no-up -- 'bun run --cwd packages/ui test' # bun:test, src/**/*.test.ts(x)
devbox run --no-up -- 'bun run --cwd packages/ui build' # dist/: ESM per module + d.ts
devbox run --no-up -- 'cd packages/ui && bun scripts/check-layers.ts'
devbox run --no-up -- 'cd packages/ui && bun scripts/check-base-migration.ts' # strict, no allow-list
devbox run --no-up -- 'cd packages/ui && bun scripts/generate.ts --check' # drift gatescripts/generate.ts writes CATALOGUE.md, gallery/src/registry.generated.ts and
src/styles/themes.css. The Devbox checkout is a one-way replica of the Mac's, so a file written
there never comes back; stream the written files home:
devbox run --no-up -- 'L=$TMPDIR/gen.txt; (cd packages/ui && bun scripts/generate.ts) > $L && echo @@@TAR && tar -czf - -T $L | base64 -w0 && echo && echo @@@END' \
| sed -n '/^@@@TAR$/,/^@@@END$/p' | grep -v -E '^(@@@|devbox: |⚡ )' | base64 -d | tar -xzf -The codemods of the Base UI migration live in scripts/codemods/ (rename-package.ts,
console-cn.ts, switch-props.ts); each prints the files it rewrote, so the same streaming
applies, and each takes --check to confirm nothing is left to rewrite.
Gallery and the Wave 0 baseline
gallery/ is a dev-only Vite app that renders every kit component in every state (KIT §5.9);
gallery/README.md has its commands. The Wave 0 baseline, the screenshots of today's Radix
primitives that the Base UI waves are reviewed against, is committed under
gallery/e2e/__screenshots__/<project>/<entry>/<state>.png (desktop Chromium 1440×810,
tablet WebKit 820×1180, phone WebKit 390×844). A wave's gallery diff is either accepted (new
baselines committed in the wave's PR) or fixed.
The shadcn CLI
Primitives in src/components are generated only here, by the shadcn CLI pinned at
[email protected], with the base-nova style of components.json. The console has no
components.json of its own, so a primitive can never be re-vendored into an app.
Run the CLI from packages/ui, in Devbox, one named primitive at a time, and review every hunk
against the primitive's // kit: local changes header before overwriting (KIT §2.1). Never
add --all --overwrite.
devbox run --no-up -- 'cd packages/ui && bunx [email protected] add <names…> --dry-run'
devbox run --no-up -- 'cd packages/ui && bunx [email protected] add <names…> --diff'
devbox run --no-up -- 'cd packages/ui && bunx [email protected] add <names…> --overwrite'Stream what the CLI writes back as shown under Commands.
