Ship the renderer-independent Components 4 foundation - #177
Conversation
|
@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 swapComponents 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:
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 boundaryComponents 4 now owns:
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 Why no TanStack or compatibility package nowTanStack 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 stylingThe default UI is intentionally familiar to Components 2/3 users rather than looking like an unrelated redesign:
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:
Products can override unlayered CSS without specificity wars. Mixed Prime products receive explicit Cratis z-index controls while configuring Prime independently. Consumer evidenceCustom-token design systemA representative custom-token design system maps its canonical tokens directly to 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 hostRepresentative 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 applicationThis 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 adapterThis 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. VerificationThe exact candidate has:
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
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. |
|
Final review update: all latest checks on |
Additional licensing research — independent verification of the PrimeReact 11 consumer impactWe 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 Verified findings
What this means
Recommended path forward
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. |
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.
- 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.
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.
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
data-cratis-partvalues, canonical state attributes, provider messages, strict public declarations, and standalone Common basic-control primitives.variant/tone/shapeprops with deprecated compatibility shims, and normalize Components-owned change callbacks to value-firstChangeHandler<T>metadata.@cratis/components/rendererwith a stable nine-slotstable-presentation/v1façade plus a broader explicitlyunstable_fourteen-slot system, static adapter schema, profile-strict provider/scopes, deterministic fallback, self-hosted built-in presentation/atomic slots, and per-implementation overlay environments.@cratis/components/renderer/builtinmanifest and independently versioned@cratis/components.conformance@1.0.0package with actual-schema metadata validation, fourteen-slot behavior/parts/state/SSR/accessibility evidence, exact planted defects, and packed runtime/declaration gates.@cratis/components.mui@1.0.0for 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.@cratis/components.primereact@1.0.0for 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.@cratis/components.primereact10@1.0.0for 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.main, adapted to the Components 4 registry and public API.ItemAddedToRegion,ItemRemovedFromRegion,NoteTextChanged,ChatMessageAdded, and the pureitemsWithinRegionhelper.@cratis/components/Chatsubpath with sidebar, topic, conversation, message-body, action, mention, and observable-query surfaces.Changed
@cratis/components/*subpaths.noImplicitAnyfor production declarations.pixi.jsfrom a nested dependency to one optional peer used only byCanvasandPivotViewer.ChatSidebaron Components-owned public props and internal React Aria interaction primitives, with no Prime package, type, provider, DOM, or theme contract.Source/Chat/Kit; retain the CanvasChatBubblecompatibility barrel and share the optional Arc messenger hook throughSource/Common/messaging.serialize-javascriptrelease after the newly frozen development graph exposed the older transitive version through Mocha.@cratis/components >=4 <5peer boundary.@eslint-react/eslint-plugin, remove obsolete dependency/publish helpers, and make immutable installs warning-free.Fixed
@layer properties;while enforcing the complete packed cascade order.@deprecatedguidance in packed declarations while stripping declaration-only prose from runtime JavaScript.onBeforeExecutelint guidance to describe the guarded runtime fallback accurately.aria-readonlyfrom individual radio options in MUI, PrimeReact 11, and PrimeReact 10.Removed
@cratis/components.styled,compatibility, andprimereact-v10-paletteexport entries.unstable_RendererExtensions/unstable_RendererPropsdeclarations rather than advertising a vendor-prop feature no component implemented.Current-main checkpoint and lineage
5d239d9d677be04f0e5610671aed4dee245db203(v3.6.0).895ee82012218f5ad66199c0f0500ced4169db0d, with Components 4 and Components 3.6.0 as its two parents.c34bb947cd84371ddda3473ca339b0d22b0753f9, with review-fixed Slice Ga60a531575d45ecadc33ea66a25256f561a34ffaand current main1c135651d2fe7bd8659bc0cd19287c1206f8d207as its two parents.fc1a40da663f45ab902bdc4eaab5f86e3c027d82, with private-Plain checkpointb25427e85b41749681e74ac8d870ee988b043443and reviewed MUI branchd98fca6d17f92f0ed5e3ccfb3eb9e354010bcce1as its two parents.64cee531e00456cf76d6b60742dea24995f647a0, with MUI checkpointfc1a40da663f45ab902bdc4eaab5f86e3c027d82and reviewed PrimeReact branchb84e0626fc8d7904512ad4d223108045670a7fa0as its two parents.042aa3ecaf2dd0c243b9923c8d3e1ace1bc25163, with PrimeReact checkpoint64cee531e00456cf76d6b60742dea24995f647a0and current main7639ab1ab5d6591f34e326db605a2fcae9e8631eas its two parents.3a37db3fb8f36b7ebdeaa2df8538a21eca866f1e, containing the independently reviewed PrimeReact 10 lineage and current main through147ecbea744d48ff42ef8e5ff7e05aed0fc6b0devia real merges.dc6bc357593094390041394bd5ca73ec7f52c1e2, containing isolated preview implementation, full matrix fixes, composed-manager proof, and traversal hardening.3ff55d34bbd42c23ea42bc50d0974789177e8384, containing the generated compatibility contract and renderer documentation.e661780f24dd0794945e448b9fce9fb7555637b4, setting the seven-package release versions and final Core peer ranges.7c4775c030663054b006c12296ab3ea30e001018, retaining current main through54b7d18dceef5b3e3f179a071789b003de86737d.341cdd36c65f1946af45dd744c7d4d2ace9c9e8e, using a repository-owned path that is locally verified before the new docs route deploys.ed481100e0a23fa9c27f09ced6b68fcafae11c1c, with prior candidate341cdd3and current mainb96a79a7a11d04098146b43ac4858ef34bc50664as its two parents.c029af1, followed by strict capability and package-manager hardening through6edabc3.6530af7, followed by codemod-window hardening6db2fac.95a8943, warning-free ESLint 10 migrationbef7498, clean-commit evidence binding87cd6d7, and final stable-slot TSDocb4badcb.3e2630f7dc47c40e53ace9b9e84f425b1f8ee92confeat/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.main, including Chat and the publication-workflow history, through the real merge.Architecture and why
Components owns the boundary
The public boundary is now:
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
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.
CratisComponentsProviderdoes not promise to configure an application's direct third-party UI usage.Styling contract
tokensdefines conservative semantic--cratis-*defaults.stylescontains structural component CSS and prefixed utilities without Tailwind Preflight.themesupplies the maintained light/dark/forced-colors baseline and can be omitted.properties -> cratis-theme -> cratis-components -> cratis-utilities.Optional Pixi and non-spatial Chat
Only
CanvasandPivotViewerrequirepixi.js@^8.20.0, as an optional peer. A no-Pixi fixture physically removes Pixi/WebGPU packages and imports every non-spatial subpath, includingChat. 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/componentsas 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-slotstable-presentation/v1profile and Conformance suite. Atomic slots, generic composition/scopes, lazy/preload behavior, and public discovery tooling remain explicitly unstable/post-GA.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.mdandDocumentation/Migration/3-to-4.md.^4.0.0, neverlatest) with--check; their bundled compatibility manifest accepts supported Components 3/4 versions and fails before scanning or writing otherwise.^4.0.0) and enable its recommended root-import ratchet; tooling patches release independently from Core.tokensandstyles; choose the baselinethemeor map canonical application tokens directly.ptkeys, state selectors, and documented behavioral differences.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
3e2630f7dc47c40e53ace9b9e84f425b1f8ee92c; 0 behind current mainb96a79a7a11d04098146b43ac4858ef34bc50664../compat-manifest.jsoncontract and typed./schemas/ui-adapter.schema.jsonasset; 24 typed JavaScript subpaths.skipLibCheck: false, with bounded exact-version Arc/Fundamentals/Pixi exceptions that fail stale.unstable_.ptfiles / 48 declarations.publish-versiontask..ai-work/release-candidate/3e2630f/: seven tarballs, seven byte-reproducible CycloneDX 1.6 SBOMs,SHA256SUMS,SHA512SUMS, and clean-commit-boundrelease-evidence.jsonwithpublicationEnabled: false; a second independent generation is byte-identical across every retained file.62a996bbd02041b3ec19fe35920aa4096130c677a899cd80a4462f1ae2d64016; SHA-51292f17660e451fda9bafc0730a873628bca9689cf5072fe28bd45874d85c25d447d1812675ae3baad31be62b41b9ecc76295007790fe97322c86c4ff331fbda1f.76f16f3e8021dec8e780221598925a23cb1b0793611d2d439670ba6dbe89e4a2; Codemods05400fd904027b6140991b7cea8a14441bd1e5e5753a84b00fd6e0ffbdf11d0b; Conformance6e214b55c8bb64902e6a4e675e52a59ffa5f813632c222c1deed5b878f04ee9b; MUI717d219653dc9cf2518b952843ad7ce78c831e0c2249d666e87259c28f58de91; PrimeReact 1156a454eb3313d2405636128ebc94cab128b7d44ad1c7e6084021b6c814bbd245; PrimeReact 1013c03e69b82e7810ce0aff7d78021f4853077a5609b6f8f9496e88677720d18e.52f718cbdaee175dceb5eccea485f8ca02271dffb2570232168acfd1d625d6bb.Publication state
.github/workflows/publish.ymlexists, isworkflow_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-candidatewithpublicationEnabled: falseand an unset Components 3eolAt; 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
@cratis/components >=4 <5range and independently versioned 1.0.0 release trains.Review and merge checklist
mainthroughb96a79a(including Components 3.6.1 and Arc 22.5 updates) is woven into V4 through real two-parent merges with no lost Chat behavior.7f0497a766e932b27704995e7ea42bfc88997f78.3a37db3fb8f36b7ebdeaa2df8538a21eca866f1e.>=4 <5peers, stable renderer profile, compatibility manifest, renderer/release documentation, and retained archive/SBOM evidence pass locally.e0b0975e36317eb541552030051c71307ebbf5d6.3e2630f7dc47c40e53ace9b9e84f425b1f8ee92c.