chore(gardening): adopt UI primitives + tone prop where the class delta is provably zero (18 swaps) - #2624
Merged
Merged
Conversation
🎩 PreviewA preview build has been created at: |
This was referenced Aug 13, 2026
camielvs
commented
Aug 13, 2026
2 tasks
camielvs
marked this pull request as ready for review
August 13, 2026 22:54
morgan-wowk
approved these changes
Aug 17, 2026
morgan-wowk
left a comment
There was a problem hiding this comment.
🤖 Agent review. All 18 primitive/tone swaps verified zero class-delta on the current base (raw elements carried no handlers/aria; twMerge preserves the overriding colour class; the !font-mono delta is disclosed). Only caveat is merge-order coupling with #2629 for the 3 size-less raw→primitive swaps — you're owning merge order. LGTM.
Collaborator
Author
Merge activity
|
camielvs
force-pushed
the
automated-gardening/react/2026-W33
branch
from
August 17, 2026 22:48
4141066 to
f699bce
Compare
…on copy Raw <p className="text-muted-foreground"> -> <Paragraph tone="subdued">. Emitted classes are identical (text-md/font-regular resolve to no CSS). Ref: ui-primitives#typography
Raw <span className="text-xs text-muted-foreground"> -> <Text size="xs" tone="subdued">. Text renders a span by default, so the element and classes are unchanged. Ref: ui-primitives#typography
Raw <span className="text-sm text-muted-foreground"> -> <Text size="sm" tone="subdued">. Ref: ui-primitives#typography
…ckLinks) className="text-muted-foreground" -> tone="subdued". cn() is twMerge, so the className already overrode tone's default text-foreground; the computed CSS is unchanged and the conflicting pair is gone. Ref: ui-primitives#typography
…(NodeListItem) Ref: ui-primitives#typography
…tails) Two sites. Ref: ui-primitives#typography
…PipelineInputValueResolution) Ref: ui-primitives#typography
…rdFavoritesView) Ref: ui-primitives#typography
…rdRecentlyViewedView) Two sites. Ref: ui-primitives#typography
…rdHomeView) Two sites; the non-mappable shrink-0 stays in className. Ref: ui-primitives#typography
…rdComponentsView) Two sites; tabular-nums / font-medium truncate stay in className. Ref: ui-primitives#typography
…ddGitHubLibraryDialogContent) Ref: ui-primitives#typography
…onduitDetails) Ref: ui-primitives#typography
…utputDetails) The hardcoded text-gray-500 has no tone equivalent and is left alone. Ref: ui-primitives#typography
camielvs
force-pushed
the
automated-gardening/react/2026-W33
branch
from
August 17, 2026 22:50
f699bce to
f205023
Compare
camielvs
added a commit
that referenced
this pull request
Aug 19, 2026
`tailwind.config.js` has been dead since the v4 upgrade. Tailwind v4 is CSS-first and does **not** auto-load a JS config — it only reads one when a stylesheet asks for it with `@config`. `src/styles/global.css` never did, so the `theme.extend.fontSize` entries in that file were never registered and `text-2xs` / `text-3xs` compiled to nothing. ```diff @import "tailwindcss"; + @config "../../tailwind.config.js"; + @plugin "tailwindcss-animate"; ``` ## Proof the directive takes effect A byte-identical build is ambiguous on its own — it is also what a silently-ignored directive looks like. So I added a probe usage (`className="… text-2xs text-3xs"`) to a real component and built the app both ways: | | `.text-2xs` | `.text-3xs` | | --- | --- | --- | | with `@config` | `font-size:.625rem` ✅ | `font-size:.5rem` ✅ | | without `@config` | not emitted | not emitted | The probe was reverted; it is not part of this diff. ## Proof nothing changes today No file under `src` uses `text-2xs` or `text-3xs` — zero occurrences. With the probe removed, the built stylesheet is **byte-identical** to `master`: ``` master: 161,345 bytes this branch: 161,345 bytes (cmp: identical) ``` That also settles the one real risk in loading a legacy config: its `content: ["./src/**/*.{js,jsx,ts,tsx}"]` key narrows source detection from v4's automatic scanning. Identical output over a full app build means no currently-used class lives outside that glob. Worth knowing if a class is ever added to a `.html`, `.mdx`, or `.mjs` file — it would need adding to `content`. ## Why `@config` rather than moving the values into `@theme` Chosen per request. The alternative — deleting `tailwind.config.js` and adding `--text-2xs: 0.625rem` / `--text-3xs: 0.5rem` to the existing `@theme inline` block — would be the more idiomatic v4 shape and would leave one place to look for theme values instead of two. Happy to switch if preferred. ## Context Found while reviewing #2624, which independently discovered the same class of bug: `text-md` and `font-regular` in `typography.tsx` also emit no CSS, because `--text-md` and `--font-weight-regular` are not defined either. That one is a separate issue — those variants are referenced by every `<Text>` in the app, so defining them **would** change rendered output, unlike this change. ## Reviewer checklist - [ ] Agree `@config` is the direction, rather than folding the two steps into `@theme inline` - [ ] Sanity-check that a legacy `content` glob is acceptable (see above) ## Validation - `pnpm vite build` ✅ — output byte-identical to `master` - `pnpm prettier --check src/styles/global.css` ✅
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.
The proof this run is built on
Read from source, not assumed:
textVariantsfills in defaults.src/components/ui/typography.tsx:21—Textdefaults totone="inherit" size="md" weight="regular" font="default", which emittext-foreground/text-md/font-regular/"".text-mdandfont-regularemit no CSS. Neither--text-mdnor--font-weight-regularexistsin the
@theme inlineblock (src/styles/global.css:194), and neither is a Tailwind default(v4 ships
text-base,font-normal).tailwind.config.jsextendsfontSizewith only2xs/3xs.So the only class a primitive adds with real CSS behind it is the
tonecolour.cnistwMerge(clsx(...))(src/lib/utils.ts:4), so a trailingclassName="text-muted-foreground"already overrode the
text-foregroundthattone="inherit"injects. Moving that colour intotone="subdued"therefore produces the identical computed colour and removes a conflictingclass pair.
That is what makes each swap below an exact mapping rather than a hopeful one.
Findings applied
Raw HTML → primitive (3) —
ui-primitives#typographyConfirmationDialogs/shared.tsx:4—<p className="text-muted-foreground">→<Paragraph tone="subdued">. Renders<p>either way; classes identical.PipelineRow.tsx:218—<span className="text-xs text-muted-foreground">→<Text size="xs" tone="subdued">.Textrenders a<span>by default.CodeViewer.tsx:63—<span className="text-sm text-muted-foreground">→<Text size="sm" tone="subdued">.Hardcoded colour className →
tone(12) —ui-primitives#typographyDocsQuickLinks.tsx:81,InputDetails.tsx:132,InputDetails.tsx:155,MissingPipelineInputValueResolution.tsx:79,DashboardFavoritesView.tsx:151,DashboardRecentlyViewedView.tsx:23,DashboardRecentlyViewedView.tsx:100—className="text-muted-foreground"→tone="subdued"NodeListItem.tsx:67—className="text-destructive"→tone="critical"DashboardHomeView.tsx:65,:147— →tone="subdued" className="shrink-0"DashboardComponentsView.tsx:71— →tone="subdued" className="tabular-nums"DashboardComponentsView.tsx:153— →tone="subdued" className="font-medium truncate"Non-mappable classes (
shrink-0,tabular-nums,font-medium truncate) stay inclassNameuntouched.className="font-mono"→font="mono"(3) —ui-primitives#typographynames this rule verbatimAddGitHubLibraryDialogContent.tsx:42ConduitDetails.tsx:109—truncate flex-1retained inclassNameOutputDetails.tsx:68— the hardcodedtext-gray-500retained (notonemaps to it)These three are class-equivalent only because
font-regularis dead, and it is worth being explicitabout that dependency, because it is not the same argument as the other 15 swaps:
twMergedropsfont-regularagainst a laterfont-monobut does not dedupe it against!font-mono, so the emitted class set genuinely changed here — onlyfont-regularcompiling to nothingkeeps the pixels equal. If
--font-weight-regularis ever added to the theme, these three sites gain aweight the raw
classNameversions never had.font="mono"also introduces!importantwhere theclassNamehad none.+25 / −19).tsxfiles scanned0.9(primitives, config)pnpm run validate:test✅ — 191 test files / 1,966 testsflagOnly: true;--promote-compilernot passed)Reviewer checklist
(mandatory:
requiresVisualReview: trueforprimitives)The engine requires every PR to survive the
reviewskill first, but that skill isdisable-model-invocationand only a human can run it.tone="subdued"site in both light and dark mode — the whole argument rests ontext-muted-foregroundbeing emitted identically, andtoneis the only real class delta.text-md/font-regularare dead classes. The threefont="mono"swapsdepend on
font-regularbeing dead (see above); the other 15 do not. If they are supposed to work,that is a separate (and more interesting) bug in
typography.tsx— every<Text>in the app iscurrently rendering at inherited size and weight. This PR does not change that either way.
Deliberately not applied
Each of these is a real convention gap, but none is an exact mapping, so all are queued for a human.
<p className="text-sm font-semibold">(RegionInput.tsx:36,ConfigInput.tsx:67,GoogleCloudSubmitter.tsx:42,:58).<Paragraph size="sm" weight="semibold">would addtext-foreground.bodydoes settext-foreground(global.css:294), so the computed colour isprobably unchanged — but proving no intermediate ancestor recolours these four sites is exactly the
ancestor-chain reasoning
requiresVisualReviewexists to send to a human. Not applied.FlexNodeCard.tsx:54,:97— the code says why: a user-configurablefontSizeneeds an inlinestyle, whichTextdoes not accept. KEEP (documented, correct).<p>with hardcoded palette colours —ContextPanelProvider.tsx:32(text-gray-500),ImportPipeline.tsx:232/:243(text-green-500 dark:text-green-400,text-red-500 dark:…),ImportComponent.tsx:228/:231/:265/:283(text-gray-500/600+dark:pairs). Notonemaps toa raw palette colour or a
dark:variant pair. Needs a design-token decision first.Text/Paragraphsites in 11 files withtext-gray-*/dark:pairs — same reason. The IDEalready flags several as
cssConflict(e.g.MissingPipelineInputValueResolution.tsx:55,ConduitDetails.tsx:81); those warnings are pre-existing, not introduced here.className="flex flex-col"in 19 files →BlockStack.blockStackVariants(
layout.tsx:13) bases onflex flex-col w-fulland defaults toitems-start justify-start gap-0.w-fullanditems-startare real layout changes (a plain flex-col column stretches its children;items-startshrinks them to content width). Per-site visual review required.<p>(noclassName) in non-test source, 13 of them inConfirmationDialogs/*(
ReplaceConfirmation.tsx,BulkUpdateConfirmationDialog.tsx,UpgradeComponent.tsx).<Paragraph>would addtext-foreground, and dialog bodies commonly inherit a muted colour fromthe shadcn
DialogDescriptionwrapper — so this swap could visibly darken dialog copy. This is thesingle largest raw-
<p>bucket and the one most likely to regress; it needs a look at the rendereddialogs, not a grep.
<h1>–<h6>in 5 files, 6<Text as="h*">in 3 files →Heading.Heading(
typography.tsx:124) accepts onlychildrenandlevel— it forces its ownsize/weightandaccepts no
className. Every site here carries classes it would drop. Not mappable.src/components/ui/date-picker.tsx:42— a genuinely exact<span className="text-muted-foreground">→
<Text tone="subdued">, but it lives inside a shadcn-derived primitive. Making onecomponents/uiprimitive depend on another is a design call, and this file is regenerable by theshadcn CLI. KEEP.
React Compiler — flag-only, nothing edited
flagOnly: trueandpromotionEnabled: falsein.github/gardening-config.json, and--promote-compilerwas not passed. Reporting only:srcfiles (58.8%) across 75 enabled entries.0 useCallback/useMemo - ready to enableinreact-compiler.config.jsis already uncommented, i.e. already enabled. The remaining commented-outentries carry 12–190
useCallback/useMemoeach and are explicitly not marked ready.src/components/shared206,src/utils61,src/hooks45,src/components/ui37,src/services22,src/providers/ComponentLibraryProvider18,src/components/PipelineRun16,src/agent26 (incl.tools,agents).(
src/models/**is inexcludeGlobsand is excluded from that ranking.)and all 6 look load-bearing rather than redundant: 3 ×
useMemowrapping a debounce factory(
PipelineNotesEditor.tsx:20,PipelineDescriptionEditor.tsx:21,useSelectionBehavior.ts:41) wherea fresh instance per render would break debouncing outright, and 3 ×
memo()on components whosereferential stability matters to React Flow / syntax highlighting (
GhostNode.tsx:25,CodeSyntaxHighlighter.tsx:18,CodeBlock.tsx:34). The compiler does not guarantee identitystability, so these are KEEPs, not cleanup debt.
New primitives
None proposed, none authored. Per the pillar spec, new primitives are flag-only and never written
without express permission — and nothing in this scan showed a recurring shape with no existing
primitive to cover it. The gaps found were all adoption gaps, not coverage gaps.
Method deviations, disclosed
category;
DashboardHomeView.tsx's two sites are byte-identical and were replaced together.Edittool. No scripted applier was used in this pillar.