Astryx 0.6.0 — all @astryxdesign/* packages ship at this version.
npx astryx upgrade --apply@astryxdesign/core
Breaking Changes
-
Remove deprecated focus-direction overrides, the hooks-path
isImeKeyEventre-export, and Resizable pixel-bound aliases.
Codemod: Runnpx astryx upgrade --applybefore updating to 0.6.0. It removes focus-hookisRtl, movesisImeKeyEventimports to@astryxdesign/core/utils, and renamesminSizePx/maxSizePxtominSize/maxSize. -
Stop emitting deprecated bare prop and state classes such as
.primary,.sm, and.checked. Components retain their stableastryx-*target classes and reflect visual props and runtime states through explicitdata-*attributes; generated runtime and built theme CSS now uses that same selector contract.
Runastryx upgrade --applyto migrate safely identifiable selectors in.cssfiles when a known Astryx target and v0.5.4 value have one or more known meanings. For example:.astryx-button.primary→.astryx-button:is(.primary, [data-variant="primary"]).astryx-button.sm→.astryx-button:is(.sm, [data-size="sm"]).astryx-switch.checked→.astryx-switch:is(.checked, [data-checked="checked"])
The codemod parses CSS selector syntax and never rewrites declarations, comments, JavaScript/TypeScript strings, unqualified classes, or custom/unknown qualified classes. Each known value becomes a specificity-preserving
:is(...)union containing the original class arm plus every v0.5.4 reflected data-attribute arm. The class arm keeps consumer-suppliedclassNamematches working; the attribute arms match v0.6 props and states. You can narrow the union later when class provenance or prop-axis intent is known. Search for unqualified old value selectors such as.primaryor.smand migrate those manually only where Astryx usage is confirmed. Migrate selectors embedded in JavaScript or TypeScript manually with the same rules.Semantic
defineTheme({components})keys such asvariant:primaryandcheckeddo not change. If you prebuild a custom theme, rerunastryx theme build <theme-file>after upgrading and deploy the regenerated.css,.js,.d.ts, and optional.variants.d.tsartifacts together. A built theme is marked__built: true, so the runtime intentionally does not regenerate stale CSS.Exported theme helpers keep their return/container shapes but intentionally return different selector bytes:
themePropsreturns the stable target class (plus target-name compatibility aliases), without bare prop/state classes; its reflecteddata-*attributes are unchanged.parseStyleKeyreturns data-attribute selector suffixes instead of.value,.prop-N, or.statesuffixes.generateThemeRuleskeeps its array contract and ordering; non-base component selectors use reflected attributes.generateThemeRulesSplitkeeps{component, prose};componentselector bytes change andproseis unchanged.generateOnMediaCSSkeeps its scoped string contract; component selector bytes change.generateThemeCSSkeeps{prose, component}and the same layers/scopes;componentinherits the new selectors andproseis unchanged.
-
Restrict
Stepper'shorizontalOptions.minimumStepWidthto a pixel number and remove compact-layout implementation fields fromuseStepperContext.
Replace CSS-length thresholds such as'7rem'with their intended pixel number. CallregisterStep(index, {getIsDisabled})instead of passing a disabled boolean; the options object is optional.StepperContextValuekeeps transition history and step registration, while step count, compact state, summary-portal coordination, and threshold measurement remain package-internal. -
Add ordered environmental adaptations to
defineTheme
Themes can now opt into CSS-first token, theme-local token, and component changes for named viewport widths, primary-pointer precision, contrast preference, and motion preference:defineTheme({ name: 'acme', adaptations: { widthBreakpoints: {sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1536}, rules: [ { when: {width: {from: 'lg', below: 'xl'}, pointer: 'coarse'}, value: {tokens: {'--size-element-md': '44px'}}, }, ], }, });
Condition fields are ANDed.
width.fromis inclusive,width.belowis exclusive, and rules cascade in declaration order so later matching writes win. Theme extension preserves the effective breakpoint map and inherited rule order; static builds retain the metadata needed for source-equivalent extension.AppShellnow acceptsxland2xlformobileNav.breakpointand resolves all five names through the nearest Theme. Mobile mode now uses the documented exclusive boundary (width < breakpoint), so an AppShell exactly at the named point renders the wider layout instead of the mobile layout.defineThemenow validates the token values authored inside an adaptation rule, rejecting non-string scalars and arrays with a length other than two instead of emitting them. Root and on-media token input keeps its existing acceptance unchanged, so themes that pass values through casts or spreads keep building. It also validates the combined portable and theme-local token graph for every reachable set of matching adaptation rules, rejecting cycles before CSS is emitted. Component writes in a rule use the same target, axis, value-domain, and extension validation as rootcomponents; a rule may not be the only place a custom value is enrolled, because generated type augmentation is unconditional.astryx theme buildtreats the adaptation generator as a core capability rather than a baseline requirement, so a theme with no adaptation intent still builds against an older installed@astryxdesign/coreand emits the same CSS as before. A theme that does carry adaptation intent — valid rules, a customwidthBreakpointsmap, or present-but-malformed adaptation metadata — fails against such a core before any output is written, withERR_CORE_INCOMPATIBLEnaming the missinggenerateAdaptationCSSexport. A complete default width map with no rules asks for nothing and still builds. Where an older core'sdefineThemedrops adaptations while resolving, the build records each rawdefineTheme()input and associates it with the theme it produced, so only the selected theme's lineage decides. An unobservable selected ancestor (including a CommonJS source package whose ESM core namespace cannot be wrapped) fails closed; an unused adaptive theme elsewhere in the import graph does not affect a plain build. The same capture preserves raw typography, color, radius, and motion axis metadata in old-core-built artifacts, allowing later current-core children to resolve partial adaptation axes exactly as if they extended the source theme.
New Features
- Banner exposes a
banner-frametheme target on the visible frame that owns whole-banner elevation and the elevated-card silhouette. The target reflectscontainerandelevation; existing Banner targets and default rendering are unchanged. CollapsibleandCollapsibleGroupacceptchevronPosition="start" | "end". The default remainsend, preserving the released trailing chevron.startmoves the disclosure arrow ahead of the label for tree/file-browser-style rows: it points inward toward content when collapsed, mirrors under RTL, and turns downward when expanded.
Set the position onCollapsibleGroupwhen direct items should share it. An individualCollapsiblemay override the group, while a Collapsible nested inside an item's content starts a new presentation scope and keeps its own default.- Add a named font-weight override to Heading with precedence over its
visual type and semantic-level defaults. - Add an
autoCompleteprop to TextInput and TextArea, forwarded to the native control unchanged.
Fixes
- ChatComposerInput: drop
aria-multilineonce triggers make the editable a combobox
aria-multilinewas hardcoded on the contenteditable element whileuseTriggerMenuowns its role, so configuringtriggersswitched the role tocombobox— which ARIA 1.2 does not listaria-multilineunder — and axe flaggedaria-allowed-attr(critical) on the 8 ChatComposerInput trigger stories and the 2 ChatLayout stories that render one. Moves the attribute into the hook'sariaProps, where the role and the attributes whose validity depends on it are decided together. - CheckboxListItem: the visible
descriptionis now the checkbox's accessible description, so the browser computes a distinct description instead of none. Item ids the description element it already renders and publishes that id to the content it renders in a slot, which keeps a plain string description's automatic single-line truncation. CheckboxInput now merges a consumer-suppliedaria-describedbywith its own description, status, and disabled-reason ids rather than replacing it. No public API changes. - CheckboxListItem: a ReactNode
labelnow names the checkbox from its visible text througharia-labelledby, the way RadioListItem already does, instead of falling back to the generic name "Checkbox".aria-labelstill replaces that name; a rich label with no text at all needs it, as it does for RadioListItem. The dev-time warning that asked foraria-labelon every rich label is gone. - Prevented removed Resizable bounds from being silently ignored and kept ambiguous spread migrations behavior-preserving (#6124)
- Keep hover from auto-scrolling open option lists (#6077)
In a scrollable listbox whose highlight follows the pointer, scrolling the highlighted option into view moved the next option under the stationary pointer, whose mouseenter re-highlighted and scrolled again — an endless loop with no user input. This was already fixed for DropdownMenu and Chat; it now covers the remaining combobox-style paths through a shared highlight owner: Selector, MultiSelector, Typeahead, DateTimeInput, and CommandPalette hover highlights move only the highlight, while keyboard navigation still scrolls the highlighted option into view. - Slider keeps its focus ring hidden for modifier-only key presses after a pointer drag while preserving keyboard navigation (#5469)
- Layout: keep content scrollbars at the content area's outer edge when
contentWidthis set.
Without panels,LayoutContentspans the available Layout width and aligns its children tocontentWidthinternally. With exactly one panel, the panel stays aligned to thecontentWidthframe while content extends across the opposite open area. A two-panel layout keeps the complete composition constrained. - Popover: apply same-gesture reopen protection through every opening path, focus genuine caller content regardless of activation modality, and keep the generated fallback close control hidden until keyboard users reach it.
- SideNavItem: a consumer-provided
aria-labelno longer gets overwritten by the collapsed-rail fallback, in both the icon-only and popover-trigger paths.
@astryxdesign/cli
Breaking Changes
-
Add ordered environmental adaptations to
defineTheme
Themes can now opt into CSS-first token, theme-local token, and component changes for named viewport widths, primary-pointer precision, contrast preference, and motion preference:defineTheme({ name: 'acme', adaptations: { widthBreakpoints: {sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1536}, rules: [ { when: {width: {from: 'lg', below: 'xl'}, pointer: 'coarse'}, value: {tokens: {'--size-element-md': '44px'}}, }, ], }, });
Condition fields are ANDed.
width.fromis inclusive,width.belowis exclusive, and rules cascade in declaration order so later matching writes win. Theme extension preserves the effective breakpoint map and inherited rule order; static builds retain the metadata needed for source-equivalent extension.AppShellnow acceptsxland2xlformobileNav.breakpointand resolves all five names through the nearest Theme. Mobile mode now uses the documented exclusive boundary (width < breakpoint), so an AppShell exactly at the named point renders the wider layout instead of the mobile layout.defineThemenow validates the token values authored inside an adaptation rule, rejecting non-string scalars and arrays with a length other than two instead of emitting them. Root and on-media token input keeps its existing acceptance unchanged, so themes that pass values through casts or spreads keep building. It also validates the combined portable and theme-local token graph for every reachable set of matching adaptation rules, rejecting cycles before CSS is emitted. Component writes in a rule use the same target, axis, value-domain, and extension validation as rootcomponents; a rule may not be the only place a custom value is enrolled, because generated type augmentation is unconditional.astryx theme buildtreats the adaptation generator as a core capability rather than a baseline requirement, so a theme with no adaptation intent still builds against an older installed@astryxdesign/coreand emits the same CSS as before. A theme that does carry adaptation intent — valid rules, a customwidthBreakpointsmap, or present-but-malformed adaptation metadata — fails against such a core before any output is written, withERR_CORE_INCOMPATIBLEnaming the missinggenerateAdaptationCSSexport. A complete default width map with no rules asks for nothing and still builds. Where an older core'sdefineThemedrops adaptations while resolving, the build records each rawdefineTheme()input and associates it with the theme it produced, so only the selected theme's lineage decides. An unobservable selected ancestor (including a CommonJS source package whose ESM core namespace cannot be wrapped) fails closed; an unused adaptive theme elsewhere in the import graph does not affect a plain build. The same capture preserves raw typography, color, radius, and motion axis metadata in old-core-built artifacts, allowing later current-core children to resolve partial adaptation axes exactly as if they extended the source theme.
New Features
- Let integration manifests add managed agent guidance
- Add an authoring-time OKLCH palette generator with a pure API, terminal and HTML previews, typed palette output, custom stops, deterministic receipts, and overwrite protection.
[feat] Expose exact solid black and white values asneutralPalettes.blackandneutralPalettes.whitefor use in semantic theme tokens. - Every command now reports what it returned in its debug logs, and a new command cannot skip it.
A command's action returns aCommandResult— either{kind: 'results', count, resultKind, ...}or{kind: 'none'}for the commands whose work is an effect (build, init, upgrade, doctor). The CommandDoc converter records it centrally, soresultCount,emptyResult,resultKind, anddirectMatchare now populated forcomponent,docs,hook,template,theme list/add/targets,discover,blog,swizzle --list,upgrade --list,layout grammar, andmanifest, not justsearchandbuild.resultKindgainstheme,integration,migration,command, andnone; a null now means the run never reached an answer rather than "this command has nothing to say". That is a change of meaning on an existing field, so recorded runs are nowschemaVersion: 3— a consumer that counted nulls as "commands with nothing to report" should branch on the version before mixing old rows with new ones. - Add
doctor integrationchecks for structural validation and Core template, component, and doc overlaps (#6173). - Add an experimental shadcn Registry compatibility guide and doc-derived registry identity metadata. It explains the package boundary, stable organized paths, copied composition model, upgrade behavior, and when to use the richer Astryx CLI.
- Add
astryx upgradetransforms for the Core 0.6 deprecated-API removals: focus direction overrides, the hooks-path IME helper import, and Resizable pixel-bound aliases.
Fixes
- Prevented removed Resizable bounds from being silently ignored and kept ambiguous spread migrations behavior-preserving (#6124)
- Add a conservative
astryx upgrade --applymigration for the Core bare selector-class removal. The transform parses.cssselector syntax, rewrites exact v0.5.4 target/value pairs to behavior-preserving old-class/data-attribute unions, covers unbounded values that v0.5.4 emitted, and leaves unknown consumer classes unchanged. - Preserve
@pathagent doc imports and remove previously duplicated managed blocks (#6164) - Refresh the Collapsible block templates with complete, current examples for single, multiple, controlled, divided, standalone, and grouped usage. The controlled step example keeps one valid step open so its progress label and Previous/Next actions never enter an invalid “Step 0” state.
- Report the fixture path when a template demo asset has an unsupported format (#6039)
Documentation
- Align Doctor help and README examples with the shipped command tree and output format (#6197).
- Clarify how to build themes with imported icon registries, including the current omission of inline registries and the separate registry compilation step.
The theme guide distinguishes a missing compiled registry from an extensionless source import: the former breaks both loading and bundling, while the latter can resolve in a bundler when the source remains beside the generated module. English, dense, and Chinese guidance now explains how output paths and--icons-specifieraffect resolution.
@astryxdesign/build
Fixes
withAstryx()refuses a Turbopack config instead of building an unstyled app. Every alias the helper installs lives innextConfig.webpack, which Turbopack never calls, so the app resolved@astryxdesign/*to dist while PostCSS compiled the library from source — disjoint class names, an exit code of 0, and an unstyled page. It now throws, naming both ways out:--webpack, or drop the helper and consume the pre-built package. Also warns when the merged alias map claims none of the packages, which reaches the same unstyled state by another route. (#6109)
@astryxdesign/theme-butter
Breaking Changes
- Requires
@astryxdesign/core@0.6.0as part of the coordinated stable release. Upgrade Core and this theme together.
@astryxdesign/theme-chocolate
Breaking Changes
- Requires
@astryxdesign/core@0.6.0as part of the coordinated stable release. Upgrade Core and this theme together.
@astryxdesign/theme-gothic
Breaking Changes
- Requires
@astryxdesign/core@0.6.0as part of the coordinated stable release. Upgrade Core and this theme together.
@astryxdesign/theme-matcha
Breaking Changes
- Requires
@astryxdesign/core@0.6.0as part of the coordinated stable release. Upgrade Core and this theme together.
@astryxdesign/theme-neutral
Breaking Changes
- Requires
@astryxdesign/core@0.6.0as part of the coordinated stable release. Upgrade Core and this theme together.
New Features
- Add an authoring-time OKLCH palette generator with a pure API, terminal and HTML previews, typed palette output, custom stops, deterministic receipts, and overwrite protection.
[feat] Expose exact solid black and white values asneutralPalettes.blackandneutralPalettes.whitefor use in semantic theme tokens.
Fixes
- Align Neutral light-mode foreground colors to darker palette stops.
@astryxdesign/theme-stone
Breaking Changes
- Requires
@astryxdesign/core@0.6.0as part of the coordinated stable release. Upgrade Core and this theme together.
@astryxdesign/theme-y2k
Breaking Changes
- Requires
@astryxdesign/core@0.6.0as part of the coordinated stable release. Upgrade Core and this theme together.
Contributors
Thanks to everyone who contributed to this release:
@cixzhang @ernestt @faga295 @freddymeta @Hashim1999164 @HelloOjasMutreja @imdreamrunner @jiunshinn @joaodotwork @josephfarina @kentonquatman @Kyujenius @rubyycheung
Full Changelog: v0.5.4...v0.6.0