Skip to contentAsheeUIAsheeUI

Migrating the core layer

The shrink: which names left `@asheeui/core` for a renderer, which moved to `@asheeui/core/dev`, and the ten that were deleted.

@asheeui/core used to be the framework's engine: class dictionaries, the class merge, per-component fallbacks, the compatibility matrix and the contracts all lived there, and both renderers reached in. That made core depend on pixels it could not compile, and it made a class string a shared contract.

Core is the contract now. It holds the tokens, the rules, the types and the configuration pipeline; the renderers own their class strings and the values only they compile. This is the consumer-facing migration for that move. CORE-MIGRATION-REPORT.md is the record; this page is what changes in your imports.

The rule

A class string is a decision about pixels, and the two platforms' pixels are not the same one. So:

  • the renderer that compiles a class string publishes it;
  • core publishes the tokens, the rules and the types both platforms name;
  • the 21 strings both renderers compile are the documented exception: each renderer states them in its own portable.ts, and packages/core/src/shared/style-parity.test.ts fails when a pair stops matching.

A contributor's one-line test for the next component: would both platforms render this exact string? If no, it belongs to a renderer. If yes, it belongs to core only if a script or both renderers read it.

Where the names went

Name (example) Before Now
BUTTON_SIZE_CLASS, ALERT_ICON_CLASS, every web *_CLASS @asheeui/core @asheeui/web — packages/web/src/styles/<component>.ts
NATIVE_BUTTON_SIZE_CLASS, every native *_CLASS @asheeui/core @asheeui/native — packages/native/src/styles/<component>.ts
SPACE_PADDING_Y_CLASS and the other paired strings @asheeui/core each renderer's portable.ts, asserted equal
cn @asheeui/core @asheeui/web (Tailwind merge) and @asheeui/native (the platform's merge)
FALLBACK_BUTTON_CONFIG, defaultButtonConfig, every per-component fallback/default @asheeui/core the renderer that states the values; native keeps the shared names with the platform's values
COMPONENT_SUPPORT, findComponentSupport, PlatformSupport, PLATFORM_SUPPORT_LABEL @asheeui/core @asheeui/core/dev
FieldConfig, Size, Radius, Space, Variant, Color, ThemeName, ExternalConfig, the typography role table, the cascade, selectTheme @asheeui/core unchanged

The class constant keeps its name on the side it moved to. A web component imports BUTTON_SIZE_CLASS from @asheeui/web; a native one imports NATIVE_BUTTON_SIZE_CLASS from @asheeui/native. Neither package imports the other's.

The ten deletions

These had no reader in the framework, its tests, the docs or any package. They are gone, not aliased:

Name Why
TextContract, ButtonContract, CardContract, StackContract, BadgeContract described props the components already declare; a second statement can disagree with the shipped component
_ColorConfigGuards a type-level helper left behind
FontSize, FontWeight, Elevation aliases; the *_STEP maps and ELEVATION_LEVEL are the values
NATIVE_STEPPER_LABEL_CLASS a native class constant with no native reader

contracts.ts itself was retired in the same work. To be precise: the five *Contract types above were deleted when C4 found no reader, and B19 then retired the module. Substitution and LinkSubstitution — the two types the renderers still read — live in packages/core/src/shared/substitution.ts and are exported from @asheeui/core (and asheeui), while ThemedContract and InputContract were deleted with the module. The module's prose now documents PORTABLE_API.

The matrix moved to a subpath

// before
import { COMPONENT_SUPPORT } from "@asheeui/core";

// now — data only tooling reads
import { COMPONENT_SUPPORT } from "@asheeui/core/dev";

The root entry no longer reaches the matrix, which is what stops an application bundle carrying the inventory. The docs generator, the parity guard and the CLI read @asheeui/core/dev.

What did not move

  • The token scales: SPACING_STEP, RADIUS_STEP, FONT_SIZE_STEP, FONT_WEIGHT_STEP, COLOR_ROLE, VARIANT, SIZE, MIN_TOUCH_TARGET, ELEVATION_LEVEL.
  • The vocabulary: DeepPartial, ComponentsConfig, ConfigIssue, validateConfig, PaletteRole, AnnouncementRole, MessageVariant, ColumnSortDirection, RowCountLabelValues.
  • The rules both platforms answer the same way: isFieldInvalid, resolveFieldStatusColor, UnderlineRadius, selectTheme, the cascade resolvers (mergeObject, resolveCascade, resolveClassKey, resolveConfigCascade, resolveRadiusKey, resolveGridColumns).
  • The shared types and the component configuration sections.

@asheeui/core still declares no dependencies and publishes no class string. pnpm check:core-charter is the guard: no class string, no dependency, no renderer import, and no export without a reader.

Finding a moved name

node scripts/audit/core-usage.mjs             # the per-file usage class of every core export
node scripts/audit/core-usage.mjs --json      # per-export rows, for a codemod

Then import it from the renderer the row names. A name that does not appear in either renderer's entry was one of the ten deletions, or an internal that was never public.

Checking the migration

pnpm build
pnpm check:core-charter      # 4/4, no class string and no unread export
pnpm check:umbrella          # the portable surface still sees the names it needs
pnpm verify:distribution     # the published core has no dependencies and resolves

The shrink is a major release of @asheeui/core. The changelog groups the moves; this guide is the index.