Conversation
Tests live under tests/ (components, theme, gates, harness) and vitest runs two projects: unit (jsdom) and package (node, for the packed-consumer test to come). tests/setup.ts keeps only what a current test needs: the drained macrotask after cleanup, sonner's dismiss, and the pointer-capture and scrollIntoView stubs. The FormData, createObjectURL and localStorage workarounds came from VisionSet application code that no longer lives here.
…espace; remove gates success, warning and info are roles in styles.css with light and dark values, exposed as bg-/text-/border-success and friends; overlay names the scrim behind a Dialog or Sheet. @theme resets --color-* so Tailwind's default palette is not part of the vocabulary, and --color-brand is no longer exposed: brand stays a CSS variable for identity only. chart-* tokens had no consumer and are gone; sidebar-* stays because two product shells use it. With the palette closed, statusPaletteIn, competingStatusPaletteIn and brandUsagesIn have nothing left to catch, and colouredClassesIn is held by an ESLint no-restricted-syntax rule instead. src/gates, the ./gates export and statusTone.ts (STATUS_INK, TONE_FILL, TONE_BORDER, StatusTone) are removed; docs/MIGRATION-0.3.md will carry the consumer migration.
…wind.css Radix emits data-state and data-orientation, Base UI emits bare data-open, data-closed and data-highlighted, and Tailwind's built-in data-* variant already matches the bare form. So the Radix components spell data-[state=open] and data-[orientation=horizontal], the combobox keeps data-open, and the custom variant layer that papered over the difference is gone with the scroll-fade, shimmer and accordion utilities nothing here used. The one custom utility a component did use, no-scrollbar, is inlined at its single call site.
…avioural tests A native <button> inside a <form> submits by default, so a Cancel could post the form; Button now renders type=button unless told otherwise, and leaves an asChild element's own semantics alone. DropdownMenuSubContent drops its exit animation to match the root surface: while one runs, Radix keeps the closed surface mounted and the next press is swallowed. Button, Dialog/Sheet, Field and DropdownMenu get their own test files, on roles, focus, aria relationships and the form/pointer outcomes a screen would silently lose. The menu tests give exit-animation utilities a real animation-name and a live getComputedStyle, so a surface that lingers fails the way it does in a browser. Field's tests state the actual contract: the anatomy is the component's, the aria wiring is the call site's.
rounded-md replaces the min(var(--radius-md), Npx) expressions — identical at the default radius — text-sm replaces text-[0.8rem], ring-3 replaces ring-[3px], the tooltip arrow takes rounded-xs (2px), and the in-button-group hooks go with the ButtonGroup that never existed. package.json: 0.3.0, sideEffects limited to CSS so a Button-only import tree-shakes, and a verify script that runs the whole pipeline in order.
tests/package/consumer.test.ts builds, packs and installs the tarball into a throwaway consumer, compiles its stylesheet with @tailwindcss/cli, imports the entry under Node, renders a Button and bundles a Button-only entry with Vite. It asserts the component utilities only @source can have found, the semantic roles in both themes, the absence of any physical palette or brand utility, the shipped file list, sideEffects, and that sonner and Base UI stay out of a Button bundle. It fails on the @source regression 0.2.0 shipped with. Two things it caught on its first run: dist/ was never cleaned, so a pack after a rename shipped stale files (build now removes dist first), and the import-resolution simulation it replaces is deleted.
A pnpm workspace member that depends on @robomous/ui-core as workspace:*, so it imports the real package through the real exports map. Vite plus @tailwindcss/vite, one page: foundations (roles, status, structure, brand as a variable, type, radius, focus), every component, and a state matrix, with a light/dark toggle. It owns examples only; pnpm catalog runs it and pnpm catalog:check typechecks and builds it in CI.
…the full pipeline docs/DESIGN.md describes the architecture that exists: a closed colour namespace of roles, status as semantic utilities, brand as a variable, one state dialect per behaviour library, no exit animation on any menu surface, and Field as anatomy whose aria wiring is the call site's. Every mention of gates, scanners, statusTone and chart tokens is gone. docs/MIGRATION-0.3.md names each removed API, every consumer site found in VisionSet and robomous-cloud, and the edit each needs. CONTRIBUTING.md and docs/components/README.md are new; README points at them. CI runs format, lint, test, build, catalog and the packed-consumer test as separate steps; the release workflow runs pnpm verify before npm publish and keeps OIDC trusted publishing with provenance. pnpm comes from package.json#packageManager, the version that wrote the lockfile.
…json, @/ alias resolved in dist styles.css imports src/theme/shadcn.css, a byte-for-byte copy of shadcn/dist/tailwind.css: the data-* variants the registry's components qualify their state styles on (matching both [data-state=x] and the bare attribute, excluding "false"), no-scrollbar, scroll-fade-* and shimmer-*. Vendored rather than imported, because the stylesheet ships as source and a package named in it is a runtime dependency of every consumer — and shadcn is the CLI. tests/theme/shadcn.test.ts holds the copy to the installed package, to git, and to devDependencies. cn-rtl-flip, which the registry names and shadcn defines nowhere, is declared in styles.css. components.json returns so `pnpm dlx shadcn@latest add <x>` installs new components into src/components with @/ imports; tsconfig declares the alias, tsc-alias rewrites it to relative paths with extensions in dist, and vitest resolves it against src. The tsconfig path for react was not needed.
…n top of shadcn/ui The Sidebar family and useIsMobile join the public surface. Its test covers the trigger and the keyboard shortcut, the cookie, the controlled caller, the data-active attribute (the reason shadcn's variant layer is load-bearing) and the Sheet it becomes under the breakpoint; tests/setup.ts gains the window.matchMedia jsdom lacks. The packed-consumer test now renders the Sidebar under Node, refuses an @/ import in dist, expects shadcn's layer and cn-rtl-flip in the compiled CSS, and shadcn absent from runtime dependencies. README, DESIGN.md and CONTRIBUTING.md describe the package as built on top of shadcn/ui: how a component arrives from the registry, the adaptation checklist, the vendored layer and how to update it, and a State attributes section that explains why the layer is back.
Attachment, Avatar, Breadcrumb, ButtonGroup, Command, ContextMenu, Drawer, Empty, HoverCard, Item, Kbd, Pagination, Popover, RadioGroup, ScrollArea, Spinner, Toggle and ToggleGroup enter through `components.json`, each adapted to the rules in DESIGN.md and owned here from then on. cmdk arrives under Command and vaul under Drawer, the first two behaviour libraries in the package that are neither Radix nor Base UI. Every one is exported by name from `src/index.ts`, has a behaviour test, a specimen in the catalog and a row in `docs/components/README.md`. The state-attribute rule is settled while they land: a state is written in shadcn's variants (`data-open:`, `data-active:`) and never in the attribute a library happens to emit, so Dialog and Combobox read alike. A `data-[…]` bracket survives only where the layer declares no variant, and for attributes that are not states. The exit-animation rule grows with the new surfaces: ContextMenuContent and PopoverContent join the menus in leaving on the frame they are dismissed, because their trigger can be pressed again on the next.
`examples/catalog` was one long page of specimens for manual inspection. It is now a documentation site: a page per component with a lede, the library its behaviour comes from, live demos, the usage a consumer copies, an anatomy tree and an API table, plus Introduction, Installation, Theming and States. Astro 7 with React islands and Tailwind v4. The site dogfoods the package: the left navigation is the package's own Sidebar, search is a Command palette over a build-time index, and the API tables are its Table. Demos live one story per file under `src/demos/<name>/`, and the file itself is the code the "View Code" panel shows, so a sample cannot drift from what it renders. API tables are read from the components' source by `scripts/extract-api.ts` with the TypeScript compiler API: cva variants with their defaults, declared props, inherited props a component gives a default to. The JSON is generated, never committed, and `tests/docs/api-extractor.test.ts` holds its shape and fails the day a component has no page. `pnpm catalog` and `pnpm catalog:check` become `pnpm docs:dev` and `pnpm docs:check`; the colon is not decoration, `pnpm docs` is a pnpm built-in and would shadow the script. `docs:dev` stops a running dev server and drops Vite's dependency cache before building, because a package rebuilt under a live server leaves the optimizer serving two incompatible bundles and every island fails to hydrate.
…copy `src/theme/tokens.ts` held `LIGHT_THEME`, `DARK_THEME`, `THEME` and `cssVar`: a TypeScript copy of the values in `styles.css`, kept for a caller that could not read CSS, and a test to hold the two in step. Nothing in the package imported it, no consumer did either, and a second copy of the palette is a second thing that can go stale. The stylesheet is now the only copy. A caller that genuinely needs a value as a string reads it from the document, which also answers for the theme the reader is in; everywhere else `var(--role)` resolves it. `tests/theme/tokens.test.ts` keeps the half that was never about the mirror: every role declared in both themes and nothing else, the palette closed, one `--color-<role>` per role, the radius scale derived from `--radius`, dark mode keyed off the class, no focus geometry in the base layer. The dark-theme check that compared against the mirror now asserts what the mirror could not — that the theme repoints its roles rather than restating the light values. BREAKING CHANGE: `LIGHT_THEME`, `DARK_THEME`, `THEME` and `cssVar` are no longer exported. docs/MIGRATION-0.3.md section 6 has the replacement.
feat: eighteen components from the shadcn registry; forty in the package
feat: the catalog becomes a documentation site, on Astro
refactor!: drop the token mirror, leaving the stylesheet as the only copy
`font-mono` used to resolve to whatever monospace the reader's machine
happened to have, which made an identifier or a commit hash look like a
different design system on every OS. It is now a declared token:
--font-mono: "Geist Mono Variable", monospace;
imported from `@fontsource-variable/geist-mono` the same way the sans
face already is, so it ships offline and needs nothing of the consumer.
No base rule puts it on `code` or `pre`. Applying it stays a call-site
decision, because a `<code>` inside a sentence is usually prose about
code rather than code.
`tests/theme/tokens.test.ts` pins the value. The packed-consumer test
now names `font-mono` in the consumer's own markup and asserts the whole
chain: both faces ship as `@font-face`, and the compiled `.font-mono`
rule carries Geist Mono. Removing the token makes that test fail with
Tailwind's own default, which is how it was checked to bite.
`src/theme/tokens.ts` went away in 9bc02ca, but two lines still described it. The stylesheet's own header promised a TypeScript mirror that no longer exists, and DESIGN.md's verification table said `tests/theme/tokens.test.ts` checks that "roles agree between the stylesheet and its mirror" — it never did, and now there is nothing to agree with. That suite's only input is `styles.css`, read as text. Both now say what is true: a caller that needs a value as a string reads it off the document with `getComputedStyle`, and the table lists what the test actually asserts — both themes declaring every role and nothing else, `.dark` repointing rather than repeating, the closed palette, the fonts, every radius step, and the absent `:focus-visible` rule. The remaining mentions are history and stay: docs/MIGRATION-0.3.md records the removal, and the plans under docs/superpowers/ are the record of when the mirror existed.
…urfaces - Checkbox, Switch, Slider, Accordion and Collapsible join the package (forty-five components), with behavioural tests and catalog pages. - `@robomous/ui-core/icons` re-exports lucide-react, so consumers and the catalog draw icons from the same set without a second dependency. - Theme: colour roles are spelled from Tailwind's neutral scale; status roles become ink/surface pairs (`--success` + `--success-surface`, etc.) that clear 4.5:1 in both themes. The palette is trimmed to eighteen scales. - Badge and Sonner toasts use the status surfaces; Table gets a bordered, rounded scroll frame with column rules and a quieter header. - Docs, DESIGN.md and theming pages updated to match.
Contributor
Author
|
Superseded by #10 — the head branch was renamed to |
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
Checkbox,Switch,Slider,Accordion,Collapsible, each with behavioural tests (tests/components/form-controls.test.tsx,tests/components/disclosure.test.tsx) and a catalog page with demos.@robomous/ui-core/icons: a new subpath that re-exportslucide-react, so consumers (and the catalog, which drops its ownlucide-reactdependency) use the same icon set the components ship with.neutralscale, and the palette is trimmed to eighteen scales. Status roles are now ink/surface pairs (--success/--success-surface, and the same for warning, info and destructive) that clear 4.5:1 contrast in both light and dark.richColors, mapped to the roles.font-monois now Geist Mono, bundled beside the sans face, and the last references to the token mirror are gone from the docs.Test plan
pnpm verifypasses locally (format, lint, typecheck, unit tests, build, docs check and build, packed-package consumer test)