fix(cli): theme override keys now match the class names components actually render (#4109)#4204
Open
jiunshinn wants to merge 1 commit into
Open
fix(cli): theme override keys now match the class names components actually render (#4109)#4204jiunshinn wants to merge 1 commit into
jiunshinn wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
PR Analysis ReportNo new or modified components detected. Bundle Size Summary
Accessibility AuditStatus: No accessibility violations detected. Generated by PR Enrichment workflow | View full report |
jiunshinn
marked this pull request as ready for review
July 22, 2026 15:30
jiunshinn
requested review from
cixzhang,
ejhammond,
imdreamrunner and
josephfarina
as code owners
July 22, 2026 15:30
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.
Problem
astryx theme buildemits TextInput component-override CSS under.astryx-textinput, but<TextInput>renders the classastryx-text-input(packages/core/src/TextInput/TextInput.tsxL399:themeProps('text-input', ...)). Every themed TextInput override ships as a dead rule and silently does nothing.Repro (before this PR), building a theme with both spellings:
Root cause
Theme CSS selectors are derived verbatim from the
componentskey: core's shared generator (packages/core/src/theme/generateThemeRules.ts,componentClassSelector) emits.astryx-<key>, and components renderastryx-<token>viathemeProps()/stableClassName()(packages/core/src/utils/themeProps.ts,packages/core/src/naming.ts). So the key is the selector contract — and the CLI'sKNOWN_COMPONENTSvalidation table (packages/cli/src/commands/build-theme.mjs) listed 15 multi-word components un-hyphenated while the components render hyphenated tokens. The table was wrong on both sides at once:textinput) validated cleanly and emitted dead CSS — silent failure.text-input) — which the first-party themes (packages/themes/butter,stone) already use — tripped the "Unknown component" warning, whose "did you mean" then steered authors to the broken spelling.Audit: every KNOWN_COMPONENTS key vs. the class core renders
Cross-checked all 53 keys against the literals passed to
themeProps()/buildClassName()/stableClassName()inpackages/core/src(and each component'sdoc.mjstheming.targets[].className).appshellastryx-app-shellapp-shellaspectratioastryx-aspect-ratioaspect-ratiocheckboxinputastryx-checkbox-inputcheckbox-inputdateinputastryx-date-inputdate-inputdropdownmenuastryx-dropdown-menudropdown-menuformlayoutastryx-form-layoutform-layoutmobilenavastryx-mobile-navmobile-navmoremenuastryx-more-menumore-menunumberinputastryx-number-inputnumber-inputradiolistastryx-radio-listradio-listsidenavastryx-side-navside-navtablistastryx-tab-listtab-listtextinputastryx-text-inputtext-input(the issue)timeinputastryx-time-inputtime-inputtopnavastryx-top-navtop-navlayerastryx-layer)avatar,badge,banner,button,card, ...,empty-state,navicon,progressbar,statusdot, ...)navicon,statusdot, andprogressbargenuinely render one-word classes — left as-is.Fix
CLI-only; core's generator already emits exactly what the (correct) key says, so it needs no change.
KNOWN_COMPONENTS: renamed the 15 keys to the rendered class token, removedlayer, and documented the contract (key ⇔stableClassNametoken) on the table. Legacy un-hyphenated keys now warn loudly at build time —⚠ Unknown component "textinput". Did you mean: text, text-input?— instead of silently shipping dead CSS, which is the "fail loudly" behavior TextInput theme overrides emit dead selector .astryx-textinput (component renders .astryx-text-input) #4109 asks for.'text-input': {'status:error': ...}would warnUnknown prop "status" ... has no variant props, which is false (TextInputrendersthemeProps('text-input', {size, status})). Synced the 15 renamed entries to the axes each component actually passes tothemeProps(), verified identical to eachdoc.mjstheming.targets[].visualProps:app-shell['variant'](was['position']),aspect-ratio['shape'],checkbox-input['size'],date-input/number-input/text-input/time-input['size','status'],form-layout['direction'],mobile-nav['side'],radio-list['orientation','size'](was missingsize),side-nav/top-nav['mode'],tab-list['size'](was['type']). Untouched entries keep their existing lists.loadKnownValuesdir matching: the component→doc-dir lookup only stripped non-letters from the directory side, so hyphenated keys (text-input) never resolved their doc file and every value was treated as custom in variant-declaration generation. Now strips both sides.Result on the first-party themes: building
stone/butterpreviously produced false "Unknown component" warnings fortext-input,date-input,number-input,time-input; after this PR those build warning-free, and the only remaining warnings are for genuinely-missing keys (field-status,multi-selector,progressbar-fill/-track,top-nav-item/-heading) — which is #4110's territory (see below).Why there is no visual-regression risk
The old selectors (
.astryx-textinputetc.) matched nothing in the DOM — no component has ever rendered those classes, so no consumer theme rule under them ever applied. A consumer theme using the old keys goes from "dead rule, silently ignored" to "dead rule, loud warning"; the emitted CSS for those keys is unchanged. A consumer using the correct hyphenated keys (like the workaround in #4109, or the first-party themes) gets identical CSS output minus the spurious warnings. Nothing that renders today renders differently after this PR.Test plan
New suite
packages/cli/src/commands/build-theme.component-selectors.test.mjs(mirrors thebuild-theme.variants.test.mjsconventions, core built viaensureCoreBuilt):text-input/tab-list/top-nav/date-inputoverrides builds warning-free and emits.astryx-text-input(+ the other hyphenated selectors), never the dead un-hyphenated forms.textinputkey still builds (warn-don't-block, matching existing validator behavior) but warnsUnknown component "textinput"withtext-inputin the suggestions.KNOWN_COMPONENTSkey (now exported) must be a class token found by scanningpackages/core/srcforthemeProps(...)/buildClassName(...)/stableClassName(...)literals — derived from source, not a hardcoded list, so the table can't drift from the DOM again (test files excluded from the scan so a stray literal can't mask a mismatch).pnpm exec vitest run packages/cli/src/commands→ 33 files, 801 tests passedpnpm exec vitest run packages/cli→ 117 files, 2026 tests passednode scripts/check-changesets.mjs→ valid (pre-1.0 patch-only)Fixes #4109
Related
input-group,popover,tooltip,toast, and per the audit also sub-element keys likefield-status,progressbar-fill/-track,top-nav-item). Deliberately not added here — this PR only corrects the mapping for keys the table already claims to support. Note for Theme CLI whitelist rejects component keys the core already supports (inputgroup, popover, tooltip, toast) #4110: with this PR the canonical spelling for InputGroup's key would beinput-group(core rendersthemeProps('input-group')).Reviewer questions
layerremoval: nothing in core renders anastryx-layerclass (Layer is a hook/primitive; only the--astryx-layer-*anchor var exists) and it has no derived-var entries, socomponents.layercould never do anything. Removed rather than kept as a whitelisted dead key — happy to reinstate if Layer theming is planned.textarea,selector,typeahead,tokenizerall render astatusaxis the table doesn't know about, producing pre-existing false "Unknown prop" warnings on the first-party themes). Left alone to keep this PR scoped to the renamed entries — can file/fix separately if wanted.