---
title: Strict toolchain migration
description: Migrate removed compatibility APIs and stricter form contracts.
---

Review the required consumer import and prop changes before updating. Reinstall the affected copied source first, then update application calls and run your checks before deploying. Installing a registry component does not migrate application calls.

Reinstall **all registry components already installed in your project** with `--overwrite`,
including components that depend on a changed item. The CLI updates dependencies of the
requested item, not its existing reverse dependents. For example, updating SanitizedHtml
alone leaves an older copied CodeComparison importing its removed helper export. Reinstall
CodeComparison as well. Review local changes before overwriting copied source.

## Keep the copied source notices

Every registry item now depends on `source-notices`, which installs the registry,
shadcn, and Buf license texts and their provenance notice under
`src/components/redpanda-ui/licenses`. Keep these files with redistributed source;
they are data assets, not application imports or runtime dependencies.

The checked shadcn CLI can remove leading comments from copied TypeScript and CSS,
including copyright headers and explanatory JSDoc. Registry JSON retains those
comments; the common notice files preserve the license trail independently of
that transformation. Do not assume that installed source is byte-identical to its
registry payload, or delete the notice directory when cleaning unused components.

## Removed compatibility APIs

| Removed                                                                                                 | Replacement                                                                                                                                                           |
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code-block-dynamic`, `DynamicCodeBlock`, `SyncCodeBlock`, its `CodeBlock` and `Pre`, `TERMINAL_THEMES` | [Code Block](/docs/code-block#migrating-from-code-block-dynamic), `SimpleCodeBlock`, `useHighlightedCode`, and `variant="terminal"`                                   |
| `sonner` registry component and demos                                                                   | [Toast](/docs/toast#migrating-from-sonner), its `Toaster`, and `toast` manager                                                                                        |
| `asyncHandler` from `lib/async-handler`                                                                 | Remove the retired wrapper and copied file. Let promise-aware APIs await asynchronous actions; handle failures explicitly at application-owned void event boundaries. |
| `Heading`, `Text`, `textVariants`, `headingVariants`                                                    | Semantic HTML with `text-heading-*`, `text-body`, `text-label`, or `text-caption` utilities                                                                           |
| `RedpandaIcon` and `RedpandaLogo` from `icons`                                                          | [Redpanda Logo](/docs/redpanda-logo), with `variant="mark"` for the icon                                                                                              |
| Stepper `initialStep`                                                                                   | `defaultStep`                                                                                                                                                         |
| Drawer `activeSnapPoint`, `setActiveSnapPoint`, `snapToSequentialPoint`                                 | `snapPoint`, `onSnapPointChange`, `snapToSequentialPoints`                                                                                                            |
| InputOTP `noScriptCSSFallback`, `pushPasswordManagerStrategy`                                           | Remove these unused props. Base UI uses real character inputs.                                                                                                        |
| `FocusScopeContentProps`, `onOpenAutoFocus`, `onCloseAutoFocus`                                         | Configure the underlying Base UI popup's `initialFocus` or `finalFocus`. The old hooks did nothing.                                                                   |
| Chart legend content's `verticalAlign` and `align`                                                      | Recharts `position`, also accepted by `ChartLegendContent`                                                                                                            |
| SimpleCodeBlock `size="md"` and `size="lg"`                                                             | `size="default"`; use `size="sm"` for compact blocks                                                                                                                  |
| `FlattenProtoOneofs<T>`                                                                                 | The generated message type, with its original discriminated oneof union                                                                                               |
| `defineStepper(...).Steps`                                                                              | `ReturnType<typeof useStepper>` for the instance type, or the real `steps` array for descriptors. The removed property was an empty placeholder.                      |
| `ShadcnAutoFormFieldComponents` and the internal `AutoFormFieldComponentRegistry` map                   | Pass only your overrides through AutoForm's `formComponents` prop. Built-in renderers are merged automatically. Use `fieldRegistry` for field matching.               |
| `useMemoizedArray` from `lib/input-utils`                                                               | Use the actual array and stable domain IDs. React Compiler owns derived-value optimization; array identity is not an event or subscription signal.                    |

Delete retired component files from consumer projects as well as their imports. The registry CLI's `--overwrite` option updates existing items; it does not delete components removed from the registry.

Also remove these retired internal files from your copied registry directory after reviewing and
preserving any local modifications. A whole-project typecheck still checks obsolete files even when
your application no longer imports them; in particular, the old barrels re-export removed symbols.

```text
components/auto-form/fields/index.ts
components/auto-form/fields/choicebox.tsx
components/auto-form/proto/index.ts
components/auto-form/proto/schema.ts
components/auto-form/proto/validation.ts
components/auto-form/slot.tsx
lib/use-proto-form/proto-paths.ts
lib/async-handler.tsx
```

Paths above are relative to your registry installation root, commonly
`src/components/redpanda-ui`. Migrate imports to the current owning modules; do not recreate the
barrels or add typecheck exclusions for these retired copies.

`RedpandaProvider` now mounts the Base UI toast provider and viewport. Replace all `sonner` imports and remove the dependency. Do not mount a second global `Toaster` beside it.

```tsx
import { toast } from '@/components/redpanda-ui/components/toast/manager';

toast.add({ title: 'Settings saved', type: 'success', timeout: 5000 });
```

Use `toast.close(id)` instead of `toast.dismiss(id)`. Replace Sonner's `duration` with `timeout` and `action` with `actionProps`. See the [toast migration table](/docs/toast#migrating-from-sonner) for promise and action calls.

### Group and Choicebox retirement

The already deprecated Group and Choicebox entries are removed too, including their demos and legacy-only tests. See the [Group migration](/docs/group) and [Choicebox migration](/docs/choicebox).

`Group`, `useGroup`, `GroupContextValue`, `GroupPosition`, and `groupItemClasses` are no longer public APIs. Use ButtonGroup for attached actions, InputGroup for input composition, and FieldGroup/FieldSet for related controls. Button/Input/Select no longer read a hidden Group context. `Choicebox*` exports and AutoForm's `choicebox` built-in are removed; migrate field metadata to `radio` or register a deliberate custom renderer. No compatibility alias remains. An explicitly requested unregistered renderer now reports a configuration error with its requested name instead of silently substituting the schema's default control. Register custom controls through the existing formComponents/fieldRegistry contract.

### Client pagination uses native actions

`SimplePagination` now renders named native buttons, with real disabled endpoints and `type="button"`, rather than placeholder links that cancel navigation. Without `onPageChange` its controls are disabled. Use `PaginationLink`, `PaginationPrevious`, and `PaginationNext` with actual destinations for navigation; these remain anchors. Tests and CSS targeting the former SimplePagination anchors must migrate. Page/ellipsis identity follows the page or named gap, not its array position. Previous/next controls adopt the standard Button spacing recipe instead of local padding overrides.

## Chart colors use theme tokens directly

This is an additional breaking styling change, not deprecated-API cleanup:
`ChartStyle` and `ChartConfig.theme` are removed. ChartContainer no longer injects
a runtime stylesheet or generates `--color-${series}` aliases. The strict
no-runtime-stylesheet rule has no Chart exception.

Keep `ChartConfig.color` as optional series metadata and pass it directly to
Recharts. Replace every old series-name reference, including gradient stops,
strokes, and per-datum fills:

```tsx
const chartConfig = {
  desktop: { label: 'Desktop', color: 'var(--color-chart-1)' },
};

<Bar dataKey="desktop" fill={chartConfig.desktop.color} />;
```

The previous `fill="var(--color-desktop)"` no longer receives an automatic
declaration. Remove standalone ChartStyle calls and move former per-series
light/dark mappings into named tokens in the application's checked theme
stylesheet, preferably using the existing chart palette. Theme CSS now owns
first-paint and live theme resolution; no observer, effect, new wrapper, or raised
browser floor is required. Caller styles and native props still forward. An explicit
`id` now also reaches the native container; previously it was consumed only to name
the generated chart selector. `data-chart` retains its `chart-` prefix.

Tooltip indicators keep explicit color, datum fill, and payload color precedence,
then use matching config color as a fallback. Legend payload color takes precedence
over the same metadata fallback. Series labels, icons, and open string keys remain.
See the [Chart migration](/docs/chart#migrating-generated-chart-colors).

Tooltip `formatter` now follows the Recharts callback contract: its fifth argument
is the original series payload array, not the current data row. Read the current
row from the third argument's `payload` property instead. The fourth argument is
the original series index, including when hidden series are omitted from display.

## Form contracts

### KeyValueField rows have stable identity

`KeyValueField.value` and `onChange` now use `KeyValueRow[]`, not plain
`KeyValuePair[]`. Every row has a stable `id` and a `pair`. Allocate IDs once when
creating or loading rows with `createKeyValueRows`; retain them through edits,
clones and reordering. Do not regenerate IDs during render or from an array index.
Project back to plain submission or validation data with `keyValuePairs`.

For React Hook Form arrays, retain its field-array identity rather than creating
a second competing identity owner. See the checked examples and detailed
[KeyValueField migration](/docs/key-value-field#keyvaluerow-identity-and-helpers)
before overwriting copied components and updating callers.

`SchemaProvider<T>` now describes the validated **output**. Its `validateSchema` function accepts `Record<string, unknown>` and returns `SchemaValidation<T>`. Validate or normalize raw values before returning success. Do not claim that incoming values already have type `T`.

AutoForm's unused internal `proto/schema` and `proto/validation` forwarding modules are
removed. Import protobuf APIs from their owning `lib/protobuf-provider` entrypoint rather
than deep-linking through AutoForm. The supported AutoForm `defaultRegistry` export remains.

Passing a generated protobuf descriptor to AutoForm infers its generated output. A dynamic descriptor cannot promise an unrelated application type. Use an explicitly typed provider adapter when transforming messages into another result.

`useProtoForm` preserves generated oneof unions and branch-specific paths. Its submit callback receives the validator's output type. `createMessage` checks field values before returning a typed message instead of asserting raw data into that type.

### Strict protobuf values

`useProtoForm.createMessage()` requires native protobuf field values; a numeric
string is not an int32 value. Register ordinary React Hook Form number inputs
with `valueAsNumber: true`. The separate `ProtoProvider`/`formValuesToMessage`
form-conversion boundary intentionally supports numeric text. Clearing a numeric
input can produce `NaN`, which is invalid rather than a successful conversion to
zero. Integer fields must fit their actual scalar range; float/double infinity
support remains governed by the protobuf type and schema constraints.

64-bit form conversion supports integer text, bigint and safe integer numbers.
Preserve integer text rather than passing large values through lossy JavaScript
numbers; fractional values are not silently truncated.

Initialize message-valued defaults with the generated protobuf APIs. For example,
use `create(TimestampSchema, { seconds: 0n, nanos: 0 })`, not a plain
`{ seconds, nanos }` object masquerading as a typed Timestamp. Strict construction
continues to throw for incompatible values; do not catch and discard those errors.

Validation reports conversion failures at their real field, repeated-item, map-row
or oneof-value paths while retaining independent constraint errors. One bad number
must not replace unrelated email or username feedback. Message-level and affected
collection constraints are reported as incomplete until conversion errors are
fixed; a diagnostic projection is never successful submission data.

Replace `setOneofValue(path, case, value, options?)` with `setOneofValue(path, { case, value }, options?)`. Paths must identify a generated oneof, and the selection must retain its case/value correlation. Replacing the whole selection removes the previous branch's values.

```ts
form.setOneofValue('preferredContact', { case: 'preferredPhone', value: '+15551234567' });
```

`getNestedErrors(path)` returns `unknown`, not a caller-selected generic type. Narrow the dynamic result before reading it:

```ts
const error = form.getNestedErrors('shippingAddress.lineOne');
if (typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string') {
  showError(error.message);
}
```

`InputGroup` now renders a native fieldset. Change root refs from `HTMLDivElement` to `HTMLFieldSetElement`. Its addons remain presentational and do not add keyboard stops.

JSONField keeps explicit empty and falsy values. Examples are placeholders, not submitted data. Mismatched values show an error and can be corrected in JSON mode. Created options no longer mutate caller-owned arrays.

## Repository checks

Use the pinned Bun version from `package.json`. Run `bun run type:check` for **both TSRS and the TypeScript 7.1 dev-canary `tsc`**. Editor diagnostics and package-only checks do not replace the root quality gate.

Registry builds use the locked development-only shadcn CLI through Bun. They no longer
download an isolated older CLI to work around incompatible Zod versions. Consumer
registry JSON retains the same schema and source targets.

The source gate rejects authored deprecated declarations as well as deprecated API usage.
Remove retired exports and publish a breaking migration rather than keeping unused
compatibility declarations or exempting their tests.

Repair violations at source. Suppression directives, compiler opt-outs, ignored-looking authored paths, and weakened project settings are checked by the root policy scripts. Keep dependency and generated-output directories separate from authored source.

The rule gate compares both compilers' actual program inputs with authored TypeScript, JavaScript, JSX, and Astro files. A workspace's own project must include its source; importing it from another workspace is not a substitute. Imports cannot move compiled code into excluded build, dependency-free output, or context directories to avoid the source checks. Alternate compiler configs require explicit review rather than silently changing a file's compiler settings.

Authored source, configuration, and documentation must be real workspace files, not symbolic
links. Scanners share a filesystem inventory and reject source links, directory links,
and broken links rather than silently skipping them. The pinned TSRS editor executable
shim is not a source file; its exact dependency target is checked.

Do not force-add files under ignored dependency, output, or context roots. Tracked files
in those roots are rejected. Compiler inputs are also checked by their real filesystem
identity: a dependency-looking link cannot launder application source from an ignored
output directory or another checkout. Copy external development source into a checked
workspace or publish and install the dependency normally.

Vue and Svelte component files and runtime imports require renderer coverage review. They cannot silently bypass the current React/Astro compiler and rule policy.

The native quality config rejects per-file overrides, inherited configs, disabled plugins, and unreviewed source exclusions even when its root rule severities remain unchanged.

### MDX authoring

Documentation MDX is now a data-only presentation format. Move executable modules,
callbacks, handlers and interactive native HTML into checked TypeScript components,
then register those components through `packages/docs/components.ts`. Named data
imports must resolve to checked local source, not generated output or dependencies.

Literal metadata, static classes, inert JSX comments and fenced code examples remain
supported. Write displayed union types as strings, for example
`type: "'default' | 'outline'"`; the JavaScript expression `'default' | 'outline'`
computes zero rather than a type description.

### HAST type dependencies

Remove the obsolete `hast` runtime package from copied highlighter installations.
The registry now installs `@types/hast` as a development dependency for the type-only
AST imports. Keep `hast-util-to-jsx-runtime`: it remains the actual runtime renderer.
Reinstall `use-shiki` with `--overwrite` to receive the corrected dependency metadata.

### Typed mocks and client components

Declare mock signatures explicitly, including when supplying an implementation:

```ts
const save = vi.fn<(name: string) => Promise<void>>(() => Promise.resolve());
const actual = await vi.importActual<typeof import('./client')>('./client');
```

Call mock factories directly rather than extracting `vi.fn` into an untyped factory alias.
The bypass gate also checks factories nested inside assertions, which the pinned native
Vitest detector does not reliably visit. Use TypeScript for these mock factories.

Use dynamic-import module arguments in `vi.mock` so factories are checked against the
actual exports. For overloaded or generic APIs, use a real-module spy (for example,
`vi.mock(import('./client'), { spy: true })`) rather than erasing overloads through a
handwritten function mock. Keep fresh result factories when each call needs a new
Response or mutable object; a shared `mockResolvedValue` is not equivalent.

The exported `test-utils/vitest-motion-setup` now uses Motion's supported
`MotionGlobalConfig.skipAnimations` setting instead of replacing its components and
hooks. Tests retain real refs, contexts, `motion.create`, and MotionValues while
animations complete immediately. Keep actual animation and reduced-motion assertions
in browser tests.

A component in a `use client` module cannot be asynchronous. This applies to named,
arrow, and default component exports, including registry source copied into another framework.
The native gate runs the Doctor detector directly instead of relying on Next application
inference. Asynchronous non-component utilities and server components remain allowed.

### Separate recipe imports

Component entry points no longer export class recipes. Import recipes from the component's
`variants` module to separate non-component exports and improve Fast Refresh compatibility.
This does not guarantee a boundary: component entries that also export hooks can still
invalidate during an edit, depending on the bundler and the importing module.
This applies to Button, Badge, Table, Avatar, Code Block, Code Tabs, and the other
components that expose a class recipe. Component and type imports keep their existing paths.

```tsx
import { Button } from '@/components/redpanda-ui/components/button';
import { buttonVariants } from '@/components/redpanda-ui/components/button/variants';
```

The same path applies inside the workspace:
`@redpanda/registry/components/button/variants`. Reinstall each affected component with
`--overwrite` to obtain its recipe module before updating imports.

Pure helper exports also moved out of component entry points:

| Helper                                       | New module beneath the registry component directory |
| -------------------------------------------- | --------------------------------------------------- |
| `escapeHtmlText`, `replaceWithSanitizedHtml` | `sanitized-html/html`                               |
| `formPathToLabel`                            | `form-error-summary/error-data`                     |
| `initTheme`, `themeScript`                   | `theme-provider/theme-script`                       |
| `toast`, `createToastManager`                | `toast/manager`                                     |

Search validators now come from `lib/use-data-table-search-params/validator`, not
the hook entry point. `useDataTableSearchParams` and its provider keep their entry point.

Replace `renderMultiSelectOptions(options)` with `<MultiSelectOptions options={options} />`.
The component preserves recursive groups, separators, item identity, and selection behavior.
Demo-only column arrays, artwork data, and the secret adapter context are no longer public exports.
Button and Badge showcase inventories moved to their demos' `data` modules.

AutoForm's field matching metadata now lives in `auto-form/fields/definitions`.
Its existing root registry exports remain available. Internal scalar helpers live in
`fields/field-values`, test-id hooks in `fields/use-field-test-ids`, and option labels
render as `<OptionLabel option={option} />` rather than calling a JSX-returning helper.
The secret adapter context is private; use `SecretSelectorAdapterProvider`.

### Primitive styling

Use Label, FormLabel, and FieldLabel's `size="sm"` or `size="lg"` for their shared
type scale, and `variant="strong"` for semibold nested-field text. Do not apply
typography utilities to labels or use a native label as an unrelated section heading.

EditableText now requires a stable `label` for the input's accessible name. Keep
the label independent of the editable value, for example `label="Topic name"`.
Validation messages describe both the display action and editing input. Enter
commits and Escape cancels without losing keyboard focus; ordinary blur still
moves focus to the next control. The specialized inline editor owns its action
appearance rather than repainting the generic Button.

Use `Card padding="none"` for edge-to-edge media or rows. Use the header, content, and footer `padding` props for their insets. A directly nested padded header or footer supplies its matching top or bottom inset inside an edge-to-edge card.

Use `NavigationMenuLink variant="trigger"` for top-level links beside menu triggers instead of applying the trigger's class recipe at the call site.

Use `PopoverContent padding="none"` instead of `p-0`, or `padding="sm"` instead of `p-2`. Its default 16-pixel inset and calendar interaction are unchanged.

Use `Command variant="standalone"` for the rounded command panels instead of adding
`rounded-lg border shadow-md`. The default elevated and embedded treatments remain unchanged.

Sidebar menus now size against Base UI's `--anchor-width`. Replace legacy `w-[--radix-dropdown-menu-trigger-width]` classes in copied demos with `w-(--anchor-width)`.

SidebarMenuButton owns its open-popup background and text colors, including when animated
hover highlighting is enabled. Remove duplicated `data-[popup-open]` color overrides from
menu triggers. Leaving the pointer over the open menu no longer clears the trigger's surface.

Use `TableCell numeric` instead of a `tabular-nums` override. Use its existing `align`,
`weight`, and `truncate` props for those treatments.

Use `TableHead variant="compact"` for compact uppercase column headings. Use TableCell's
`density`, `font`, `size`, and `tone` props instead of duplicating padding, monospace,
font-size, and text-color utilities. The default table and cell appearance is unchanged.
See [cell appearance](/docs/table#cell-appearance) for the available values.

Use `Avatar shape="square"` instead of applying `rounded-lg` separately to the root and
fallback. AvatarImage and AvatarFallback now inherit the root radius. Loaded images in
square avatars now retain the square treatment instead of reverting to a circle.
The default round avatar is unchanged.

Input now preserves native caller styles instead of overwriting them when measuring adornments.
Measured prefix and suffix padding uses typed CSS custom properties. Check copied editable-text
inputs that previously relied on their explicit width being ignored.

Brand icons and the treadmill illustration now use fixed artwork variables supplied by the theme.
Reinstall `@redpanda/theme` with `--overwrite` when updating these components. Keep the new
`style/artwork-colors.css` file beside `theme.css`; its import is already included.
The vector paths and default artwork colors remain unchanged in light and dark themes.
These fixed pigments are not interface color or action-state utilities.

Auth0's fixed hosted-login colors now have named theme tokens, including hover and focus shadow colors. Update the
theme when reinstalling Auth0 too. The provider, focus, and background colors retain
their existing values; they do not switch to the application's dark palette.

### AutoForm context and failure reporting

The internal compatibility aliases `AutoFormRenderContext`, `AutoFormRuntimeContext`,
`useAutoFormRenderContext`, `useAutoFormRuntimeContext`,
`InternalAutoFormRenderContextValue`, and `InternalAutoFormRuntimeContextValue` are removed.
Use the single `useAutoForm()` context API instead of depending on the former split.

Field-change callback failures now appear in a separate alert. They are not field
validation failures and do not mark a valid input as invalid. Handle expected failures
inside your callback; do not rely on a browser-global unhandled-error reporter.

Boolean fields reject non-boolean values instead of treating strings such as `"false"`
as enabled. Array and map renderers reject scalar collection values. Invalid schema/value
combinations in JSONField show a recoverable error rather than silently coercing data.
Correct the input or use its JSON editor before submitting.

Payload-editor changes cancel ownership of an earlier parse result, including when the
newer text is malformed, the editor is reset, or the form unmounts. Custom asynchronous
parsers may still finish their own work; their obsolete result is not applied to the form.

`useTheme()` now requires a matching ThemeProvider, normally supplied by RedpandaProvider.
Calling it without a provider throws instead of returning an inert setter.

### Calendar composition

Use `Calendar variant="outlined"` for a framed calendar, `variant="elevated"` for a frame with a small shadow,
and `variant="embedded"` inside an already padded surface. The default remains the padded, unframed calendar.
Replace root border, shadow, background, and padding overrides with these variants.

`CalendarDayButton` uses DayPicker's day props directly. Configure locale on `Calendar`,
not separately on a day-button override. Its `data-day` attribute now uses stable
`yyyy-MM-dd` values, matching DayPicker's own date metadata, instead of locale-formatted text.
Changing `testId` no longer remounts custom day components or discards their state.

### Form and documentation IDs

Demo controls use instance-scoped IDs and matching label and description references.
Use role- or label-based browser locators instead of fixed demo IDs. `Auth0Input`
also generates its default DOM ID; pass `id` explicitly when a host integration
requires a fixed value. Its submitted field name remains `username`.

Component docs retain the stable `#recent-changes` heading fragment in their single
page footer. When composing several `ComponentChangelog` regions, pass a distinct
`headingId` for each region that needs a stable fragment. Omitting it keeps
instance-scoped IDs for accessible labeling; those generated IDs are not stable
permalink targets.

### Inset field labels

Replace `<Label className="px-1">` with `<Label inset>` for labels aligned with field
controls. Default label padding and native `htmlFor` activation are unchanged. The
`inset` prop is consumed by Label and is not forwarded to the DOM.

### Menu item and label recipes

Replace `<DropdownMenuItem className="gap-2 p-2">` with
`<DropdownMenuItem padding="comfortable">`. Standard padding remains the default;
items already own their icon/text gap.

Use `<DropdownMenuLabel variant="muted">` for small, subtle section headings and
`variant="identity"` for unpadded identity blocks. These replace the former
`text-body-sm text-subtle` and `p-0 font-normal` overrides without changing their
spacing, typography, or theme colors. Default section labels remain unchanged.

### Option labels

Replace `className="font-normal"` on Label, FieldLabel, and FormLabel with
`variant="option"`. Default labels retain medium weight. FormLabel owns its
validation color through Label's `invalid` appearance instead of restyling it.

### Badge and filter appearance

Badge and `badgeVariants` accept only `tone`, `variant="solid" | "subtle" | "outline"`,
`size`, and `disabled` for appearance. The exported `BadgeVariant` and `BadgeVariantKey`
compatibility types are removed. JavaScript callers using retired spellings now receive
an explicit error too.

Replace `variant="success-inverted"` with `tone="success" variant="subtle"`, and
`variant="primary-outline"` with `tone="primary" variant="outline"`. Replace the flat
`disabled` styles with the `disabled` prop. Preserve emphasis with
`disabled variant="subtle"` for `disabled-inverted` or `disabled variant="outline"`
for `disabled-outline`. The `simple`, `ghost`, and `link` aliases
are removed; choose a semantic tone/emphasis and use `render` for a real anchor.
Bare `variant="outline"` now uses the default tone's outline treatment rather than
the former generic border/foreground recipe.

Call `badgeVariants({ tone: 'success', variant: 'subtle' })` instead of passing a
flat style key. Its former CVA `class`/`className` options are also removed; merge
caller-owned layout separately.

DataTableFilter uses the same two axes. Its default is still `tone="primary"`
with `variant="subtle"`. Replace `variant="default-outline"` with
`tone="default" variant="outline"`. `DataTableFilterVariant` now means only the
three emphasis values, not the internal Badge style-key union. The variants demo
no longer presents fake-disabled filter controls.

## Readable code defaults

Code Block, Code Editor, Code Tabs, and `useShiki` now use
`github-light-high-contrast` for their default light syntax palette. The dark default
remains `github-dark-default`. Custom themes remain supported; check their ink against
the real background rather than assuming a theme name guarantees readable contrast.

Code Comparison focus annotations now outline focused lines instead of blurring and
fading the surrounding code. The default highlight color is `var(--color-accent)`,
not a solid destructive surface. No hover gesture is needed to read the full example.

## Input appearance ownership

Replace Input color, shadow, typography, and padding overrides with the finite `appearance`, `tone`, `textStyle`, and `size` props. `tone="success"` now owns verified-value focus feedback, while `aria-invalid` remains authoritative. Use `textStyle="monospace"` for machine values and `InputStart` / `InputEnd` for measured adornment space.

Composite fields use `appearance="embedded"` so their containing control owns the focus ring. Inline editable text uses `appearance="inline"` and inherits its surrounding heading or body typography instead of falling back to the normal input text size. Compact list search uses the existing `sm` spacing scale.

`Auth0Input` is the hosted provider’s branded native field, not a restyled general Input. Its props follow native input props; general Input adornment, step-control, and appearance props do not apply.

Native color-swatch controls use `ColorInput` rather than transparent text Input overrides. `presentation="swatch"` retains the painted proxy at rest and reveals the native picker on keyboard focus.

### Native form composition and choice labels

Wrap popup triggers in `FormControl`, rather than rendering `FormControl` inside a
trigger's render prop. The outer control supplies the ID before the trigger creates
its internal relationship. This keeps FormLabel activation, accessible naming, and
validation descriptions bound to the real trigger.

```tsx
<FormControl>
  <PopoverTrigger render={<Button variant="outline">Choose a date</Button>} />
</FormControl>
```

Label and FormLabel now own their whole-choice surfaces. Replace card, pill, attached,
tile, and compact-card appearance overrides with the corresponding `surface` prop.
Selection and disabled paint follow the real control, not a duplicated React state.
Checkbox cards now share the radio-card spacing, rounding, and selected treatment;
enabled provider tiles no longer look disabled. Ordinary optional labels use the
shared label gap, and labels in addons inherit their container's text tone.

### Banner native props

Banner now forwards its native div ref and caller style contract. A supplied `height`
prop takes precedence over `style.height`; otherwise native height is retained, with
`3rem` as the default. Other caller style properties are no longer overwritten by the
component's height calculation. Authored repository styles still use the theme and
CSS custom-property policy; forwarding consumer props is not a styling exemption.

### Textarea recipes and native validation

Replace appearance overrides on `Textarea` with its finite recipes. Use `textStyle="monospace"` for scalar code values, `textStyle="code"` for JSON, or `appearance="document" textStyle="code-compact"` for recessed CSS/code documents. Keep `className` for layout/dimensions. Existing `size` and `resize` props remain supported, and `TextareaProps` is exported.

Use `InputGroupTextarea` for composite inputs: its group owns border/focus, while the textarea keeps native multiline editing and content autosizing without a resize handle. Decorative addons now focus textarea controls as well as inputs.

Remove redundant destructive-border classes; set `aria-invalid` or use `Field` validation context. Name JSON editors and associate visible errors with `aria-describedby`. The AutoForm payload JSON editor now exposes these semantics while retaining invalid drafts for correction.

The form-control matrix no longer paints fake Hover/Focus columns. It shows real rest, filled, invalid, and disabled states in an inert specimen grid; its link opens the live interaction gallery for actual keyboard/pointer states.

### Finite lazy-loader maps

Runtime imports must use literal specifiers. Unbounded dynamic imports and bundler
`glob`/context calls can pull test-only implementations into production, so the source
gate rejects them. Reusable test support belongs outside runner-discovered test entries.
Type-only ambient imports and inert JSON fixtures remain supported.

After adding a playground route, run `bun run --filter=playground update:components`.
After adding or removing a registry demo, run `bun run --filter=docs update:demos`.
Both loader maps remain checked source. `bun run lint:loaders` rejects missing or stale
maps without modifying them; the ordinary root lint and quality commands require it.

## Auth0 action ownership (breaking)

`Auth0Button` and `Auth0SubmitButton` now own their two fixed hosted-login appearances, rather than repainting a general Button. They render through Base UI's native button primitive; no generic headless action or new global Button variant is exported.

Remove these inherited general-Button props from Auth0 actions:

- `variant`, `size`, `weight`, `shape`
- `className`, `style`
- `as`, `to`, `href`, `target`, `rel`, `icon`

Put layout spacing on a containing element. Do not replace an appearance override with paint on a wrapper, child, or custom `render`. Hosted palette values remain the centrally declared `static-auth0-*` theme tokens. Use the general Button for a general application action rather than passing application Button recipes into a hosted Auth0 action.

Native attributes and events, `ref`, `render`, `nativeButton`, `focusableWhenDisabled`, `disabled`, `isLoading`, and `testId` remain supported. `render` still infers non-native keyboard behavior for a known non-button intrinsic; custom component renders can state `nativeButton={false}` explicitly. Loading sets busy/disabled state and retains a centered spinner without replacing the label's natural geometry. A focusable disabled action remains keyboard-focusable but cannot activate.

Provider connection IDs remain open strings. Known IDs select the existing icon; unknown IDs keep an empty icon slot. `data-provider`, primary/secondary action markers, `data-slot="button"`, test IDs, and native form metadata remain intact. Submit defaults remain `type="submit"`, `name="action"`, `value="default"`; native caller overrides still work.

### Intentional visual corrections

The provider's declared hosted border was previously overridden by the general outline Button's important neutral border: measured rest color was `rgb(89, 114, 166)`. It now uses the existing hosted token, `rgb(201, 202, 206)`, including hover. Pressing a provider action no longer adds the general outline Button’s neutral active background/border over the hosted treatment; the existing visible keyboard focus treatment remains authoritative. Provider icons now honor their declared 20-pixel size instead of the general Button's 16-pixel descendant SVG rule.

The measured 52-pixel action height, 3-pixel corners, text/padding, resting shadow, active scaling, and composed visible keyboard focus remain preserved. Auth0's light hosted surface remains fixed under both application themes; no claim is made that the corrected border/icon pixels match the old bug.

Reinstall the affected Auth0 component and its installed reverse dependents through the registry CLI. Registry regeneration must carry the direct Base UI dependency and Spinner registry dependency instead of retaining a general Button dependency solely for these actions.

## MotionHighlight: explicit item and foreground composition

This is a breaking composition cleanup. `MotionHighlight` no longer discovers or wraps children. Wrap each selectable foreground in an explicit `MotionHighlightItem`. Remove `controlledItems` and `itemsClassName`; put the latter's wrapper classes on the appropriate item.

```tsx
<MotionHighlight defaultValue="topics">
  <MotionHighlightItem value="topics" render={<Button onClick={openTopics}>Topics</Button>} />
</MotionHighlight>
```

The old `<MotionHighlightItem><Button>Topics</Button></MotionHighlightItem>` implicitly cloned its child. Move that foreground element to `render`. Plain `children` now populate the default foreground `div`; they are not inspected, cloned, or treated as a render target. A render callback receives the supported Base UI props and `{ active, disabled, value }` state.

Remove `asChild`. Items consistently retain their decorative wrapper while enabled. `render` replaces the foreground, not that wrapper. An item `ref` still references its wrapper `div`; put the native button/link ref on the element passed to `render`. Disabling the entire decoration with `enabled={false}` removes the wrapper, clears its ref, and preserves the native foreground/ref/actions.

Provide `value` explicitly for controlled/default selection. Identity is no longer inferred from the foreground's `id` or `data-value`. `value` takes precedence over the item's own `id`; without either, a React-generated identity is used. Put a semantic `role` on the item when requesting automatic `aria-selected`; it is forwarded to the foreground and only selection-supporting roles receive that attribute. Ordinary buttons and links do not receive invalid `aria-selected`.

Native/render handlers run once, before item handlers and internal activation. `event.preventDefault()` cancels decorative activation; Base UI's supported `preventBaseUIHandler()` composition also remains available. Native Enter/Space activation remains native rather than being synthesized by the highlighter. Hover handlers likewise run once and can cancel before activation.

`disabled` is decorative: omitted item values inherit the root; explicit `disabled={false}` overrides that inherited setting. It prevents highlight activation, not the underlying action. Set `disabled` on the native Button separately when the action itself must be unavailable.

Actual Sidebar and Files consumers retain their existing wrapper, peer/data-slot, foreground and native-control composition. This slice does not change the parent indicator's layout animation or introduce LazyMotion.

## MiniCalendarDay: owned date-action appearance

`MiniCalendarDay` now owns its finite month/day layout, selected/today paint, focus feedback, disabled state, and loading treatment directly on the installed Base UI Button primitive. `MiniCalendarNavigation` remains the ordinary registry Button; its existing appearance API was not changed.

### Breaking API changes

- Date actions no longer inherit generic Button appearance props (`variant`, `size`, `weight`, `shape`, `icon`), polymorphic/navigation aliases (`as`, `to`, `href`, `target`, `rel`), `className`, `style`, or replacement `children`. Use a containing element for layout and a normal Link for navigation. No unstyled/style escape was added.
- Date actions own `aria-pressed`; select through `MiniCalendar`'s `value`, `defaultValue`, and `onValueChange` instead of writing that attribute.
- A day `onClick` now composes with selection instead of replacing it. The caller runs first; `event.preventDefault()` or `event.preventBaseUIHandler()` cancels selection. Existing handlers that implemented selection themselves should instead observe `onValueChange`, or explicitly cancel if replacing selection is intentional.

```tsx
<MiniCalendar onValueChange={setDate} value={date}>
  <MiniCalendarDays>
    {(day) => <MiniCalendarDay date={day} disabled={!canSelectDate(day)} key={day.toISOString()} />}
  </MiniCalendarDays>
</MiniCalendar>
```

Native attributes/ref, native `type` override, Base UI `render`/`nativeButton`/`focusableWhenDisabled`, `disabled`, `isLoading`, and `testId` remain supported. A day defaults to `type="button"`, so ordinary selection does not submit an enclosing form. Loading preserves the accessible date name and sets busy/disabled semantics. A known intrinsic non-button render automatically receives Base UI's non-native keyboard behavior; custom components can provide `nativeButton={false}`.

Default icon-only navigation now has `Previous dates` / `Next dates` labels. Custom navigation render elements retain their own names; the existing custom-layout demo now explicitly labels both icon-only actions. The existing seven-day demo now demonstrates disabled weekends.

## LoadingAnimation: explicit SVG progress

`LoadingAnimation` no longer inspects or clones SVG children to inject `progress`.
Pass progress explicitly to the SVG component. The container's `progress` still
controls Lottie playback; it does not overwrite independently controlled child props.

```tsx
<LoadingAnimation type="svg">
  <YourSVGComponent progress={progress} />
</LoadingAnimation>
```

Existing WaitingRedpanda and RunningRedpanda compositions already pass their progress
explicitly. Their playback behavior remains unchanged.

## CheckboxView: decoration, not an inaccessible checkbox

`CheckboxView` no longer exposes a non-operable `role="checkbox"`. It is hidden
from the accessibility tree. Its containing control must supply the accessible
name, keyboard behavior, and selected/pressed state. Use `Checkbox` for an actual
checkbox, not a click handler on the decoration.

The decorative API now contains only `checked`, `variant`, `className`, `ref`, and
`testId`. Remove inherited div event, ARIA, role, and tab-index props. Query the
owning control by role or the decoration by its explicit test ID; the old
`[role="checkbox"]` selector no longer identifies decorative ink. Its visual
checked/indeterminate treatment and div ref remain supported.

## ItemGroup: native list structure

`ItemGroup` renders `ul` rather than a div with a list role. Its native props and
ref now use `HTMLUListElement`. Wrap each item in an explicit `li`, and keep an
inter-item separator inside the preceding `li`. Do not put `role="listitem"` on a
linked Item: the enclosing `li` owns collection semantics and the anchor keeps
its link semantics. Standalone Item composition and its `render` prop are unchanged.

```tsx
<ItemGroup>
  <li>
    <Item render={<a href="/topics/orders" />}>Orders</Item>
  </li>
  <li>
    <Item>Audit</Item>
  </li>
</ItemGroup>
```

## Documentation environment

The optional `LOGO_DEV_TOKEN` must be a publishable `pk_` key, never a secret
`sk_` key. Logo badges embed this value in statically published image URLs.
Empty/unset values remain optional. The centralized typed environment module uses
Astro's `import.meta.env` server boundary; components no longer read `process.env`.
See [Logo.dev's key distinction](https://github.com/logo-dev/logo-api) and
[Astro environment variables](https://docs.astro.build/en/guides/environment-variables/).

## ButtonGroup: native action grouping

`ButtonGroup` now renders a native `fieldset`, with implicit group semantics.
Change div refs and native props to `HTMLFieldSetElement`. Its orientation,
connected-button treatment, nested groups, and ordinary Tab navigation remain;
this is not a toolbar with arrow-key focus management. Supply `aria-label` or
`aria-labelledby` when the group needs an accessible name. The native `disabled`
prop now disables its descendant form controls through browser semantics.

## Open demo exports

Open-state demos now have named exports, like the other registry demos. Replace
`import DialogOpenDemo from '.../dialog-open'` with
`import { DialogOpenDemo } from '.../dialog-open'`. The same change applies to
AlertDialog, Combobox, Command, Credenza, Drawer, DropdownMenu, HoverCard,
Menubar, MultiSelect, Popover, Select, Sheet, and Tooltip open-state demos.
There is no default-export compatibility alias.

## Canonical demo entrypoints

Documentation loader shards now select the exact named `PascalCaseFolderDemo`
export at compile time. Both `loadDemo('theme-background')` and
`loadDemo('theme-background-demo')` select `ThemeBackgroundDemo`, never the first
helper function in a module. Default-export and arbitrary first-function fallback
behavior is removed; fix a demo's named export rather than depending on module order.

`InputGroupTextareaExample` is now `InputGroupTextareaDemo`.
`ButtonGroupDropdownDemo` is now `ButtonGroupDropdownMenuDemo`. There are no aliases.
Regenerate checked demo loader maps with `bun run --filter=docs update:demos`.

## Calendar native date-action ownership

`CalendarDayButton` now owns its finite date-action appearance on a true native button, using the installed DayPicker protocol rather than repainting the general registry Button. The single/range endpoint/range middle/today/focus/disabled states, native events, native attributes, custom day content and stable `day.isoDate` metadata remain supported. DayPicker, not a second keyboard manager, owns keyboard movement and selection.

### Breaking public paint-map changes

Removed from `Calendar`: **`classNames`, `styles`, `modifiersClassNames`, `modifiersStyles`**. Legacy map props also fail explicitly at runtime, including when forwarded through an object spread, instead of overriding the owned recipes or being silently discarded. Remove all four keys from wrapper props and forwarded objects: a present key with an `undefined` value is still a retired prop and fails the same guard. Supported optional props keep their declared undefined behavior; this is not a blanket ban on undefined values.

Use `variant` for the existing finite surface treatment. The new **`booked`** prop accepts a DayPicker `Matcher | Matcher[]` and owns the real struck-through booking treatment, including the existing full-opacity booked-cell presentation. It marks appearance only; pass `disabled` for unavailable dates. Existing custom `modifiers` still reach DayPicker, and other selection/availability semantics are unchanged.

```tsx
const bookedDates = [new Date(2026, 9, 26), new Date(2026, 9, 27)];

<Calendar
  booked={bookedDates}
  disabled={bookedDates}
  mode="single"
  onSelect={setDate}
  selected={date}
  variant="elevated"
/>;
```

**Distinctions, not a claim that every style surface disappeared:**

- Calendar root **`className` and singular `style` remain** the separate existing layout/cell-sizing contract. This slice did not migrate those APIs or their existing cell-size callers. They are not replacements for the retired day-paint maps.
- Incoming **`CalendarDayButton.className` and `.style` remain DayPicker native protocol props**, needed by custom day wrappers forwarding the framework's props; no general Button `variant`/size/render/navigation inheritance is introduced. `components.DayButton` still supports real custom native day content.
- `buttonVariant` still styles only month navigation via the existing Button recipe. The month-navigation styling and existing root cell-size overrides were not hidden or redesigned.

Forward all received native props and the original `day`/`modifiers` from a custom `components.DayButton` to `CalendarDayButton`. Do not replace DayPicker's supplied `disabled` with `modifiers.disabled`: it deliberately leaves the focused disabled day natively focusable with `aria-disabled`, while suppressing selection. Date actions default to `type="button"` and retain an explicit native `type` override.

Forwarded refs now compose with the internal focus ref rather than replacing it. The public `CalendarDayButtonProps` includes the native button ref; React object refs and callback cleanup are retained. `CalendarProps` also preserves DayPicker's discriminated selection modes while omitting the retired maps.

## Theme gallery demo imports

The aggregate `@redpanda/registry/demo/theme` exports `ThemeDemo`, not individual section demos. Import an individual demo directly from its existing dedicated entrypoint instead:

```tsx
// Before
import { ThemeBackgroundDemo } from '@redpanda/registry/demo/theme';
// After
import { ThemeBackgroundDemo } from '@redpanda/registry/demo/theme-background';
```

| Export                 | Dedicated entrypoint suffix |
| ---------------------- | --------------------------- |
| ThemeBackgroundDemo    | theme-background            |
| ThemeBordersDemo       | theme-borders               |
| ThemeChartDemo         | theme-chart                 |
| ThemeElevationDemo     | theme-elevation             |
| ThemeFamilyMapDemo     | theme-family-map            |
| ThemeSemanticPairsDemo | theme-semantic-pairs        |
| ThemeShapeDemo         | theme-shape                 |
| ThemeSidebarDemo       | theme-sidebar               |
| ThemeStaticDemo        | theme-static                |
| ThemeTextDemo          | theme-text                  |
| ThemeToneBordersDemo   | theme-tone-borders          |
| ThemeTypographyDemo    | theme-typography            |

Use `@redpanda/registry/demo/<suffix>` for each mapping. Dedicated export names and rendered sections are unchanged.

## Documentation preview controls

Preview fullscreen/restart controls now have explicit accessible action names and
fullscreen state. The unused tweak-pane branch is removed. Controls use the owned
Button recipes and dynamic viewport sizing instead of restyling Motion buttons.
The external editor affordance is a real link: its complete registry URL is encoded
as one query parameter and retains normal browser link behavior. Its inverse Button
recipe and always-visible text intentionally replace the bespoke responsive paint.

## ListView: move row interaction to an explicit action

`ListView` is now a presentational grid, not a button-like container. Remove `interactive`, all root event handlers (including `onClick`), `role`, `tabIndex`, and `contentEditable`. Whole-row hover/pointer behavior is removed. Put the action in `ListViewEnd`: a native Button for an action, an application Link for navigation, or a Switch/menu trigger. Do not wrap the flow-content row in a button.

```tsx
<ListView>
  <ListViewStart title="Cluster A" description="Production workload" />
  <ListViewEnd>
    <Button onClick={openDetails} type="button">
      Open cluster
    </Button>
  </ListViewEnd>
</ListView>
```

The root's finite prop surface is `children`, `className`, `id`, `ref`, `style`, `title`, `hidden`, `inert`, `dir`, `lang`, `size`, `variant`, and `testId`. Generic DOM passthrough is removed; legacy event/role props in spread objects are not forwarded at runtime. The ref still targets the layout `HTMLDivElement`; existing slot and group APIs remain unchanged. Label icon-only actions and switches with an item-specific accessible name. The demo's four former no-op row actions now have actual independent selection Buttons with `aria-pressed` and visible state.

## MultiSelect: separate the opener and selected-value actions

Replace the former `MultiSelectTrigger > MultiSelectValue` nesting:

```tsx
<MultiSelect>
  <label htmlFor="framework-choice">Frameworks</label>
  <MultiSelectControl className="w-96">
    <MultiSelectValue maxDisplay={3} maxItemLength={20} />
    <MultiSelectTrigger id="framework-choice" placeholder="Select frameworks" />
  </MultiSelectControl>
  <MultiSelectContent>{/* Existing search/list/items */}</MultiSelectContent>
</MultiSelect>
```

- Import new `MultiSelectControl`. Move the outer width/layout classes there.
- Move `placeholder` from Value to Trigger. Value no longer accepts it.
- Trigger no longer accepts children. It is a genuine native opener, not a DIV role button. Its native event target/ref is HTMLButtonElement; native button attributes go there. `type="button"` is owned, not configurable. No public render/nativeButton/asChild escape added.
- Keep selected chips outside the opener; the shell itself is noninteractive. Native removal buttons are independent siblings, not pressable descendants.
- Wrap only Trigger in FormControl, not the shell; pass field ID, name, ref, onBlur, invalid/described-by metadata to Trigger. Associated labels/aria-labelledby now name the real button without being overridden by the generic fallback label.
- SimpleMultiSelect composes this anatomy internally. Its options/value/width API remains; aria-label/aria-labelledby are additionally supported. onOpenChange can access Base UI details, while existing boolean-only handlers remain assignable.

## JSONField array editing identity

No public prop/export changed. Internal insert/remove transactions now preserve retained row controls and local state after the parent accepts the emitted JSON. This includes React Hook Form, which clones accepted values. Ignoring or rejecting an update no longer changes row identity.

Unmatched external document replacements reset form-local state safely, including reordered/replaced arrays. The implementation never guesses row identity from values. An equal-value external replacement during a pending edit is indistinguishable from an equivalent cloned acknowledgment; use a standard React key for an unconditional reset:

```tsx
<JSONField key={documentRevision} onChange={setValue} schema={schema} value={value} />
```

Only JSON domain values reach onChange/submission; UI IDs remain private. Existing schema-owned created choices survive normal mode changes.

### Card section recipes and calendar examples

Use `<CardHeader separator>` or `<CardFooter separator>` instead of authored border/padding overrides. The recipe adds the section border and 16px inset for `size="sm"`, 24px for other sizes including omitted default md. Existing unseparated defaults and legacy border class spacing remain unchanged; this API addition alone is nonbreaking.

The calendar examples intentionally standardize compact Cards to the existing sm recipe (8px section gap, one horizontal inset), footer gaps to finite values, time-preset footer insets to24px, and preset actions to natural wrapping. Inline calendar cells are40px instead42px to fit mobile without overflow. These are reviewed example appearance changes, not exact geometry preservation.

### Navigation Menu recipes

Use `NavigationMenuLink variant="featured" | "icon" | "rich"` for promotional destinations, compact icon rows, and icon/title/description rows instead of overriding owned paint/spacing. Existing default/trigger variants and native link/ref/render contracts remain.

Use `NavigationMenuPanelButton direction="forward" | "back"` for inline submenu actions, not destination links. It is a native non-submitting Button; pass onClick/disabled/ref normally. The demo restores Back/originating-action focus across panel changes.

Examples intentionally standardize featured padding to24px and Home columns to equal widths, and collapse wide mobile grids into readable single columns. Popup dimensions/position snap to Base UI's measured values instead of animating layout properties; entrance/content compositor motion remains. No claim of pixel-identical example output or measured performance gain.

## Upload and loading controls

Dropzone owns its upload-action recipe instead of repainting Button. Use `appearance="avatar"` instead of `className="size-32 rounded-full p-0"`. Its `className` now controls the outer layout, while the forwarded ref and test ID still identify the native upload action. The native file input is a hidden sibling, not a second exposed control nested in a button. Keep custom action content phrasing-only and noninteractive; put independent actions outside Dropzone.

Every rejected file and validation reason is displayed in the built-in, associated error message. `onError` receives an `AggregateError` containing all rejection reasons and filenames; remove duplicate validation alerts from callers. A later accepted selection clears that error. Native picker/validator errors and synchronous `onDrop` failures remain visible even without an `onError` callback. Upload action identity no longer resets when the accepted file list changes.

Button loading states retain their accessible action name while the spinner is visible. Icon-only loading examples provide explicit names. Native disabled/busy behavior and geometry are unchanged.

The documentation package-manager selector adopts the owned ToggleGroup recipe, with a named group, exactly one pressed choice, and arrow/Space keyboard selection. This intentionally replaces the custom miniature paint and spacing; generated commands keep their selected manager and overwrite behavior.

## AutoForm: replace marker children with explicit slots

`AutoFormSlot` and its before/after marker-child API are removed, without a compatibility alias. Pass `slots: readonly AutoFormSlotDescriptor[]` instead:

```tsx
<AutoForm
  schema={schema}
  slots={[
    { id: 'intro', placement: 'start', content: <p>Account details</p> },
    { id: 'name-help', placement: 'before', field: 'username', content: <p>Choose a username.</p> },
  ]}
/>
```

Every descriptor has a caller-owned unique/nonblank stable ID and exactly one placement: `start`, or `before`/`after` with a root schema field key. Do not infer IDs from labels/content/index. Unknown root anchors and invalid IDs fail visibly with no usable form below the error. Correct the slots through a new array to retry with the same schema; ordinary valid descriptor-array changes do not reset form state.

Start content appears once per currently rendered mode/step panel, not once per configured step. Anchored content follows its field across Simple/Advanced and steps, hiding when the field is not part of the current projection; JSON has no form slots. Slot state remains stable on insertion/reordering within a mounted placement. Ordinary children are no longer traversed or assigned replacement keys: existing non-stepper children still precede fields, and stepper children still follow controls. Replace prior ambiguous markers with both before+after by separate explicitly keyed descriptors if both placements are genuinely wanted.

## Stat: explicitly render linked labels

`labelLink` is removed without an alias. Replace empty link elements with `renderLabelLink`, applying the supplied `StatLabelLinkProps` (`className`, `children`) to the actual native or framework link:

```tsx
<Stat
  label="Topics"
  value="6"
  renderLabelLink={(props) => <a {...props} href="/topics" />}
/>

<Stat
  label="Topics"
  value="6"
  renderLabelLink={(props) => <Link {...props} to="/clusters/$clusterId/topics" params={{ clusterId }} />}
/>
```

The caller owns href/to, refs, events, accessible-name overrides, and any explicit class merging. Supplied children contain the label and decorative arrow; supplied className retains the caption/hover/focus recipe. Stat no longer clones or inspects link elements. Plain and linked Stat remain statically server-renderable; no new client directive/hooks/composition dependency.

## Explicit identity in animated AvatarGroup, BadgeGroup and DotGroup

- Animated `components/avatar-group` replaces JSX children with readonly `{id, content: ReactElement}` items. The static AvatarGroup from `components/avatar` is unchanged.
- BadgeGroup replaces JSX children with readonly items containing required `id`, `content` and plain-text `textValue`. Its overflow renderer receives the actual typed item records, including inferred domain fields, not ReactNode children. Hidden textValue entries remain available to assistive technology before a supplementary visual tooltip opens.
- DotGroup replaces arbitrary JSX children/implicit injected props with explicit `{id,kind:'status',variant?,pulsing?,label?}` or `{id,kind:'count',count,max?,variant?,label?}` descriptors. Status-only groups support all sizes; count/mixed groups require sm/md/lg. Compose arbitrary custom elements separately; there is no compatibility clone path.
- IDs must be nonblank, unique and independent of editable display content or list positions. They survive reorder/edit/delete; changing an ID intentionally remounts its item. Empty/duplicate IDs fail loudly. Badge/Dot maxVisible must be a nonnegative integer.

Avatar motion now owns local synchronous minimal Motion features, preserving supported full-Motion content through a non-strict boundary. No provider setup is required. Measured cold-consumer reduction is not guaranteed for apps already loading full Motion.

## Tags and removable badge examples

`TagsValue` with `onRemove` now requires an item-specific `removeLabel`, for example
`removeLabel="Remove React"`. Omit both props for passive tags. The name is consumed
by the native action, not forwarded to the tag span. Removing a selected tag restores
focus to its enclosing picker without opening the popup.

Tags removal actions now use the standard destructive-ghost icon Button, and the
removable Badge example uses large Badges with standard ghost icon actions. These
are intentional target-size, typography, wrapping, and theme-tone changes rather
than pixel-identical miniature controls. Its reset action uses the normal link
Button. Repeated keyboard removal/reset retains focus on the next available action.

## CommandDialog owns its dialog recipe

`CommandDialog.commandProps.variant` is removed. Remove nested `variant` overrides; the wrapper now always selects the actual `dialog` recipe, including its input, group, and option geometry. Use a standalone `Command` for another surface variant. `commandProps.size`, filtering, labels, metadata, and layout-only `className` remain supported.

Triggerless keyboard palettes can pass `finalFocus`, with the same public contract as `DialogContent`, to identify their closing focus destination. Capture the focused control before opening, not again while closing. The Command dialog demo and documentation demonstrate an invoking-control ref, so Escape or the shortcut returns focus to the original control rather than the document body.

## Files and CodeTabs

- CodeTabs owns compact code-viewer spacing. Remove inherited generic Tabs size; supported visual variant is standard (default). Native root/ref/keyboard props remain. Controlled value is authoritative: onValueChange requests a change but cannot override rejected parent value. onCopy receives selected original source string, not a clipboard event.
- Folder no longer accepts generic AccordionItem render replacement. Keep Files/Folder/File composition; customize children/sideComponent. Native root/folder/file refs remain. File leaves remain passive display divs; folders are Tab/Enter/Space disclosures, not ARIA trees.
- Replace Git-state recolors/indicators with status="modified" or status="untracked". Owner provides text tone/M/U or dot and permanent readable status. sideComponent overrides visual indicator, not accessible status.
- Folder layout and CodeTabs underline now position immediately instead of interpolating layout dimensions. Folder icon/moving row-hover motion remains; keyboard focus has its own 3px ring. Copy uses standard ghost hover/pressed tones.
- Files owns native anatomy. Update selectors targeting generic Accordion implementation/private file-button wrapper to actual file, folder-trigger, folder-content slots. install-tabs root/header/content slots remain.

## Compact annotation examples

JSONField now surfaces an invalid JSON draft after the existing 300-millisecond
pause instead of silently swallowing its parse failure. The draft remains editable;
correcting it clears the error. Parent update failures are visible too, and switching
to the Form view still requires valid JSON. This is intentional feedback, not a
successful coercion or a reason to discard the user's pending text.

JSONField's schema-type annotations use the default outline Badge spacing rather
than miniature padding overrides. Avatar notification examples use CountDot for
counts, with intrinsic width for multiple digits and accessible notification labels;
the overflow count remains visually capped at `99+`. Avatar status dots use their
existing `stacked` halo recipe. Refresh the affected examples with their primitive
dependencies; notification count typography and halo geometry intentionally standardize
to CountDot rather than the old repainted Badge.

### Table pinned boundary columns

Use matching TableHead/TableCell pinned="start" or pinned="end" instead of caller sticky/edge/border/background classes. One boundary column per logical edge only; there is no multi-column offset API. The component owns an opaque card surface, correct hover/expanded/selected row washes, separators and stacking. That card surface remains opaque with Table variant="simple"; horizontal pinning does not enable a vertically sticky header. Existing native refs/attributes/content remain supported. No public API removed.

The three examples now use standard Badge/Button/Table recipes, human column labels, named search/page-size controls, and a wrapping mobile toolbar. Intentional standardization, not pixel parity. Refresh installed Table and the affected demos together so copied variants.ts and consumer calls agree.

### Menu-triggered confirmation examples

The nested-menu demo now shows two supported compositions: a controlled dialog under `DropdownMenuContent keepMounted`, and the recommended controlled sibling dialog. The former active “Broken” branch was removed: its current `closeOnClick={false}` behavior did open the dialog, contradicting the explanation. The unmounting risk is now factual, inert guidance rather than a deliberately incorrect interactive example.

Use a real `DropdownMenuItem.onClick` to set dialog state; wrapping it in a non-native `AlertDialogTrigger` changed its exposed role to button and interfered with menu semantics. A kept-mounted descendant whose invoking menu item becomes hidden supplies the actual menu-trigger ref as the popup's `finalFocus`. The sibling example does not need that override. The existing topic example intentionally retains its menu while its confirmation is open.

All fixed account/topic/cluster confirmation copy and Cancel/action labels remain unchanged, now owned by distributable `lib/demo-delete-confirmation`. Dialog roots, triggers and open-state teaching remain in the demos. No component API is removed by this extraction.

## Sidebar anatomy

- Sidebar now owns its mobile navigation Dialog, rail, contextual separator and navigation highlight recipes rather than repainting Sheet/Button/Separator. Keep existing Sidebar composition, state callbacks, rail actions and menu APIs.
- Mobile Sidebar native ref and attributes now reach the actual popup (previously the ref was dropped). It still uses the configured portal container and slide/overlay behavior. Escape/outside dismissal restores opener focus.
- An accessible Close sidebar action is paint-clipped at rest and visibly revealed on keyboard focus. Its absolute box never changes; it cannot intercept the pointer while clipped. This adds a real modal escape for assistive technology without inserting an idle header row.
- SidebarSeparator no longer accepts generic Separator variant: its contextual sidebar-border style always overrode that color prop. Orientation, decorative/semantic roles, ref/native attributes remain.
- Generic Sheet implementation selectors are no longer Sidebar anatomy. Use sidebar, sidebar-overlay and existing menu slots. collapsible="none" still stays inline on mobile; the right inspector example intentionally follows that contract.

### Interaction-state examples use owned recipes

Topic information in the registry interaction-state demo is now a semantic data table, not rows that look pressable but have no action. The Playground's service actions remain real native buttons with exclusive pressed state and visible Selected text; they now use the existing ghost/lg recipe and list-owned dividers instead of repainting Button. Standard40px targets/radius/padding/typography intentionally replace the former custom row treatment. No component API is removed.

## Theme specimen copy actions

Theme token captions and swatches now use standard link/ghost Button recipes.
The color sample is inert content inside the action, not a replacement Button
background. Copying still writes the complete token class string. Captions wrap
instead of truncating; link color, focus treatment, target size, and prose width
intentionally follow the shared recipes. No public theme-token API is removed.

## ListCard: explicit single-action composition

- Replace root `onClick={selectModel}` with `action={{ label: 'Select model', onPress: selectModel }}`. `onPress` has no DOM-event argument. It is not a compatibility alias for the removed click API.
- Action roots are Base UI nonnative buttons backed by an actual div, preserving flow-content markup and `HTMLDivElement` refs. `action` owns role, tab order, accessible name, disabled state and optional controlled `pressed` state; these cannot be overridden with root props in the action branch.
- Action-card descendants must be noninteractive. This is an authoring contract: arbitrary React children cannot be statically validated, and ListCard does not traverse or clone them. Place links, switches, menus, buttons and interactive overflow inside a passive `<ListCard>` instead. The old test blessing a nested button and card activation was removed, not preserved as compatibility behavior.
- `action.disabled` blocks pointer/keyboard activation and removes the root from the tab sequence. Native div keyboard handlers remain available: prevent Enter in `onKeyDown`, Space in `onKeyUp`; native click-capture cancellation is honored.
- Use `action.pressed` only for toggle selection; the parent remains its sole state owner. The example model grid now selects a model and displays the result instead of running a no-op callback. Model enablement survives list/grid switches.
- The action focus treatment intentionally changes from a 2px outline to the standard 3px ring. Optional pressed selection has an accent wash. Default passive appearance, Card size/variant defaults and footer controls remain unchanged.
- Shared Card root recipe is a real `card/variants.ts` source dependency, not a new Card styling switch. Reinstall/regenerate Card and ListCard together so copied source includes this helper and rewritten flattened imports. There is no new value reexport from Card's component entrypoint.

### InputGroup appearance and native actions

- Use `InputGroup shape="pill"` and `InputGroupButton shape="pill"` instead of local `--radius` or rounded-class overrides. The button's inherited pill contract now works even with icon-xs/xs sizing; defaults are unchanged.
- Replace `className="border-b"` on block-start addons and `className="border-t"` on block-end addons with `separator`. This finite prop owns the border and adjacent padding. It is deliberately unavailable on inline addons.
- Attach popover/tooltip triggers to the native `InputGroupButton`, not to an addon containing a button. Name icon actions; expose `aria-pressed` for actual favorite state.
- Use semantic `<code>` for filenames inside `InputGroupText`. The sample intentionally adopts normal-weight standard code typography (previously medium), retaining Geist Mono 14px/21px; it is not a proven redundant-weight removal.
- The script sample now edits actual data, copies the current draft with visible errors, resets the sample, and reports real line count. The no-op Run control and fabricated cursor-position status were removed; it does not execute code.
- Dropdown/popover samples adopt standard owner radii, typography and description colors rather than bespoke paint. These are intentional visual changes, not exact pixel-parity claims.

Addon click-to-focus now ignores physically portaled targets: selecting a dropdown item restores focus to its invoking button instead of incorrectly focusing a sibling input. Decorative input/textarea and SVG button focus remain supported.

## Table viewport and row focus ownership

`Table` adds `stickyHeader` and `onViewportScroll?: React.UIEventHandler<HTMLElement>`. For a sticky header, give the parent an explicit height and let `Table stickyHeader` own the sole scrolling surface. Remove caller-owned `overflow-auto`, border/rounding, sticky-header positioning, header paint/shadow, and move viewport scroll handling from the old outer wrapper to `onViewportScroll`. Do not move native table `ref`, `onScroll`, `className`, or other table attributes: those still target the actual `<table>`.

The new sticky viewport is a named native section with keyboard scrolling. Its name shares the table's `aria-label`/`aria-labelledby`, or defaults to “Scrollable table.” Default unbounded tables are unchanged. TableRow owns its real inset three-pixel keyboard focus ring; activatable rows still need their actual activation, eligibility, and keyboard protocol. The ring paints on a pointer-inert row pseudo-element because WebKit does not paint collapsed-table row box shadows. Native cell actions and pinned-cell positioning remain independent. Expanded DataTable cells use existing `density="none"` rather than padding overrides. Faceted selected-value badges now use standard Badge medium weight instead of forcing normal weight.

Demo status/category names are explicit human labels rather than CSS capitalization; `warn` is now displayed as “Warning.” Sticky and virtual demos use the standard Table card surface/separator rather than a caller-painted background and duplicated outer border. These are intentional design/correctness changes, not pixel-parity claims. No existing public props are removed.

## Form error summaries and native navigation examples

FormErrorSummary now subscribes to its React Hook Form provider's actual error
state. It displays every distinct criteria message, with an explicit fallback for
messageless error leaves. Error actions focus inputs owned by the same form,
including external form-associated controls. Ambiguous standalone targets report
a visible error instead of focusing an arbitrary same-name field. Existing props
remain accepted, but backend debug details are now hidden by default. Enable the
new `showDebug` prop only from trusted development configuration, using the same
value during server rendering and hydration. Keep private diagnostics hidden in
production.

The anchor-tab examples now use real per-instance panel fragments. Native Enter,
click, Back and Forward update their selection. These instance IDs are not a
persistent routing contract: use application-owned router state for permanent
shareable routes. The playground no longer sets an obsolete document-wide base
URL that redirected fragment links away from their current page. Navigation-menu
operations links lead to actual Redpanda documentation instead of missing anchors.

### Native dropdown menu navigation

Use the new `DropdownMenuLinkItem` for navigation instead of nesting an anchor inside `DropdownMenuItem` and transferring padding to the child. It delegates to the installed Base UI `Menu.LinkItem`: the actual menu item is the native anchor, preserving `href`, browser modifiers, refs, native fragment navigation, keyboard behavior and supported render composition. Action items remain `DropdownMenuItem`.

`DropdownMenuLinkItem` shares the existing `inset`/`padding` recipe; use `aria-current="page"` for persistent current-page appearance rather than consumer colors. Its default `closeOnClick` is `false`, matching Base UI; opt into `closeOnClick` when the menu should close on local navigation. There is no invented disabled-link API.

`DropdownMenuItem.render` now works instead of being silently overwritten by a hardcoded DIV. Existing supplied hosts, refs, handlers and state-aware class callbacks now participate in the documented Base UI composition contract. For native buttons retain Base UI's `nativeButton` flag; links should use the actual LinkItem part.

The project-link example now points to real factual summary sections with stable `project-navigation` destination IDs. Native fragments stay on the current page and focus the destination; modified-click opens the same real target in a new tab without a custom navigation/focus machine. When rendering multiple copies of `DropdownMenuLinkTriggerDemo`, pass a distinct, stable `id` to each. Do not use `useId()` for bookmarkable URL fragments: its generated identity can differ in a fresh render tree. These example IDs are not a new application routing API.

The person-menu sample uses existing `padding="comfortable"` directly on the row instead of a zero-padding action containing a separately padded Item. Its 8px row gap, 4px content gap and standard menu radius are intentional existing-recipe adoption, not exact pixel-parity. The meaningful avatar grayscale treatment is unchanged and still flagged for the next finite owner slice.

### Toggle Group composition

No public props were removed. Attached corner geometry now follows the actual DOM buttons instead of inspecting and rewrapping React children. Fragments and shared choice components can render direct sibling items without making every item look like an isolated segment. Detached, horizontal, vertical and outline recipes retain their existing geometry. A single attached choice rounds all four corners. Avoid DOM wrappers between the group and its buttons.

### Multi-select command appearance

MultiSelect keeps its public selection, option-metadata, search, maximum-count and focus contracts.
Its embedded Command and CommandList now use their standard spacing rather than a second 4px
horizontal inset and extra list padding. Disabled options use CommandItem's owned foreground and
50% opacity instead of applying an additional muted-text repaint. These are intentional appearance
changes; anchored popup widths and native keyboard/removal behavior stay unchanged. Option caching
no longer relies on manual memoization for effect stability, including in consumers without React Compiler.

### AutoForm imperative methods

`onFormInit` is removed rather than exempting its mount-effect callback. Use `formRef` for the live
React Hook Form methods; it preserves the validated output type and becomes `null` on unmount.
Read methods from event handlers instead of copying the form instance into parent state. Continue
using `onSubmit` and `onFieldChange` for their actual events. A native form DOM ref belongs in
`formProps.ref`, not `formRef`.

```tsx
import { useRef } from 'react';
import type { UseFormReturn } from 'react-hook-form';
import { AutoForm, type AutoFormProps } from '@/components/redpanda-ui/components/auto-form';
import { Button } from '@/components/redpanda-ui/components/button';

type Values = { username: string };

function Editor({ schema }: { schema: AutoFormProps<Values>['schema'] }) {
  const formRef = useRef<UseFormReturn<Record<string, unknown>, unknown, Values>>(null);
  return (
    <>
      <AutoForm<Values> formRef={formRef} schema={schema} />
      <Button onClick={() => formRef.current?.reset()} type="button">
        Reset fields
      </Button>
    </>
  );
}
```

### Scroll progress refs and motion

ScrollProgress's ref now returns only its actual native scroll container, or `null` in document
scrolling mode and after unmount. It no longer fabricates a detached DIV when no container exists.
The scroll-hint demo uses a static decorative arrow instead of a perpetual 1.25-second bounce;
actual horizontal and vertical reading-progress updates still track native scrolling.

## Tabs styling ownership and live examples

No Tabs props were added or removed. Unopted default segmented pills keep their recipe.

- Vertical example uses the existing `size="xl"` (16px list-to-panel gap) and standard segmented-pill list instead of repainting it transparent with zero padding. Its fill and 4px inset are an intentional appearance standardization, not parity.
- `ScrollableTabsList` still composes the same DragScrollArea and TabsList. Underline-only 1px baseline space now belongs to a native content wrapper inside the unchanged scroll viewport. Consumers depending on incidental direct-child DOM selectors should target TabsList semantics/anatomy instead. Public list refs/props still reach the actual tablist.
- Underline trigger focus is now a 3px **inset** ring of the same color, fixing the scrollport's clipped top/bottom focus paint. Default segmented trigger focus stays exterior. Scroll viewport dimensions, masks, 4px opaque baseline band, drag cancellation and keyboard scrolling remain unchanged.
- Combined Tabs/ToggleGroup example moves its 12px horizontal /16px bottom content inset onto its actual native content container. This is not a new panel padding prop or a global default change.
- Appearance matrix is now two live, labeled specimens with actual panels and neutral Overview/Activity/Unavailable labels. Forced hover paint, pointer blocking, removed tab stops and the fake fourth-state cell are gone. Hover is demonstrated by real pointer interaction. Matrix height/copy intentionally change.
- Disabled Base UI tabs remain focusable for discoverability but cannot activate on Enter/Space. This supported behavior is retained, not changed to match an incorrect test assumption.

### Owned Avatar identity and Spinner context appearance

Use `AvatarFallback tone="primary" | "success" | "brand"` for the corresponding owned fill/foreground pairs;
`default` preserves the neutral fallback. These are identity tones, not an online/offline announcement. Keep
presence in a separately named status indicator.

Use `AvatarImage monochrome` for photo-only desaturation. The static `AvatarGroup` exported from `avatar`
separately accepts `monochrome` for its direct avatar subtrees, including fallbacks. Do not conflate those effects.

Use `Avatar frame="group"` for the shared 3px background separator around overlapping avatars and animated
`AvatarGroup overlap="tight"` for 12px overlap. Defaults remain no frame and standard 8px overlap; dimensions,
stable item IDs, native refs, motion, inverted stacking and tooltip composition remain unchanged. The static and
animated AvatarGroup APIs are separate exports from different modules.

Use `Spinner tone="primary" | "brand" | "informative" | "success" | "warning" | "destructive"` for context ink
instead of consumer text-color classes. Its `default` tone inherits current color, preserving Button/Badge ink.
The role remains a pending status; tone does not claim task completion or failure. Native SVG props, refs and
caller-provided names remain supported. Docs no longer suggest pasting a second incomplete spinner implementation.

Spinner examples adopt existing Item/ItemMedia corner recipes rather than inheriting a local radius override.
The Badge radius override was proven ineffective and removed without changing its measured appearance. The download
sample is now explicitly an illustrative, non-live snapshot: 129 MB / 1000 MB matches 12.9%, and the no-op Cancel
was removed. No download, charge or cancellation workflow was fabricated.

## Shared sidebar application chrome

The six Cloud and self-hosted Console examples now use `SidebarDemoOrganizationSwitcher` for their complete organization menu. Provide stable, nonempty organization IDs, the selected ID as `value`, and `onValueChange`. Keep that selected-ID state in the application/demo owner, outside the mobile sidebar popup: closing a mobile sidebar unmounts its contents. Reordering organization data retains selection by ID and displays current name/plan data. Empty collections, duplicate IDs, or an unavailable selected ID fail visibly instead of rendering a blank sidebar.

The shared `SidebarDemoPageHeader` owns the existing Admin/Cloud/Console breadcrumb and collapse toggle. Page bodies, navigation, account menu and trial/billing behavior stay in their existing owners. No component API was removed by this extraction.

Self-hosted organization-logo rendering now honors its supplied menu size. The settled menu image changes from distorted22×32px to square16×16px; the32×32px header image and its existing rounding remain unchanged. This is a deliberate geometry fix, not byte-identical visual parity.

When copying these examples manually, copy the two checked `lib/sidebar-demo-*` owners and their ordinary dependencies too. Registry generation handles the dependency closure; no new runtime dependency is required.

## Stepper: explicit slots and native process actions

Breaking changes:

- Replace `Stepper.Step` marker children with required `title` and optional `description` / `panel` ReactNode props. Rendered content is not discovered by component name or identity. Fragments, text and multiple nodes are preserved. Existing `Stepper.Title`, `Description`, `Panel` and `Controls` still support Base UI `render` and native refs.
- `title` is the visible step-label slot, not the native HTML tooltip attribute. Put status chips into `description`; keep inline active-panel conditions explicit. Panels no longer silently disappear in horizontal mode.
- Horizontal/vertical indicators are native named buttons in a navigation landmark and ordered list, not incomplete tabs. Update role/name locators from numeric `tab` to `button` named by the visible title, and selected-state checks to `aria-current="step"`. Remove custom `role`, `tabIndex`, `aria-selected` and `aria-controls` props; Stepper owns its semantics. Panels are ordinary content, not fake tabpanels. Circle layout is a named, noninteractive progress indicator.
- Tab/Shift+Tab visit enabled native actions. Left/Right focus actions horizontally, Up/Down vertically; Home/End focus the first/last eligible action. Convenience keys are scoped to the Provider and never activate. Enter/Space invokes the consumer's `onClick`, preserving validation. `preventDefault()` cancels convenience handling. Native disabled and Stepperize linear-policy-ineligible actions are skipped. There is no cross-instance DOM lookup.
- Step indicator `variant` keeps the finite shared Button tone recipes but rejects `unstyled` and `null`. The owner preserves the separate current-step halo and native keyboard-focus ring. `className` belongs to the list-item layout, not the private indicator's paint.
- The variants demo now keeps the current flow step while switching layout, rather than remounting/resetting the flow. Per-step status chips illustrate navigation position, not live provisioning outcomes. Tracking scrolls a panel on active-step changes; it does not move focus and does not recenter on typing.

```tsx
<Stepper.Step
  of={step.id}
  title={<Stepper.Title>{step.title}</Stepper.Title>}
  description={<Stepper.Description>{step.description}</Stepper.Description>}
  panel={methods.is(step.id) ? <Stepper.Panel>Step content</Stepper.Panel> : null}
  onClick={() => {
    methods.goTo(step.id).catch(reportVisibleNavigationError);
  }}
/>
```

Use an external `Stepper.Panel` for shared horizontal content when appropriate. Keep the existing typed Stepperize Provider, controlled-step options, async guards and shared/local `useStepper` behavior.

## Example and documentation density

Examples use the existing control recipes rather than repainting their colors, padding or type locally. The changelog expansion control now uses the standard small ghost Button, with a 32px target; inline code uses the 12px token. The Create Layout provider field adopts standard form/radio spacing and a native support Link. Selection-toolbars, cluster-name callouts and theme-palette pane headers provide at least 8px around bounded text; the CRUD example uses a divider instead of a nested full card. On narrow screens, its named search input wraps with the actions rather than collapsing until its hint disappears. These are intentional appearance and accessibility fixes, not unchanged screenshot claims.

## An externally owned DataTable controller

Additive API; no existing DataTable props or exports removed. The convenience DataTable keeps its controller defaults, page clamping, responsive visibility and toolbar callback.

```tsx
import { DataTableView, useDataTable } from '@/components/redpanda-ui/components/data-table';

const table = useDataTable({ data, columns });
// Existing filter, facet and URL hooks keep this same controller.
return <DataTableView table={table} />;
```

`showPagination` defaults false and only controls footer visibility. It never changes manualPagination, slices server data, clamps external state, inserts selection columns, or takes ownership of model options. Set those on useDataTable. Existing DataTable.pagination behavior remains unchanged; it is not the new footer-only setting.

The view requires the actual bound AppTable-capable controller and consumes no selected `.state` shape. Full, narrow and null-selected controllers remain supported without asserting projected state back to full state.

Reinstall @redpanda/data-table through the CLI to receive the complete renderer and row helper closure. Existing consumers only need to migrate if replacing repeated standard table rendering. No dependency/version change introduced.

The URL-filter demo's status hint now uses standard Badge typography rather than a local monospace override. It describes active URL state, not executable code. This is an intentional appearance standardization.

## Sidebar actions and navigation

`SidebarMenuButton` is the native action owner. New `SidebarMenuLink` owns main navigation and `SidebarMenuSubLink` owns nested navigation. **`SidebarMenuSubButton` and its props export were removed**, without a deprecated alias: that misleading name previously defaulted to an anchor (or, incorrectly, a tooltip button). Replace it with `SidebarMenuSubLink` and move the destination/children onto the owner:

```tsx
<SidebarMenuSubLink href="/projects">Projects</SidebarMenuSubLink>
```

Use `SidebarMenuLink href="/projects"` instead of rendering an anchor through a disabled `SidebarMenuButton`. Native link paths require a destination. Enabled controls still accept `render`, including an actual TanStack Router Link; native role, href, ref, Enter/modifier/context behavior and caller cancellation are retained.

**Disabled controls cannot use `render`.** This is enforced by the type union and a descriptive runtime error for JavaScript callers. Custom renderers can restore navigation attributes or run their own router callbacks before merged handlers; pretending those paths are disabled would be unsafe. For conditional availability, branch between a disabled native owner without `render` and the enabled router-rendered owner.

Native actions remain type=button by default and do not submit enclosing forms. Disabled native buttons use native disabled behavior. Disabled links retain ARIA link semantics, remove href, reject activation and skip Tab. Associate a persistent disabled reason with aria-describedby and a unique useId-based text node; do not rely on visual Tooltip alone.

Disabled navigation now dims exactly once. Knowledge Base changes from inherited effective25% opacity to the owner50% recipe; Reassign Partitions stays effectively50%. The standard More row uses normal menu foreground instead of a custom70% tint. These are intentional standardizations, not pixel-identical parity.

## Fixed-banner Sidebar example

The fixed-banner demo now actually exercises the existing `bannerHeight` app-shell contract. Its desktop sidebar is collapsible and starts below the fixed40px banner; the native SidebarInset owns the content offset. Mobile starts with navigation closed, exposes a real Toggle Sidebar action, and opens an unoffset modal navigation dialog above the banner. Escape restores focus to the opener.

This intentionally replaces the demo's always-inline navigation. It does not change the public `collapsible="none"` contract: inline sidebars do not become mobile dialogs or consume desktop fixed-container offsets. Keep inline layouts in normal document flow; do not rely on bannerHeight to pad them.

The three navigation labels, badges and trial/upgrade text are unchanged. This repairs an existing overlap where the Workspace group label was painted behind the absolute banner. No new runtime dependency or shared shell was added.

## Profile examples and radio appearance

Additive RadioGroup API: `variant="attached"` owns joined vertical choices (zero gap, overlapping borders, rounding and shadow). Pair with existing `Label surface="attached"`. Standard appearance and horizontal layout remain unchanged; TypeScript rejects attached+horizontal. Arbitrary supported radio values, native form naming, refs, keyboard behavior and event cancellation remain supported.

Use existing FormItem horizontal/card-horizontal recipes and standard RadioGroup spacing rather than local gap/padding paint. This intentionally standardizes the affected examples: radio gaps12px→10px, option-row gaps12px→8px and switch-card padding12px→16px. No utility-shaped spacing props or generic styling escape added.

The attached example now fits its real container:420px minimum replaced by full available width362px desktop/342px mobile. This fixes actual narrow-viewport clipping; it is not pixel parity.

Both profile examples now share concrete email, notification preferences, DOB and email-preference components in the existing profile-form-example library. They retain their shared exact input/output schema and typed RHF control. Manual Username/Bio composition versus SimpleFormField remains intentional. The email-preference heading uses sentence case.

Standalone notification form now forwards controlled value/name/blur, first actual radio ref and real group error IDs. Invalid submission focuses/describes the control; native FormData contains the selected value.

No public prop removed. CLI consumers reinstall changed components/demos through the CLI to receive complete helper dependencies. RadioGroup now includes its finite variants helper and the existing class-variance-authority dependency; profile examples share the expanded substantive profile library item.

## ScrollArea fade example

The gradient-fade example uses native Tailwind mask utilities rather than duplicate inline mask declarations. The fade now occupies10% of each vertical edge (25.6px at the demonstrated256px height) instead of a fixed24px. Native wheel scrolling and the280×256 mask owner remain unchanged; Chromium and WebKit were exercised. Reinstall the example through the CLI; no component API changed.

## Self-contained Motion features

CopyButton and ScrollProgress retain their existing HTMLMotionProps contracts,
including drag and layout. Their local synchronous LazyMotion owners include
domMax; MotionHighlight also includes domMax for shared background layout.
Consumers do not need to add a provider. Existing MotionConfig inheritance and
full Motion child/render elements remain supported; no strict context is imposed.
Files uses its included MotionHighlight feature owner for its private folder icon.

Unpublished LookoutMark and PlaygroundMark application chrome now accept only
actual supported className and (Lookout) burstSignal inputs, not arbitrary SVG
Motion props. They are not registry-distributed public APIs. Their existing hover,
fan/burst geometry and appearance stay unchanged. Playground shuffle failures are
reported while mounted, but canceled/unmounted lifetimes do not report errors.

No claimed 30kB or frame-rate improvement. Paired standalone production probes
show domMax provider overhead (small byte regressions) and closed domAnimation
owner savings; full application output must be reported separately.

## Empty surfaces

Reinstall the Empty registry item before adopting its new additive root variants:

```tsx
// Dashed outline: replace local border/border-dashed paint.
<Empty variant="outline">...</Empty>

// Token-backed recessed gradient: keep caller-owned layout separate.
<Empty className="h-full" variant="muted">...</Empty>
```

The default surface, all compound exports, native root div attributes/ref, `testId`, and consumer className forwarding remain supported. `EmptyMedia` still has its separate default/icon variants. The root's finite variants do not include null or unstyled.

The default and secondary-action demos now use the standard link recipe's action ink instead of local subtle text paint. This is an intentional appearance change, not pixel parity. Their underlying illustrative Create/Import/Upload/Refresh actions have not gained backend behavior.

## Native button-styled navigation

Use the new ButtonLink export from the existing Button registry item for native destinations. It requires href, shares standard Button appearance, and forwards native anchor attributes/ref and caller cancellation. It does not accept unstyled/null variants, render/as, disabled or isLoading. Keep actual router Links and apply the public buttonVariants helper. Button remains an action owner: rendering an anchor through it deliberately gives button keyboard semantics; changing only role does not undo Space activation.

The four Empty documentation destinations now use native ButtonLink. They keep their destinations and appearance while announcing links, activating with Enter and leaving Space to native scrolling. No remote documentation availability or backend action success is implied. Reinstall @redpanda/button and changed Empty demos through the CLI.

## Existing SVG artwork

Existing path coordinates with four or more decimal places were rounded to two digits. IDs, view boxes, native attributes and theme paint remain unchanged. Paired real-browser raster checks cover all38 exported icons at16/32/256px in both themes, the docs editor mark, and automatic/controlled treadmill states; small antialiasing differences are intentional, not pixel-parity claims. Controlled treadmill progress now uses the native SVG transform attribute rather than CSS transform; observed matrices and frame geometry are unchanged. No new artwork or external asset fetch was introduced.

## Breadcrumb standard anatomy

Reinstall Breadcrumb to get component-owned icon/text alignment and 6px spacing. Compose icons and text normally; remove repeated inline-flex/items-center/gap-1.5 classes from BreadcrumbLink and BreadcrumbPage. BreadcrumbItem already owns the same spacing for adjacent controls such as CopyButton. Native props/refs, className forwarding, custom render elements/children and event cancellation remain supported; no new public prop is required.

The responsive example now uses the existing wrapping list layout and readable full labels rather than80px caps and ellipses. This intentionally changes mobile presentation to two rows. Its desktop collapsed destinations use the existing DropdownMenuLinkItem with closeOnClick, so Enter follows the native href and dismisses the menu instead of merely clicking a wrapper around an anchor. The mobile Drawer still uses its native links and explicit Close action.

The Card example uses standard size=sm instead of local padding (vertical8→16px, horizontal32→24px), and its icon-only Home destination has an accessible name. These are intentional standardization/accessibility changes, not pixel parity. CopyButton behavior and callbacks are unchanged.

## MotionHighlight parent geometry and root ref

Parent mode now owns native attachment, measurement, resize/scroll observers and optional frame following. Independently attached, replaced or removed items no longer leave stale geometry. Duplicate active values retain the first connected wrapper in DOM order; removing it selects the surviving match. Explicit item `forceUpdateBounds={false}` still overrides the root, while inherited/explicit true follows transforms.

`MotionHighlight`'s already advertised `ref` now points to its real parent container. It is null in children mode, while disabled through `enabled={false}`, and after unmount. Callback-ref cleanup is preserved. No native-prop passthrough or new polymorphic root API was added.

Breaking context API change: the exported `useMotionHighlight()` hook no longer exposes `setBounds` or `clearBounds`. Use explicit `MotionHighlightItem` composition and native item/root refs; consumers must not publish child-measured rectangles into parent state. The context now registers native item attachments for its single parent geometry owner.

Native activation, cancellation, hover, decorative disabled inheritance, item foreground/ref composition and children-mode shared layout remain supported. Changing mode still changes the actual wrapper structure and can remount children; this is not a state-preserving layout switch.

### Runtime layout data remains data

Auth0's existing `imageURL`/`minHeight`, OTP's closed `textAlign` option, and drag-scroll edge gradients now use their component-owned classes and typed CSS custom properties rather than authored inline appearance. Their public values and consumer style precedence are unchanged. Drag-scroll keeps the underline protection and native horizontal scrolling; the supported modern browser path uses standard mask compositing.

## Grouped controls use existing recipes

Reinstall ButtonGroup: its separator now respects the existing Separator `default`, `subtle` and `strong` variants instead of repainting every choice. Orientation, dimensions and decorative semantics remain unchanged.

Use the existing Button icon size, InputGroup pill shapes and warning/ghost variants rather than padding overrides, ambient radius variables or state paint classes. Grouped menu/popover examples use standard corners and typography; currency metadata uses standard Select typography. The voice example preserves the draft and only toggles input availability. It does not record or send audio. InputGroupText uses associated static guidance rather than a fabricated character countdown, and accepts unrestricted multiline drafts.

## Standard table rendering and demo actions

The basic, full-featured, cluster and list-layout examples use the existing `DataTableView` with their externally owned controllers. Columns, filtering, sorting, visibility, selection, custom toolbars and external pagination remain under caller control. Empty cells now span visible columns, including after hiding a column and filtering to no matches. Column labels are literal accessible text rather than CSS capitalization.

The cluster and list-layout examples share the demo-only `DemoClusterRowActions({clusterId})` support owner. Copy cluster ID performs a real clipboard write and reports synchronous or asynchronous failure visibly. View details, Edit cluster and Delete cluster are disabled because these examples do not implement those operations. Production applications must supply their real actions, not import that demonstration contract. Include the helper when copying these demos manually; registry installation resolves its dependency closure.

## Native action recipes and modal previews

Dialog close actions and CodeEditor copy actions use their existing ghost recipes instead of local color/effect overrides. The label information action uses the standard24px icon button, with standard action ink and native popover/focus behavior. The destructive confirmation uses the existing destructive action variant.

`AlertDialogOpenDemo` now opens from its real Delete cluster trigger instead of making its entire document inert on mount. Alert dialogs remain modal even when their portal is visually contained. Cancel or confirm closes the surface; other gallery examples and navigation are usable before opening it. The component's modal semantics are not relaxed, and the example does not perform a backend deletion.

## Progress status variants

Reinstall Progress and replace descendant color overrides with `variant="success"` or `variant="warning"`. Its default primary fill and native value/range/indeterminate semantics are unchanged. The status is explicit, not inferred from the percentage. Keep a visible label or status icon alongside color, and provide an accessible name. The backup/quota examples now do so.

## Checkbox rows and InputGroup demo recipes

No public API change. Demo row actions now use the standard ghost/md Button recipe: 36px rather than33px rows, 8px rather than12px internal gap, standard radius and button ink. The bounded50-row list retains its native row buttons, pressed state, select-all/mixed behavior and scrolling.

InputGroup Help uses existing `shape="pill"`; redundant Spinner text paint is removed. URL text uses standard8px rather than forced4px left padding. These are intentional design-system standardizations, not promises of pixel-identical rendering. Existing input values and action behavior are unchanged.

## Information-only controls use Popover

Tooltip is intentionally a supplementary visual label for an independently named action; do not invent Tooltip ARIA. Dedicated information icons use actual native Button/InputGroupButton with PopoverTrigger, optional openOnHover, PopoverTitle and PopoverDescription. Keyboard and touch activation must work without hover.

Six InputGroup/Label information controls now follow this anatomy, retaining historical demo paths/exports. Essential Bootstrap formatting and password requirements stay visibly associated with inputs while popups are closed. Password now enforces its stated native eight-character minimum. The examples do not submit/verify credentials. Larger standard popovers and persistent labels/instructions are intentional appearance corrections, not pixel parity.

## Date and disclosure demo recipe corrections

No component API change. Disclosure actions use standard32square icon-sm Buttons. Date triggers and caption selects use existing standard paint/padding; preset controls use real external spacing.

The mobile date drawer uses embedded Calendar with spacing-scale11 cells:44square date targets,308px calendar width, fitting320px viewports. This intentionally replaces the old42.66px narrow /52px wide viewport clamp and redundant calendar padding. Selection, native month/year controls, keyboard protocol and focus restoration remain.

Today/Tomorrow now resolve local calendar days when activated, including after midnight and across daylight-saving changes, instead of a fixed2022 sample date. Time presets only describe the selected date/time; misleading booking success and the inactive Review meeting action are removed. Nothing books/submits a meeting.

## Sidebar application examples: truthful navigation and shared chrome

The Admin, Cloud, Cluster, Serverless and Console examples now share finite application catalogs through `SidebarDemoNavigation`. The Cloud examples share `SidebarDemoCloudFrame`, whose optional `billing` slot renders the existing distinct trial feature under its SidebarProvider. These are demo support modules, not new generic product navigation or layout APIs. Existing demo exports, including both separately tested SidebarOptInForm features, remain available.

- Leaf destinations are native links, not empty disclosure buttons. Their existing `#` destinations remain illustrative document-top links; no backend routes, selected-route state or account operations are invented. Real AI groups retain native disclosure buttons and now expose the previously discarded Cluster/Serverless Knowledge Base item, with its unavailable reason.
- Admin and Console integration branding is static information, not an inert focusable button. The existing `Redpanda Admin / Integration` text is intentionally preserved in both. The decorative logo no longer repeats an unrelated accessible label. Static branding does not independently animate width/height/padding.
- Sidebar feedback now disables tooltip behavior whenever the effective TooltipContent is hidden. Expanded available links no longer open an invisible tooltip that consumes the mobile dialog's first Escape. Collapsed title hints, applicable unavailable-item hints, native disabled navigation guards, caller hidden overrides and upstream visual-only Tooltip semantics remain intact. No new tooltip ARIA or open-state machine is introduced.

When copying an older application example, use a native SidebarMenuLink for a destination, or pair a CollapsibleTrigger with actual content for a group. Do not retain the old empty Collapsible wrappers. The newly visible Cluster/Serverless child intentionally moves the following rows downward; the static identity is intentionally removed from tab order. Cloud organization logos retain their actual pre-existing32px rendering; this change does not silently implement the previously ignored logo className.

## Trace links and example catalogs

Reinstall Trace for the additive `TraceLink` export. It owns the standard source/file icons, line/title anatomy and accessible new-tab hint. Source links open a new tab; `variant="file"` uses the current tab. Native anchor attributes/ref and caller cancellation remain available. Existing custom anchor/TraceLine composition stays supported. The linked-source and file-step examples now share this owner without changing their destinations or labels.

The standalone and dialog Command examples share their demonstration catalog, preserving the standalone disabled Calculator. The basic and badge-count MultiSelect examples share their grouped technology options; search, selection, badge counts and outer state remain caller-owned. Include the corresponding demo support module when copying examples manually; registry installation follows its dependency closure.

The Item size example now demonstrates actual `md`, `sm` and `xs` recipes instead of repeating the basic example. Its documentation now names the supported `md` default, not an unsupported `default` size.

## Gallery sizing and CountDot stacking

Comparison galleries use scale-backed native sizing and intrinsic tracks rather than hardcoded grid lengths. Button matrix columns retain their minimum readable widths and horizontal scrolling on narrow screens. The palette editor uses standard fixed-height scrollable panes instead of viewport-percentage heights; its maximum dialog width and native CSS editing remain intact. The unknown-component state uses a standard spacing-scale minimum height. These are intentional layout standardizations.

`dotStackedVariants` is no longer exported from the general utility module. Use CountDot's public `stacked` prop. Its existing halo geometry is owned by the CountDot recipe and remains unchanged. Do not copy the private recipe into consumer code.

## Profile example field ownership and Bio validation

- The lightweight `lib/profile-form-fields` registry item now owns substantive username, Bio and notification presentations plus the canonical profile schema/metadata. The rich `lib/profile-form-example` composes it; standalone Input/Textarea/Radio examples no longer copy Calendar/Checkbox/Popover/Select/Switch merely to share field UI.
- Reinstall affected installed registry items through the CLI, including their dependents: Form/FormSimple, InputForm, TextareaForm, RadioGroupForm and the profile helpers. This is not an instruction to hand-edit copied implementations.
- The canonical schema target is `components/redpanda-ui/lib/profile-form-fields/profile-schema.ts`; shared presentation installs at `components/redpanda-ui/lib/profile-form-fields.tsx`.
- Standalone Bio now starts with the controlled empty-string default and shares the real 10–160-character canonical limits. The old maximum message incorrectly said 30; it now truthfully says 160. Each outer form still owns its controller, schema selection, validation and submission. Manual FormField versus SimpleFormField teaching is retained.
- Shared notification presentation preserves native group name/value/blur, controller disabled state and first-radio error-focus ref. Instance-specific labels/errors remain associated without duplicate IDs.

## Toast description native element

- `ToastDescription` now defaults to a `div`, not a `p`, because its public React-node content legitimately includes block content such as preview `pre` or lists. This repairs invalid HTML/nesting warnings; it does not change the toast manager lifecycle or announcements.
- Default native props/events/ref are now typed for `HTMLDivElement`. Update paragraph-specific ref annotations and selectors such as `p[data-slot="toast-description"]`. Prefer the stable slot or public testId contract over assuming an HTML tag.
- Base UI description IDs/accessible association, state-aware className/style, and custom `render` composition remain supported. Custom rendering remains responsible for an appropriate semantic container; a `p` is only suitable for phrasing content.

## Message and activity recipe ownership

- `MessageScrollerContent variant="conversation"` owns the repeated padded chat
  layout (24px gap,16px padding). The unpadded default32px gap, used by `useChat`,
  remains unchanged.
- `MessageFooter` owns metadata/action wrapping and 6px spacing. The simple message
  demo now uses the existing compact8px MessageGroup gap instead of its custom20px.
- `Marker status="running"` owns the content shimmer. The caller owns work/status
  text and whether `role="status"` is appropriate; there is no invented async state.
- `Timeline variant="activity"` is a compact rail-free row list. Its shared12px gap
  and 12px vertical padding replace the plain activity demo's 10px vertical padding.
  Activity inside CardContent no longer adds another16px horizontal inset. Content
  uses foreground. The default stepper/control contract is unchanged.
- `TraceIcon variant="disclosure"` identifies only the trailing rotating chevron;
  `TraceCollapsibleTrigger` supplies it automatically. Leading tool icons and
  default icon geometry remain unchanged. The equally scoped reduced-motion rule
  prevents the variant selector from winning over the motion preference.
- The illustrative agent demo now copies real filename/response text. The sample
  download has no actual fixture, so its native button is disabled, as are the
  disconnected retry/feedback controls. Each has its own persistent explanation.
  These are availability corrections, not a backend integration. Explanations live
  outside Message so the assistant avatar remains aligned with its actual footer.

No exports were retired. Replace cross-component spacing/shimmer repainting with
these owned recipes, not utility-shaped override props. Four component MDX pages
were updated; Trace docs remain under the parent's independent TraceLink ownership.

## Table appearance is owned by real table concepts

- Use `Table variant="striped"`, not index-dependent row background classes. It owns alternating rendered body rows and the matching opaque pinned-column wash, without overriding existing hover, expanded or selected feedback.
- `TableRow pinned="top" | "bottom"` represents rows the caller already orders into those sections. This adds the pinned-row rest treatment; it does not reorder data or imply sticky rows. It is separate from existing logical-edge column pinning.
- Use `TableCell variant="detail"` with the actual visible-column `colSpan` and existing density for a full-width expanded detail cell. The detail cell owns its stable recessed ground. Defaults remain unchanged.
- Compact severity labels are explicit Info/Warn/Error text, not lower-case data painted with CSS capitalization.

The local Dropzone table example now explicitly states that uploads are simulated and files stay in the browser. It exposes each actual failure reason with an inline live message and associated retry description; retry only clears its own error. Removing a selected file also removes it from the picker summary, while keeping independent rows. Native Download saves the original file contents.

The example uses the existing small Table typography, standard16px cell grid and destructive-ghost/icon-sm retry recipe. Its two bespoke inline-edge cell paddings are intentionally standardized;32px button targets remain and owned icons use their standard16px size. This is not exact pixel parity or a new arbitrary spacing/color API. No task-menu backend behavior or unrelated Card/Carousel contract is changed.

## Carousel spacing and native demo actions

- Set `Carousel gap="sm" | "md"` instead of repainting `CarouselContent` negative margins and `CarouselItem` padding. The owner coordinates both sides on the active scroll axis; the unchanged default is `md` (16px), while `sm` is 4px.
- Numeric carousel examples use actual owned Card recipes. Three horizontal examples now reserve their existing arrow gutters inside the container rather than overflowing narrow galleries. The responsive example uses `Card padding="none"` for edge-to-edge square numeric frames; the vertical example deliberately uses the standard small Card inset. These are intentional specimen geometry changes, not universal pixel parity.
- The interactive Card example remains a native link to `https://redpanda.com/docs`, with keyboard focus on that link and the standard outlined Card recipe. Its bespoke hover shadow/transition is removed.
- Task row menus now perform real Copy task ID and expose clipboard failures. Edit, Make a copy, Favorite, Labels, and Delete are visibly disabled because the example has no backend for them. The nonfunctional Delete shortcut is no longer advertised. Row menu and sortable-column triggers use the standard ghost Button open-state treatment.

No Carousel or Card prop was retired; `gap` is additive. Backend operations were not invented, and existing task data, sorting, visibility, row selection, pagination, Embla API callbacks and ownership remain unchanged.

## Overlay and consumer recipe ownership

`DialogContent` now owns close clearance for its direct headerless `DialogBody`.
When the built-in close is shown and no `DialogHeader` exists, omit manual right
padding overrides on the Body; its content reserves48px. Headered dialogs and
`showCloseButton={false}` retain their existing16px body padding. The headerless
example therefore gains16px usable width versus its old double-padded layout.

Use an adjacent `CopyButton` for a second independent credential action rather
than adding another `InputEnd` beside Input's password toggle. The read-only
password example deliberately keeps reveal disabled and copies the example value
through the existing accessible copy lifecycle.

Consumer examples now use standard owned CommandItem padding, DrawerFooter
padding, PopoverHeader spacing and Tooltip typography. Static swatch actions use
semantic figures and owned ghost icon actions; their accessible copy names end
in “static swatch”. Side labels use actual literal casing. No new spacing props,
public component removals or general Tooltip announcement changes in this slice.

## Applications: Compiler-owned memoization

Lookout, Docs Changelog/InstallPicker and Playground ComponentIndex no longer use manual useMemo/useCallback (26 calls removed). No registry distribution/API/dependency change is introduced by this application-only slice. Native navigation, filtering, persisted choices and explicit request actions retain their contracts.

Lookout's initial audit is now explicitly mount-owned: a local timer-fired Effect Event starts the initial request, with the pending timer canceled during StrictMode cleanup/unmount. Ordinary event handlers retain the actual explicit all-project/project rescan function. The Effect Event is never passed to children or called by UI handlers. This prevents initial fetching from depending on an incidental callback cache identity; don't replace it with `[load]`, function-ref storage or mirrored state.

Docs external-store subscription/read callbacks remain genuinely stable at module scope. Playground's invariant alphabetic discovery list is module-derived. Reactive filters/groups/counts and ordinary UI callback optimization belong to React Compiler; they are not mirrored into state or cached manually.

## Native ref cleanup and uncompiled Registry consumers

Input and MultiSelect popup refs now honor React 19 cleanup-returning callback
refs on replacement and unmount. If a callback returns cleanup, that cleanup
runs according to React's protocol; do not also rely on a second null callback
for the same cleanup. Object refs and actual native input/Popup render composition
remain supported. No additional DOM wrappers or public props were introduced.

PopoverAnchor retains native anchor state and caller refs without a cached
callback wrapper. MultiSelect preserves controlled/uncontrolled selection,
metadata, cancellation, native keyboard/touch focus and portal lifetimes.

Manual memoization is removed from these owners and useControllableState.
Do not use the custom dispatcher identity as an Effect scheduling or mount
signal; use actual reactive state and event ownership. Controlled changes remain
proposals: a parent ignoring a callback does not change the displayed value.
React Compiler is an optimization, not a correctness requirement for copied
Registry consumers; the primary Registry tests remain uncompiled.

## Compiler-owned optimization and real lifecycle inputs

Manual `useMemo`, `useCallback`, and `React.memo` are no longer supported repository
patterns. `react-compiler:check` rejects direct and aliased imports, runtime namespace
access, capability re-exports, and captured dynamic/CommonJS React namespaces.
Lexically unrelated bindings and genuine type-only references remain valid. Astro
programs are parsed with the installed native parser; use explicit named React APIs
there rather than passing a namespace into template expressions. Discarded side-effect
module preloads do not expose a cache API and remain valid.

Do not recreate a manual cache using state, refs, a function cell, or a WeakMap. The
removed `useMemoizedArray` helper has no compatibility alias: use the actual array and
stable domain IDs. An object's or command's incidental reference identity is not a
request, subscription, reset, or mount signal. Depend on real state/data inputs and
own subscriptions and cleanup explicitly. React documents memoization as an
[optimization, not a semantic guarantee](https://react.dev/reference/react/useMemo).
Copied registry components remain behaviorally valid without a Compiler transform.

CodeTabs highlighting now reacts to its actual `codes`/`items` and language/theme
inputs, not a cached normalization array. Selection and unrelated layout renders do
not restart highlighting. Existing cancellation still prevents an older source result
from replacing a newer one.

## Authored declarations are checked, too

Both mandatory native compilers now force `skipLibCheck=false` and
`skipDefaultLibCheck=false`; project configuration drift cannot re-enable either.
This is necessary because TypeScript's
[`skipLibCheck` applies to declaration files](https://www.typescriptlang.org/tsconfig/skipLibCheck.html),
not merely dependencies. Invalid authored `.d.ts` declarations, including declarations
marked as default libraries, must fail the same gate as implementation source.
Dependency-origin diagnostics are counted and reported separately; they cannot hide
an authored diagnostic or an unrecognized compiler failure.

The playground no longer redeclares Rsbuild's `ImportMetaEnv.MODE` with a conflicting
union or modifier. The installed bundler owns that declaration. The validated runtime
schema accepts Rsbuild's `none` mode and the test runner's `test` mode, and
`strictImportMetaEnv` removes the permissive fallback for undeclared environment keys.

## Standard control recipes and Alert actions

- Use existing MiniCalendar spacing and surface recipes in the custom-navigation
  example; its bespoke card ground, shadow, 16px inset and 8px day gap are removed.
  Date selection and native navigation remain unchanged.
- Field checkbox examples use the standard FieldGroup gap. Hard disks now really
  starts checked: the old `defaultChecked` was incorrectly placed on its label.
- Notification examples use standard Card borders and whitespace rather than a
  selected-border override or consumer-added dividers. Skeleton cards use the owned
  `variant="card"` radius. These are deliberate appearance standardizations.
- PaginationCard uses `Card size="sm"` and unmodified PaginationContent. Its number
  destinations are native PaginationLinks, not anchors rendered with button semantics;
  Space does not navigate and Enter follows the destination.
- Chart filtering uses existing CardHeader/CardFooter `separator` recipes and the
  Card's own content inset. Its wider plot area is deliberate, not exact pixel parity.
- Links with a direct SVG own their inline alignment and 4px icon gap. Plain prose
  links retain normal inline flow. The MotionHighlight specimen uses its real `row`
  highlight recipe, without painting padding or a border onto the decorative indicator.
- Ghost Button families own persistent `aria-pressed` and popup-open feedback using
  their respective token ramps. Disabled controls do not receive those active paints.
  CodeBlock wrapping uses that contract; `CodeBlockActions visibility="always"`
  replaces an opacity override for persistent floating actions.
- Put independent Alert controls in `AlertAction`. It now occupies its own native
  grid track spanning the title and description, rather than absolute positioning.
  Remove hand-added right padding and absolute close positioning. The dismissible
  example preserves at least 8px text/action clearance at narrow and wide widths.

## Automatic DataTable selection is native anatomy, not a fabricated data column

`DataTable selectable` still renders the familiar page and row checkboxes, with the same controlled selection, eligibility, row-activation guards and bulk-action APIs. It now passes the caller's data-column array unchanged to TanStack and renders one dedicated leading native header/cell outside that schema. Grouped headers use a single selection header spanning the native header rows. Empty, loading and expanded cells span visible leaf data columns plus this selection cell.

**Breaking metadata change:** automatic selection no longer injects or reserves a TanStack column ID named `select`. `getAllColumns()`, header groups, column enumeration, sizing, visibility, order and pinning metadata describe caller data columns only. Remove synthetic `getColumn('select')`, `columnOrder`/`columnPinning`/`columnSizing` references and calculations assuming the automatic UI control is a vendor data column. A caller's genuine data-column ID `select` is now supported without collision.

Use `selectable` to show/hide automatic controls and existing `row.toggleSelected()`, `table.toggleAllPageRowsSelected()`, row-selection state/options and eligibility predicates to control behavior. Automatic controls stay leading and unpinned; actual data-column logical `start`/`end` pinning and order remain vendor-owned. This is not a new sticky-pinning feature. Consumers needing a custom movable/pinned selection column can keep an explicit stable column schema and existing controller with `DataTableView`, which continues to render exactly its caller-owned columns and never adds automatic controls.

The prior array-normalization `useMemo` is removed rather than renamed into another cache. Caller Column instances remain stable on unrelated parent renders; selection updates still do not rerender unrelated data cells. Two actual flat-table demos are pixel/geometry unchanged across light/dark and desktop/narrow viewports. External-controller/manual selection examples are unchanged. No deprecated compatibility alias or new public styling API is added.

## Form and filter commands use real lifecycle inputs

`useProtoForm` returns an immutable current-render snapshot rather than mutating RHF's
controller object. Its real `control`, registration, setters and form state remain
RHF-owned. Helpers and the returned wrapper no longer promise stable reference identity.
Call `createMessage`, `setOneofValue` and server-error commands from the actual user,
submit or request operation. Read `form.formState.errors` during render for reactive
validation, and use the current render's `getNestedErrors` reader. Do not use helper or
whole-wrapper identity as an Effect or subscription signal.

`useTokenPagination` commands and `useDataTableFilter().actions` are commands, not stable
Effect dependencies. Depend on actual pagination/filter values and invoke commands at
fetch completion or user action. Previously captured pagination commands still apply
in order to the current reducer state.

AutoForm derives schema, field, default and context values without manual React caches.
Schema providers must keep render-time parsing and default generation pure; exact
invocation counts are not an API contract. Equivalent controlled values retain the
native form, draft and dirty state. Actual schema changes retain the existing
intentional reset boundary.

Text-filter edits keep their 200-millisecond deadline across parent renders and use the
latest committed action handler. Accepted edits do not trigger recurring updates;
unmount cancels pending work. Both text editors have the accessible name
`Filter <column display name>`. A parent refusing a controlled change still determines
the applied value and description.

Reinstall `auto-form`, `use-proto-form`, `use-token-pagination`,
`use-data-table-filter`, `data-table-filter` and their installed dependents. The filter
now uses `useEffectEvent`: retain its generated React and React DOM 19.2-or-newer
dependency floor. No new entrypoint or retired file is introduced by this slice.

Opening a text editor without editing no longer emits an unchanged filter update.
Option search matches human labels while preserving stored IDs, and loading options
may be absent or explicitly `undefined` at runtime.

## File-upload preview ownership

`useFileUpload` owns object URLs only for its mounted resource lifetime. Removal,
clearing and effect cleanup revoke them; StrictMode and Activity reconnection
restore retained files with fresh previews. Do not keep a hook-owned preview in a
parent after unmount. Retain the `File` and let the longer-lived owner create and
revoke its own URL. Caller-supplied `initialFiles` URLs remain caller-owned.

Generated file IDs are opaque, stable selection identities, not UUID-format data.
They use Web Crypto random values without requiring HTTPS-only `randomUUID`, so
ordinary file selection continues to work on plain-HTTP deployments.

## One React Doctor policy owner

The repository-root `doctor.config.ts` is the only Doctor configuration. Nested copies,
alternative root formats, the legacy `react-doctor.config.json`, and
`package.json#reactDoctor` fail the source gate, even if they would make Doctor report
no findings. Malformed or unreadable manifests also fail closed. Repair diagnostics in
their actual owner rather than adding a configuration file that hides them.
