diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index c04cf1d9..2c7a1da0 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -12,6 +12,7 @@ on:
push:
branches: ["main"]
paths:
+ - ".github/workflows/documentation.yml"
- "Documentation/**"
permissions:
diff --git a/.github/workflows/markdown-verification.yml b/.github/workflows/markdown-verification.yml
index 91d755e2..c0d947d7 100644
--- a/.github/workflows/markdown-verification.yml
+++ b/.github/workflows/markdown-verification.yml
@@ -6,6 +6,8 @@ on:
branches:
- main
paths:
+ - '.github/workflows/markdown-verification.yml'
+ - '.markdownlint.json'
- 'Documentation/**'
- 'README.md'
- 'release.md'
@@ -20,6 +22,8 @@ on:
branches:
- '**'
paths:
+ - '.github/workflows/markdown-verification.yml'
+ - '.markdownlint.json'
- 'Documentation/**'
- 'README.md'
- 'release.md'
@@ -39,32 +43,7 @@ permissions:
contents: read
jobs:
- markdown-lint:
- runs-on: ubuntu-latest
- timeout-minutes: 15
- steps:
- - name: Checkout code
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- with:
- persist-credentials: false
-
- - name: Lint Markdown files
- uses: DavidAnson/markdownlint-cli2-action@eb5ca3ab411449c66620fe7f1b3c9e10547144b0 # v18
- with:
- globs: |
- README.md
- release.md
- Documentation/**/*.md
- Documentation/**/*.mdx
- Source/*.md
- Migrator/*.md
- ESLint/*.md
- Conformance/*.md
- Adapters/**/*.md
- Storybook/*.md
- scripts/*.md
-
- link-verification:
+ markdown-verification:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
@@ -78,11 +57,10 @@ jobs:
with:
node-version: 23.x
- - name: Check repository-local Markdown and MDX links
- run: |
- node Documentation/verify-local-links.mjs Documentation
- node Documentation/verify-local-links.mjs Adapters
- node Documentation/verify-local-links.mjs scripts
+ # Run the same entry point contributors use locally so linting,
+ # authoring validation, and local-link checks cannot drift apart.
+ - name: Check Markdown and MDX authoring
+ run: ./Documentation/verify-markdown.sh
- name: Check external links in consumer documentation
uses: JustinBeckwith/linkinator-action@7b6b0bc671f6264e1a8daa4488a5bd91ce61dcd4 # v2.4.2
diff --git a/.markdownlint.json b/.markdownlint.json
index 1114b19f..249f4bed 100644
--- a/.markdownlint.json
+++ b/.markdownlint.json
@@ -4,16 +4,28 @@
"MD013": false,
"MD033": {
"allowed_elements": [
- "antml:function_calls",
- "antml:invoke",
- "antml:parameter",
"a",
- "Steps",
"Aside",
+ "Badge",
+ "Card",
"CardGrid",
+ "Code",
+ "FileTree",
+ "Fragment",
+ "FullStackTabs",
+ "Icon",
+ "LinkButton",
+ "LinkCard",
+ "OsAwareTabs",
+ "Recap",
"SimpleCard",
- "Card",
- "StorybookEmbed"
+ "StackDiagram",
+ "Steps",
+ "StorybookEmbed",
+ "TabItem",
+ "Tabs",
+ "TopicHero",
+ "YouWillLearn"
]
},
"MD041": false,
diff --git a/Documentation/Chat/index.md b/Documentation/Chat/index.md
index fafcf425..f33f682e 100644
--- a/Documentation/Chat/index.md
+++ b/Documentation/Chat/index.md
@@ -1,4 +1,7 @@
-# Chat
+---
+title: Chat
+description: Build topic-based chat with host-owned data, mentions, emoji, and message actions.
+---
The `Chat` components give an application a topic-based chat that opens in a sidebar next to the view — a topics list, the conversation for the picked topic, `@`-mentions of people and agents, emoji, and per-message actions — without the library holding any opinion about where the data lives or what a backend looks like.
diff --git a/Documentation/CommandDialog/index.md b/Documentation/CommandDialog/index.md
index 0c98ff43..ddf4b0a6 100644
--- a/Documentation/CommandDialog/index.md
+++ b/Documentation/CommandDialog/index.md
@@ -1,4 +1,7 @@
-# CommandDialog
+---
+title: CommandDialog
+description: Execute Arc commands in a dialog with automatic form state, validation, and result handling.
+---
The `CommandDialog` component provides a dialog interface for executing commands with built-in form handling and validation.
diff --git a/Documentation/CommandForm/checkbox-field.md b/Documentation/CommandForm/checkbox-field.md
index 610de7b9..1f6cea63 100644
--- a/Documentation/CommandForm/checkbox-field.md
+++ b/Documentation/CommandForm/checkbox-field.md
@@ -25,4 +25,3 @@ import { CheckboxField } from '@cratis/components/CommandForm';
- Default value is `false`.
- Validation state is reflected through `aria-invalid` and `data-invalid`.
-
diff --git a/Documentation/CommandForm/index.md b/Documentation/CommandForm/index.md
index 48f1fc6e..1748ee6f 100644
--- a/Documentation/CommandForm/index.md
+++ b/Documentation/CommandForm/index.md
@@ -1,4 +1,7 @@
-# CommandForm
+---
+title: CommandForm
+description: Build type-safe Arc command forms with reusable fields, validation, and automatic value binding.
+---
The `CommandForm` component provides form field components for building command input forms with automatic type handling and validation.
diff --git a/Documentation/CommandForm/input-text-field.md b/Documentation/CommandForm/input-text-field.md
index e9c0a4ae..f003f544 100644
--- a/Documentation/CommandForm/input-text-field.md
+++ b/Documentation/CommandForm/input-text-field.md
@@ -28,4 +28,3 @@ import { InputTextField } from '@cratis/components/CommandForm';
- Default value is an empty string.
- The field spans full width within its container.
- Validation state is reflected through `aria-invalid` and `data-invalid`.
-
diff --git a/Documentation/CommandForm/number-field.md b/Documentation/CommandForm/number-field.md
index c7e9c577..ba9db363 100644
--- a/Documentation/CommandForm/number-field.md
+++ b/Documentation/CommandForm/number-field.md
@@ -30,4 +30,3 @@ import { NumberField } from '@cratis/components/CommandForm';
- The field spans full width within its container.
- Validation state is reflected through `aria-invalid` and `data-invalid`.
- When the value is cleared, it falls back to `0`.
-
diff --git a/Documentation/CommandForm/slider-field.md b/Documentation/CommandForm/slider-field.md
index 1bb6585e..6ed615c7 100644
--- a/Documentation/CommandForm/slider-field.md
+++ b/Documentation/CommandForm/slider-field.md
@@ -28,4 +28,3 @@ import { SliderField } from '@cratis/components/CommandForm';
- Default value is `0`.
- The slider spans full width within its container.
- The selected numeric value is rendered centered beneath the slider track.
-
diff --git a/Documentation/CommandStepper/index.md b/Documentation/CommandStepper/index.md
index 45717a74..deb569ac 100644
--- a/Documentation/CommandStepper/index.md
+++ b/Documentation/CommandStepper/index.md
@@ -1,18 +1,15 @@
-# CommandStepper
+---
+title: CommandStepper
+description: Split an Arc command form into validation-aware steps for wizard-style workflows.
+---
-The `CommandStepper` component is a command-scoped stepper foundation for wizard-style flows.
+The `CommandStepper` component executes one Arc command through an inline, multi-step form.
## Purpose
-`CommandStepper` establishes a `CommandForm` context and focuses on step rendering and validation-driven navigation.
+Use `CommandStepper` when the wizard belongs directly in a page region, panel, or route. It establishes a `CommandForm`, renders `StepperPanel` steps with built-in navigation, and executes the command from the final step.
-Use it when you want to:
-
-- Render `StepperPanel` steps with built-in previous and next navigation
-- Color step number circles based on validation state
-- Keep form validation and step transitions scoped to a single command
-
-`StepperCommandDialog` is built on top of `CommandStepper` and adds command execution, submission flow, and dialog behavior.
+`CommandStepper` and [`StepperCommandDialog`](../StepperCommandDialog/index.md) are sibling public components that share the private `CommandStepperContent` rendering primitive. Both execute the command. Choose `StepperCommandDialog` when the wizard should be modal; the dialog also owns cancel, busy state, and authorization routing.
## Basic Usage
@@ -52,12 +49,16 @@ export const ProjectWizard = () => {
- `showSubmit`: Show the built-in submit action on the last step (default: `true`)
- `okLabel`: Submit button label. Falls back to the provider's `messages.stepper.submit`, then `'Submit'`
- `isBusy`: Disables the navigation controls while something is running
-- `onSubmit`: Submit callback invoked on the last step
-- Any `CommandForm` props, including `initialValues`, `currentValues`, `validateOnInit`, and validation callbacks
+- Other applicable `CommandForm` props, including `initialValues`, `currentValues`, `validateOnInit`, field-validation callbacks, and inherited command execution callbacks:
+ - `onSuccess`: Callback invoked with the typed response after successful command execution
+ - `onValidationFailure`: Callback invoked with validation results when command execution returns validation errors
+ - `onFailed`: Callback invoked with the full command result for an unsuccessful, non-validation result
- `onBeforeExecute`: Transform command values before execution — it must **return** the values to run with, and it runs only on submit, so it can never satisfy required-field validation (seed those through `initialValues`)
- `linear` (default `true`), `orientation` (`'horizontal'` default / `'vertical'`), `headerPosition` (`'top'` default / `'bottom'`), `start`, `end`, `onChangeStep`, and `pt`: the active `StepperCustomizationProps` surface. It maps onto stable `root`, `list`, `step`, `header`, `number`, `title`, `separator`, `panels`, and `panel` parts.
- `ptOptions` and `unstyled`: retained temporarily for source compatibility; ignored because Cratis part attributes always merge and styling is CSS-owned.
+Because `CommandStepper` has no outer dialog, it has no `dialogPt` or `dialogUnstyled` props; `pt` targets the stepper directly.
+
Conditional steps written as `{condition && }` are counted correctly — only the panels that actually render are counted, so navigation and the per-step validation state stay in step with what is on screen. A `<>…>` fragment wrapping several panels still counts as **one** step.
## Validation Indicators
@@ -72,4 +73,4 @@ The step number circles are then styled based on state:
## See Also
-- [StepperCommandDialog](../StepperCommandDialog/index.md)
+- [StepperCommandDialog](../StepperCommandDialog/index.md) — place the same kind of command wizard in a modal dialog
diff --git a/Documentation/Common/action-menubar.md b/Documentation/Common/action-menubar.md
new file mode 100644
index 00000000..aed096f7
--- /dev/null
+++ b/Documentation/Common/action-menubar.md
@@ -0,0 +1,59 @@
+---
+title: ActionMenubar
+description: Render a horizontal toolbar of page-level command actions.
+---
+
+
+
+
+`ActionMenubar` renders a flat set of command actions from `@cratis/components/Common`. Use it for page-level actions; use [`Toolbar`](../Toolbar/index.md) for a canvas-style tool palette with active tools, groups, folders, or fan-out panels.
+
+## Basic usage
+
+```tsx
+import { ActionMenubar } from '@cratis/components/Common';
+
+;
+```
+
+The root is a `div` with `role='toolbar'` and `data-cratis-part='root'`. Pass `aria-label` to set the toolbar's accessible name.
+
+## `ActionMenuItem`
+
+| Field | Type | Purpose |
+| ----------- | ------------------------------------------- | -------------------------------------------- |
+| `label` | `string` | Visible button label. |
+| `icon` | `ReactNode` | Content rendered before the label. |
+| `command` | `() => void` | Invoked when the action is activated. |
+| `disabled` | `boolean` | Disables the action button. |
+| `className` | `string` | Extra class name for the action button. |
+| `severity` | `ButtonSeverity` | Maps the action severity to a button tone. |
+| `template` | `(item: ActionMenuItem) => ReactNode` | Fully replaces rendering for this menu item. |
+
+When `template` is present, `ActionMenubar` renders its result directly instead of rendering a Components `Button`. The item's `severity`, `disabled`, and `className`, the shared `pt`, and the normal button label, icon, and command wiring therefore do not apply unless the template implements them.
+
+## `ActionMenubarProps`
+
+| Prop | Type | Required | Purpose |
+| ------------ | ------------------ | -------- | ---------------------------------------------------------- |
+| `model` | `ActionMenuItem[]` | Yes | Actions rendered from left to right. |
+| `className` | `string` | No | Extra class name for the toolbar root. |
+| `aria-label` | `string` | No | Accessible name for the toolbar. |
+| `pt` | `ButtonParts` | No | Part attributes applied to every non-template action button. |
+| `ptOptions` | `object` | No | Deprecated, retained for source compatibility, and ignored. |
+| `unstyled` | `boolean` | No | Deprecated, retained for source compatibility, and ignored. |
+
+`pt` is the [`ButtonParts`](basic-controls.md) surface, not a toolbar-root parts object. It is passed to each `Button` created from the model. Use `className` to identify the `ActionMenubar` root.
+
+## See also
+
+- [DataPage](../DataPage/index.md) — the list-page composition that uses `ActionMenubar` for its action row
+- [Toolbar](../Toolbar/index.md) — canvas-style tool palettes
+- [Stable component parts](../Styling/pass-through.md) — Components-owned parts and state attributes
diff --git a/Documentation/Common/index.md b/Documentation/Common/index.md
index 0926ffef..37c4bf92 100644
--- a/Documentation/Common/index.md
+++ b/Documentation/Common/index.md
@@ -1,4 +1,7 @@
-# Common Components
+---
+title: Common Components
+description: Reference the shared controls, provider, layout, icons, and form utilities used across Components.
+---
The Common module provides reusable UI components and the styling setup primitive that serve as building blocks for applications.
@@ -8,6 +11,7 @@ The Common module provides reusable UI components and the styling setup primitiv
- **TextInput / TextArea**: Native text controls with semantic string changes and real element refs.
- **Checkbox / Radio / Switch**: Native form choices with semantic boolean changes and browser-owned submission and reset behavior.
- **Button / IconButton**: Native actions with semantic variants, tones, loading, and disabled behavior.
+- **ActionMenubar**: Horizontal toolbar of page-level command actions.
- **Surface**: A bounded `div`, `section`, or `article` container with no invented interaction state.
- **Icon / IconDisplay**: Unified icon type that accepts a CSS class string or any React node.
- **Page**: Layout primitive for consistent page structures.
@@ -17,6 +21,7 @@ The Common module provides reusable UI components and the styling setup primitiv
## See Also
- [Basic controls](basic-controls.md) — native form, ref, change, part, and state contracts
+- [ActionMenubar](action-menubar.md) — page-level command actions
- [CratisComponentsProvider](cratis-components-provider.md) — locale, labels, and toaster configuration
- [Icon](icon.md) - Icon type and IconDisplay component
- [Page](page.md) - Page layout component
diff --git a/Documentation/Common/toc.yml b/Documentation/Common/toc.yml
index c99986f1..901b0759 100644
--- a/Documentation/Common/toc.yml
+++ b/Documentation/Common/toc.yml
@@ -4,6 +4,8 @@
href: cratis-components-provider.md
- name: Basic controls
href: basic-controls.md
+- name: ActionMenubar
+ href: action-menubar.md
- name: Icon
href: icon.md
- name: Page
diff --git a/Documentation/DataPage/index.md b/Documentation/DataPage/index.md
index 8bca029e..d218bc4d 100644
--- a/Documentation/DataPage/index.md
+++ b/Documentation/DataPage/index.md
@@ -1,4 +1,7 @@
-# DataPage
+---
+title: DataPage
+description: Combine query-backed tables, actions, selection, and optional details in a complete data page.
+---
The `DataPage` component provides a complete page layout for displaying and managing data from queries, including table view, menu actions, and optional detail panels.
diff --git a/Documentation/DataPage/menu-items.md b/Documentation/DataPage/menu-items.md
index 056b8cc3..b5e766b4 100644
--- a/Documentation/DataPage/menu-items.md
+++ b/Documentation/DataPage/menu-items.md
@@ -96,7 +96,7 @@ import { FaFloppyDisk, FaDownload, FaUpload } from 'react-icons/fa6';
```
-:::caution
+:::caution[Pass an icon component]
Don't pass `icon="pi pi-save"` (a PrimeIcons CSS class) or `icon={}` (a JSX element).
DataPage instantiates the icon itself, so the prop must be the component type: `icon={FaFloppyDisk}`.
:::
diff --git a/Documentation/DataTables/index.md b/Documentation/DataTables/index.md
index c5cd573f..defa4250 100644
--- a/Documentation/DataTables/index.md
+++ b/Documentation/DataTables/index.md
@@ -1,4 +1,7 @@
-# DataTables
+---
+title: DataTables
+description: Render local, paged, and observable query data with sorting, filtering, and selection.
+---
The DataTables module provides a semantic local-array table plus specialized Arc query and observable-query wrappers.
diff --git a/Documentation/Dialogs/index.md b/Documentation/Dialogs/index.md
index 15debcca..c2c37c6d 100644
--- a/Documentation/Dialogs/index.md
+++ b/Documentation/Dialogs/index.md
@@ -1,4 +1,7 @@
-# Dialogs
+---
+title: Dialogs
+description: Present custom content, busy states, and confirmations with the shared dialog components.
+---
The Dialogs module provides common dialog components for user interactions.
diff --git a/Documentation/Display/index.md b/Documentation/Display/index.md
index 403d4c7a..d12d2f1c 100644
--- a/Documentation/Display/index.md
+++ b/Documentation/Display/index.md
@@ -1,4 +1,7 @@
-# Display
+---
+title: Display
+description: Show statuses, counts, avatars, progress, and loading states with presentational primitives.
+---
The `Display` components are small, presentational primitives for status and feedback — tags, badges, chips, avatars, progress, and loading skeletons. Import them from `@cratis/components/Display`.
diff --git a/Documentation/Filter/index.md b/Documentation/Filter/index.md
index 55a351d4..2dfea4e3 100644
--- a/Documentation/Filter/index.md
+++ b/Documentation/Filter/index.md
@@ -1,6 +1,9 @@
-# FilterPanel
+---
+title: FilterPanel
+description: Build reusable filter panels with option, range, and custom editors.
+---
-The `FilterPanel` component provides a standalone, reusable filter UI that can be placed next to any data view. It renders as a positioned dropdown anchored below a trigger button and supports single-select, multi-select, numeric range (with histogram), and fully custom filter editors declared as children.
+The `FilterPanel` component provides a standalone, reusable filter UI that can be placed next to any data view. It renders as a positioned dropdown anchored below a trigger button and supports single-select, multi-select, numeric range (with histogram), and fully custom filter editors declared as children. If you are choosing between faceted, column, and global filtering, start with [Choosing a component](../choosing-a-component.md#filtering).
## Key Features
diff --git a/Documentation/Migration/index.md b/Documentation/Migration/index.md
index e97340ca..b6426029 100644
--- a/Documentation/Migration/index.md
+++ b/Documentation/Migration/index.md
@@ -1,4 +1,7 @@
-# Migration
+---
+title: Migration
+description: Move @cratis/components applications between major versions with focused upgrade guides.
+---
Guides for moving `@cratis/components` forward across major versions. Each guide is scoped to one version jump and lists the changes a consuming application can observe: dependencies, imports, styling, licensing, behavior, and verification.
diff --git a/Documentation/ObjectContentEditor/index.md b/Documentation/ObjectContentEditor/index.md
index 5bd2a5b4..4d8e8766 100644
--- a/Documentation/ObjectContentEditor/index.md
+++ b/Documentation/ObjectContentEditor/index.md
@@ -1,4 +1,7 @@
-# ObjectContentEditor
+---
+title: ObjectContentEditor
+description: Explore complex JSON objects with schema-aware rendering and breadcrumb navigation.
+---
The `ObjectContentEditor` component displays and allows exploration of complex JSON objects with schema-aware rendering and navigation.
diff --git a/Documentation/ObjectNavigationalBar/index.md b/Documentation/ObjectNavigationalBar/index.md
index fad938be..d88caf3b 100644
--- a/Documentation/ObjectNavigationalBar/index.md
+++ b/Documentation/ObjectNavigationalBar/index.md
@@ -1,4 +1,7 @@
-# ObjectNavigationalBar
+---
+title: ObjectNavigationalBar
+description: Navigate hierarchical objects with a controlled breadcrumb bar and back action.
+---
The `ObjectNavigationalBar` component provides breadcrumb navigation for hierarchical data structures.
diff --git a/Documentation/PivotViewer/index.md b/Documentation/PivotViewer/index.md
index 168cae3d..c6dc2c7d 100644
--- a/Documentation/PivotViewer/index.md
+++ b/Documentation/PivotViewer/index.md
@@ -1,4 +1,7 @@
-# PivotViewer
+---
+title: PivotViewer
+description: Explore large datasets through interactive grouping, filtering, zooming, and spatial rendering.
+---
The `PivotViewer` component provides an interactive, high-performance visualization for exploring large datasets with dynamic grouping, filtering, and zooming capabilities.
diff --git a/Documentation/SchemaEditor/index.md b/Documentation/SchemaEditor/index.md
index 9db59001..5a4e1f21 100644
--- a/Documentation/SchemaEditor/index.md
+++ b/Documentation/SchemaEditor/index.md
@@ -1,4 +1,7 @@
-# SchemaEditor
+---
+title: SchemaEditor
+description: Create and edit supported JSON Schema structures in an interactive table.
+---
The `SchemaEditor` component provides an interactive table-based interface for creating and editing JSON schemas.
diff --git a/Documentation/StepperCommandDialog/index.md b/Documentation/StepperCommandDialog/index.md
index ca3a5600..8a5a3d3d 100644
--- a/Documentation/StepperCommandDialog/index.md
+++ b/Documentation/StepperCommandDialog/index.md
@@ -1,6 +1,9 @@
-# StepperCommandDialog
+---
+title: StepperCommandDialog
+description: Execute Arc commands through a multi-step dialog with validation-aware navigation.
+---
-The `StepperCommandDialog` component provides a multi-step wizard dialog interface for executing commands, built on top of the Cratis-owned `CommandStepper`.
+The `StepperCommandDialog` component executes one Arc command through a modal, multi-step form. It and [`CommandStepper`](../CommandStepper/index.md) are sibling public components that share the private `CommandStepperContent` rendering primitive. Both execute the command. Choose `CommandStepper` when the wizard belongs inline; the dialog additionally owns cancel, busy state, and authorization routing.
## Purpose
@@ -277,10 +280,10 @@ The count is not fixed for the lifetime of the dialog either. A late-resolving q
- `@cratis/arc/commands` for command execution
- `@cratis/arc.react/commands` for form handling
-- the Cratis-owned Stepper and `StepperPanel` for the wizard UI
+- the private `CommandStepperContent` rendering primitive and public `StepperPanel` marker for the wizard UI
- The Cratis [`Dialog`](../Dialogs/dialog.md) for the modal wrapper
## See Also
- [Advanced Features](advanced-features.md) - Field validation, transformation, and change tracking across steps
-- [CommandStepper](../CommandStepper/index.md) - Standalone stepper foundation component
+- [CommandStepper](../CommandStepper/index.md) — render an executing command wizard inline without a modal dialog
diff --git a/Documentation/Styling/index.md b/Documentation/Styling/index.md
index 41d7e152..7ace2cd8 100644
--- a/Documentation/Styling/index.md
+++ b/Documentation/Styling/index.md
@@ -41,3 +41,7 @@ import './product-components.css';
```
React Aria is internal. Never style React Aria class names or undocumented DOM structure.
+
+## See also
+
+- [Troubleshooting](../troubleshooting.md) — installation, styling, rendering, and form symptoms, including product CSS precedence
diff --git a/Documentation/TimeMachine/index.md b/Documentation/TimeMachine/index.md
index 5f73d648..8fd52425 100644
--- a/Documentation/TimeMachine/index.md
+++ b/Documentation/TimeMachine/index.md
@@ -1,4 +1,7 @@
-# TimeMachine
+---
+title: TimeMachine
+description: Explore versions, events, and read-model changes along an interactive timeline.
+---
The `TimeMachine` component provides an interactive timeline visualization for exploring the evolution of data over time through events and state changes.
diff --git a/Documentation/Toolbar/index.md b/Documentation/Toolbar/index.md
index d8e23ee6..f806a390 100644
--- a/Documentation/Toolbar/index.md
+++ b/Documentation/Toolbar/index.md
@@ -1,10 +1,13 @@
-# Toolbar
+---
+title: Toolbar
+description: Build canvas-style tool palettes with groups, contexts, slots, folders, and fan-out panels.
+---
The `Toolbar` component provides a canvas-style icon toolbar with support for orientations, active states, animated context switching, separators, fan-out sub-panels, and drag & drop onto surfaces.
Toolbar belongs to the [Advanced React capability profile](../ui-foundation.md#capability-profiles) — a specialized, React-only surface with no Pixi dependency, despite its canvas-adjacent purpose.
-**`Toolbar` is not a default page action row.** It is built for a canvas/tool-palette interaction — active tools, groups, slots, folders, and fan-out panels — not for an ordinary page's list of commands. `DataPage`'s built-in action row renders `ActionMenubar` (from `@cratis/components/Common`), not `Toolbar`. Reach for `ActionMenubar`, or a product-owned action row, for flat page-level actions; reach for `Toolbar` only when the surface is genuinely a spatial tool palette. See [Choosing a component: Actions and tool palettes](../choosing-a-component.md#actions-and-tool-palettes).
+**`Toolbar` is not a default page action row.** It is built for a canvas/tool-palette interaction — active tools, groups, slots, folders, and fan-out panels — not for an ordinary page's list of commands. `DataPage`'s built-in action row renders [`ActionMenubar`](../Common/action-menubar.md) (from `@cratis/components/Common`), not `Toolbar`. Reach for `ActionMenubar`, or a product-owned action row, for flat page-level actions; reach for `Toolbar` only when the surface is genuinely a spatial tool palette. See [Choosing a component: Actions and tool palettes](../choosing-a-component.md#actions-and-tool-palettes).
Pass React icon nodes or product-owned SVGs for a dependency-free toolbar. Consumer-owned icon-font class strings remain accepted, but Components does not install an icon font or infer provider base classes; the product must load the matching stylesheet and pass the complete class string.
diff --git a/Documentation/Types/index.md b/Documentation/Types/index.md
index f2a03313..87a4ed73 100644
--- a/Documentation/Types/index.md
+++ b/Documentation/Types/index.md
@@ -1,4 +1,7 @@
-# Types
+---
+title: Types
+description: Reference the shared TypeScript types and constants exported by Components.
+---
The `Types` module exports shared TypeScript types and constants used across the component library. Import from `@cratis/components/types`.
@@ -102,4 +105,3 @@ interface NavigationItem {
```
Used internally by `ObjectNavigationalBar`.
-
diff --git a/Documentation/building-a-form.md b/Documentation/building-a-form.md
index 10af7150..47dd1a1c 100644
--- a/Documentation/building-a-form.md
+++ b/Documentation/building-a-form.md
@@ -1,8 +1,6 @@
---
title: 'Recipe: Building a form'
description: Execute a command from a typed form using CommandDialog and CommandForm fields, with validation handled for you.
-sidebar:
- order: 3
---
**Goal:** collect input and run an Arc command — with the confirm button disabled while it executes, validation wired up, and no manual fetch.
@@ -71,5 +69,6 @@ const [AddAuthorDialog, showAddAuthor] = useDialog(AddAuthor);
## Next
-- [Displaying data](/components/displaying-data/) — render the results.
-- The full field set and dialog options are in the Components reference and Storybook.
+- [Displaying data](displaying-data.md) — render the results
+- [CommandDialog](CommandDialog/index.md) — execution callbacks, busy behavior, dialog options, and context
+- [CommandForm](CommandForm/index.md) — form binding, field discovery, and available field components
diff --git a/Documentation/choosing-a-component.md b/Documentation/choosing-a-component.md
index b5cb07c3..3f32c9bb 100644
--- a/Documentation/choosing-a-component.md
+++ b/Documentation/choosing-a-component.md
@@ -1,6 +1,6 @@
---
title: Choosing a component
-description: A decision guide for the overlapping Components — CommandDialog vs StepperCommandDialog, DataPage vs DataTables, and Dialog vs CommandDialog.
+description: Choose among Components command, data, action, filtering, feedback, structured-data, history, and conversation surfaces.
---
Several Components solve similar-looking problems, and it's not always obvious which one to reach for.
@@ -13,11 +13,13 @@ The question is whether confirming the form **runs a command**, and whether it's
| You want to… | Use | Why |
| -------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
-| Collect a few fields and run one command | [`CommandDialog`](./CommandDialog/index.md) | Instantiates, validates, and executes the command; handles the footer and button states. The default. |
-| Run a command, but gather input across **named steps** | [`StepperCommandDialog`](./StepperCommandDialog/index.md) | A wizard over a single command — validate per step, navigate back and forth, execute at the end. |
-| Embed command fields **in a page**, not a dialog | [`CommandForm`](./CommandForm/index.md) | The same typed fields `CommandDialog` uses, without the dialog chrome. |
-| Collect data and return it **without** running a command | [`Dialog`](./Dialogs/index.md) | A confirmation or data-entry dialog that hands values back to the caller. No command involved. |
-| Edit ordinary local React state | [`Common` basic controls](./Common/basic-controls.md) | Native text and choice controls expose semantic values without binding an Arc command. |
+| Collect a few fields and run one command | [`CommandDialog`](./CommandDialog/index.md) | Instantiates, validates, and executes the command; handles the footer and button states. The default. |
+| Run a command through named steps in a modal | [`StepperCommandDialog`](./StepperCommandDialog/index.md) | A wizard over a single command — validate per step, navigate back and forth, execute at the end. |
+| Run a command through named steps inline on the page | [`CommandStepper`](./CommandStepper/index.md) | The inline command wizard for a panel, route, or page region; it executes on the final step. |
+| Embed command fields **in a page**, not a dialog | [`CommandForm`](./CommandForm/index.md) | The same typed fields `CommandDialog` uses, without the dialog chrome. |
+| Collect data and return it **without** running a command | [`Dialog`](./Dialogs/dialog.md) | A confirmation or data-entry dialog that hands values back to the caller. No command involved. |
+| Edit ordinary local React state | [`Common` basic controls](./Common/basic-controls.md) | Native text and choice controls expose semantic values without binding an Arc command. |
+| Select one or more values in ordinary local React state | [`Dropdown`](./Dropdown/index.md) | Binds a value or array to local options without binding an Arc command. |
Rule of thumb: **if confirming the dialog executes a generated command, it's a `CommandDialog`** (or its
stepper variant). If it just gathers values and returns them, it's a `Dialog`. Never reach for
@@ -40,9 +42,39 @@ If you're building a list-screen-with-actions from scratch, start with the
[list screen recipe](./list-screen-with-actions.md), which composes `DataPage` with `CommandDialog`
actions.
+## Selection, status, and feedback
+
+| You want to… | Use | Why |
+| ------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------- |
+| Select one or more values from local options | [`Dropdown`](./Dropdown/index.md) | Supports single, filtered, and multiple selection through a controlled value. |
+| Show status, counts, people, progress, or loading | [`Display`](./Display/index.md) | Provides tags, badges, chips, avatars, messages, progress indicators, and skeletons. |
+| Send an app-wide transient notification | [`Notifications`](./Notifications/index.md) | Provides one shared toast queue, an imperative API, and an optional app-wide toaster. |
+
+### Filtering
+
+Choose the filtering surface by where its state belongs:
+
+| You want to… | Use | Why |
+| ----------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
+| Filter individual table columns | [`Column` filters](./DataTables/index.md#columnfiltermenu) | A column can use the built-in `ColumnFilterMenu`; the menu keeps draft changes until Apply. |
+| Search configured fields in a `DataPage` | [`DataPage.globalFilterFields`](./DataPage/index.md#filtering-scope) | Adds global search over the rows in the currently loaded query page. |
+| Present option, numeric-range, or custom filter groups | [`FilterPanel`](./Filter/index.md) | Supplies a standalone faceted panel while the host owns how its filter state applies to the data view. |
+
+Table column and `DataPage` global filters operate on the currently loaded query page. Filtering the complete result set belongs in query arguments and server logic before paging; see [`DataPage` filtering scope](./DataPage/index.md#filtering-scope).
+
## Actions and tool palettes
-Use `ActionMenubar` or an ordinary product action row for flat page commands. `DataPage`'s built-in toolbar already renders `ActionMenubar`, not `Toolbar` — that is the default action row for a page, not a canvas tool palette. Use [`Toolbar`](./Toolbar/index.md) only for a genuine canvas/tool-palette interaction with active tools, groups, slots, folders, and fan-out panels. It is not a one-for-one replacement for a generic Prime Toolbar, and it is not a page-level action row wearing a different name.
+Use [`ActionMenubar`](./Common/action-menubar.md) or an ordinary product action row for flat page commands. `DataPage`'s built-in toolbar already renders `ActionMenubar`, not `Toolbar` — that is the default action row for a page, not a canvas tool palette. Use [`Toolbar`](./Toolbar/index.md) only for a genuine canvas/tool-palette interaction with active tools, groups, slots, folders, and fan-out panels. It is not a one-for-one replacement for a generic Prime Toolbar, and it is not a page-level action row wearing a different name.
+
+## Structured data, history, and conversation
+
+| You want to… | Use | Why |
+| ------------------------------------------------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
+| Explore or edit an object through a JSON Schema | [`ObjectContentEditor`](./ObjectContentEditor/index.md) | Renders schema-aware properties and navigation through nested objects and arrays. |
+| Create or edit a supported JSON Schema | [`SchemaEditor`](./SchemaEditor/index.md) | Edits properties, supported types, and formats in a validated table interface. |
+| Add controlled breadcrumbs to hierarchical data | [`ObjectNavigationalBar`](./ObjectNavigationalBar/index.md) | Renders a host-owned navigation path with breadcrumb and back actions. |
+| Explore supplied versions, events, and state changes | [`TimeMachine`](./TimeMachine/index.md) | Provides an interactive timeline while the host supplies the version data. |
+| Build topic-based conversations from host-owned data | [`Chat`](./Chat/index.md) | Provides topic, conversation, mention, emoji, and message-action surfaces with callback outputs. |
## Spatial workspaces
@@ -56,6 +88,6 @@ A typical CRUD screen combines these: a `DataPage` lists the rows, a toolbar but
`CommandDialog` to add one, and selecting a row opens another `CommandDialog` to edit it. That whole
screen is the [list screen with actions](./list-screen-with-actions.md) recipe.
-Components does not attempt to replace every toolkit widget. Tabs, sidebars, timelines, knobs, select-button groups, general popovers, and specialized locale-aware inputs may remain product-owned or in a separately configured UI toolkit until an intentional Components API exists.
+Components does not ship every toolkit widget. Tabs, general-purpose sidebars, knobs, select-button groups, general popovers, grouped or expandable tables, controlled lazy/server table sorting, and specialized locale-aware inputs remain product-owned or in a separately configured UI toolkit. See [Coming from PrimeReact](./coming-from-primereact.md) for the current replacement boundaries.
Still deciding how to style any of this? See [Styling](./Styling/index.md).
diff --git a/Documentation/decisions/0001-dom-coupled-contract.md b/Documentation/decisions/0001-dom-coupled-contract.md
index be52b87b..582b4e4b 100644
--- a/Documentation/decisions/0001-dom-coupled-contract.md
+++ b/Documentation/decisions/0001-dom-coupled-contract.md
@@ -2,7 +2,6 @@
title: DOM-coupled public component contract
description: Why Components intentionally exposes React, HTML, native form, ref, and DOM event semantics while keeping renderer vendors private.
sidebar:
- order: 1
badge: { text: Accepted, variant: tip }
---
diff --git a/Documentation/decisions/0002-component-classification.md b/Documentation/decisions/0002-component-classification.md
index 87978ce2..43ab8e1e 100644
--- a/Documentation/decisions/0002-component-classification.md
+++ b/Documentation/decisions/0002-component-classification.md
@@ -2,7 +2,6 @@
title: Public component classification
description: The architecture categories assigned to every component exported by the public package barrels.
sidebar:
- order: 2
badge: { text: Accepted, variant: tip }
---
diff --git a/Documentation/decisions/0003-kernel-boundary.md b/Documentation/decisions/0003-kernel-boundary.md
index 5ee6e063..b9f63d52 100644
--- a/Documentation/decisions/0003-kernel-boundary.md
+++ b/Documentation/decisions/0003-kernel-boundary.md
@@ -2,7 +2,6 @@
title: Repository-owned kernel boundary
description: The explicit React-free and browser-DOM-free computation kernel enforced in Components source and emitted package graphs.
sidebar:
- order: 3
badge: { text: Accepted, variant: tip }
---
diff --git a/Documentation/decisions/0004-stable-presentation-renderer-profile.md b/Documentation/decisions/0004-stable-presentation-renderer-profile.md
index 172eb763..b11078e8 100644
--- a/Documentation/decisions/0004-stable-presentation-renderer-profile.md
+++ b/Documentation/decisions/0004-stable-presentation-renderer-profile.md
@@ -2,7 +2,6 @@
title: Stable presentation renderer profile
description: The bounded nine-slot renderer contract promoted after independent conformance proof.
sidebar:
- order: 4
badge: { text: Accepted, variant: tip }
---
diff --git a/Documentation/displaying-data.md b/Documentation/displaying-data.md
index 1d63b1ae..e3ae0d2d 100644
--- a/Documentation/displaying-data.md
+++ b/Documentation/displaying-data.md
@@ -1,8 +1,6 @@
---
title: "Recipe: Displaying data"
description: Render query results in a data table that updates live, and build list-and-detail screens with DataPage.
-sidebar:
- order: 4
---
**Goal:** show the results of an Arc query in a table — and have it update on its own when the underlying read model changes.
@@ -43,7 +41,7 @@ For the common "table on the left, details on the right" screen, `DataPage` give
import { DataPage } from '@cratis/components/DataPage';
```
-See the DataPage reference for menu items, the details panel, and selection wiring.
+See [DataPage](DataPage/index.md) for menu items, the details panel, and selection wiring.
## Tips
@@ -52,5 +50,6 @@ See the DataPage reference for menu items, the details panel, and selection wiri
## Next
-- [Building a form](/components/building-a-form/) — the write side.
-- [Build a full-stack feature](/build-a-full-app/) — the table and the form together against one Arc slice.
+- [Building a form](building-a-form.md) — the write side
+- [DataTables](DataTables/index.md) — choose the Arc query wrapper and configure columns
+- [DataPage](DataPage/index.md) — compose a query-backed list screen with actions and details
diff --git a/Documentation/getting-started.mdx b/Documentation/getting-started.mdx
index 9e142bf3..33f42c3d 100644
--- a/Documentation/getting-started.mdx
+++ b/Documentation/getting-started.mdx
@@ -1,8 +1,6 @@
---
title: Getting started
description: Install Components, mount the provider, and render your first type-safe command form.
-sidebar:
- order: 2
---
import { Steps, Aside } from '@astrojs/starlight/components';
@@ -168,10 +166,14 @@ Do not target React Aria class names or internal DOM structure. See [Styling](/c
You installed one UI package, imported Components-owned styles, and mounted a locale/toast provider. Components owns the public React contract, Arc supplies command and query behavior, and React Aria supplies selected low-level interaction primitives internally.
+Next, [build a live library screen](/components/tutorial/) — the tutorial guides you step by step through a list-and-detail page with query-backed tables and command dialogs.
+
## Where to go next
+- [Build the library screen tutorial](/components/tutorial/) — follow one screen from a live table through command actions and list-and-detail composition
- [Building a form](/components/building-a-form/)
- [Displaying data](/components/displaying-data/)
- [Choosing a component](/components/choosing-a-component/)
+- [Troubleshooting](/components/troubleshooting/) — installation, styling, rendering, and form symptoms
- [Migrate from Components 3](/components/migration/)
- [Understand the UI foundation](/components/ui-foundation/) — including the capability profiles and capability matrix behind Foundation, Advanced React, and Spatial components
diff --git a/Documentation/index.mdx b/Documentation/index.mdx
index 2677ef4c..784b513b 100644
--- a/Documentation/index.mdx
+++ b/Documentation/index.mdx
@@ -21,6 +21,9 @@ import TopicHero from '@components/TopicHero.astro';
## Start here
+
+ Build a live list-and-detail screen with query-backed tables and command dialogs.
+
Install the package, mount the provider, and render your first proxy-driven
screen.
diff --git a/Documentation/list-screen-with-actions.md b/Documentation/list-screen-with-actions.md
index d807be5b..08f54f23 100644
--- a/Documentation/list-screen-with-actions.md
+++ b/Documentation/list-screen-with-actions.md
@@ -1,8 +1,6 @@
---
title: "Recipe: A list screen with actions"
description: Combine a live table with command dialogs to build the everyday "list things, add/edit/remove them" screen.
-sidebar:
- order: 6
---
**Goal:** the most common screen in any app — a table of things with a toolbar to add, and per-row actions to edit or remove. This recipe wires [displaying data](/components/displaying-data/) and [running commands](/components/building-a-form/) together.
@@ -51,9 +49,10 @@ You never write the glue between the write and the read: the "add" command appen
- **Edit and remove are just more commands.** Model them as commands (`RenameAuthor`, `RemoveAuthor`) and open them in dialogs the same way; the table updates itself.
- **Keep the table's read model specialized.** The list query and a detail query can read different, purpose-built read models — don't force one model to serve every screen.
-- For a plain table without the detail panel, use [`DataTableForObservableQuery`](/components/displaying-data/) directly.
+- For a plain table without the detail panel, use [`DataTableForObservableQuery`](DataTables/data-table-for-observable-query.md) directly.
## Next
-- [Building a form](/components/building-a-form/) and [Displaying data](/components/displaying-data/) — the two halves in detail.
-- [Build a full-stack feature](/build-a-full-app/) — the backend slice this screen sits on.
+- [Building a form](building-a-form.md) and [Displaying data](displaying-data.md) — the two halves in detail
+- [DataPage](DataPage/index.md) — configure the action row, columns, selection, and details panel
+- [CommandDialog](CommandDialog/index.md) — execute the add, edit, and remove commands
diff --git a/Documentation/multi-step-form.md b/Documentation/multi-step-form.md
index 9ec3e677..3d125bff 100644
--- a/Documentation/multi-step-form.md
+++ b/Documentation/multi-step-form.md
@@ -1,8 +1,6 @@
---
title: "Recipe: Multi-step form"
description: Gather a command's input across several named steps with StepperCommandDialog.
-sidebar:
- order: 5
---
**Goal:** one command needs more input than fits comfortably on a single screen. Split it into a wizard — named steps the user moves through — that still executes a single command at the end.
@@ -38,9 +36,10 @@ export const RegisterMemberWizard = () => (
## When to use a wizard vs. a plain dialog
-Reach for a wizard when the input is genuinely staged or long enough that one screen would overwhelm. For three or four fields, a single [CommandDialog](/components/building-a-form/) is friendlier — don't add steps for their own sake.
+Reach for a wizard when the input is genuinely staged or long enough that one screen would overwhelm. For three or four fields, a single [CommandDialog](CommandDialog/index.md) is friendlier — don't add steps for their own sake.
## Next
-- [Building a form](/components/building-a-form/) — the single-step version and the field set.
-- The full stepper options are in the Components reference and Storybook.
+- [Building a form](building-a-form.md) — the single-step recipe and the field set
+- [StepperCommandDialog](StepperCommandDialog/index.md) — modal navigation, validation, callbacks, cancellation, and busy state
+- [CommandStepper](CommandStepper/index.md) — execute the same kind of multi-step command inline
diff --git a/Documentation/renderers/index.md b/Documentation/renderers/index.md
index 04212e5a..0a481494 100644
--- a/Documentation/renderers/index.md
+++ b/Documentation/renderers/index.md
@@ -116,3 +116,4 @@ Use the schema for editor/build-time manifest validation, then run `@cratis/comp
presentation.
- Use [custom composition](custom-composition.md) when the workflow itself must be vendor-native.
- Check [unsupported renderer claims](unsupported.md) before promising replacement behavior.
+- Use [Troubleshooting](../troubleshooting.md#an-adapter-still-renders-a-built-in-control) when an active adapter falls back to a built-in control.
diff --git a/Documentation/storybook.mdx b/Documentation/storybook.mdx
index cc047068..f2bdb255 100644
--- a/Documentation/storybook.mdx
+++ b/Documentation/storybook.mdx
@@ -6,12 +6,10 @@ tableOfContents: false
import StorybookEmbed from '@components/StorybookEmbed.astro';
-Use the **Renderer** manager control to run the same stable story id with the zero-config Cratis built-in renderer, Material UI, PrimeReact 11, or PrimeReact 10. Changing renderer replaces the composed preview iframe. The manager preserves the current story id when the target preview contains it, but component-local state, unsaved control changes, open overlays, and in-progress interactions are reset by that remount.
+Use the **Renderer** control to run a story with the built-in renderer, Material UI, PrimeReact 11, or PrimeReact 10. Changing renderer remounts the preview iframe.
-Use the Appearance toolbar to compare the maintained baseline in light and dark modes with a representative product-owned token mapping. Every renderer preview owns its provider and theme boundary while preserving strict profile validation and Core fallback for slots outside the selected adapter's profile. Stories load the same prefixed, layered, no-Preflight Cratis CSS contract as the published package.
+Use the **Appearance** toolbar to switch between light and dark baseline modes.
-PrimeReact 10 and 11 are built as separate preview projects because their incompatible `primereact` peer majors must never enter one dependency graph. Each preview verifies the exact upstream version resolved from the checked-in adapter matrix. The PrimeReact 11 preview is a bounded test fixture: it uses PrimeReact's public contexts and the adapter's non-secret boolean setup attestation without mounting the real license manager. Components and this Storybook do not receive, read, store, log, serialize, bundle, or proxy a license key.
-
-Every story module also generates an autodocs page from the public component types and TSDoc. CI validates the metadata-discovered adapter inventory, type-checks the configuration, asserts peer-major isolation, builds and compares every preview index, then runs every discovered stable story in Playwright Chromium under baseline light and dark modes for the built-in renderer and every metadata-discovered public adapter. The current V4 inventory is four previews × 277 stories × two appearances: **2,216 story/appearance/axe cases**, with zero story or renderer exclusions. Generated preview indexes identify the exact stories; [#217](https://github.com/Cratis/Components/issues/217) tracks replacing release-snapshot count constants with generated reviewable inventories. Private adapters such as Plain are excluded from composition. Each `play()` interaction runs, and Accessibility-addon axe violations are errors. This is a browser component-test gate, not a claim of universal browser or assistive-technology conformance; keyboard, focus, busy, empty, invalid, disabled, open-overlay, and responsive states still require deliberate story coverage and release review.
+Every story generates an autodocs page from public component types and TSDoc.
diff --git a/Documentation/toc.yml b/Documentation/toc.yml
index 47536727..0d82c424 100644
--- a/Documentation/toc.yml
+++ b/Documentation/toc.yml
@@ -70,5 +70,7 @@
href: Common/toc.yml
- name: Types
href: Types/toc.yml
+- name: Troubleshooting
+ href: troubleshooting.md
- name: Migration
href: Migration/toc.yml
diff --git a/Documentation/troubleshooting.md b/Documentation/troubleshooting.md
new file mode 100644
index 00000000..2484a995
--- /dev/null
+++ b/Documentation/troubleshooting.md
@@ -0,0 +1,37 @@
+---
+title: Troubleshooting
+description: Find the owning guidance for common Components installation, styling, rendering, and form symptoms.
+---
+
+
+
+
+Start from the symptom you can observe. Each entry points to the page that owns the current contract and remedy.
+
+## The command submit button stays disabled
+
+Required command values must be visible to client validation before submission. Put non-input required values in `initialValues`, or keep custom controls synchronized through `currentValues`; a value first created in `onBeforeExecute` arrives too late to satisfy the validity gate. Follow [Building a form](building-a-form.md#tips) for the short rule and [CommandDialog advanced features](CommandDialog/advanced-features.md#custom-inputs) for custom inputs.
+
+## Yarn PnP cannot resolve `rxjs` from Arc React
+
+The current `@cratis/arc.react@22.6.2` package imports `rxjs` without declaring it. A strict Yarn PnP consumer needs the temporary `packageExtensions` entry and `rxjs` version shown in [Getting started](getting-started.mdx#yarn-pnp-with-arc-react-2262).
+
+## Product CSS does not win over Components styles
+
+Import the product stylesheet after the Components `tokens` and `styles` entries. Unlayered product CSS wins over the low-priority Components layers; a product that uses its own cascade layers must declare their order explicitly. See the [Styling cascade contract](Styling/index.md#cascade-contract) and the [mixed styling example](Styling/mixing-paths.md).
+
+## A component import from the package root no longer compiles
+
+The Components 4 package root is setup-only. Import components from explicit subpaths and use the current namespace-to-subpath mapping in [Migrate from Components 3 to 4](Migration/3-to-4.md#import-from-explicit-subpaths).
+
+## An adapter still renders a built-in control
+
+The public adapters implement the declared presentation slots rather than replacing every Components export. With the default `rendererFallback='core'`, undeclared slots use the built-in implementation; `rendererFallback='throw'` rejects that fallback without adding adapter coverage. See [Primitive adaptation: Fallback](renderers/primitive-adaptation.md#fallback) and [Unsupported renderer claims](renderers/unsupported.md#no-transparent-full-catalog-replacement).
+
+## Server rendering crashes on a browser global
+
+Use the [UI foundation capability matrix](ui-foundation.md#capability-matrix) to check the server-rendering boundary for the component profile. For a custom portal container, resolve `document` inside `overlayEnvironment.getContainer` and return `null` when it is unavailable, as shown in [Choose an overlay container](Common/cratis-components-provider.md#choose-an-overlay-container).
+
+## See also
+
+For generated proxies, command authorization, command validation, and Arc request behavior, use [Arc troubleshooting](/arc/troubleshooting/).
diff --git a/Documentation/ui-foundation.md b/Documentation/ui-foundation.md
index 611a8ea1..5076f635 100644
--- a/Documentation/ui-foundation.md
+++ b/Documentation/ui-foundation.md
@@ -1,8 +1,6 @@
---
title: UI foundation
description: How Components owns its public React contracts and delegates selected interaction primitives.
-sidebar:
- order: 2
---
Components 4 owns its public React markup, TypeScript types, tokens, documented parts, and component behavior without exposing its internal interaction library as a consumer contract. The public component contract is deliberately coupled to React and the browser DOM: standard HTML attributes, React refs, native element types, form behavior, and DOM event semantics are intentional guarantees. React Aria supplies selected focus, keyboard, overlay, collection, and date interaction primitives internally. These implementation facts do not establish accessibility conformance for every component or application.
@@ -219,7 +217,8 @@ The Components 4 major candidate uses these repository release checks:
- The setup root and every non-spatial subpath load without Pixi, while Canvas and PivotViewer fail specifically on the missing optional peer until it is installed.
- Declared Arc peer versions are exercised against the packed artifact.
- Representative custom-theme and pass-through consumers compile after following the guide.
-- Specs, Storybook, package exports, SSR, keyboard/focus behavior, responsive layouts, dark mode, forced colors, and reduced motion pass.
+- Specs, package exports, SSR, keyboard/focus behavior, responsive layouts, dark mode, forced colors, and reduced motion pass.
+- Storybook builds and runs under baseline light and dark modes for the built-in renderer and every metadata-discovered public adapter; private ui-adapter workspaces are excluded, and the Plain DOM renderer is a conformance fixture, not an adapter workspace.
- The migration guide works without repository-specific knowledge.
- Every packed public JavaScript subpath passes strict TypeScript 6 validation or matches a bounded machine-readable upstream exception with exact installed versions and an unmet removal condition. Components-owned cascades additionally require their matching upstream TS2834/TS2835 root cause in the same compiler run.
diff --git a/Documentation/verify-authoring.mjs b/Documentation/verify-authoring.mjs
new file mode 100644
index 00000000..4a4d0619
--- /dev/null
+++ b/Documentation/verify-authoring.mjs
@@ -0,0 +1,100 @@
+// Copyright (c) Cratis. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+import { readdir, readFile, stat } from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const documentationRoot = path.dirname(fileURLToPath(import.meta.url));
+const validAsideVariants = new Set(['note', 'tip', 'caution', 'danger']);
+const errors = [];
+
+async function filesBelow(directory) {
+ const files = [];
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
+ const entryPath = path.join(directory, entry.name);
+ if (entry.isDirectory()) {
+ files.push(...await filesBelow(entryPath));
+ } else if (/\.mdx?$/i.test(entry.name)) {
+ files.push(entryPath);
+ }
+ }
+
+ return files;
+}
+
+function validateContent(file, content) {
+ const isMarkdown = path.extname(file).toLowerCase() === '.md';
+ let fence;
+
+ for (const [index, line] of content.split('\n').entries()) {
+ const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
+ if (fenceMatch) {
+ const marker = fenceMatch[1];
+ if (!fence) {
+ fence = { character: marker[0], length: marker.length };
+ } else if (marker[0] === fence.character && marker.length >= fence.length) {
+ fence = undefined;
+ }
+ continue;
+ }
+
+ if (fence) continue;
+
+ const asideMatch = line.match(/^\s*:::(\w[\w-]*)/);
+ if (asideMatch && !validAsideVariants.has(asideMatch[1])) {
+ errors.push(`${relative(file)}:${index + 1}: Unknown Starlight aside variant '${asideMatch[1]}'. Use note, tip, caution, or danger.`);
+ }
+
+ if (isMarkdown && /^\s*import\s+(?:.+\s+from\s+)?['"]/.test(line)) {
+ errors.push(`${relative(file)}:${index + 1}: Imports require .mdx; in .md they render as visible prose.`);
+ }
+
+ if (isMarkdown) {
+ const componentMatch = line.match(/^\s*<\/?([A-Z][A-Za-z0-9.]*)\b/);
+ if (componentMatch) {
+ errors.push(`${relative(file)}:${index + 1}: <${componentMatch[1]}> requires .mdx; in .md it renders as an inert element.`);
+ }
+ }
+ }
+}
+
+async function validateLandingCollisions(files) {
+ for (const file of files) {
+ const extension = path.extname(file);
+ const possibleDirectory = file.slice(0, -extension.length);
+ let directoryStats;
+ try {
+ directoryStats = await stat(possibleDirectory);
+ } catch {
+ continue;
+ }
+
+ if (!directoryStats.isDirectory()) continue;
+
+ const entries = await readdir(possibleDirectory);
+ if (entries.some(entry => /^index\.mdx?$/i.test(entry))) {
+ errors.push(`${relative(file)}: Conflicts with ${relative(possibleDirectory)}/index.md[x]. The site demotes the directory index to /overview/ and can orphan it; keep one landing page for the route.`);
+ }
+ }
+}
+
+function relative(file) {
+ return path.relative(path.dirname(documentationRoot), file).split(path.sep).join('/');
+}
+
+const files = await filesBelow(documentationRoot);
+for (const file of files) {
+ validateContent(file, await readFile(file, 'utf8'));
+}
+await validateLandingCollisions(files);
+
+if (errors.length > 0) {
+ console.error('Documentation authoring validation failed:');
+ for (const error of errors) console.error(` - ${error}`);
+ process.exit(1);
+}
+
+const markdownCount = files.filter(file => path.extname(file).toLowerCase() === '.md').length;
+const mdxCount = files.length - markdownCount;
+console.log(`Documentation authoring validation passed for ${files.length} files (${markdownCount} .md, ${mdxCount} .mdx).`);
diff --git a/Documentation/verify-markdown.sh b/Documentation/verify-markdown.sh
index 64a1cdfa..b9fde748 100755
--- a/Documentation/verify-markdown.sh
+++ b/Documentation/verify-markdown.sh
@@ -15,7 +15,7 @@ echo "=========================================="
echo ""
# Always resolve from this script rather than trusting an inherited/symlinked PWD.
-cd "$ROOT_DIR"
+cd "$ROOT_DIR" || exit 1
echo "Working directory: $(pwd -P)"
echo ""
@@ -53,9 +53,9 @@ else
fi
echo ""
-# Step 2: Link Verification
+# Step 2: Starlight Authoring Validation
echo "=========================================="
-echo "Step 2: Running local link verification..."
+echo "Step 2: Validating Starlight authoring..."
echo "=========================================="
echo ""
@@ -64,7 +64,25 @@ if ! command -v node >/dev/null 2>&1; then
exit 1
fi
+node "$SCRIPT_DIR/verify-authoring.mjs"
+AUTHORING_EXIT_CODE=$?
+
+echo ""
+if [ $AUTHORING_EXIT_CODE -eq 0 ]; then
+ echo "✓ Starlight authoring validation passed!"
+else
+ echo "✗ Starlight authoring validation failed with exit code $AUTHORING_EXIT_CODE"
+fi
+echo ""
+
+# Step 3: Link Verification
+echo "=========================================="
+echo "Step 3: Running local link verification..."
+echo "=========================================="
+echo ""
+
LINK_EXIT_CODE=0
+node "$SCRIPT_DIR/verify-local-links.mjs" --self-test || LINK_EXIT_CODE=$?
for SCAN_ROOT in "$SCRIPT_DIR" "$ROOT_DIR/Adapters" "$ROOT_DIR/scripts"; do
node "$SCRIPT_DIR/verify-local-links.mjs" "$SCAN_ROOT" || LINK_EXIT_CODE=$?
done
@@ -81,12 +99,13 @@ echo ""
echo "=========================================="
echo "Summary"
echo "=========================================="
-if [ $LINT_EXIT_CODE -eq 0 ] && [ $LINK_EXIT_CODE -eq 0 ]; then
+if [ $LINT_EXIT_CODE -eq 0 ] && [ $AUTHORING_EXIT_CODE -eq 0 ] && [ $LINK_EXIT_CODE -eq 0 ]; then
echo "✓ All checks passed!"
exit 0
else
echo "✗ Some checks failed:"
[ $LINT_EXIT_CODE -ne 0 ] && echo " - Markdown linting"
+ [ $AUTHORING_EXIT_CODE -ne 0 ] && echo " - Starlight authoring validation"
[ $LINK_EXIT_CODE -ne 0 ] && echo " - Link verification"
exit 1
fi
diff --git a/Documentation/why-components.md b/Documentation/why-components.md
index c4f0c3c7..dcead31f 100644
--- a/Documentation/why-components.md
+++ b/Documentation/why-components.md
@@ -1,8 +1,6 @@
---
title: Why Components
description: Why Components owns React composition around Arc proxies and selected interaction primitives.
-sidebar:
- order: 1
---
You can connect an Arc-generated command or query to any React UI. Without Components, every application repeatedly builds command execution state, validation display, dialogs, observable subscriptions, paging, selection, empty/pending states, localization, and accessibility behavior.