refactor(ui-core): shadcn is the authoritative primitive contract, and VisionSet extends it - #812
Merged
Merged
Conversation
…files, and every chip is a Badge
…our gate allows a mix of tokens
…iles, inverted by the preset
…ine option is composed at the call site
…al files; the combobox is a pattern; scoped radix packages retire
…ical tooltips need
…d by a snapshot gate
…me it is dismissed
…nd muted, with no stroke
… primitives that needed them
…, and the hand-built one retires
…thout next-themes
…ionSet extends it
…the installer does
complete-promote-* and start-job-* are outline buttons now, not "secondary" — and the page's one filled-button check is scoped to Button (data-slot="button") so it stops also matching the batch-state Badge, which legitimately carries data-variant="default" for the in_annotation state.
…s the outline-with-radius shape RecipeList's target chip was an outline Badge hand-rounded with className="rounded-md" — exactly the shape the quiet variant exists to replace. shadcn_extensions.test.mjs now flags an outline Badge carrying a rounded-* className utility, in either attribute order, self-tested both directions.
Canonical SelectTrigger is w-fit whitespace-nowrap, so the below-lg class picker sized to its selected option's text instead of its container and pushed the schema wider than a 320px viewport. The picker's own wrapper gets min-w-0 and the trigger gets w-full; either alone still leaves a 284px overflow. Updates the narrow/light schema visual baseline to the now-full-width trigger.
…ile names menu.ts cited a "Menu row" heading that no longer exists; styles.css cited "Borders and Focus" where DESIGN.md's own H3 reads "Borders and focus". product-principles.md still named the retired PascalCase primitive files Tabs.tsx and Badge.tsx, which are tabs.tsx and badge.tsx now.
…lows a theme flip shadcn_extensions.test.mjs sweeps every DropdownMenuContent call site outside the shadcn snapshot for lib/menu.ts's menuSurface class, self- tested against a tag with and without it; today's 8 call sites all pass. sonner.test.tsx adds a third case that flips <html> from light to dark after mount and waits for the toaster's data-sonner-theme to follow, covering the MutationObserver path the other two cases don't reach.
Status Palette now notes that Badge contrast is measured against page/card surfaces: on a full bg-muted panel the warning ink measures ~4.3:1, so a status Badge needs a re-check before it lands on a muted panel. Rides with a stray double blank line in DatasetScreen.tsx.
The cycle suite is the one suite the realignment never ran — the frontend image has no `uv`, so its `webServer` could not start and CI is the only place it runs. Three of its assertions still read v1's vocabulary, and the walk stopped at the first: the composed step on the run outcome card is `default` (the call site fills it), `Open batch` beside it is `outline`, and the same control on a batches row takes the prop's own default, which is `outline` too. `composedTransitions.test.tsx` already asserted these three values; only the browser walk was left behind. The prop's docstring said "secondary" for the same reason.
… JSX `TAG_ATTRIBUTE_RULES` walks JSX opening tags, so it sees a component spelling `variant="primary"` and misses a spec asserting the same name through the DOM — which is how `cycle.spec.ts` carried "primary" through the whole realignment with CI the only thing that caught it. Three shapes per attribute, because a spec has three ways to say it: the two arguments of `toHaveAttribute`, a `[data-variant=…]` selector, and `getAttribute`/`dataset` compared with `toBe`/`toEqual`. The first matches across newlines on purpose: prettier puts those arguments on their own lines once the call runs long, and that wrapped form is the one that got through. Only values no primitive carries any more are listed. `secondary` and `success` are still real variants, so an assertion naming one asks about its own call site, which a vocabulary sweep cannot answer — the second stale assertion in that file was `secondary`, and this gate would not have found it.
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.
Realigns
@visionset/ui-coreso that shadcn/ui is the authoritative implementation of every primitive. VisionSet extends it only by adding lines; it no longer forks it.Plan:
.agents/plans/2026-08-26-shadcn-foundation-realignment.md· Report:...-realignment-report.md· Ledger:...-realignment-ledger.mdWhat changes
The 21 primitives now equal the shadcn CLI 4.19.0 snapshot (preset
b2iH→radix-nova), with added lines only. The gatetests/scripts/shadcn_canonical.test.mjschecks every file against its pristine snapshot infrontend/ui-core/shadcn/. Measured at head: +0 −0 everywhere except two files.cvaentriessuccess warning info quieton emerald / amber / sky / muted. Geometry, padding, radius, focus and data slots are shadcn's base string, untouched. Zero coloured status borders.next-themesreplaced by a hook reading.darkon<html>. Thenext-themesdependency is gone. MarkedSHADCN FRAMEWORK ADAPTER; the gate names the exact two removed lines.Surviving proprietary deviations: 0 (
SHADCN DEVIATIONcount = 0).API and vocabulary
primary(42 call sites →default),successandsize="md"removed. Final:default outline secondary ghost destructive link.accent(7) →default, unnamed/neutral→secondary,quiet(3) kept,success/warningre-paletted.title=, the description registry,SelectItem meta=,FieldHint→FieldDescription, Progress'svariant). Each case is recomposed at the call site.menuSurface(lib/menu.ts) is applied by composition at all 8 call sites, now gated.sheet,combobox(official, Base UI-backed),input-group. The hand-rolledpatterns/Combobox.tsx(0 product consumers) is deleted.Badge.tsx,Button.tsx,Card.tsx,Combobox.tsx,Dialog.tsx,Feedback.tsx,Input.tsx,Menu.tsx,Select.tsx,Table.tsx,Tabs.tsx).Tokens and status colour
success/warning(+-foreground) are retired fromstyles.css,tokens.ts, tests and the styleguide, and added toRETIRED_DECLARATIONS. Noinfotoken was created. Remaining extensions:stage,brand,origin-*. All status colour outside the Badge now lives in one file:patterns/statusTone.ts.Contrast (WCAG, ink over the 10 % tint composited on the page background, gamma-space): emerald 4.88 light / 8.96 dark · amber 4.67 / 9.89 · sky 5.29 / 8.02 — all ≥ 4.5:1. Documented latent case:
warningmeasures ≈4.3:1 on a fullbg-mutedpanel; no status Badge renders there today.Docs and gates
DESIGN.mdrewritten (21 sections) — shadcn named as the authoritative primitive contract, plus the update workflow, the status palette, the stroke policy and the deviation policy. New gate filetests/scripts/shadcn_extensions.test.mjsmachine-checks the extension contract. Styleguide rewritten. 9 visual baselines regenerated and reviewed image by image, light and dark, by the implementer and an independent reviewer.Product integrity
data-testidset: identical to the base across every changed.tsx. The onlyaria-*/roleremovals belong to the deleted PascalCase primitives; net additions come from the canonical Field / InputGroup.Validation
Everything ran inside
mcr.microsoft.com/playwright:v1.62.1-noble.pnpm install --frozen-lockfilepnpm version:check && pnpm generate:client:checkpnpm -r build/pnpm -r lintpnpm -r testpnpm test:scriptsNot run here: the cycle suite — its
webServerspawnsuvand the frontend image has nouv. The CI jobbrowser cycle (chromium)covers it. No host-native validation was performed.Merge state
Rebased on nothing — the branch is based on
a4c3630andorigin/mainhas since advanced to92d19d4(#811). Checked withgit merge-tree: no textual conflicts, and a sweep of the merged tree finds no retired vocabulary (variant="primary"|"success"on Button,size="md",FieldHint, PascalCase primitive imports,--success/--warning,bg-success). #811 touchedRecipeEditor.tsxandPreprocessingTab.tsx, which this branch also touched, but in non-overlapping regions — CI runs on the merge commit and is the authority on the combined state.Rulings made without asking
refactor/shadcn-canonical-primitives(merged onto main as the first commit) instead of redoing its 30 commits.patterns/statusTone.test.ts— a test asserting exact classes has to hold them.bg-successindicator overrides were removed rather than re-pointed at emerald (Progress carries no polarity). A completed batch's bar is no longer green; the state stays in the Badge and label.scripts/shadcn_add.shnow relativises every primitive after a CLI run, because the CLI rewrites dependency files too.lineTabsList lives at the pattern layer (ProjectNav); the primitive stays canonical.