Skip to content

Ship the renderer-independent Components 4 foundation - #177

Draft
woksin wants to merge 298 commits into
mainfrom
feat/react-aria-foundation
Draft

Ship the renderer-independent Components 4 foundation#177
woksin wants to merge 298 commits into
mainfrom
feat/react-aria-foundation

Conversation

@woksin

@woksin woksin commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Components 4 replaces the PrimeReact-backed Components 3 renderer contract with Components-owned React APIs, markup, declarations, semantic tokens, typed parts, and state attributes while preserving the established component catalog.

React Aria supplies selected interaction primitives internally; it is not the public prop, DOM, styling, provider, or type contract. Semantic native HTML and Components-owned implementations remain the default where they are simpler.

This is a major-version source candidate. The publication workflow is manual and deliberately fails closed; this PR does not authorize publication.

Added

  • Add Components-owned typed parts, stable data-cratis-part values, canonical state attributes, provider messages, strict public declarations, and standalone Common basic-control primitives.
  • Add a generated authoritative parts/state manifest with exact-key assertions and fail-closed source, selector, alias, dynamic-render, false-value, and generated-byte verification.
  • Add semantic Button variant/tone/shape props with deprecated compatibility shims, and normalize Components-owned change callbacks to value-first ChangeHandler<T> metadata.
  • Add bounded Components 4 migration codemods for Components 3 root namespaces, Button appearance props, and value-first change handlers, with a bundled compatibility preflight plus the independently versioned ESLint root-import ratchet.
  • Add packed-artifact gates for aggressive tree shaking, Prime-family absence, CSS layer order, export loading, API docs, public types, module graphs, optional Pixi, provider identity, package managers, Arc compatibility, renderer boundaries, and React/DOM-free kernel closures.
  • Add @cratis/components/renderer with a stable nine-slot stable-presentation/v1 façade plus a broader explicitly unstable_ fourteen-slot system, static adapter schema, profile-strict provider/scopes, deterministic fallback, self-hosted built-in presentation/atomic slots, and per-implementation overlay environments.
  • Add a typed public declaration for the renderer schema JSON export so strict external TypeScript and Conformance builds resolve the exact packed asset without treating JSON as a Rollup JavaScript entry.
  • Add the heavy opt-in @cratis/components/renderer/builtin manifest and independently versioned @cratis/components.conformance@1.0.0 package with actual-schema metadata validation, fourteen-slot behavior/parts/state/SSR/accessibility evidence, exact planted defects, and packed runtime/declaration gates.
  • Add a private, non-published plain-DOM falsification fixture implementing all nine stable presentation slots through public contracts only; it passes 100/100 bounded checks with zero skips and required no Core ABI widening.
  • Add independently versioned @cratis/components.mui@1.0.0 for MUI 9.x, Emotion React >=11.5 <12, and Emotion Styled >=11.11 <12, implementing the nine stable presentation slots with peer-only vendor dependencies, theme/SSR guidance, packed Core verification, and 100/100 zero-skip Conformance evidence.
  • Add independently versioned @cratis/components.primereact@1.0.0 for PrimeReact 11.x, implementing the same stable profile with application-owned license/provider setup, peer-only vendor packages, CRATIS-UI-1005, and 100/100 zero-skip Conformance evidence.
  • Add independently versioned @cratis/components.primereact10@1.0.0 for patched PrimeReact 10.9.9+, implementing the same stable profile with MIT/no-key provider and global-theme guidance, explicit legacy Node runtime interop, separate upstream-major isolation, packed Core verification, and 100/100 zero-skip Conformance evidence.
  • Add a private composed Storybook with metadata-discovered built-in, MUI, PrimeReact 11, and PrimeReact 10 previews; each preview has its own dependency graph and build attestation, while one manager switches renderers and preserves stable story ids.
  • Add dedicated Canvas controls, Canvas minimap, and Chat message-body autodocs plus controlled, disabled, read-only, loading, semantic-element, tooltip, paginator, progress, and toaster interaction coverage across the four-renderer matrix.
  • Add generated compatibility schema v2 at the repository root and as byte-identical Core/Codemod package contracts, including seven-package GA scope, support windows, independent trains, verified adapter peer sets, codemod preflight, and owner-gated Components 3 EOL.
  • Add an 18-case packed adapter matrix across MUI, PrimeReact 11, and PrimeReact 10; minimum/current peer boundaries; and npm, strict pnpm, and Yarn PnP.
  • Add a read-only source-candidate evidence job and generator producing seven clean-commit-bound tarballs, SHA-256/SHA-512 manifests, seven reproducible CycloneDX 1.6 SBOMs, and one evidence index without publication authority.
  • Add renderer coexistence, primitive-adaptation, custom-composition, unsupported-scope, licensing-policy, stable-profile ADR, and durable release-policy documentation.
  • Add Canvas region auto-membership and optional Arc messenger notifications from current main, adapted to the Components 4 registry and public API.
  • Add ItemAddedToRegion, ItemRemovedFromRegion, NoteTextChanged, ChatMessageAdded, and the pure itemsWithinRegion helper.
  • Add the explicit @cratis/components/Chat subpath with sidebar, topic, conversation, message-body, action, mention, and observable-query surfaces.
  • Add complete Components 3 → 4 migration, architecture, styling, capability-profile, Chat, and Canvas messaging documentation.
  • Add dedicated autodocs and state/interaction stories for every Display component, replacing overview-only coverage while retaining the aggregate gallery.
  • Add artifact-level TSDoc fidelity checks plus public class, enum, static, and constructor-property member coverage.
  • Add deterministic PivotViewer regressions for empty filters, constant/invalid numeric buckets, search, worker response correlation, and index-based selection identity.

Changed

  • Make the package root setup-only; import components from explicit @cratis/components/* subpaths.
  • Replace renderer-owned provider/theme/pass-through contracts with Components-owned configuration, tokens, parts, and semantic state.
  • Enable strict TypeScript and noImplicitAny for production declarations.
  • Keep React Aria as an exact internal dependency and isolate its prop variance behind an internal bridge.
  • Move pixi.js from a nested dependency to one optional peer used only by Canvas and PivotViewer.
  • Rebuild ChatSidebar on Components-owned public props and internal React Aria interaction primitives, with no Prime package, type, provider, DOM, or theme contract.
  • Move the generic, Pixi-free Chat Kit to Source/Chat/Kit; retain the Canvas ChatBubble compatibility barrel and share the optional Arc messenger hook through Source/Common/messaging.
  • Make tables render the loaded server page with authoritative server totals; complete-result filtering/sorting remains a server-query concern.
  • Extend the migration codemod to rewrite safe named root re-exports while refusing ambiguous wildcard/dynamic/CommonJS forms.
  • Reconcile Components 4 with Components 3.6.0 through a real two-parent merge without reintroducing the Components 3 renderer contract.
  • Route nine presentation and five atomic component facades through local Core renderer declarations while preserving zero-config DOM, native refs/forms, setup-root isolation, and one behavior owner.
  • Commit one root Yarn 4.5.3 lockfile, pin the actual React Aria/internationalized graph, and make every repository CI install immutable with lock/config/runtime-aware cache keys.
  • Replace direct scheduled package mutation with a permissions-empty, manual fail-closed gate until an owner-approved PR-producing updater exists.
  • Pin the patched serialize-javascript release after the newly frozen development graph exposed the older transitive version through Mocha.
  • Record the accepted DOM-and-React public contract, classify every exported component, and machine-enforce the repository's exact React/DOM-free computation kernel.
  • Keep Conformance on its own ABI-aligned release train and exclude it from Core lockstep version stamping.
  • Reconcile current main's Arc/Arc React/Arc Vite 22.5.0, Pixi 8.20.1, and Node declaration updates into one reproducible V4 dependency graph.
  • Add stable declaration-merged, boolean-only renderer setup attestations; runtime filters non-boolean values, nested providers inherit frozen setup, and renderer scopes forward it without carrying credentials.
  • Finalize Core, ESLint, and Codemods at 4.0.0; promote Conformance and the three renderer adapters to independently versioned 1.0.0 packages with an honest @cratis/components >=4 <5 peer boundary.
  • Replace unsupported ESLint 10 React tooling with @eslint-react/eslint-plugin, remove obsolete dependency/publish helpers, and make immutable installs warning-free.
  • Make the local Components quality gate use the committed lockfile immutably, cover every public V4 workspace, and run the private Storybook preflight explicitly.

Fixed

  • Keep built-in AutoCommandForm field providers alive under aggressive production tree shaking.
  • Handle malformed or non-JSON-representable SchemaEditor input without crashing and recover when valid input returns.
  • Keep id-less Canvas items available to minimap/fit-to-content while preventing generated React IDs from leaking into region-membership messages.
  • Keep Canvas registry metadata transitions observable and identity specs isolated.
  • Preserve provider-fed asynchronous Chat mention candidates with stale-result cancellation.
  • Give Chat topic lists semantic list and button markup and maintain readable light/dark token contrast.
  • Keep non-spatial Chat imports outside the Canvas/PivotViewer graph while retaining Canvas compatibility exports.
  • Use repository-local migration-guide links so hosted link checks do not depend on GitHub blob endpoints for files already verified in the checkout.
  • Link cross-product guidance through the canonical Cratis documentation site instead of an unauthenticated GitHub blob endpoint.
  • Treat external-link HTTP 429 responses as transient warnings while retaining failures for broken links; pin every action in the Markdown workflow and disable checkout credential persistence.
  • Stabilize Storybook browser tests by pre-bundling the Arc messaging entry before the suite starts.
  • Reduce external-link checker concurrency and exclude immutable commit-pinned self-documentation anchors from the unauthenticated external scan so GitHub HTTP 429 limits cannot make the documentation gate flaky.
  • Accept Tailwind's valid leading @layer properties; while enforcing the complete packed cascade order.
  • Make DatePicker Today obey date and date-time bounds, and preserve provider-owned accessible labels through legacy locale merging.
  • Prevent DataTable date filters from mutating caller-owned dates and preserve loaded-page row indices after filtering/sorting.
  • Reject malformed nested SchemaEditor shapes, keep nested required arrays consistent across rename/delete/type changes, and block empty names from saving.
  • Fix PivotViewer zero-match filters, constant numeric grouping, global search, concurrent worker correlation, stale async results, and consumer-ID/layout-index collisions.
  • Preserve TSDoc and @deprecated guidance in packed declarations while stripping declaration-only prose from runtime JavaScript.
  • Correct onBeforeExecute lint guidance to describe the guarded runtime fallback accurately.
  • Restrict SchemaEditor names and DataTable/Pivot property paths to explicit own data rather than reserved prototype keys or inherited values.
  • Prevent generated Storybook fonts from entering package archives and reject every undeclared packaged font asset.
  • Exercise package-graph report generation after final Rollup build so local CI covers the hosted report artifact path.
  • Type-check every Conformance fixture against prepared public Components declarations and keep private fixture metadata bound to its real source/export.
  • Correct JSX key ordering discovered by the supported ESLint 10 React rules without changing rendered behavior.
  • Reject release evidence when the requested commit differs from HEAD or tracked/staged bytes differ before or after generation.
  • Forward read-only semantics to adapted checkboxes while correctly omitting unsupported aria-readonly from individual radio options in MUI, PrimeReact 11, and PrimeReact 10.
  • Handle typed conditional JSON assets in Rollup and the packed export verifier without inventing JavaScript or CommonJS entry points.
  • Reject inherited or reserved metadata keys consistently in Conformance, Storybook adapter discovery, and Core renderer-contract verification instead of consulting object prototypes.

Removed

  • Remove all PrimeReact, PrimeIcons, PrimeUI, and Prime theme dependencies and peers from @cratis/components.
  • Remove renderer-specific styled, compatibility, and primereact-v10-palette export entries.
  • Remove root component-family namespaces and documented accidental implementation exports.
  • Remove unused unstable_RendererExtensions / unstable_RendererProps declarations rather than advertising a vendor-prop feature no component implemented.
  • Remove the obsolete source-first/continue-on-failure workspace publisher and unused deployment auto-approval workflow; publication remains a separate human-approved project.

Current-main checkpoint and lineage

  • Components 3.6.0: 5d239d9d677be04f0e5610671aed4dee245db203 (v3.6.0).
  • Real reconciliation merge: 895ee82012218f5ad66199c0f0500ced4169db0d, with Components 4 and Components 3.6.0 as its two parents.
  • Current-main package-refresh merge: c34bb947cd84371ddda3473ca339b0d22b0753f9, with review-fixed Slice G a60a531575d45ecadc33ea66a25256f561a34ffa and current main 1c135651d2fe7bd8659bc0cd19287c1206f8d207 as its two parents.
  • MUI adapter merge: fc1a40da663f45ab902bdc4eaab5f86e3c027d82, with private-Plain checkpoint b25427e85b41749681e74ac8d870ee988b043443 and reviewed MUI branch d98fca6d17f92f0ed5e3ccfb3eb9e354010bcce1 as its two parents.
  • PrimeReact 11 adapter merge: 64cee531e00456cf76d6b60742dea24995f647a0, with MUI checkpoint fc1a40da663f45ab902bdc4eaab5f86e3c027d82 and reviewed PrimeReact branch b84e0626fc8d7904512ad4d223108045670a7fa0 as its two parents.
  • Current-main discoverability merge: 042aa3ecaf2dd0c243b9923c8d3e1ace1bc25163, with PrimeReact checkpoint 64cee531e00456cf76d6b60742dea24995f647a0 and current main 7639ab1ab5d6591f34e326db605a2fcae9e8631e as its two parents.
  • PrimeReact 10 adapter merge: 3a37db3fb8f36b7ebdeaa2df8538a21eca866f1e, containing the independently reviewed PrimeReact 10 lineage and current main through 147ecbea744d48ff42ef8e5ff7e05aed0fc6b0de via real merges.
  • Dynamic renderer Storybook merge: dc6bc357593094390041394bd5ca73ec7f52c1e2, containing isolated preview implementation, full matrix fixes, composed-manager proof, and traversal hardening.
  • Release guidance merge: 3ff55d34bbd42c23ea42bc50d0974789177e8384, containing the generated compatibility contract and renderer documentation.
  • Final package compatibility commit: e661780f24dd0794945e448b9fce9fb7555637b4, setting the seven-package release versions and final Core peer ranges.
  • Current-main documentation merge: 7c4775c030663054b006c12296ab3ea30e001018, retaining current main through 54b7d18dceef5b3e3f179a071789b003de86737d.
  • Renderer documentation link fix: 341cdd36c65f1946af45dd744c7d4d2ace9c9e8e, using a repository-owned path that is locally verified before the new docs route deploys.
  • Current-main dependency merge: ed481100e0a23fa9c27f09ced6b68fcafae11c1c, with prior candidate 341cdd3 and current main b96a79a7a11d04098146b43ac4858ef34bc50664 as its two parents.
  • Stable presentation-profile merge: c029af1, followed by strict capability and package-manager hardening through 6edabc3.
  • Compatibility/release-safety merge: 6530af7, followed by codemod-window hardening 6db2fac.
  • Source-candidate SBOM evidence merge: 95a8943, warning-free ESLint 10 migration bef7498, clean-commit evidence binding 87cd6d7, and final stable-slot TSDoc b4badcb.
  • Final local source candidate: 3e2630f7dc47c40e53ace9b9e84f425b1f8ee92c on feat/react-aria-foundation, containing the documentation, architecture, migration, Storybook coverage, typed schema-export, metadata-ownership, local-gate, and packed-export review tranches through real merge commits.
  • The Components 4 branch retains its Components 3 lineage and now contains current main, including Chat and the publication-workflow history, through the real merge.
  • Existing Button, Message, ProgressSpinner, Canvas, Note, Region, Chat, and ChatBubble families shipped on the Components 3 line; Components 4 carries them forward and changes their foundation rather than presenting them as new catalog families.
  • Repository-owner provenance confirmation records that Components shipped through Einar's Cratis pull requests may remain and evolve; material originating from Ada may not be retained.

Architecture and why

Components owns the boundary

The public boundary is now:

  1. Consumers import Components-owned APIs from explicit subpaths.
  2. Components owns public props, declarations, markup semantics, labels, documented parts, state attributes, and tokens.
  3. React Aria or semantic HTML supplies low-level interaction behavior internally.
  4. Arc continues to own generated command/query transport and React contexts.
  5. Applications own their typography, spacing, elevation, motion, and product-specific token values unless a component documents otherwise.

This removes a renderer's DOM, provider configuration, theme system, types, release cadence, and licensing boundary from the Components contract. It also avoids reimplementing focus traps, keyboard collections, overlays, and internationalized calendar behavior without a specialist interaction foundation.

Long term, Components can evolve or replace internal mechanisms without forcing consumers to rewrite renderer selectors and types. Semver now tracks the API Components actually owns.

Setup-only root and explicit subpaths

import { CratisComponentsProvider } from '@cratis/components';
import { ChatSidebar } from '@cratis/components/Chat';
import { CommandDialog } from '@cratis/components/CommandDialog';
import { DataTableForQuery, Column } from '@cratis/components/DataTables';
import { Canvas, CanvasItem } from '@cratis/components/Canvas';

The root no longer traverses Arc-heavy, Pixi-heavy, or unrelated component graphs. The real packed runtime and declaration closures are verified per subpath.

React Aria remains internal

React Aria is confined to implementation and type-boundary files. No React Aria API is re-exported. Consumers style Components-owned parts and tokens, not React Aria classes or slots.

Keeping React Aria as an internal exact dependency avoids forcing every consumer to install and version an implementation detail. CratisComponentsProvider does not promise to configure an application's direct third-party UI usage.

Styling contract

import '@cratis/components/tokens';
import '@cratis/components/styles';
import '@cratis/components/theme'; // optional
  • tokens defines conservative semantic --cratis-* defaults.
  • styles contains structural component CSS and prefixed utilities without Tailwind Preflight.
  • theme supplies the maintained light/dark/forced-colors baseline and can be omitted.
  • Consumer unlayered CSS wins over the low-priority package layers.
  • The packed order is guarded as properties -> cratis-theme -> cratis-components -> cratis-utilities.

Optional Pixi and non-spatial Chat

Only Canvas and PivotViewer require pixi.js@^8.20.0, as an optional peer. A no-Pixi fixture physically removes Pixi/WebGPU packages and imports every non-spatial subpath, including Chat. The spatial fixture proves both spatial entries use the application's one real Pixi resolution.

Canvas intentionally exposes selected Pixi types where a facade would duplicate the upstream API and force casts. Chat's canonical kit is Pixi-free and does not resolve through physical Canvas/PivotViewer files; Canvas preserves compatibility exports from its own barrel.

Provider, SSR, and accessibility

One Components-owned context carries locale and owned messages. A packed SSR probe verifies that the root provider reaches independently emitted subpaths. Browser-only behavior is effect-gated; portals and hydration use stable server snapshots.

Focused accessibility specs and 2,216 Storybook browser/axe cases cover all 277 stories under built-in, MUI, PrimeReact 11, and PrimeReact 10 in light and dark. The docs do not claim universal browser or assistive-technology conformance from that bounded Chromium evidence.

Renderer direction for #191 and #192

The completed V4 direction keeps @cratis/components as the zero-configuration built-in/default package and explicit subpaths as the consumer facade. A private plain-DOM fixture falsifies built-in assumptions, and concrete MUI, PrimeReact 11+, and PrimeReact 10 packages pass the stable nine-slot stable-presentation/v1 profile and Conformance suite. Atomic slots, generic composition/scopes, lazy/preload behavior, and public discovery tooling remain explicitly unstable/post-GA.

  • Components owns semantic props/events, tokens, parts/state, Arc composites, and custom components.
  • Renderer packages own vendor mapping, provider/theme/SSR integration, and behavior they explicitly claim.
  • Each interaction has one behavior owner: Cratis presentation mode or adapter atomic mode, never two competing focus/keyboard/dismissal systems.
  • Declared profiles fail loudly when incomplete; slot fallback is deterministic and reported, never silent.
  • Full renderers remain expressible, but transparent replacement of the whole catalog is not promised.
  • PrimeReact 10 is a required separate adapter package with its own upstream-major compatibility and maintenance line.
  • Storybook must discover and switch dynamically among built-in, MUI, PrimeReact 11 and PrimeReact 10 implementations before V4 completion.
  • Source-first/CSS systems use recipes or theme bridges; commercial widget suites default to portal/theme interop unless written redistribution permission exists.

The reproducible-install, public-contract normalization, renderer ABI, built-in self-hosting, kernel-boundary, Conformance, private plain-DOM falsification, MUI, PrimeReact 11, PrimeReact 10, isolated four-renderer Storybook, compatibility, documentation, and final package-version tranches are complete locally.

Migration

The definitive guides are Source/MIGRATION.md and Documentation/Migration/3-to-4.md.

  1. Keep the application's exact current Components version while preparing.
  2. Run the bounded Components 4 codemods (^4.0.0, never latest) with --check; their bundled compatibility manifest accepts supported Components 3/4 versions and fails before scanning or writing otherwise.
  3. Review diagnostics, then apply supported root import and named re-export rewrites.
  4. Resolve default imports, whole-package namespaces, dynamic/CommonJS imports, side-effect imports, wildcard re-exports, and unknown symbols manually.
  5. Install the compatible Components 4 ESLint plugin (^4.0.0) and enable its recommended root-import ratchet; tooling patches release independently from Core.
  6. Remove Prime packages only after the application no longer imports them directly.
  7. Import tokens and styles; choose the baseline theme or map canonical application tokens directly.
  8. Migrate provider values, pt keys, state selectors, and documented behavioral differences.
  9. Install one compatible Pixi peer only for Canvas/PivotViewer.
  10. Run strict types, lint, specs, Storybook/browser checks, and the production build.

If no tooling release satisfies the published compatibility range, stop rather than substituting latest; the Codemod preflight and shipped compatibility manifest are authoritative.

Public surface and evidence

  • Exact candidate: 3e2630f7dc47c40e53ace9b9e84f425b1f8ee92c; 0 behind current main b96a79a7a11d04098146b43ac4858ef34bc50664.
  • Export map: 30 entries, including the shipped ./compat-manifest.json contract and typed ./schemas/ui-adapter.schema.json asset; 24 typed JavaScript subpaths.
  • Strict public types: all 24 packed entries pass TypeScript 6 Bundler + NodeNext under skipLibCheck: false, with bounded exact-version Arc/Fundamentals/Pixi exceptions that fail stale.
  • Every reachable public declaration/member has TSDoc; packed declarations retain guidance while runtime JavaScript excludes declaration prose.
  • Source specs: 380 files / 1,353 tests passed.
  • ESLint workspace: 1 / 120; Codemods: 6 / 103; Conformance: 4 / 20; Storybook preflight: 2 / 9; MUI: 1 / 8; PrimeReact 11: 1 / 9; PrimeReact 10: 1 / 11.
  • Release policy: 11/11; release evidence: 13/13; renderer-matrix validation: 4/4.
  • Conformance: built-in 148/148, private Plain 100/100, MUI 100/100, PrimeReact 11 100/100, and PrimeReact 10 100/100, all with zero skips.
  • Stable renderer profile: nine exact presentation slots, mandatory render/parts/static-SSR capabilities, no unsupported fidelity, no atomic slot leakage; setup attestations and overlay-environment type are stable while broader renderer mechanics remain unstable_.
  • Packed adapter boundaries: all 18 adapter/boundary/manager combinations pass locally — three adapters × minimum/current peer sets × npm/strict pnpm/Yarn PnP.
  • Storybook: four isolated previews, each 67 files / 277 stories / 67 autodocs, plus one composed manager; 4 × 277 × 2 = 2,216 Chromium interaction/axe cases pass with zero story or renderer exclusions.
  • Generated parts: 54 components / 283 parts / 402 component-part states / 45 pt files / 48 declarations.
  • Package graph: 24 subpaths / 34 exact kernel modules; Prime is absent from Core; Pixi is reachable only from Canvas/PivotViewer; 22 non-spatial subpaths import with Pixi physically absent.
  • Packed production, aggressive tree shaking, CSS layer/budget, provider identity, SSR, no-Pixi, spatial single-resolution, package archives, and all exports pass.
  • Aggregate CSS: 200,172 raw / 30,381 gzip / 1,117 blocks, under every budget.
  • Immutable Yarn 4.5.3 install completes with zero warnings. Production dependency audit reports no suggestions.
  • Compatibility schema v2 is deterministic and byte-identical at root, Core, and Codemods. Codemod preflight accepts supported Components 3/4 windows and rejects absent/unsupported packages before scanning or writing.
  • Publication and dependency-update mutation paths remain fail closed; self-falsification tests reject automatic triggers, write permissions, missing retention, publication drift, and the obsolete publish-version task.
  • Final evidence under .ai-work/release-candidate/3e2630f/: seven tarballs, seven byte-reproducible CycloneDX 1.6 SBOMs, SHA256SUMS, SHA512SUMS, and clean-commit-bound release-evidence.json with publicationEnabled: false; a second independent generation is byte-identical across every retained file.
  • Final Core archive: 537,850 bytes / 2,275,928 unpacked / 1,028 files; SHA-256 62a996bbd02041b3ec19fe35920aa4096130c677a899cd80a4462f1ae2d64016; SHA-512 92f17660e451fda9bafc0730a873628bca9689cf5072fe28bd45874d85c25d447d1812675ae3baad31be62b41b9ecc76295007790fe97322c86c4ff331fbda1f.
  • Other archive SHA-256 values: ESLint 76f16f3e8021dec8e780221598925a23cb1b0793611d2d439670ba6dbe89e4a2; Codemods 05400fd904027b6140991b7cea8a14441bd1e5e5753a84b00fd6e0ffbdf11d0b; Conformance 6e214b55c8bb64902e6a4e675e52a59ffa5f813632c222c1deed5b878f04ee9b; MUI 717d219653dc9cf2518b952843ad7ce78c831e0c2249d666e87259c28f58de91; PrimeReact 11 56a454eb3313d2405636128ebc94cab128b7d44ad1c7e6084021b6c814bbd245; PrimeReact 10 13c03e69b82e7810ce0aff7d78021f4853077a5609b6f8f9496e88677720d18e.
  • Root lockfile SHA-256: 52f718cbdaee175dceb5eccea485f8ca02271dffb2570232168acfd1d625d6bb.
  • Post-V4/release work is tracked durably by Prepare owner-authorized Components 4 publication #207Add renderer bundle and runtime performance regression budgets #218; existing Track strict TypeScript validation for Pixi and upstream declarations #176 owns upstream strict declaration exceptions.

Publication state

.github/workflows/publish.yml exists, is workflow_dispatch-only, has no automatic trigger or publication capability, prints the blocked-release prerequisites, and exits non-zero. It is intentionally fail-closed.

Publication remains a separate owner-approved project tracked by #207. Compatibility metadata stays source-candidate with publicationEnabled: false and an unset Components 3 eolAt; validation refuses publication authorization until owners approve that date. The remaining work is the real manual tooling-first publisher, five absent npm package records, trusted publishers, protected human-reviewed environment, candidate-tag registry verification, dist-tag promotion, final tag/release, and explicit owner approval. This PR publishes nothing.

Deliberate limitations

Review and merge checklist

  • Repository owner confirmed the retained Components 3 lineage and reuse boundary.
  • Current main through b96a79a (including Components 3.6.1 and Arc 22.5 updates) is woven into V4 through real two-parent merges with no lost Chat behavior.
  • No Prime dependency/import/type topology is forced by Components 4.
  • Strict TypeScript, package, API, graph, Pixi, provider, Storybook, tooling, docs, workflow, and confidentiality gates pass on the certified local checkpoint.
  • Publication remains manual, blocked, and fail-closed.
  • Hosted checks pass for exact preceding candidate 7f0497a766e932b27704995e7ea42bfc88997f78.
  • MUI passes its required V4 Conformance, package and review gates.
  • PrimeReact 11 passes its required V4 Conformance, package and review gates.
  • PrimeReact 10 passes its required local V4 Conformance, package-manager, package and review gates.
  • Hosted checks pass for exact preceding candidate 3a37db3fb8f36b7ebdeaa2df8538a21eca866f1e.
  • Dynamic Storybook renderer switching passes build, index, composed-manager, peer-isolation, and 2,216-case browser/axe gates.
  • Final seven-package versions, >=4 <5 peers, stable renderer profile, compatibility manifest, renderer/release documentation, and retained archive/SBOM evidence pass locally.
  • Hosted checks pass for exact preceding candidate e0b0975e36317eb541552030051c71307ebbf5d6.
  • Hosted checks pass for exact final candidate 3e2630f7dc47c40e53ace9b9e84f425b1f8ee92c.
  • Keep the PR draft until separate owner authorization; source readiness alone is not merge/publication approval.
  • Merge only with a real merge commit after separate explicit authorization; do not squash or rebase.

@woksin

woksin commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

@woksin @einari — this comment records why this major takes this direction, why now, what was proven, and what we are deliberately not claiming yet.

Why this is not merely a renderer swap

Components 3 placed Cratis application behavior behind PrimeReact runtime, provider configuration, public types, pass-through slots, theme variables, DOM assumptions, and licensing. PrimeUI's published terms explicitly say that an internal wrapper/design system does not remove developer-seat requirements, and SDK/framework-style redistribution is OEM territory requiring separate clarification.

That meant the commercial renderer was not actually an implementation detail. It was an invisible condition of using Arc-bound dialogs, fields, tables, pages, and notifications.

The technical cost matched the licensing cost:

  • renderer types leaked into every product provider and pt contract;
  • products translated their tokens through Prime presets/variables before Components could use them;
  • exact renderer DOM and slot changes made deep consumer customization brittle;
  • accessibility improvements depended on another product's composition/release decisions;
  • consumers with their own design system still installed and licensed a UI kit they were replacing.

We shipped Components 3 stabilization first (#169, #171, #172 → v3.5.0) so correctness and accessibility were not held hostage by this major migration.

The long-lived boundary

Components 4 now owns:

  • React props, events, markup, and product behavior;
  • semantic --cratis-* tokens;
  • typed pt parts;
  • stable data-cratis-part and state attributes;
  • accessibility composition and regression specs;
  • a maintained baseline appearance and Storybook.

React Aria remains internal for focus, overlays, collections, keyboard behavior, and internationalized dates. It is Apache-2.0 and style-free, but its types/classes/DOM are not the consumer contract. Native HTML is used when simpler.

Arc and @cratis/arc.react continue to own command/query state and generated bindings. Components owns visual policy. This avoids moving UI choices into Arc and preserves the right future seam.

Why no TanStack or compatibility package now

TanStack Table is a good headless engine, but current Components tables do not yet need enough grouping/pinning/faceting complexity to justify another abstraction. #109 should extract query/table state into Arc React only after a second renderer or real consumer proves the contract.

A Prime compatibility package is not published without written licensing/OEM clarity. Components 3 is the compatibility line while consumers migrate.

Baseline appearance and styling

The default UI is intentionally familiar to Components 2/3 users rather than looking like an unrelated redesign:

  • Lara-adjacent blue actions and neutral surfaces;
  • familiar 6px radii, control density, overlays, tables, and light/dark depth;
  • default Button remains the expected primary action.

We intentionally differ where exact Lara reproduction would retain weaker contrast or semantics. Actions/statuses, focus rings, control boundaries, disabled state, forced colors, and reduced motion are stronger.

The stylesheet contract is now real rather than aspirational:

  • tokens — semantic seam with conservative light defaults;
  • styles — prefixed internal utilities and layered structure, with no Tailwind Preflight/reset or token duplication;
  • theme — optional explicit/automatic dark/light, forced colors, and subtree behavior.

Products can override unlayered CSS without specificity wars. Mixed Prime products receive explicit Cratis z-index controls while configuring Prime independently.

Consumer evidence

Custom-token design system

A representative custom-token design system maps its canonical tokens directly to --cratis-*, including actions, controls, statuses, elevation, and focus. Strict provider typing caught a stale attempted migration that still passed Prime-era global pt/unstyled/ripple; correcting it produced a passing representative compile.

Its locale-aware Prime InputNumber remains an honest bounded Prime island until #175 supplies proven parity. We do not claim this archetype is fully migrated.

Deeply customized canvas/compositor host

Representative Dialog, CommandDialog, Stepper, Toolbar, and stable-part usage compiles. Stable parts, state attributes, and product-owned compositor markers preserve deep customization without making product vocabulary part of the Components contract.

Broad direct Prime and local canvas usage make this a staged migration, with existing obligations retained while those surfaces remain.

Components 2/PrimeReact 10 application

This archetype can move directly to Components 4 while retaining PrimeReact 10 as a bounded direct-use island. It does not need an intermediate PrimeReact 11 migration.

Components 3/PrimeReact 11 multi-entry application with an advanced table adapter

This archetype can start baseline-first with Components light/dark while retaining its separate PrimeReact 11 provider and application-owned advanced table adapter. Components does not force canvas-specific Toolbar composition onto ordinary page actions or claim that its default table implements advanced server state.

Verification

The exact candidate has:

  • 262 test files / 795 tests passing;
  • 25/25 export subpaths loading;
  • Prime-free emitted JS/declarations;
  • valid source maps and extension-complete ESM;
  • prefixed/no-Preflight CSS gates;
  • complete MIT/OFL notices and asset verification;
  • Storybook production build plus a11y addon and representative light/dark/product/mobile visual review;
  • automated axe foundation scan;
  • npm Arc 20.3.1 / 21.19.4 / 22.1.0, pnpm, and Yarn PnP exact-artifact validation;
  • representative compile evidence for the custom-token design system and deeply customized canvas/compositor host;
  • 404 KB tarball, SHA-256 1d769e97283d4f57ce70aa2a9ce85952d9e96c182d3d045026b2cfe8e0d71fe2.

What remains deliberately open

A future Vue/Svelte frontend should use Arc transport/generated-client contracts and framework-native state/components. We should extract cross-framework contracts only after another implementation proves them, not pre-abstract React details today.

Requested review focus

  1. Public API ergonomics and named behavior versus pt precedence.
  2. Keyboard/focus/SSR/hydration behavior.
  3. Baseline light/dark familiarity and accessibility.
  4. Custom-token ownership and deeply customized canvas/compositor seams.
  5. Arc/Arc React versus Components ownership.
  6. Migration honesty around direct Prime and advanced tables.
  7. Storybook usefulness as the maintained visual contract.

Please treat renderer leakage, inaccessible behavior, undocumented migration edits, misleading compatibility, or a custom-design-system dead end as blockers. This PR should remain unmerged until you are both comfortable with the major and the RC pilot plan.

Comment thread Documentation/verify-local-links.mjs Fixed
Comment thread Documentation/verify-local-links.mjs Fixed
@woksin

woksin commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Final review update: all latest checks on 3d340d6 are green, including aggregate CodeQL, JavaScript build, 25 exports, Arc 20/21/22, pnpm, Yarn PnP, semver label, Markdown, and the truthful local-link gate (101 docs files / 107 local links / 0 broken). The two earlier CodeQL threads are outdated and resolved after replacing the vulnerable regex/sanitizer with deterministic parsing and adversarial self-tests. No human review has arrived. This major remains intentionally unmerged for @woksin and @einari plus RC-pilot review.

@woksin

woksin commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Additional licensing research — independent verification of the PrimeReact 11 consumer impact

We have done additional research to be certain about what PrimeReact 11 means for Components 3 consumers, prompted by downstream reports that apps on Components 3 show a license notice. Everything below was verified against primary sources: the published @primereact/core@11.1.0 and @primeui/license-manager@1.0.0 npm artifacts (code inspected directly), PrimeReact 11's bundled LICENSE.md, and the primeui.dev license/pricing pages.

Verified findings

  1. The downstream reports are accurate. PrimeReactProvider verifies a license key in a useEffect on mount with no condition on unstyled, theme, or NODE_ENV. Without a valid key it emits a console warning and calls showInvalidLicenseBanner() — a fixed-position, bottom-right, z-index: 2147483647, closed shadow DOM red badge reading "Invalid PrimeUI License", in development and production. Every Components 3 app reaches this through CratisComponentsProvider, on every styling path — including fully unstyled and including the MIT @cratis/components/theme CSS. (This matches what Correct the licensing documentation for PrimeReact 11 #140 documented; we re-verified it against the shipped artifact rather than trusting our own docs.)
  2. A key must be obtained from a PrimeUI account in all cases. The free Community tier is eligibility-limited (org: <$1M revenue and <5 developers and <10 employees and <$3M outside funding; individuals/students/non-profits/non-commercial OSS also qualify), supports up to 4 developers, and requires annual renewal. The license-manager code enforces key expiry with a 30-day grace period for community keys, after which the banner returns. Commercial keys are per-developer, perpetual, with the key checked against the installed version's embedded releaseDate (2026-08-05 for 11.1.0).
  3. Verification is offline — an ed25519 signature check against an embedded public key, no telemetry, no remote connection. Confirmed in the code; matches LICENSE.md.
  4. The upstream primefaces/primereact repository is archived. All future PrimeReact development happens under the PrimeUI commercial model. Existing MIT versions (≤10) stay MIT.
  5. The OEM exposure is real and priced: PrimeStore lists OEM licensing as a flat-annual-fee add-on for "redistributing PrimeUI to your customers", and the license restrictions clause requires it for redistribution as a component library or development tool. This validates the PR's position of publishing no Prime compatibility package without written PrimeTek clarification.

What this means

  • Components 3.x is a transitional line, not a steady state. Every 3.x consumer either holds and annually renews a PrimeUI key (paid for any org above the small-team thresholds) or ships with a permanent red banner in production. Components 2.x (PrimeReact 10, MIT) is the only fully MIT-safe prior line.
  • This PR is the escape hatch, and the research hardens the case for it. We verified the packed Components 4 artifact is Prime-free in runtime deps, peers, emitted JS, and declarations, and that React Aria (Apache-2.0, Adobe-maintained, actively released) does not leak into the public API — so the foundation swap is real, not cosmetic.

Recommended path forward

  1. Proceed with Components 4 as designed — renderer independence is the correct long-term posture for both the product and the library; no architectural change requested.
  2. Publish an explicit Components 3.x support-horizon statement (in migration.md and the release notes): what class of fixes 3.x receives and for how long after 4.0 stable. "Temporary compatibility line" is currently undated, and the license-renewal treadmill makes parking on 3.x costlier than it looks.
  3. Docs: state the cost of staying on 3 inside the 3→4 guide. Documentation/migration.md says Components 3 "remains the compatibility line" but the banner/key/renewal facts live only in migration-from-2.md. Add a short "What staying on Components 3 means" note (key required on every styling path, banner without one, community-key annual renewal + 30-day grace) so a reader of only the 3→4 guide understands the ongoing obligation.
  4. Docs: strengthen ui-foundation.md § "Why PrimeReact is no longer the default" with the two objective facts it currently omits: runtime license enforcement on all styling paths (verified in the shipped artifact), and the archived upstream repository. Both are verifiable and make the rationale self-evident.
  5. Docs (minor, migration-from-2.md): add that keys are obtained from a PrimeUI account, note the 30-day community grace period and the releaseDate coverage check, and — a genuinely positive fact for consumers' compliance reviews — that verification is offline with no telemetry.

None of this blocks the PR; items 2–5 are documentation-scoped and could land as a follow-up commit on this branch or a fast-follow. The standing disclaimer applies: this summarizes public terms and shipped code behavior for architectural transparency; it is not legal advice.

woksin added a commit that referenced this pull request Aug 24, 2026
 pre-stable subset)

Implements the pre-stable subset of #174 for PR #177: fixes confirmed bypass
defects, wires six new provider message groups (prop -> provider -> English
fallback), and adds regression/precedence/sentinel specs. Framework/library
profile change; every existing public prop and its precedence is preserved.

Bypass fixes (Fix 1):
- FilterPanel.tsx: header clear-button "title" hardcoded "Clear filter" /
  "Clear range" next to a correctly-resolved aria-label from the same prop;
  title now mirrors the same value. A searchable filter group with no own
  searchPlaceholder now falls back to the panel-level searchPlaceholder
  prop instead of a second hardcoded "Search..." literal.
- SchemaEditor.tsx: the back-navigation Tooltip's content hardcoded
  "Navigate back" while the same button's aria-label correctly resolved
  labels.navigateBack; both now come from the same source.
- RangeHistogramFilter.tsx: the bar tooltip's unit word ("...42 items") was a
  hardcoded, unoverridable English literal; added itemsLabel (default
  "items").

Dropdown (Fix 2): "Show options" (2 sites) and "Clear selection" (4 sites)
now resolve pt override -> CratisComponentsProvider messages.dropdown ->
English fallback across all six render paths (native/filtered x
single/multiple), preserving per-instance pt overrides.

New provider message groups (Fix 3), each documented with TSDoc and wired
prop -> provider -> English fallback:
- dialog (ok/cancel/yes/no/close) - resolved once in Dialog.tsx, so
  CommandDialog and StepperCommandDialog (which forward these props straight
  through) share the same precedence without duplicating it.
- stepper (next/previous/submit) - CommandStepperContent (shared by
  CommandStepper and StepperCommandDialog) and StepperCommandDialog's own
  Cancel button (which reuses dialog.cancel).
- notifications (dismiss/region) - Toaster.
- dataTable (selectRow/search/searchAriaLabel) - DataTableCore.
- columnFilter (filterTriggerAriaLabel/valueAriaLabel/matchModeAriaLabel/
  matchModeLabel/clear/apply/true/false) - ColumnFilterMenu, added as a
  provider tier between its existing labels prop and English defaults.
- datePicker.label - the orphaned "Date" aria-label fallback in
  DatePickerInput used when neither an explicit aria-label nor a placeholder
  is given.

Docs (Fix 4): English and nb-NO CratisComponentsProvider examples for every
group in Documentation/Common/cratis-components-provider.md, plus precedence
notes in Dialog/Dropdown/Notifications/CommandStepper/StepperCommandDialog/
DataTables docs. No React Aria locale data copied into messages.

Specs (Fix 5): regression specs for every bypass fix; a dedicated
precedence spec per new provider group (English fallback -> provider
message -> prop override, in that order); and a sentinel-provider spec
(Source/Common/for_CratisComponentsProvider/when_every_owned_label_is_overridden.tsx)
that renders every audited surface under one provider with distinct
sentinel messages and asserts none render their English default.

No new ESLint rule: documented in ESLint/README.md why a hardcoded-label
rule would need semantic judgment an AST visitor can't make reliably,
producing either high noise or an allowlist that stops catching new
bypasses; the sentinel-provider spec is the reliable gate instead.

Explicitly out of scope, unless a bypass surfaces there later: PivotViewer,
ObjectContentEditor, SchemaEditor's own generated cell/header copy,
TimeMachine's relative/plural text, and identifier-derived
formatTitle/formatPropertyName. #174 is not fully closed by this change.

Verification: tsc -b (Source/tsconfig.json) clean; eslint clean;
vitest run 275 files / 846 tests passed.
woksin added 2 commits August 25, 2026 12:41
- Add a canonical import rule (root is setup-only; components ship
  from explicit subpaths) with a complete root-namespace-to-subpath
  migration mapping, both namespace-preserving and named forms, and
  a placeholder codemod invocation that is explicitly flagged as not
  yet shipped. No behavior changes: the root namespace re-exports
  remain exactly as they are today.
- Define three equal-support capability profiles (Foundation,
  Advanced React, Spatial) and state explicitly that grouping is a
  documentation/adoption aid, not a weaker semver or stability tier.
- Add a capability matrix covering state ownership, data/persistence,
  Arc/Chronicle relationship, SSR characteristics, extra peers, and
  performance shape per profile, grounded in the current source
  (SSR guards, effect-scoped Pixi/worker setup, transport-neutral
  props).
- Formalize the optional Pixi peer and single-peer-resolution rule,
  and document Canvas's DOM+Pixi dual-layer architecture.
- Document the aggregate CSS manifest as the current styling source
  of truth, plus objective criteria for a future stylesheet or
  package split.
- Add PivotViewer's worker/fallback architecture and searchFields
  contract, TimeMachine's transport-neutral data contract, and a
  grounded JSON Schema supported-subset statement shared by
  SchemaEditor and ObjectContentEditor.
- Clarify that Toolbar is not a default page action row.
- Markdown lint and local link checks pass.
woksin added 20 commits August 28, 2026 21:58
Produce pack-once archives, deterministic checksums, archive-bound CycloneDX SBOMs, and a fail-closed hosted evidence artifact without adding publication authority.
Clarify the stable renderer boundary, setup-only imports, provider ownership, and post-V4 issue scope while removing stale version and evidence-count claims. Complete documentation navigation and keep local Markdown verification aligned with CI.
Account for the complete Components 3.6 root surface, preserve Button truthiness, and harden packed CLI preflight verification. Clarify the source-to-target order, rollback points, package-manager commands, and renderer adapter boundaries so consumers can migrate without guessing.
Add dedicated docs and interactions for previously implicit visual components and controlled/stateful primitives. Correct checkbox read-only ARIA forwarding across each stable presentation adapter so the expanded matrix verifies the documented contract.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants