Own the components, and take the palette from the design file - #4
Merged
Merged
Conversation
The package was built to guard the fidelity of what the shadcn CLI wrote, which cost 2,191 lines of snapshots to protect 24 lines of real difference. What was actually needed was the ability to install components with components.json. These two documents record the audit, the design that follows from it, and the task-by-task plan.
A bare h-auto would not displace data-[size=default]:h-8: tailwind-merge replaces within a group only when the modifiers match, so both would survive and the attribute selector would win. The helper carried the modifiers for this reason.
The snapshots under shadcn/ duplicated src/primitives/ almost byte for byte: 19 of 21 components differed by a single import line. What they guarded was a fidelity this package no longer wants, since the components are going to be restyled. Also drops the shadcn CLI from dependencies, where it was never imported and reached every consumer's production tree.
Task 7 said to make variantClasses and FOUNDATION_BADGE internal, but the gate tests live in their own file and import them from the module, so unexporting them is not possible. They move into the test file that uses them, which lands the published surface on eight either way.
Progress read value only to size the indicator and never forwarded it to Radix's Root, which is what derives aria-valuenow. progressAria made every caller repeat the number, and nothing caught the ones that forgot — opt-in accessibility.
inlineLink was a class string a caller had to remember because the component could not carry the size itself. It can now.
twoLineTrigger had to qualify on data-[size=default] because it arrived from outside and had to outrank the base class. As a prop it qualifies on nothing.
menuSurface fixed two things from the outside at every call site, and a gate existed to catch the sites that forgot it. Both are defaults now, so there is nothing left to forget.
The local module reimplemented what shadcn-ui/cn ships compiled. The name and signature a consumer sees are unchanged; clsx and tailwind-merge leave dependencies, and src/lib is now empty.
Moving the stylesheet invalidates tailwind.css, which a gate asserts literally, and the cn migration left the utils alias pointing at a directory it had just deleted. The CLI writes that alias into every component it generates, so the plan now measures the value instead of assuming it.
What is left scans for design rules: no colour inside a class string, brand as identity only, one home for the tokens, one status palette, one icon set. The rest guarded a v1 vocabulary that no repository still speaks.
primitives/ carried the idea of an untouchable layer beneath another layer, which is the premise this release retires. theme/ puts the stylesheet next to the token mirror and the status vocabulary.
They were .mjs with hand-written declarations and a test command of their own. tsc now emits the types and vitest covers them, so test:gates and its CI step are gone.
DESIGN.md argued from what upstream would not let us change. It now argues from the rules we hold ourselves to, and names the gate behind each one. Also drops the inherited VisionSet narrative that no longer describes anything here.
styles.css opens with `@import "shadcn/tailwind.css"` and ships as source for the consumer's Tailwind build, so the package is a runtime dependency of this one. The audit that removed it grepped for `from "shadcn"` and never saw the CSS import; nothing here compiles CSS, so lint, build and test all stayed green while the published package was broken. Adds src/theme/imports.test.ts, which resolves every package the stylesheet imports through that package's own exports map — under the `style` condition a CSS bundler asks for, since Node's own resolver reports tw-animate-css as missing — and fails naming the specifier that landed nowhere. Confirmed failing before the dependency came back.
Overriding a utility made sense while the correction arrived from outside the component. Owning the component, removing it is clearer, and the design doc now says which one shipped.
The components are this repository's to author from now on, so the generator config and the CLI dependency it needed are gone, and the gate that held that config to a schema goes with them. src/theme/tailwind.css now carries the variant layer the stylesheet imports: the `data-*` variants every state style in the package qualifies on, which match both the `data-state` attribute the behaviour libraries set and the bare attribute Tailwind's own shorthand would cover alone. Colour comes from the design file. `foreground` and `primary` move to oklch(0.2 0 0), `secondary-foreground` to oklch(0.269 0 0), and `brand` becomes Robomous orange: oklch(0.663 0.205 39.9), verified to round-trip to #F5580B, which rounding all three channels to three decimals does not. Three defects turned up on the way: - `Separator` sat in src/components/, imported by field.tsx and exported by nothing, so no consumer could reach it. The gate that would have caught that no longer existed; src/gates/design.test.ts now compares the directory against the public surface. - src/theme/imports.test.ts resolved package specifiers and skipped relative ones by design, so the moment the stylesheet imported a sibling, the exact breakage that guard exists to catch went unguarded. It now also asserts the sibling exists and is committed: `files: ["src"]` packs the working tree, so an uncommitted file publishes fine from here and is missing from the clone CI builds from. - The token contract compared quote style, so a formatter run that spelled a font family in double quotes failed it without changing anything a browser resolves. Also declares the formatting this repository never did — .editorconfig, .gitattributes, a Prettier config and the two scripts. Nothing is reformatted here; that is the next commit.
Prettier runs in CI from this commit, and this is the one pass that makes the tree match it. Almost all of it is semicolons on the component modules — the hand-written modules under src/theme/ and src/gates/ already carried them, so this converges the two halves of the package on one answer rather than imposing a new taste. The rest is Prettier's own line breaking. Nothing here changes behaviour: lint, build and the seventy-five tests pass unchanged on both sides of it. The regexes in tokens.test.ts that match the stylesheet's base layer already allowed whitespace between selectors, so splitting a selector list across lines does not reach them. Markdown stays out of Prettier's reach, for reasons written into .prettierignore: it rewrites `*emphasis*` as `_emphasis_`, and it pads table cells until DESIGN.md carries single lines past 170 characters — in the two files whose whole convention is a hard wrap at 100.
`cn` arrives in this release as a dependency rather than a local module, so it is a new transitive dependency for everything downstream. VisionSet holds third-party releases for three days before it will resolve them, and both versions `^0.2.5` admits are younger than that — 0.2.5 by hours — so the range as written cannot be installed there at all. The floor was never a requirement: `pnpm add` picked it. 0.2.4 is the newest version already past that wait, exports the `cn` the components import, and builds and passes the suite unchanged.
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
The package writes its own components from now on. The generator config and
the CLI dependency it needed are gone, the four patch helpers dissolved into
the components they were patching from outside, the gates are compiled with
the rest of the package, and the palette comes from the design file.
Twenty-two commits, each self-contained. The two worth reading on their own
are the last: one is the whole functional change, the other is a mechanical
formatting pass.
What a consumer sees
progressAria,menuSurface,twoLineTrigger,inlineLinkare gone.Each described a limitation rather than a decision, and each is now the
component's own behaviour:
Progressforwards itsvalue, a menu sizes toits items and leaves on the frame it is dismissed,
SelectTriggertakesmultiline,Buttontakessize="inline".legacyVocabularyIn,statusTokenUtilitiesIn,retiredDeclarationsIn— plusmenuSurfaceGapsIn, which guarded a helper that no longer exists. Thepublished surface is eight helpers.
Separatoris newly exported. It existed, was used internally, andreached no consumer.
foregroundandprimarytooklch(0.2 0 0),secondary-foregroundtooklch(0.269 0 0), andbrandtooklch(0.663 0.205 39.9)—#F5580B, Robomous orange.cnis a dependency rather than a local module, andstyles.cssimports a sibling
tailwind.cssthat ships with it.Test plan
pnpm format:check && pnpm lint && pnpm build && pnpm test— 75 testsnpm pack --dry-runcarriessrc/theme/styles.css,src/theme/tailwind.css,dist/gates/anddist/components/separator,and no generator config
missing, a sibling that is uncommitted, a component module that is not
exported
publishing anything: robomous-cloud lint/build green with 38 tests,
VisionSet lint/build green with 2425 tests