Files
WRNexusJS/docs/plans/2026-10-19-design-system-component-contracts.md
T

9.8 KiB

Component design contract proposal — version 1

Date: 2026-10-19. Historical Phase 0 specification. Phase 2 implements the initial versioned core contracts; see its implementation report and the current coverage. Legacy selector observations below remain distinct from the published version-1 owner/part hooks.

Contract envelope

Each descriptor must contain contractVersion, canonical component, source, family, support, parts, axes, states, slots, tokens, constraints, fixtures and compatibility. Optional fields: provider-owned regions, structural dependencies, deprecated mappings and replacement guidance. Use canonical declaration names; mount aliases and filename casing are separate metadata.

support is one of: legacy-uncontracted, token-compatible, recipe-supported, custom-owned, provider-owned, unsupported. Promote only after evidence exists. A component can contain provider-owned subregions while its wrapper is recipe-supported. Record those regions explicitly rather than changing the meaning of the overall status.

Parts and selectors

Proposed hooks: data-wrn-component="Button" on the owner and data-wrn-part="control" on a styleable element. Repeated components must be scoped by their nearest owner so parent recipes cannot leak into nested components. Preserve existing classes; hooks are additive. Wrapper and interactive control are distinct parts. Do not add wrapper elements solely to satisfy a recipe. A native element can serve more than one semantic role if documented.

Contracts enumerate each part's supported style properties, repeated/conditional presence, state source and token bindings. Arbitrary CSS selectors and HTML injection are not permitted in canonical recipes. Application CSS is an explicit escape hatch outside portable guarantees.

Axes and states

Declare actual accepted values and defaults, not simply string from props. Keep existing names; do not rename all defaults to a common variant. Distinguish independent axes such as variant, color and size. Enumerate supported compound conditions and reject impossible combinations. Explicit existing instance props override system defaults for that axis; ambiguous legacy defaults require an adapter rather than silently changing behavior.

State sources include native pseudo-classes, ARIA values, existing data attributes and internal behavior. Document each mapping. Recipes style states but never mutate ARIA or drive interaction logic. Distinguish focus-visible from focus, active press from selection, and disabled from readonly. Loading may imply disabled only when existing behavior does so. Rules for multiple simultaneous states must be explicit per component; preserve focus visibility and disabled semantics. List unsupported states rather than inventing them.

Resolution decisions

Resolve values: built-in baseline -> extended systems in declared order -> selected system -> mode/density/scope overrides -> explicit application theme overrides. Locks constrain editing/generation, not browser security. Reject extension cycles. Accent updates only its declared semantic roles. Instance recipes apply after component recipes within their scope; an explicit instance size/variant selects that recipe rather than overriding every recipe property.

Resolve recipes: base -> selected axis rules -> matching compound rules in declared order -> matching state rules in declared order -> approved instance overrides. Validate conflicting state rules and show provenance. Do not rely on object key iteration as undocumented precedence.

This semantic order does not by itself guarantee CSS precedence. Phase 1 must prove insertion order/specificity against existing extracted component CSS in development and production. Initial recommendation: preserve legacy CSS, compile opted-in recipes under a dedicated system owner scope, and emit their stylesheet after extracted component styles. Publish the exact selector strategy only after the Phase 1 cascade fixture passes. No blanket !important or global cascade-layer rewrite.

Representative contracts

Component/family Observed legacy hooks Proposed parts States and constraints
Button .wrn-action, .wrn-btn, controlClass, pill, variant/color/size classes root, control, copy, label, description, icon, spinner, tooltip Native button and link branches need the same visual contract; disabled/loading semantics remain distinct from styling; icon-only names preserved
Input .wrn-next--field, field-control classes, invalid data state root, heading, label, controlContainer, control, icon, hint, help, error Native validity, focus-visible, readonly, disabled, invalid; error IDs and output events unchanged
Select .wrn-next--select, .wrn-next__select-indicator, shared field styles Same field parts plus indicator and floatingLabel Native single/multiple rendering; do not promise styling of operating-system option popups
Card data-ui-component, data-wrn-card, layout/radius/shadow attributes, .wrn-next__card-* root, panel, media, content, header, heading, description, actions, footer Repeated panels and nested controls need ownership boundaries; existing named slots preserved
Modal .wrn-modal, layer/backdrop/panel/header/body classes, dialog role root, trigger, layer, backdrop, panel, header, heading, close, body, footer Open/closed, focus containment/restoration and dismissal remain behavior-owned; scoped context follows actual mount behavior
Navigation/menu Existing component-specific classes root, list, item, trigger, indicator, panel, label Selected/expanded/disabled; preserve keyboard behavior; contract actual components individually
Selection controls Existing switch/checkbox/radio implementations root, control, indicator, label, description, error Checked/unchecked/mixed only where implemented; native controls remain accessible
Alert/feedback Existing family-specific classes root, icon, title, description, actions, dismiss Semantic color roles, optional dismissal; do not invent loading states
Typography/layout Existing headings, containers and grids root plus actual structural parts Responsive text, long content, RTL; no interactive states unless present
Charts/flow/editors Wrapper and provider-owned canvas/content wrapper, toolbar, viewport, fallback where present CSS tokens may not reach pixels drawn by canvas/provider; adapters required and coverage reported

These are migration targets. Exact slot names, variants, parts and state mappings must be source-verified when each descriptor is implemented; this table does not announce new runtime APIs.

Property and safety boundaries

Allow typed colors, dimensions, typography, borders, elevation and supported motion. Bound dangerous geometry and document clipping/overflow constraints. Angular decoration must not clip focus or reduce hit areas; keep label/icon readable. Compose decorative and interaction transforms separately. Preserve visibility rules for inactive branches, native control semantics and reduced-motion behavior. A recipe cannot change event handlers, HTML semantics, arbitrary URLs or executable expressions.

Required fixture matrix

For each representative contract: current baseline, square compact, soft spacious, angular expressive; light/dark; default/hover/focus-visible/active plus supported states; long label and localization; narrow viewport; keyboard interaction; SSR and hydration; nested scopes; overlay context; app token overrides; local component CSS; ejected shadowing; conflicting variant/state rules. Compare identical content. Capture real screenshots separately from hash checks. Native popup/provider-owned limitations must appear in the report.

Versioning

Adding an optional part or token can be additive if existing output remains stable. Removing/renaming a part, changing a supported property's meaning or breaking state/slot mappings requires a contract-major migration. Record engine range and per-component contract requirements; schema version and system revision are independent. Generated metadata must be checked against sources before publishing. Unknown contracts fail with actionable diagnostics rather than silently dropping recipes.