Skip to content

Primitive adaptation profile

The stable-presentation/v1 profile is deliberately small. It lets an adapter render common controls with vendor-native primitives while Components retains its public props, semantic values, refs, parts, and composition contracts. Stable means this exact nine-slot adaptation boundary; it never means that an adapter replaces the full Components catalog.

Slot IDComponents contractStable presentation responsibility
common.buttonButtonPropsNative button semantics and presentation
common.iconButtonIconButtonPropsAccessible icon-only button
common.textInputTextInputPropsText-like native input
common.textAreaTextAreaPropsMultiline native input
common.checkboxCheckboxPropsBoolean checkbox
common.radioRadioPropsOne radio option
common.switchSwitchPropsBoolean switch
common.progressProgressBarPropsDeterminate or indeterminate progress
common.surfaceSurfacePropsNon-interactive semantic container

This profile does not include tooltip, dropdown, dialog, date picker, or table paginator. Those are atomic interaction slots with a separate behavior boundary. It also does not include a composite such as DataPage or CommandDialog.

Adapter packages import the bounded contract from @cratis/components/renderer:

import {
CRATIS_PRESENTATION_ABI_VERSION,
CRATIS_PRESENTATION_PROFILE,
cratisPresentationSlotIds,
definePresentationUiLibrary,
type CratisPresentationUiLibrary,
} from '@cratis/components/renderer';

The stable renderer exports are deliberately bounded:

ExportUse
CRATIS_PRESENTATION_PROFILEExact profile id: stable-presentation/v1.
CRATIS_PRESENTATION_ABI_VERSIONRenderer ABI major required by the profile.
cratisPresentationSlotIds / CratisPresentationSlotIdCanonical immutable slot order and its identifier type.
CratisPresentationSlotsMapping from each slot id to the exact public Components props/ref contract.
CratisPresentationSlotDeclaration / CratisPresentationSlotMapOne presentation-owned implementation and the complete nine-slot table.
CratisPresentationCapabilityId / CratisPresentationCapabilitiesBounded capability vocabulary and required tuple shape.
CratisPresentationUiLibraryImmutable stable manifest shape.
definePresentationUiLibrary()Runtime validation plus a defensive frozen copy for JavaScript and TypeScript callers.
CratisRendererSetupExtensions / CratisRendererSetupDeclaration-merged, non-secret boolean setup attestations.
CratisPresentationUiLibraryProviderPropssetup and children passed to an adapter provider.
CratisOverlayEnvironmentIndependent, on-demand portal-container lookup supplied by the host.

Symbols on @cratis/components/renderer whose names start with unstable_ are not promoted by this profile. Do not use the generic manifest, composition, renderer scopes/islands, atomic slots, internal hooks, lazy loading, or discovery tooling as a stable dependency.

CratisPresentationSlotMap requires all nine exact component contracts. Every declaration must use mode: 'presentation' and either fidelity: 'native' or fidelity: 'emulated'. A stable manifest must declare slot.render, parts.passthrough, and ssr.staticRender; RTL, forced-colors, and reduced-motion capabilities remain optional evidence. definePresentationUiLibrary() checks the same requirements for JavaScript callers, rejects duplicate capabilities, and returns a defensive frozen copy. Use CratisRendererSetupExtensions only for non-secret boolean setup attestations and CratisOverlayEnvironment for an independent host portal-container lookup.

The six stable capability ids have narrow meanings:

CapabilityRequirementMeaning
slot.renderrequiredEvery declared slot supplies a render component for its exact contract.
parts.passthroughrequiredDocumented typed parts and pt destinations remain available.
ssr.staticRenderrequiredEvery slot produces deterministic static server markup without browser globals.
rtloptional evidenceThe adapter has bounded evidence for right-to-left input.
forcedColorsoptional evidenceThe adapter has bounded evidence under forced-color host input.
motion.reducedoptional evidenceThe adapter has bounded evidence under reduced-motion host input.

Optional evidence flags remain bounded claims; they are not universal browser or accessibility certification.

Removing or changing a v1 slot is a breaking change. Adding a required slot needs a new profile and version; stable-presentation/v1 will not silently grow. The open fourteen-slot manifest, composition, atomic behavior, renderer scopes, internal hooks, diagnostics, and built-in full manifest remain unstable_ APIs.

Eight profile controls expose a typed *Parts object and pt prop that sends ordinary React HTML attributes to documented destinations. ProgressBar instead exposes its documented root, indicator, and label through stable data-cratis-part markers and a root className; it has no pt prop. Conforming adapters preserve each slot’s exact public customization contract rather than inventing a uniform prop that the component does not declare.

import { Button } from '@cratis/components/Common';
export const SaveAction = () => (
<Button
label='Save'
pt={{
root: { className: 'account-save' },
label: { className: 'account-save-label' },
}}
/>
);

The adapter may add wrappers or arrange documented parts differently. Descendant order, sibling position, and vendor-generated classes are not portable. Target the named part itself rather than an undocumented path through the vendor DOM.

Components contracts expose state through public props, native attributes and pseudo-classes, and documented data-* attributes. Examples include disabled, invalid, checked, loading, and Button appearance values. The exact state set belongs to each component contract; an adapter must not invent a vendor event or class as the only observable state.

Use typed props and stable part/state selectors. Do not depend on a vendor’s generated class names, private data attributes, or internal state objects when the code must work across adapters.

Adaptation does not weaken the public type. A text input still emits the next string through the Components ChangeHandler<string> shape. Ref-capable controls still identify the documented native element. Button type, form participation, disabled/read-only behavior, names, values, and validation remain part of the bounded profile.

A vendor-native wrapper is conforming only when it accepts the exact Components props and preserves those semantics. A type cast that hides an incompatible vendor callback is not an adapter.

Every slot declaration chooses one ownership mode:

  • presentation preserves the Components behavior contract while the adapter supplies the presentation implementation;
  • atomic gives one adapter implementation the complete interaction.

Do not render a second interactive semantic root inside the first, forward one user action to two independent state machines, or stack a vendor focus owner around a built-in focus owner. One user interaction must produce one semantic value change and have one keyboard/focus owner.

All three concrete adapters currently declare only the nine presentation slots. Their conformance reports cover those declared controls, not the five atomic slots or any composite workflow.

With the default rendererFallback='core', a partial adapter uses the built-in implementation for an undeclared slot. The zero-configuration built-in path is the default, not an adapter warning. When an active adapter falls back, Components reports the bounded fallback diagnostic after mount.

Set rendererFallback='throw' when a host must reject every undeclared slot:

import { CratisComponentsProvider } from '@cratis/components';
import { muiUiLibrary } from '@cratis/components.mui';
import type { PropsWithChildren } from 'react';
export const StrictRendererBoundary = ({ children }: PropsWithChildren) => (
<CratisComponentsProvider library={muiUiLibrary} rendererFallback='throw'>
{children}
</CratisComponentsProvider>
);

This strict boundary rejects built-in fallback; it does not make the adapter implement additional slots. A screen that renders an undeclared atomic control fails instead of becoming vendor-native.

Renderer ABI major 1 and @cratis/components.conformance provide machine-checked evidence for the exact manifest and environment under test. Passing checks do not certify every browser, visual state, assistive technology, vendor theme, security posture, or production application. Read the adapter’s packaged CONFORMANCE.md and peer metadata for its exact evidence.

Read custom composition before adapting a workflow that exceeds these nine slots, and keep the unsupported claims visible in product decisions.