@epilot/zenith-react 0.2.0 → 0.3.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +278 -0
- package/codemods/README.md +5 -3
- package/codemods/migrate/volt/field-combobox.ts +37 -0
- package/codemods/migrate/volt/index.ts +2 -0
- package/codemods/migrate/volt/same-name.ts +2 -17
- package/codemods/radix-to-base-ui.ts +4 -1
- package/dist/_virtual/_zenith-runtime-family_autocomplete.js +7 -0
- package/dist/_virtual/_zenith-runtime-family_autocomplete.js.map +1 -0
- package/dist/components/callout/callout.js +0 -1
- package/dist/components/callout/callout.js.map +1 -1
- package/dist/components/command/command.js +263 -179
- package/dist/components/command/command.js.map +1 -1
- package/dist/components/field/field-combobox.js +213 -177
- package/dist/components/field/field-combobox.js.map +1 -1
- package/dist/components/field/field.js +281 -247
- package/dist/components/field/field.js.map +1 -1
- package/dist/components/list/list-sortable.js +86 -72
- package/dist/components/list/list-sortable.js.map +1 -1
- package/dist/components/list/list.js +206 -205
- package/dist/components/list/list.js.map +1 -1
- package/dist/components/tooltip/tooltip.js +10 -11
- package/dist/components/tooltip/tooltip.js.map +1 -1
- package/dist/index.d.ts +91 -256
- package/dist/index.js +120 -119
- package/dist/renderer/runtime-contract.generated.js +4 -4
- package/dist/renderer/runtime-contract.generated.js.map +1 -1
- package/package.json +27 -4
- package/dist/vendor/.bun/@radix-ui_primitive@1.1.3/vendor/@radix-ui/primitive/dist/index.js +0 -10
- package/dist/vendor/.bun/@radix-ui_primitive@1.1.3/vendor/@radix-ui/primitive/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-compose-refs@1.1.2_0ac650c021fb1b59/vendor/@radix-ui/react-compose-refs/dist/index.js +0 -30
- package/dist/vendor/.bun/@radix-ui_react-compose-refs@1.1.2_0ac650c021fb1b59/vendor/@radix-ui/react-compose-refs/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-context@1.1.2_0ac650c021fb1b59/vendor/@radix-ui/react-context/dist/index.js +0 -71
- package/dist/vendor/.bun/@radix-ui_react-context@1.1.2_0ac650c021fb1b59/vendor/@radix-ui/react-context/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-dialog@1.1.15_2b23df507bdda662/vendor/@radix-ui/react-dialog/dist/index.js +0 -258
- package/dist/vendor/.bun/@radix-ui_react-dialog@1.1.15_2b23df507bdda662/vendor/@radix-ui/react-dialog/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-dismissable-layer@1.1.11_2b23df507bdda662/vendor/@radix-ui/react-dismissable-layer/dist/index.js +0 -128
- package/dist/vendor/.bun/@radix-ui_react-dismissable-layer@1.1.11_2b23df507bdda662/vendor/@radix-ui/react-dismissable-layer/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-focus-guards@1.1.3_0ac650c021fb1b59/vendor/@radix-ui/react-focus-guards/dist/index.js +0 -18
- package/dist/vendor/.bun/@radix-ui_react-focus-guards@1.1.3_0ac650c021fb1b59/vendor/@radix-ui/react-focus-guards/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-focus-scope@1.1.7_2b23df507bdda662/vendor/@radix-ui/react-focus-scope/dist/index.js +0 -138
- package/dist/vendor/.bun/@radix-ui_react-focus-scope@1.1.7_2b23df507bdda662/vendor/@radix-ui/react-focus-scope/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-id@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-id/dist/index.js +0 -14
- package/dist/vendor/.bun/@radix-ui_react-id@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-id/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-portal@1.1.9_2b23df507bdda662/vendor/@radix-ui/react-portal/dist/index.js +0 -17
- package/dist/vendor/.bun/@radix-ui_react-portal@1.1.9_2b23df507bdda662/vendor/@radix-ui/react-portal/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-presence@1.1.5_2b23df507bdda662/vendor/@radix-ui/react-presence/dist/index.js +0 -72
- package/dist/vendor/.bun/@radix-ui_react-presence@1.1.5_2b23df507bdda662/vendor/@radix-ui/react-presence/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-primitive@2.1.3_2b23df507bdda662/vendor/@radix-ui/react-primitive/dist/index.js +0 -37
- package/dist/vendor/.bun/@radix-ui_react-primitive@2.1.3_2b23df507bdda662/vendor/@radix-ui/react-primitive/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-slot@1.2.3_0ac650c021fb1b59/vendor/@radix-ui/react-slot/dist/index.js +0 -51
- package/dist/vendor/.bun/@radix-ui_react-slot@1.2.3_0ac650c021fb1b59/vendor/@radix-ui/react-slot/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-use-callback-ref@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-use-callback-ref/dist/index.js +0 -14
- package/dist/vendor/.bun/@radix-ui_react-use-callback-ref@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-use-callback-ref/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-use-controllable-state@1.2.2_0ac650c021fb1b59/vendor/@radix-ui/react-use-controllable-state/dist/index.js +0 -55
- package/dist/vendor/.bun/@radix-ui_react-use-controllable-state@1.2.2_0ac650c021fb1b59/vendor/@radix-ui/react-use-controllable-state/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-use-escape-keydown@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-use-escape-keydown/dist/index.js +0 -15
- package/dist/vendor/.bun/@radix-ui_react-use-escape-keydown@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-use-escape-keydown/dist/index.js.map +0 -1
- package/dist/vendor/.bun/@radix-ui_react-use-layout-effect@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-use-layout-effect/dist/index.js +0 -7
- package/dist/vendor/.bun/@radix-ui_react-use-layout-effect@1.1.1_0ac650c021fb1b59/vendor/@radix-ui/react-use-layout-effect/dist/index.js.map +0 -1
- package/dist/vendor/.bun/aria-hidden@1.2.6/vendor/aria-hidden/dist/es2015/index.js +0 -53
- package/dist/vendor/.bun/aria-hidden@1.2.6/vendor/aria-hidden/dist/es2015/index.js.map +0 -1
- package/dist/vendor/.bun/cmdk@1.1.1_2b23df507bdda662/vendor/cmdk/dist/chunk-NZJY6EH4.js +0 -18
- package/dist/vendor/.bun/cmdk@1.1.1_2b23df507bdda662/vendor/cmdk/dist/chunk-NZJY6EH4.js.map +0 -1
- package/dist/vendor/.bun/cmdk@1.1.1_2b23df507bdda662/vendor/cmdk/dist/index.js +0 -313
- package/dist/vendor/.bun/cmdk@1.1.1_2b23df507bdda662/vendor/cmdk/dist/index.js.map +0 -1
- package/dist/vendor/.bun/get-nonce@1.0.1/vendor/get-nonce/dist/es2015/index.js +0 -8
- package/dist/vendor/.bun/get-nonce@1.0.1/vendor/get-nonce/dist/es2015/index.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll-bar@2.3.8_0ac650c021fb1b59/vendor/react-remove-scroll-bar/dist/es2015/component.js +0 -72
- package/dist/vendor/.bun/react-remove-scroll-bar@2.3.8_0ac650c021fb1b59/vendor/react-remove-scroll-bar/dist/es2015/component.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll-bar@2.3.8_0ac650c021fb1b59/vendor/react-remove-scroll-bar/dist/es2015/constants.js +0 -8
- package/dist/vendor/.bun/react-remove-scroll-bar@2.3.8_0ac650c021fb1b59/vendor/react-remove-scroll-bar/dist/es2015/constants.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll-bar@2.3.8_0ac650c021fb1b59/vendor/react-remove-scroll-bar/dist/es2015/utils.js +0 -26
- package/dist/vendor/.bun/react-remove-scroll-bar@2.3.8_0ac650c021fb1b59/vendor/react-remove-scroll-bar/dist/es2015/utils.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/Combination.js +0 -12
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/Combination.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/SideEffect.js +0 -116
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/SideEffect.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/UI.js +0 -32
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/UI.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/aggresiveCapture.js +0 -17
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/aggresiveCapture.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/handleScroll.js +0 -69
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/handleScroll.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/medium.js +0 -6
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/medium.js.map +0 -1
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/sidecar.js +0 -8
- package/dist/vendor/.bun/react-remove-scroll@2.7.2_0ac650c021fb1b59/vendor/react-remove-scroll/dist/es2015/sidecar.js.map +0 -1
- package/dist/vendor/.bun/react-style-singleton@2.2.3_0ac650c021fb1b59/vendor/react-style-singleton/dist/es2015/component.js +0 -12
- package/dist/vendor/.bun/react-style-singleton@2.2.3_0ac650c021fb1b59/vendor/react-style-singleton/dist/es2015/component.js.map +0 -1
- package/dist/vendor/.bun/react-style-singleton@2.2.3_0ac650c021fb1b59/vendor/react-style-singleton/dist/es2015/hook.js +0 -16
- package/dist/vendor/.bun/react-style-singleton@2.2.3_0ac650c021fb1b59/vendor/react-style-singleton/dist/es2015/hook.js.map +0 -1
- package/dist/vendor/.bun/react-style-singleton@2.2.3_0ac650c021fb1b59/vendor/react-style-singleton/dist/es2015/singleton.js +0 -31
- package/dist/vendor/.bun/react-style-singleton@2.2.3_0ac650c021fb1b59/vendor/react-style-singleton/dist/es2015/singleton.js.map +0 -1
- package/dist/vendor/.bun/tslib@2.8.1/vendor/tslib/tslib.es6.js +0 -28
- package/dist/vendor/.bun/tslib@2.8.1/vendor/tslib/tslib.es6.js.map +0 -1
- package/dist/vendor/.bun/use-callback-ref@1.3.3_0ac650c021fb1b59/vendor/use-callback-ref/dist/es2015/assignRef.js +0 -7
- package/dist/vendor/.bun/use-callback-ref@1.3.3_0ac650c021fb1b59/vendor/use-callback-ref/dist/es2015/assignRef.js.map +0 -1
- package/dist/vendor/.bun/use-callback-ref@1.3.3_0ac650c021fb1b59/vendor/use-callback-ref/dist/es2015/useMergeRef.js +0 -27
- package/dist/vendor/.bun/use-callback-ref@1.3.3_0ac650c021fb1b59/vendor/use-callback-ref/dist/es2015/useMergeRef.js.map +0 -1
- package/dist/vendor/.bun/use-callback-ref@1.3.3_0ac650c021fb1b59/vendor/use-callback-ref/dist/es2015/useRef.js +0 -26
- package/dist/vendor/.bun/use-callback-ref@1.3.3_0ac650c021fb1b59/vendor/use-callback-ref/dist/es2015/useRef.js.map +0 -1
- package/dist/vendor/.bun/use-sidecar@1.1.3_0ac650c021fb1b59/vendor/use-sidecar/dist/es2015/exports.js +0 -19
- package/dist/vendor/.bun/use-sidecar@1.1.3_0ac650c021fb1b59/vendor/use-sidecar/dist/es2015/exports.js.map +0 -1
- package/dist/vendor/.bun/use-sidecar@1.1.3_0ac650c021fb1b59/vendor/use-sidecar/dist/es2015/medium.js +0 -68
- package/dist/vendor/.bun/use-sidecar@1.1.3_0ac650c021fb1b59/vendor/use-sidecar/dist/es2015/medium.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable Zenith changes are recorded here. Zenith begins at `0.1.0`; earlier
|
|
4
|
+
history belongs to the source project and remains available through Git.
|
|
5
|
+
|
|
6
|
+
## Unreleased
|
|
7
|
+
|
|
8
|
+
## [0.3.0-next.2] - 2026-10-30
|
|
9
|
+
|
|
10
|
+
- changed: @epilot/zenith-react depends on @epilot/zenith-runtime ^0.2.0 so compatible runtime releases reach consumers independently. — @wmolinari ([!153](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/153))
|
|
11
|
+
- changed: **BREAKING**: FieldCombobox and Command, the search list inside it, run on Base UI's Autocomplete instead of `cmdk`. Options are data: pass them as `items` on `FieldComboboxContent` and render them with a function in `FieldComboboxList` (and in `FieldComboboxCollection` inside each `FieldComboboxGroup`, which takes its group's `items`). `FieldComboboxItem`'s `value` is the item, and `onClick`, which also runs on Enter, replaces `onSelect`. `items` is required on `FieldComboboxContent`. The search text moves from `FieldComboboxInput`'s `value`/`onValueChange` to `FieldComboboxContent`'s, `FieldComboboxGroupLabel` replaces `FieldComboboxGroup`'s `heading`, and filtering is a case-insensitive "contains" that keeps the items' order. The Combobox page's "Changed in this version" section shows the new form (#106) — @wmolinari ([!151](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)) (experiment: https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)
|
|
12
|
+
- removed: **BREAKING**: from FieldCombobox and Command: `FieldComboboxItem`'s `keywords` and `forceMount` (pass `filter={null}` for items the caller already filtered), `FieldComboboxLoading`'s `progress` and `label`, `FieldComboboxSeparator`'s `alwaysRender`, `FieldComboboxList`'s `label` (pass `aria-label`), `id` on `FieldComboboxList` and `FieldComboboxItem` (Base UI's ids are what `aria-controls` and `aria-activedescendant` point at), `asChild` on every search part, Label's props on `FieldComboboxGroupLabel` (now a `<div>`), cmdk's fuzzy ranking, Home/End, Ctrl+N/J/P/K and the Alt/Meta arrow jumps, the `cmdk-*` attributes, and the `cmdk` dependency, so `dist/` bundles no `@radix-ui` module (#106) — @wmolinari ([!151](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)) (experiment: https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)
|
|
13
|
+
- changed: `FieldLabel` inside a `Field` renders with an id (a caller's own `id` wins), and `FieldComboboxList` points its `aria-labelledby` at the Field's label that names the trigger (the one whose `htmlFor` is the trigger's id, else the first `FieldLabel`), so the list carries the trigger's name (#106) — @wmolinari ([!151](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)) (experiment: https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)
|
|
14
|
+
- added: `FieldComboboxCollection`, and `@epilot/zenith-runtime/v1` serves Base UI's `autocomplete`; publish the runtime before `@epilot/zenith-react` (#106) — @wmolinari ([!151](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)) (experiment: https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/151)
|
|
15
|
+
- fixed: CalloutAction no longer runs past the right edge of its Callout when the callout has an icon; its width is no longer forced to 100% on top of the icon indent, so a wrapping label or full-width row now wraps inside the callout (#61) — @wmolinari ([!139](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/139))
|
|
16
|
+
- fixed: the Radix to Base UI codemod reads the parameter count of an imported handler declared with `satisfies`, so a zero-parameter handler no longer gets an event argument — @wmolinari ([!149](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/149))
|
|
17
|
+
- fixed: `SortableList` keeps the lifted row under the pointer and drops it where the pointer is inside a transformed container, such as a `PopoverContent` — @josh.stewart.epilot ([!155](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/155))
|
|
18
|
+
- changed: `SortableList` drag grips are named after the row's text instead of its `value`; the new `ListItem` `textValue` names rows without text — @josh.stewart.epilot ([!155](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/155))
|
|
19
|
+
- fixed: `TooltipContent` fills each line of wrapping text before breaking it, instead of balancing the lines and leaving an empty strip on the right — @josh.stewart.epilot ([!156](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/156))
|
|
20
|
+
|
|
21
|
+
## [0.2.0] - 2026-10-28
|
|
22
|
+
|
|
23
|
+
0.2.0 stabilizes the 0.2.0 prerelease series: component families use Base UI,
|
|
24
|
+
the shared runtime serves its primitives and the core token sheet, and React 18
|
|
25
|
+
and 19 remain supported. The upgrade guide, codemods, migration sweep and shared
|
|
26
|
+
test setup cover the move from 0.1.0. Detailed prerelease entries remain below.
|
|
27
|
+
The per-family runtime experiment in 0.2.0-next.6 is superseded: stable 0.2.0 uses
|
|
28
|
+
one runtime file and the single `@epilot/zenith-runtime/v1` import-map key.
|
|
29
|
+
|
|
30
|
+
`@epilot/zenith-runtime` moves independently to 0.2.0 for the restored single-file
|
|
31
|
+
runtime. React and tokens pin it exactly. Publish runtime before React and deploy
|
|
32
|
+
it through root-config before applications on this release, with `react` and
|
|
33
|
+
`react-dom` external and the JSX runtime from that same page React version.
|
|
34
|
+
|
|
35
|
+
- fixed: The testing guide says to wait for `onOpenChange` before reading it: a mouse press opens `Select` on the next animation frame, so `user.click` can resolve before the Select is open (#128) — @wmolinari ([!130](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/130))
|
|
36
|
+
- fixed: Switch, Checkbox, RadioGroup and Select keep a named control's hidden form input inside the component, so a page with many named controls in an app's `overflow: auto` container no longer grows a page scrollbar. With a `name`, outside a `Field`, a Switch, Checkbox or Select renders inside a `data-slot="<control>-box"` span (`className` and `ref` stay on the control); that span is the flex/grid item, so placement (an auto margin, `align-self`/`justify-self`, grid or flex placement, a fixed Select width in a flex row) goes on an element around the control — @wmolinari ([!134](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/134))
|
|
37
|
+
- changed: `@epilot/zenith-runtime` is served as one `/v1` SystemJS file again: the per-family entries (`/v1/core.js`, `/v1/<family>.js`) and their `./v1/*` exports are removed, and a host keeps the single `@epilot/zenith-runtime/v1` import-map key — @wmolinari ([!133](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/133))
|
|
38
|
+
- changed: `@epilot/zenith-react` imports the runtime from the single `@epilot/zenith-runtime/v1` again, so applications keep externalizing that one key and their build configuration does not change; 0.2.0-next.6, which imported the per-family entries, is superseded — @wmolinari ([!133](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/133))
|
|
39
|
+
|
|
40
|
+
## [0.2.0-next.6] - 2026-10-28
|
|
41
|
+
|
|
42
|
+
`@epilot/zenith-runtime` moves independently to 0.2.0-next.4 for the per-family split
|
|
43
|
+
and per-file build identity (#126). React and tokens pin it exactly. Publish the
|
|
44
|
+
runtime before React. Hosts must deploy the whole `dist/system/` folder first,
|
|
45
|
+
with both the exact `/v1` import-map key and the `/v1/` prefix pointing into one
|
|
46
|
+
immutable runtime build, and `react` and `react-dom` external.
|
|
47
|
+
|
|
48
|
+
- added: `@epilot/zenith-runtime/v1/core.js` and one entry per Base UI family (`@epilot/zenith-runtime/v1/<subpath>.js`), each carrying a build identity so a page can never mix two runtime releases, with shared internals in common chunks; `./v1` keeps exporting everything — @wmolinari ([!128](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/128))
|
|
49
|
+
- changed: `@epilot/zenith-react` takes the runtime core from `/v1/core.js` and each family from its own `/v1/<subpath>.js`, so a page fetches only the families it renders; hosts add the import-map prefix `"@epilot/zenith-runtime/v1/": "<base>/v1/"` and deploy the runtime first — @wmolinari ([!128](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/128))
|
|
50
|
+
- fixed: `SegmentedControl` keeps its selected pill aligned when labels, fonts or the container resize, and recalculates equal item widths before measuring the pill (#127) — @wmolinari ([!127](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/127))
|
|
51
|
+
|
|
52
|
+
## [0.2.0-next.5] - 2026-10-27
|
|
53
|
+
|
|
54
|
+
`@epilot/zenith-runtime` moves to 0.2.0-next.3 for the startup React compatibility check (#116).
|
|
55
|
+
React and tokens pin it exactly. Hosts must serve this runtime through root-config before
|
|
56
|
+
applications on React 0.2.0-next.5 deploy, with `react` and `react-dom` external and
|
|
57
|
+
`react/jsx-runtime` from the same React version. Publish the runtime before React.
|
|
58
|
+
|
|
59
|
+
- added: the 0.2.0 upgrade guide in the documentation's setup section (`/docs/getting-started/upgrade-0-2`), linked from the docs index and the package README: host serves the new runtime first with `react` and `react-dom` external, bump the packages, run the `radix-to-base-ui` codemod (its output comes through the application's Prettier), run the migration sweep, add the test setup, check the attribute renames, and the two rules for pages that still mix Volt 1.x and Zenith (migrate the dialog shell first; one toaster per page) (#120) — @wmolinari ([!119](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/119))
|
|
60
|
+
- changed: the Dialog, Alert Dialog, Drawer, Popover, Dropdown Menu, Select and Tooltip docs pages point at the one `radix-to-base-ui` codemod in the installed package instead of the per-family codemods the Radix teardown folded into it, so they agree with the upgrade guide (#120) — @wmolinari ([!119](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/119))
|
|
61
|
+
- fixed: the Testing page's Jest config block carries the `moduleNameMapper` the page requires, so a copied block no longer fails with `Cannot find module`, and the missing `require` condition is stated for `@epilot/zenith-react` only (the runtime's `/v1` has one) (#120) — @wmolinari ([!119](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/119))
|
|
62
|
+
- added: `@epilot/zenith-react/testing/setup.cjs`, a Vitest and Jest setup file that gives jsdom 26 and older the `PointerEvent` Base UI needs for Switch, Checkbox and Select clicks; new Testing guide lists the four test changes for 0.2.0-next.4 (#113) — @wmolinari ([!120](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/120))
|
|
63
|
+
- added: the migration sweep (`scripts/migration-sweep.mjs`) reports Radix-era `data-state` selectors, Tailwind `data-[state=…]` variants, JSX attributes and test queries with the Base UI attribute per family (`data-state="open"` → `data-open`, `data-popup-open` or `data-panel-open`; `checked` → `data-checked` or `data-selected`; `active` → `data-active`; `on` → `data-pressed`, …), and reads of `--radix-*` CSS variables with the Base UI variable (`--transform-origin`, `--available-width`, `--anchor-width`, `--accordion-panel-height`, …); the text report prints each finding's fix under it. The rename table for the upgrade guide is generated from the same data by `scripts/migration-renames.mjs` (#119) — @wmolinari ([!122](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/122))
|
|
64
|
+
- fixed: `DropdownMenu`'s test for `onCloseAutoFocus` with `preventDefault()` no longer fails when Escape lands before Base UI's initial focus, which arrives one animation frame after the menu opens; it now waits for focus to enter the menu first. The component was already right: a menu that focus never entered leaves focus on the trigger, and one it did enter leaves it where it was (#103) — @wmolinari ([!111](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/111))
|
|
65
|
+
- fixed: `ScrollOverflow` edge gradient defaults to the Zenith body token instead of Volt 1.x `var(--volt-body)`, so the fade shows without Volt's stylesheet (#110) — @wmolinari ([!116](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/116))
|
|
66
|
+
- fixed: the `radix-to-base-ui`, `from-volt` and `from-base-elements` codemods print each rewritten file through the project's own Prettier (the copy installed above the file, with the config that applies to it), so the output passes the project's format check as it is. A project without Prettier keeps the unformatted output and gets one note to run its formatter (#114) — @wmolinari ([!121](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/121))
|
|
67
|
+
- fixed: `Select` and `Combobox` triggers inside a `Field` suppress their own keyboard focus ring, leaving only the `Field` box's focus ring; triggers outside a `Field` retain their own focus ring (#115) — @wmolinari ([!113](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/113))
|
|
68
|
+
- fixed: a host that serves `@epilot/zenith-runtime` with a React of its own, or with a `react/jsx-runtime` of another React version than the page's, now stops the application at startup with a message naming the host rule (`react` and `react-dom` external, the page's single copy; `react/jsx-runtime` from that same version) instead of crashing inside the first served primitive with a minified `useMemo` of null. `assertCompatibility` takes the application's `react` and `@epilot/zenith-react` passes its own before any component renders; the runtime README and the host-sharing docs state the rule (#116) — @wmolinari ([!117](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/117))
|
|
69
|
+
- fixed: the `radix-to-base-ui` codemod calls a handler reference with the event only when its declaration takes one, so `const close = () => …` on a content part's `onEscapeKeyDown` becomes `close?.()` and typechecks (TS2554 before). It reads a `const` or `function` declaration, `useCallback`, a type annotation, a destructured prop's type and relative imports; a reference it cannot explain keeps getting the event (#117) — @wmolinari ([!118](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/118))
|
|
70
|
+
- fixed: `Toggle` and `ToggleGroupItem` with `asChild` and a component child (such as `Button`) keep `type="button"` and a native `disabled`, so they no longer submit a surrounding form or log a Base UI console error, like `TabsTrigger` (#118) — @wmolinari ([!115](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/115))
|
|
71
|
+
|
|
72
|
+
## [0.2.0-next.4] - 2026-10-27
|
|
73
|
+
|
|
74
|
+
`@epilot/zenith-runtime` moves to 0.2.0-next.2: it now serves the Base UI primitives and the core
|
|
75
|
+
token sheet (#98), and React and tokens pin it exactly. Hosts must serve it through their import map
|
|
76
|
+
before an application on this React deploys (`DECISIONS.md` D13); 0.2.0-next.1 on npm ships the
|
|
77
|
+
0.2.0-next.0 code.
|
|
78
|
+
|
|
79
|
+
- changed: decided the family-by-family move from Radix to Base UI (DECISIONS.md D12), with the port manifest, the `zenith-port-family` and `zenith-add-family` skills, issue and MR templates, and the MR evidence tooling (`vr/compare.mjs`, `scripts/ports/`) — @wmolinari
|
|
80
|
+
- removed: `@epilot/zenith-react` no longer declares or bundles a Radix package of its own: every family runs on Base UI. The one Radix left in the bundle is the Dialog `cmdk` imports for `Command`, until #106 replaces cmdk — @wmolinari ([!102](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/102))
|
|
81
|
+
- changed: one consumer codemod, `packages/react/codemods/radix-to-base-ui.ts`, moves the removed dismissal props of every overlay family (`Dialog`, `AlertDialog`, `Drawer`, `Popover`, `FieldCombobox`, `DropdownMenu`, `Select`, `Tooltip`) into the root's `onOpenChange` in a single run — @wmolinari ([!102](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/102))
|
|
82
|
+
- fixed: `DateRangePicker`'s calendar is modal again, as before the move to Base UI: while it is open, Tab stays inside it and the rest of the page is hidden from screen readers. Base UI only does this for a modal popover holding a close part, which the picker now carries, out of sight (#95) — @wmolinari ([!101](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/101))
|
|
83
|
+
- fixed: `DateRangePicker`'s input group, which opens the calendar, no longer logs Base UI's "expected a native <button>" console error, which failed test suites that fail on `console.error`, and is no longer an extra unnamed Tab stop (#95) — @wmolinari ([!101](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/101))
|
|
84
|
+
- fixed: `DateTimePicker` panels taller than the viewport scroll their calendar and time area again and keep Cancel and Apply in view. The overflow correction looked for Radix's popper wrapper, gone since the popover moved to Base UI, so it never ran; it now also subtracts the panel's padding and gaps, which had left the footer partly off-screen (#94) — @wmolinari ([!100](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/100))
|
|
85
|
+
- fixed: `DateTimePicker`'s read-only input, which opens the panel, no longer logs Base UI's "expected a native <button>" console error, which failed test suites that fail on `console.error` (#94) — @wmolinari ([!100](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/100))
|
|
86
|
+
- fixed: `FieldComboboxTrigger`'s chevron turns over while the combobox is open, as `SelectTrigger`'s does. The rule sat on the chevron, which never carries the open state, so it matched nothing under Radix or Base UI (#93) — @wmolinari ([!98](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/98))
|
|
87
|
+
- added: structural rules in `codemods/migrate/from-base-elements.ts` for Button, IconButton/ElevatedIconButton/TooltipIconButton, the Dialog family, TextField, Typography/H1–H6/*Text, Tooltip, Switch, Checkbox, the Tabs family, Select + MenuItem, Alert/Notification/FeedbackMessage (→ Callout), the Accordion family and ToggleButtonGroup: simple shapes are rewritten onto the Zenith parts (Dialog → Dialog + DialogContent panel, TextField → Field + FieldLabel + FieldInput, Tabs → Tabs/TabsList/TabsTrigger/TabsContent, …), anything else gets a one-line TODO naming the fix — @wmolinari ([!70](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/70))
|
|
88
|
+
- added: a component used as a value (`styled(Dialog)`, `component={Button}`) stays on base-elements for that use with a TODO; when Zenith's parts need Zenith's root (Dialog, Tabs, Accordion, ToggleButtonGroup) the whole family stays — @wmolinari ([!70](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/70))
|
|
89
|
+
- added: consumer migration codemods `codemods/migrate/from-volt.ts` (import swap, stylesheet removal, TanStack types, helper TODOs) and `codemods/migrate/from-base-elements.ts` (CircularProgress → Spinner, Divider → Separator, Skeleton, Avatar, Paper → Surface, Chip/Chip360 → Pill, and a one-line fix hint for every export without a Zenith equivalent); each run prints one `<file> <line> <Component>: <fix>` line per place to look — @wmolinari ([!69](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/69))
|
|
90
|
+
- added: `codemods/fixtures.test.ts` runs every codemod on its fixtures, asserts one report per TODO and an idempotent rerun, and typechecks the output fixtures against the public types; `run-fixtures.sh` is a CLI over it — @wmolinari ([!69](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/69))
|
|
91
|
+
- changed: `Accordion` is built on Base UI instead of Radix; its props, `type`/`collapsible` values, `forceMount`, `asChild`, `orientation`/`dir` arrow-key navigation, defaults and `data-slot` values are unchanged. An open item and its panel carry `data-open`, the trigger `data-panel-open`, and the panel height is `--accordion-panel-height`, instead of `data-state` and `--radix-accordion-content-height`; a disabled trigger stays focusable with `aria-disabled` instead of the native `disabled` attribute (#82) — @wmolinari
|
|
92
|
+
- **BREAKING**: Alert Dialog runs on Base UI instead of Radix. `AlertDialogContent` no longer takes `onEscapeKeyDown` or `onFocusOutside`; `AlertDialog`'s `onOpenChange` now receives `eventDetails` as its second argument, whose `reason` says what closed the alert dialog and whose `cancel()` keeps it open. The `radix-to-base-ui` codemod (`packages/react/codemods/radix-to-base-ui.ts`) moves existing handlers there — @wmolinari ([!82](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/82))
|
|
93
|
+
- changed: Alert Dialog's state attributes are Base UI's (`data-open`/`data-closed`, `data-starting-style`/`data-ending-style`) instead of `data-state`, focus moves to Cancel one frame after opening (to the first focusable element when there is no Cancel), `AlertDialogContent` without `AlertDialogPortal` portals itself to the body, and an Escape already handled inside the alert dialog or by a layer above it leaves it open — @wmolinari ([!82](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/82))
|
|
94
|
+
- changed: `Avatar` is built on Base UI instead of Radix; its props, defaults, `data-slot` values, `HTMLSpanElement` ref and image and fallback loading behaviour are unchanged (#79) — @wmolinari
|
|
95
|
+
- deprecated: `Avatar`'s `asChild` has no effect; the root's only children are the image and its fallback, so on Radix it never rendered an element of yours (#79) — @wmolinari
|
|
96
|
+
- changed: BreadcrumbItem `asChild` composes through Base UI's `useRender` (the shared composer Surface uses) instead of the Radix Slot. Props, defaults and `data-slot` values are unchanged; the child's classes now come before the link styling so the recipe stays authoritative, an `asChild` child wrapped in an array (as MDX passes it) is accepted, and a Fragment child throws — @wmolinari ([!87](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/87))
|
|
97
|
+
- changed: Button renders Base UI's button, and `asChild` composes through Base UI's `useRender` instead of the Radix Slot. Props, defaults, `data-slot` values and form submission are unchanged; the native button gains `tabindex="0"` and, when disabled, `data-disabled`; an `asChild` child wrapped in an array (as MDX passes it) now replaces the button instead of rendering nothing, and `asChild` without exactly one child element renders the button instead of throwing — @wmolinari ([!84](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/84))
|
|
98
|
+
- changed: `Checkbox` is built on Base UI instead of Radix and still renders a native `<button>`; its `id`, `HTMLButtonElement` ref, `disabled` behaviour, defaults, `data-slot` values and three-state `checked` / `defaultChecked` / `onCheckedChange` are unchanged, except that `onCheckedChange` receives Base UI's `eventDetails` as a second argument. The root carries `data-checked` / `data-unchecked` / `data-indeterminate` instead of `data-state`, and a hidden checkbox input follows the button outside forms too (#76) — @wmolinari
|
|
99
|
+
- added: `Checkbox` takes `readOnly` (blocks toggling and sets `aria-readonly`), `inputRef` (the hidden input) and `uncheckedValue` (submitted while unchecked) from Base UI (#76) — @wmolinari
|
|
100
|
+
- deprecated: `Checkbox`'s `asChild` has no effect; with the indicator as its only child it rendered the indicator as the checkbox, never an element of yours (#76) — @wmolinari
|
|
101
|
+
- **BREAKING**: Dialog runs on Base UI instead of Radix. `DialogContent` no longer takes `onEscapeKeyDown`, `onPointerDownOutside`, `onInteractOutside` or `onFocusOutside`; `Dialog`'s `onOpenChange` now receives `eventDetails` as its second argument, whose `reason` says what closed the dialog and whose `cancel()` keeps it open. The `radix-to-base-ui` codemod (`packages/react/codemods/radix-to-base-ui.ts`) moves existing handlers there — @wmolinari ([!62](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/62))
|
|
102
|
+
- changed: Dialog's state attributes are Base UI's (`data-open`/`data-closed`, `data-starting-style`/`data-ending-style`) instead of `data-state`, focus moves in one frame after opening, `DialogContent` without `DialogPortal` portals itself to the body, and an Escape already handled inside the dialog leaves it open — @wmolinari ([!62](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/62))
|
|
103
|
+
- **BREAKING**: Drawer runs on Base UI's Dialog instead of Radix. `DrawerContent` no longer takes `onEscapeKeyDown`, `onPointerDownOutside`, `onInteractOutside` or `onFocusOutside`; `Drawer`'s `onOpenChange` now receives `eventDetails` as a second argument, whose `reason` says what closed the drawer and whose `cancel()` keeps it open. An Escape that a control inside the drawer prevents, or that closes a popover or menu inside it, no longer closes the drawer as well. The `radix-to-base-ui` codemod (`packages/react/codemods/radix-to-base-ui.ts`) moves existing handlers there — @wmolinari ([!83](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/83))
|
|
104
|
+
- **BREAKING**: DropdownMenu runs on Base UI's Menu instead of Radix. `DropdownMenuContent` and `DropdownMenuSubContent` no longer take `onEscapeKeyDown`, `onPointerDownOutside`, `onInteractOutside` or `onFocusOutside`; `DropdownMenu`'s and `DropdownMenuSub`'s `onOpenChange` now receive `eventDetails` as their second argument, whose `reason` says what opened or closed the menu and whose `cancel()` keeps it as it is. `DataTableColumnOrder` and `DataTableColumnVisibility` take `DropdownMenu`'s props, so their `onOpenChange` receives `eventDetails` too. The `radix-to-base-ui` codemod (`packages/react/codemods/radix-to-base-ui.ts`) moves existing handlers there and reports `onSelect` handlers that call `preventDefault()` — @wmolinari ([!77](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/77))
|
|
105
|
+
- changed: DropdownMenu's checkbox and radio items leave the menu open when chosen, and so do `DataTableColumnVisibility`'s column checkboxes, and an item's `onSelect` can no longer keep the menu open with `preventDefault()` (cancel the `item-press` in `onOpenChange` instead) — @wmolinari ([!77](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/77))
|
|
106
|
+
- changed: DropdownMenu's state attributes are Base UI's (`data-open`/`data-closed`, `data-starting-style`/`data-ending-style` on the content, `data-popup-open` on an open trigger, `data-checked`/`data-unchecked` on checkbox and radio items) instead of `data-state`, and its `--radix-dropdown-menu-*` CSS variables are `--transform-origin`, `--available-width`, `--available-height`, `--anchor-width` and `--anchor-height`. The menu opens on mouse down with its items a frame later, an Escape already handled inside it leaves it open, a submenu portals next to its parent, and `DropdownMenuPortal`'s `container` and `forceMount` now apply to the content inside it, as `DropdownMenuContent`'s own `forceMount` now does — @wmolinari ([!77](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/77))
|
|
107
|
+
- **BREAKING**: Popover runs on Base UI instead of Radix. `PopoverContent` no longer takes `onEscapeKeyDown`, `onPointerDownOutside`, `onInteractOutside` or `onFocusOutside`, and neither does `FieldComboboxContent`, which takes its props; `Popover`'s and `FieldCombobox`'s `onOpenChange` now receive `eventDetails` as a second argument, whose `reason` says what closed the popover and whose `cancel()` keeps it open. The `radix-to-base-ui` codemod (`packages/react/codemods/radix-to-base-ui.ts`) moves existing handlers there — @wmolinari ([!76](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/76))
|
|
108
|
+
- changed: Popover's state attributes are Base UI's (`data-open`/`data-closed`, `data-starting-style`/`data-ending-style`) instead of `data-state`, and its `--radix-popover-*` CSS variables are now `--transform-origin`, `--available-width`, `--available-height`, `--anchor-width` and `--anchor-height`. `PopoverContent` without `PopoverPortal` portals itself to the body, focus moves in one frame after opening, an Escape already handled inside the popover leaves it open, a `modal` popover no longer traps Tab, `sticky="always"` also keeps the content in view along the side axis, and `updatePositionStrategy="always"` no longer re-measures on every animation frame — @wmolinari ([!76](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/76))
|
|
109
|
+
- changed: `RadioGroup`, `RadioGroupItem` and `SegmentedControl` are built on Base UI instead of Radix; items still render a native `<button role="radio">`, and every export, prop, default, `data-slot` value, Home/End key, `orientation`, `loop` and `dir` is unchanged, except that `onValueChange` receives Base UI's `eventDetails` as a second argument. Items carry `data-checked` / `data-unchecked` instead of `data-state`, and a hidden radio input follows each item for form submission (#81) — @wmolinari
|
|
110
|
+
- deprecated: `asChild` on `RadioGroupItem`, `SegmentedControl` and `SegmentedControlItem` has no effect; the segmented group renders its own element, and the indicator is an item's only child (#81) — @wmolinari
|
|
111
|
+
- changed: ScrollArea runs on Base UI instead of Radix, with the same props, defaults and rendering. An overflowing `ScrollAreaViewport` is now a keyboard stop without a `tabIndex` of your own, the root, viewport and content wrapper carry `role="presentation"`, the root and viewport gain Base UI's `data-has-overflow-*`, `data-overflow-*` and `data-scrolling` attributes, the native scrollbars are hidden by Zenith's stylesheet instead of an inline `<style>` (so the viewport's `nonce` is accepted and ignored), and jsdom suites that polyfill `ResizeObserver` also need `Element.prototype.getAnimations` — @wmolinari ([!81](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/81))
|
|
112
|
+
- changed: ScrollOverflow runs on Base UI instead of Radix, with the same props, defaults and rendering. The viewport takes `tabIndex` `0` while content overflows and `-1` otherwise, the root, viewport and content wrapper carry `role="presentation"`, they gain Base UI's `data-has-overflow-*`, `data-overflow-*` and `data-scrolling` attributes, the native scrollbars are hidden by Zenith's stylesheet instead of an inline `<style>`, `type="hover"` keeps hiding the track while the content fits, `scrollHideDelay` and `asChild` are accepted and ignored, and jsdom suites that polyfill `ResizeObserver` also need `Element.prototype.getAnimations` — @wmolinari ([!99](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/99))
|
|
113
|
+
- **BREAKING**: Select runs on Base UI instead of Radix, and so does `TablePagination`'s page-size picker. `SelectContent` no longer takes `onEscapeKeyDown` or `onPointerDownOutside`; `Select`'s `onOpenChange` now receives `eventDetails` as its second argument, whose `reason` says what opened or closed the select and whose `cancel()` keeps it as it is. The `radix-to-base-ui` codemod (`packages/react/codemods/radix-to-base-ui.ts`) moves existing handlers there. `value=""` still means no selection and `onValueChange` still receives the value alone — @wmolinari ([!93](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/93))
|
|
114
|
+
- changed: Select's state attributes are Base UI's (`data-popup-open` on an open trigger, `data-open`/`data-closed` and `data-starting-style`/`data-ending-style` on the content, `data-selected` on the chosen item) instead of `data-state`, and its `--radix-select-*` CSS variables are `--transform-origin`, `--available-width`, `--available-height`, `--anchor-width` and `--anchor-height`. The listbox role moves to the list inside `SelectContent`, a form receives the value through a hidden input, the select opens on mouse down (and on Enter or Space through the trigger button's click) with its options a frame later, a click chooses an option only when the press started on it, a closed list stays mounted and hidden once the trigger has had focus, an Escape already handled inside the open list leaves it open, `position="item-aligned"` opens beside the trigger on touch and where there is no room, and `asChild` works on `SelectTrigger`, `SelectContent` and `SelectItem`, where it threw — @wmolinari ([!93](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/93))
|
|
115
|
+
- changed: `Surface` and `AppearanceBoundary` compose `asChild` through Base UI's `useRender` instead of the Radix Slot; handler, ref, style and class merging, the child's element and every prop are unchanged (#89) — @wmolinari
|
|
116
|
+
- changed: `Switch` is built on Base UI instead of Radix and still renders a native `<button>`; its `id`, `HTMLButtonElement` ref, `disabled` behaviour, defaults and `data-slot` values are unchanged, and every prop it had keeps its type except `onCheckedChange`, which receives Base UI's `eventDetails` as a second argument. The root and thumb carry `data-checked` / `data-unchecked` instead of `data-state`, and a hidden checkbox input follows the button outside forms too (#68) — @wmolinari
|
|
117
|
+
- added: `Switch` takes `readOnly` (blocks toggling and sets `aria-readonly`), `inputRef` (the hidden input) and `uncheckedValue` (submitted while off) from Base UI (#68) — @wmolinari
|
|
118
|
+
- deprecated: `Switch`'s `asChild` has no effect; with the thumb as its only child it rendered the thumb as the switch, never an element of yours (#68) — @wmolinari
|
|
119
|
+
- changed: `Tabs`, `TabsList`, `TabsTrigger` and `TabsContent` are built on Base UI instead of Radix. Every prop keeps its name, type and default, and `activationMode` still defaults to `"automatic"` (Base UI's own default is manual; Zenith keeps selecting the tab the arrow keys focus). `onValueChange` receives Base UI's `eventDetails` as a second argument, whose `cancel()` keeps the current tab — @wmolinari ([!86](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/86))
|
|
120
|
+
- changed: Tabs' state attributes are Base UI's: the selected tab carries `data-active` instead of `data-state="active"`, and panels carry `data-starting-style`/`data-ending-style` while they enter and leave. A disabled tab carries `aria-disabled` instead of the native `disabled` attribute and stays reachable by the arrow keys without being selected; the pointer selects on click rather than mouse down; an inactive `forceMount` panel is `hidden`; and the root renders `dir` only when it is passed — @wmolinari ([!86](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/86))
|
|
121
|
+
- changed: `Toggle`, `ToggleGroup`, `ToggleGroupItem`, `OptionCard` and `OptionCardGroup` are built on Base UI instead of Radix. Every prop keeps its name, type and default: a `type="single"` group still takes and reports a string (`""` for none), its items are still radios with `aria-checked`, and `loop`, `orientation`, `dir`, `rovingFocus` and `asChild` still work. `onPressedChange` and `onValueChange` receive Base UI's `eventDetails` as a second argument, whose `cancel()` keeps the current state — @wmolinari ([!95](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/95))
|
|
122
|
+
- changed: The pressed state attribute is Base UI's: a pressed Toggle, ToggleGroupItem or OptionCard carries `data-pressed` instead of `data-state="on"`, and an unpressed one carries no state attribute. CSS and test selectors that read `data-state` need updating — @wmolinari ([!95](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/95))
|
|
123
|
+
- **BREAKING**: Tooltip runs on Base UI instead of Radix. `TooltipContent` no longer takes `onEscapeKeyDown` or `onPointerDownOutside`; `Tooltip`'s `onOpenChange` now receives `eventDetails` as its second argument, whose `reason` says what opened or closed the tooltip and whose `cancel()` keeps it in its state. The `radix-to-base-ui` codemod (`packages/react/codemods/radix-to-base-ui.ts`) moves existing handlers there. Every other prop keeps its name, type and default, including the Radix placement props (`sticky`, `avoidCollisions`, `collisionBoundary`, `hideWhenDetached`, …) — @wmolinari ([!75](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/75))
|
|
124
|
+
- changed: Tooltip's content carries `role="tooltip"` itself instead of a visually hidden copy of its text, and the trigger's `aria-describedby` points at it while open (or at its `aria-label`). State attributes are Base UI's (`data-open`/`data-closed`, `data-starting-style`/`data-ending-style`) instead of `data-state`; the trigger opens on `mouseenter`; and `TooltipContent` without `TooltipPortal` portals itself to the body. Escape still closes an open tooltip before the Dialog, menu or control under it — @wmolinari ([!75](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/75))
|
|
125
|
+
- changed: `@epilot/zenith-runtime/v1` now serves the Base UI primitives behind every ported family (21 families on Base UI 1.7.0: `dialog`, `switch`, `toast`, `tooltip`, …, plus the `csp-provider`, `direction-provider` and `merge-props` utilities whose React contexts the served primitives read, indexed by `BASE_UI_FAMILIES`) and the core token sheet by its content hash (`tokenSheets`, `getTokenSheet`, `ensureTokenSheet`), next to the Griffel engine. The runtime gains `react` and `react-dom` as peer dependencies; its SystemJS bundle depends on those two import-map entries only and renders on React 18 and 19 hosts alike. `assertCompatibility` takes `primitives` and `tokenSheets` expectations and names a family or sheet the runtime does not serve. The served SystemJS artefact grows from 39 to 379 KB minified (14 to 118 KB gzip), paid once per page instead of once per application. Every served family, member and sheet hash is pinned by the runtime's ABI test (#98) — @wmolinari
|
|
126
|
+
- changed: `@epilot/zenith-react` takes its Base UI primitives and its core token sheet from `@epilot/zenith-runtime/v1` instead of bundling them: an app that shares the runtime through its host's import map no longer downloads Base UI or the 41 KB sheet (the seven-family app in the benchmark drops from 277.6 to 49.9 KB minified, 83.4 to 13.4 KB gzip, with every family on Base UI). An app that bundles the runtime keeps only the families it renders and pays about what it paid before (312.7 to 333.0 KB minified, 95.7 to 101.1 KB gzip). The React package requires a runtime that serves them; an older runtime on the page stops the app at startup with a named cause, never a blank page. (#98) — @wmolinari
|
|
127
|
+
- changed: Tabs: the selected tab in a vertical rail no longer draws a leading bar; it stays bold and keeps its fill, and horizontal tabs are unchanged — @wmolinari ([#111](https://gitlab.com/e-pilot/product/frontend/zenith/-/work_items/111))
|
|
128
|
+
- added: Zenith plugin for coding agents in `plugins/zenith/`, with the repository as its own Claude Code marketplace (`claude plugin install zenith@zenith`). It bundles the Zenith MCP connection and the `zenith` skill with `compose`, `review`, `tokens`, `migrate` and `setup` modes (`/zenith:compose`, `/zenith:review`, …) — @wmolinari ([!68](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/68))
|
|
129
|
+
- changed: the `zenith` skill moved from `skills/zenith/` to `plugins/zenith/skills/zenith/`. `skills/zenith` stays as a link to it, so existing `npx skills add` installs still resolve — @wmolinari ([!68](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/68))
|
|
130
|
+
- added: "Coding agents" docs page (`/docs/getting-started/agents`) and an "Agents" section in the `@epilot/zenith-react` README, covering install for Claude Code, Codex, Cursor, Copilot and OpenCode, the commands and troubleshooting — @wmolinari ([!68](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/68))
|
|
131
|
+
- added: the plugin's `migrate` mode covers two sources, Volt (v1) and `@epilot/base-elements`. It runs the shipped `migrate/from-volt` or `migrate/from-base-elements` codemod (then the shipped `radix-to-base-ui` transform), fixes only the `REP` lines the run prints, and finishes with the migration checker. A new `migrate-setup.md` reference lists the host fixes earlier migrations needed: `AppearanceBoundary` and `font-family` at the surface root, static imports, the Jest preset and module mapping, and the jsdom `useId` fix — @wmolinari ([!68](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/68))
|
|
132
|
+
- fixed: Toast's success, info, warning and error icons render at 1.25rem in Safari/WebKit again; since the Base UI port they collapsed to 0px there and no icon showed — @wmolinari ([!71](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/71))
|
|
133
|
+
- fixed: SortableList moves a lifted item exactly one row per arrow key instead of a fixed 25px — @wmolinari ([!71](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/71))
|
|
134
|
+
- fixed: SortableList no longer causes a hydration mismatch on its drag handles' `aria-describedby` when server-rendered — @wmolinari ([!71](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/71))
|
|
135
|
+
- fixed: the Card docs' basic usage sample no longer nests the icon import inside the component import — @wmolinari ([!71](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/71))
|
|
136
|
+
- fixed: the published declarations compile again for consumers on TypeScript 4.6 and 4.9. Dialog's `onOpenChange` details and Switch's props had pulled Base UI's internals into `dist/index.d.ts`, including TypeScript 5 syntax that a 4.x compiler cannot parse, even with `skipLibCheck`. Zenith now declares those types itself, with the same shape, so code that compiles today still does. `verify:consumer` now compiles the packed declarations on TypeScript 4.6.4, 4.9.5 and 5.7.3, and merge request pipelines run that check before merge — @wmolinari ([!72](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/72))
|
|
137
|
+
- fixed: `Popover` with `modal` keeps Tab inside the open popover and hides the rest of the page from screen readers again, as before the move to Base UI; this also restores it for `DateTimePicker`'s and `DateRangePicker`'s panels. Base UI only does this for a modal popup holding a close part, which `PopoverContent` now carries, out of sight (#107) — @wmolinari ([!103](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/103))
|
|
138
|
+
- fixed: `PopoverTrigger asChild` with an `<input>` or `<div>` child no longer logs Base UI's "expected a native <button>" console error; such a trigger is announced as a button and opens on Enter and Space, and an `<input>` keeps `type="button"`. `<button>` and component children such as `Button` are unchanged (#105) — @wmolinari ([!103](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/103))
|
|
139
|
+
- fixed: Toast installs its styles once per page instead of once per app. `Toaster` and `toast()` now run on Base UI's headless Toast instead of the vendored sonner, whose stylesheet every app bundling Zenith appended again (97 CSS rules per app). Every toast rule is now a Griffel atom installed through the runtime renderer, so a second app adds 0 rules and no `<style>` element. — @wmolinari ([!65](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/65))
|
|
140
|
+
- changed: Toast `theme` now scopes Zenith's light or dark tokens to the toast region. Before, `theme="dark"` only switched sonner's own palette, which turned descriptions and close buttons unreadable on Zenith's toasts. Without `theme`, toasts follow the surrounding theme as before. — @wmolinari ([!65](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/65))
|
|
141
|
+
- changed: Toast keyboard: `F6` moves focus into the toast stack (was `alt+T`; the region's label names it), and `Escape` dismisses the focused toast unless it is `dismissible: false`. — @wmolinari ([!65](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/65))
|
|
142
|
+
- changed: Toast DOM is Zenith's own: every part carries a `data-slot` (`toaster`, `toast-list`, `toast`, `toast-icon`, `toast-content`, `toast-title`, `toast-description`, `toast-close`, `toast-cancel`, `toast-action`) and the `[data-sonner-*]` attributes are gone, so CSS overrides written against them stop matching (that DOM was sonner's, marked `@slot-foreign-dom`, not public API). `toast.promise()` now always returns `{ unwrap }`, and toasts beyond `visibleToasts` are hidden and inert until a newer one closes. — @wmolinari ([!65](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/65))
|
|
143
|
+
- changed: **BREAKING**: `List` no longer accepts `sortable` or `onReorder`, so an app that renders a plain `List` no longer bundles dnd-kit (a List-only consumer drops from 148.7 KB to 100.9 KB minified, 38.7 KB to 22.6 KB gzip). Drag-and-drop reordering moved to `SortableList`, imported from `@epilot/zenith-react/list/sortable`: replace `<List sortable onReorder={…}>` with `<SortableList onReorder={…}>`. It takes every other `List` prop and renders the same markup. — @wmolinari ([!63](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/63))
|
|
144
|
+
- changed: **BREAKING**: `ensurePaletteTokens()` and `ensurePaletteTokensFor()` no longer install the palette sheet synchronously on their first call. The palette is now a lazy chunk: until it has loaded, a call starts the load, returns `false` and leaves no sheet in the document; the sheet installs when the chunk lands (2 frames locally, ~63 ms at +50 ms latency). Once loaded, both install synchronously as before. Code that reads palette colours right after the call should `await preparePaletteTokens({ document })` instead. Components render palette colours one chunk fetch late on the first palette render of a page — @wmolinari ([!64](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/64))
|
|
145
|
+
- changed: Token JS is smaller in every app. The palette sheet is a lazy chunk, loaded the first time a component resolves a palette family (Avatar, Badge, Pill, Skeleton `ai`), so an app that never uses a palette colour downloads none of it; token names carry an 8-character hash instead of 16, which takes the core sheet from 49.7 to 40.8 KB. On a measured consumer bundle, tokens in the initial chunk drop from 87 to 43 KB and the initial JS from 371 to 311 KB (98 to 90 KB gzip); the palette chunk is 41 KB. `tokens.*` names are unchanged; only the hashed CSS custom-property strings move — @wmolinari ([!64](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/64))
|
|
146
|
+
- added: `preparePaletteTokens({ document })`, which loads the palette chunk and resolves once the sheet is installed, for tokens-only consumers that paint palette colours on their first frame — @wmolinari ([!64](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/64))
|
|
147
|
+
|
|
148
|
+
## [0.2.0-next.3] - 2026-10-21
|
|
149
|
+
|
|
150
|
+
0.2.0-next.2 was not published to npm; its changes ship in this release.
|
|
151
|
+
`@epilot/zenith-runtime` is unchanged: React pins 0.2.0-next.0, the runtime hosts already serve
|
|
152
|
+
(0.2.0-next.1 ships the same code).
|
|
153
|
+
|
|
154
|
+
- fixed: `DrawerContent` now spaces its landmarks at the layout tier, as Volt's spacing guideline asks: content padding and the gap between `DrawerHeader`, body and `DrawerFooter` are `spacingLayout1` (24px) instead of `spacingGroup2` (16px). The built-in close button moves to 24px from the top and right so it stays in line with the padded header. `flush` still removes the padding; `Dialog`, `AlertDialog` and `Popover` keep the group tier (#62) — @Wilian
|
|
155
|
+
|
|
156
|
+
## [0.2.0-next.2] - 2026-10-21
|
|
157
|
+
|
|
158
|
+
- fixed: DataTable row and header rules now paint in Safari (they were a `box-shadow` on the `<tr>`, which WebKit doesn't draw), and the header rule no longer disappears under sticky or pinned header cells — @wmolinari ([!55](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/55))
|
|
159
|
+
- fixed: DataTable no longer clips the focus ring of a control in a header cell, such as the select-all Checkbox — @wmolinari ([!55](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/55))
|
|
160
|
+
|
|
161
|
+
## [0.2.0-next.1] - 2026-10-21
|
|
162
|
+
|
|
163
|
+
- changed: The documented progress, meter and slider pairing is now a `<family>Soft` track with a `<family>Indicator` fill (Colour and contrast docs). `<family>Track` resolves to the same colour as `<family>Indicator`, so a bar built from that pair shows no progress; the catalogue now marks every `*Track` token "do not use for a progress track". Nothing is removed and no token value changes. A contract test keeps Soft and Indicator distinct in every family and mode (#21) — @Wilian
|
|
164
|
+
- fixed: A selected horizontal `TabsTrigger` is now semibold over the existing grayA3 fill, and default-variant hover is grayA2 in both orientations, lighter than the selected tab. Tab labels no longer wrap, and plain-text labels reserve their semibold width, so selection never shifts the layout; tabs with icons or other elements are not reserved (#18) — @Wilian
|
|
165
|
+
- fixed: A selected `TabsTrigger` in a vertical `Tabs` now carries a semibold label and a 3px leading bar (`accentA11`, or `errorA11` on a destructive trigger), instead of only a faint grayA3 tint at the unselected weight. In the default variant, vertical hover drops to grayA2, so a hovered tab no longer reads as the selected one. A plain-text label reserves its semibold width in every state, so selecting a tab never widens the rail or moves the panel. Horizontal tabs are unchanged (#18) — @Wilian
|
|
166
|
+
- fixed: `DrawerContent` now pads its content by `spacingGroup2` (16px), the same as `Dialog`, `AlertDialog` and `Popover`, instead of 12px, so header and footer content no longer sit inside the 16px corner radius. The built-in close button moves to 16px from the top so it lines up with the padded header, matching its existing 16px right inset. `flush` still removes the padding (#19) — @Wilian
|
|
167
|
+
- fixed: Under `prefers-reduced-motion: reduce`, `Drawer`, `AlertDialog` and `DropdownMenu` no longer slide or scale: their animations collapse to 0.01ms, as `Dialog`'s already did. `Popover` and `Select` popups now also collapse when closing, not only when opening. Backdrops keep their opacity-only fade (#20) — @Wilian
|
|
168
|
+
- changed: `Breadcrumb` now declares and documents `aria-label`, the accessible name of its navigation landmark, so the catalogue and docs tell consumers to pass a translated label per locale. Runtime behaviour is unchanged: the default stays "Breadcrumb", and passing `aria-label` already overrode it. This matches `closeButtonLabel` on `DialogContent` and `DrawerContent` (#22) — @Wilian
|
|
169
|
+
- added: `BreadcrumbItem` accepts `truncate`, which keeps a long crumb on one line and cuts it with an ellipsis when the trail runs out of room, with the full text as its `title`. The breadcrumb, its list and the truncating item may then shrink below their content, so a long entity title in a view header no longer pushes the status and actions aside. Trails without `truncate` lay out as before, and `className` still lands on the inner link or page element (#23) — @Wilian
|
|
170
|
+
- fixed: `Breadcrumb` resets its list's markers, margin and padding, so the trail no longer shows "1." markers beside the separators or a 40px indent on a page without a CSS reset — @Wilian
|
|
171
|
+
- fixed: Portalled overlays now share one z-index (1300), so a `DropdownMenu`, `Popover`, `Tooltip`, `Select`, `TablePagination` page-size select or `AlertDialog` opened from inside a `Drawer` or `Dialog` paints above it instead of under it. Previously `AlertDialog`, `Tooltip`, `DropdownMenu` and the Selects sat at 50 and `Popover` had no z-index; with equal z-index the later-opened overlay, appended to `body` last, is on top (#24) — @Wilian
|
|
172
|
+
- changed: `Card` uses the container role tokens, the same recipe as `Surface kind="section"`: `surfaceContainer` fill, `borderContainer` edge at `borderWidthContainer`, `surfaceRadiusContainer` radius and `shadowContainer`, instead of a literal shadow and a page-coloured background. It now follows the theme: in dark mode the card gets the container fill rather than vanishing into the page. Visible differences in the default (flat) treatment: the corner radius goes from 0.75rem to 0.5rem, the light shadow is the container shadow, and the card is 2px shorter because the container border width is 0. Under `data-surface="bordered"`, in light and dark, the same tokens give a 1rem radius, a 1px `gray-a5` hairline, a `gray-a2` fill and no shadow (#25) — @Wilian
|
|
173
|
+
- changed: `Callout` follows `Card` to the container radius (`surfaceRadiusContainer`: 0.75rem to 0.5rem in the default treatment, 1rem under `data-surface="bordered"`), so callouts and cards still have matching corners side by side. Only the radius follows; Callout keeps its own fill and border (#25) — @Wilian
|
|
174
|
+
- fixed: the published `migration-sweep` CLI finds the generated token files where the package actually ships them (`dist/packages/tokens/dist/generated/`, falling back to the pre-0.2 `dist/tokens/`), instead of failing on a missing `dist/tokens/core.generated.js` against every installed package. It also runs when invoked through a symlinked path (worktrees, pnpm, macOS `/tmp`), where Node used to skip it silently and exit 0 (#26) — @Wilian
|
|
175
|
+
- fixed: Components no longer depend on the host page's CSS reset; Field, Button, SelectTrigger and the other native controls set their own box-sizing, border and inherited font. A new gate:host-reset renders every specimen with and without a preflight and fails on any difference. (#31) — @Wilian
|
|
176
|
+
|
|
177
|
+
## [0.2.0-next.0] - 2026-10-19
|
|
178
|
+
|
|
179
|
+
- fixed: `Callout` rendered with square corners because the port dropped v1's `rounded-xl`. Its root now uses `radiusXl`, the same radius as `Card`, so a Callout beside or inside a Card no longer reads as a rendering error (#4) — @Wilian
|
|
180
|
+
- fixed: `DateTimePicker` and `DateRangePicker` now connect their `label` to their control. Both rendered a `FieldLabel` wired to nothing, so a click on the label (now a full-width row) did nothing and the control was not named by it. A click on the label now activates the control: `DateTimePicker`'s input takes focus, and `DateRangePicker` opens its calendar as a click on the start input does. Screen readers announce the label: `DateTimePicker`'s input is named by it unless `inputAriaLabel` is passed, and the range inputs read as the label followed by "Start date" or "End date". Without a `label`, nothing changes (#16) — @Wilian
|
|
181
|
+
- added: `DialogContent` accepts `closeButtonLabel`, the accessible name of its built-in close button, so screen-reader users hear it in the product's language rather than a hardcoded English "Close". It defaults to "Close", so existing dialogs are unchanged; a `DialogClose` replacement is now only needed when the dismissal needs its own words, such as "Keep editing" (#7) — @Wilian
|
|
182
|
+
- added: `DrawerContent` accepts `closeButtonLabel`, the accessible name of its built-in close button, so screen-reader users hear it in the product's language rather than a hardcoded English "Close". It defaults to "Close", so existing drawers are unchanged. This matches `DialogContent`'s prop of the same name (#7) — @Wilian
|
|
183
|
+
- fixed: `FieldLabel` no longer shrinks to its text. It fills the label row, so a click anywhere across the top of a `Field` focuses its control instead of landing on an inert container; on a 400px field with a short label, most of the row was previously dead. Nothing renders differently: the Field, FieldSelect and FieldCombobox visual specimens are pixel-identical before and after (#14) — @Wilian
|
|
184
|
+
- fixed: A `FieldLabel` in a disabled `Field` now dims (opacity 0.5) and shows a not-allowed cursor across its row. It never dimmed: the rules targeted the standalone `label` slot rather than `field-label`, and their sibling selectors needed the label after the control, while it comes first. The label now reads as disabled when the Field's input, textarea or select trigger is disabled, or when the Field carries `data-disabled` as in Volt. A disabled button beside an enabled control does not dim it. The `field/disabled`, `field/textarea-disabled` and `field-select/disabled` visual baselines change accordingly (#14) — @Wilian
|
|
185
|
+
- fixed: A `Field` no longer grows when a password manager (1Password, NordPass) injects its button inline. Field's root was a column flex, so an inline injection became a new row and the field grew by its height plus the gap (+32px measured). The root is now a two-column grid whose second column is empty and zero-wide: every part still spans the full width and stacks as before, and a non-Zenith element placed directly after the control (input, textarea, select trigger or input group) sits beside it. It is centred on the control's row and allowed to extend into the field's padding, so a button taller than the control does not stretch the row; the same applies inside `FieldInputGroup`. All 289 visual specimens render pixel-identically before and after. An element a manager appends after other content inside the Field, such as a `FieldDescription`, still starts a new row (#5) — @Wilian
|
|
186
|
+
- fixed: In development and test, every consumer got one `console.error` from `@griffel/core` per component style definition ("A provided renderer has different classNameHashSalt"), with a single Griffel copy installed. Griffel 1.21.3 runs its salt check only inside the block that computes a definition's classes the first time, where the remembered salt is always `undefined`. So it fired on every first computation with a non-empty salt, which Zenith always uses, and could never see a real mismatch. The vendored copy in `@epilot/zenith-runtime` is patched (`patches/@griffel%2Fcore@1.21.3.patch`, Bun `patchedDependencies`) to remember the salt the classes were hashed with and to compare it on every later call. A renderer with a different salt is now reported once per definition, and same-salt use is silent; runtime tests cover both directions. The bug is unfixed upstream through 1.21.4, so re-check on the next bump and drop the patch once it is fixed there. The React test helper's allowlist for this message, which blamed `vi.resetModules()`, is removed (#10) — @Wilian
|
|
187
|
+
- fixed: `list_tokens` told consumers to import semantic and spacing tokens from `@epilot/zenith-tokens`, a private package that does not exist on npm. Both group descriptions now name `@epilot/zenith-react/tokens`, the same specifier `get_token` emits, and a catalogue test pins the two tools to one answer (#12) — @Wilian
|
|
188
|
+
- fixed: The rest of the consumer surfaces no longer point at the private `@epilot/zenith-tokens` package: the token docs pages (overview, colour, reference), the shipped `code-example` and `surface-density-playground` examples, the generated token reference text and `PRODUCT.md`. All now name `@epilot/zenith-react/tokens` or `@epilot/zenith-react/tokens.json`, and a repository test fails if any consumer-facing file names the private package again (#12) — @Wilian
|
|
189
|
+
- fixed: `DialogOverlay`, `AlertDialogOverlay` and `DrawerOverlay` now re-stamp the nearest `AppearanceBoundary`'s `data-theme` and `data-surface`, as their content parts already did. Overlays are portalled into `document.body`, where those attributes do not follow, so an overlay styled with tokens resolved them in the page's appearance rather than the boundary's. The built-in overlay paints a fixed translucent black, so nothing changes visually by default (#8) — @Wilian
|
|
190
|
+
- fixed: `TabsTrigger` now selects its tab on a click that arrives without a mousedown — `element.click()`, assistive technology that dispatches a synthetic click, and browser automation — in both automatic and manual `activationMode`. Previously only mousedown, Enter/Space and focus switched tabs, so these clicks did nothing. Ctrl-click, disabled triggers and a caller's `preventDefault()` in `onClick` still do not select (#9) — @Wilian
|
|
191
|
+
- changed: To make that possible `Tabs` now owns its selected value and always passes it to Radix controlled (`""` selects nothing, as an uncontrolled Root without a `defaultValue` did), because Radix exposes no way to select from outside its own handlers. `value`, `defaultValue` and `onValueChange` behave as before and report each change once. A click selects only when `event.detail === 0`: that is a deliberate heuristic, not an accident. A detail-0 click is one no pointer produced, so nothing selected it; a pointer click has `detail >= 1` and Radix already selected, or deliberately refused, on its mousedown. Assistive technology that sends a full mousedown sequence instead of a bare click was already handled by that mousedown, so both kinds of AT click select. The click Enter or Space synthesises is skipped because Radix selected on the key (#9) — @Wilian
|
|
192
|
+
- fixed: After a `keydown` on a `TabsTrigger` that a caller prevented, the next bare `click()` on that trigger did nothing. The trigger flagged any Enter or Space as "Radix already selected", but a prevented key skips Radix and synthesises no click, so the flag was never cleared. Only a key Radix acted on is flagged now (#9) — @Wilian
|
|
193
|
+
- fixed: A controlled `Tabs` in automatic `activationMode` whose owner declines a change (does not update `value`) is now told once per pointer press, not twice. Radix selects on mousedown and again on the focus that press causes while the tab is still unselected; this is unchanged through Radix Tabs 1.2.1, so Zenith now drops the duplicate from the same press. Every separate press, and a later focus that no press caused, is still reported. In automatic mode, moving focus to a tab and then pressing Enter or Space remains two actions and two reports (#17) — @Wilian
|
|
194
|
+
- added: `makeZenithStyles` is documented as public API for consumer layout. Zenith ships no layout primitives, so the space between components has always been the consumer's; the adopting guide now says which tool is supported for it, and that its values come from `@epilot/zenith-react/tokens`.
|
|
195
|
+
- changed: First 0.2.0 prerelease. All five workspaces move together to `0.2.0-next.0` with exact internal pins, so `@epilot/zenith-react` requires `@epilot/zenith-runtime@0.2.0-next.0` — the runtime ships the Griffel salt-diagnostic fix, which React consumers only receive through that pin. Published on `next`; `latest` stays at 0.1.0 until promoted — @Wilian
|
|
196
|
+
|
|
197
|
+
## [0.1.0] - 2026-09-17
|
|
198
|
+
|
|
199
|
+
- Established the independent Zenith package, runtime, token, docs, and MCP
|
|
200
|
+
identities.
|
|
201
|
+
- Retained the Griffel component system and replaced cross-project comparison
|
|
202
|
+
fixtures with Zenith-owned visual snapshots.
|
|
203
|
+
- Added `CodeBlock` and `CodeEditor`, including an owned monospace font token.
|
|
204
|
+
- Added a fail-closed, Zenith-only Amplify build and structured MCP request/tool
|
|
205
|
+
logs.
|
|
206
|
+
- Removed the legacy component workspace and completed CSS Modules, AI Elements,
|
|
207
|
+
calibration, and prototype-report workspaces.
|
|
208
|
+
- changed: 29 component families gained accessible-semantics tests that resolve their subject through the accessibility tree — role, accessible name, and the state conveyed to assistive technology (disabled, read-only, required, expanded, selected, checked, pressed, current) — plus keyboard-model coverage where a family has one. No public API changed — @Wilian
|
|
209
|
+
- added: The MCP catalogue now carries `anatomy`, `doDonts`, `accessibility` and `guidelines` per component, and each guideline declares the components it governs via `appliesTo`, so an agentic consumer can get from a component to the rule that constrains it and back. — @wmolinari
|
|
210
|
+
- fixed: `Field` and `Select` canonical examples no longer teach the composition their own guidelines name as the "Don't"; both now wire `htmlFor`/`id`/`aria-describedby` explicitly, which is what produces an accessible field today. — @wmolinari
|
|
211
|
+
- fixed: Component pages' per-component frontmatter (`requiredParts.X`, `compositionNote.X`, `aliases.X`, `useCases.X`, `constraints.X`, `requiresProvider.X`) is now read by the catalogue build. Eight families shipped `composition: null` while the prose describing them sat unused, and `search_components` advertised intent aliases it could not match — its own documented example, "info banner" → Callout, returned Tooltip. — @wmolinari
|
|
212
|
+
- fixed: `drawer.mdx` and `popover.mdx` declared required parts that omitted `DrawerPortal`/`PopoverPortal`, which are structurally mandatory because neither Content self-portals. — @wmolinari
|
|
213
|
+
- removed: The generated MCP catalogue and the docs API surfaces are no longer committed. `bun run build:mcp` and the docs `prepare:*` scripts produce them, and CI, the Amplify buildspec and `prepack` all run those before anything reads them. This takes 29 MB of build products out of the repository and removes the class of defect where a committed artefact ships stale provenance to the live agent surface. — @wmolinari
|
|
214
|
+
- changed: Authored component contracts move from one 1,547-line `catalogue-metadata.ts` to a `*.catalogue.ts` beside each family, aggregated by the original file. Being inside `src` puts them under the type checker for the first time, which immediately caught a `density` entry in `DataTable.supportedOverrides` that the type never allowed. — @wmolinari
|
|
215
|
+
- added: Accessible-semantics tests for CodeBlock and CodeEditor, pinning the copy control's name and live region, the named-only code region, and the editor's textbox name, invalid state and joined `aria-describedby`. — @wmolinari
|
|
216
|
+
- changed: repository identity gate now sweeps the whole tracked tree (adding `.mdc`/`.mjs`/`.yml`/`.yaml`/`.json`) instead of six hand-listed roots, and asserts every `*.md` path cited in tracked text resolves to a tracked file — @Wilian ([!7](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/7))
|
|
217
|
+
- fixed: removed 86 citations of markdown documents that do not exist, including three reaching consumers through the emitted `dist/index.d.ts`; the reasoning in each comment is kept, only the dead filename is gone — @Wilian ([!7](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/7))
|
|
218
|
+
- added: docs structure tests for navigation reachability, duplicate sibling titles and orphaned examples — @Wilian ([!7](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/7))
|
|
219
|
+
- removed: `docs/content/docs/components/gradients.mdx`, which documented `volt-bg-gradient-*` CSS classes Zenith does not ship; `getting-started/field-anatomy.mdx`, an unreachable redirect stub for a never-published URL; and `docs/examples/field-actions.tsx`, which had no importer — @Wilian ([!7](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/7))
|
|
220
|
+
- changed: retitled the `overview` docs section from "Guidelines" to "Writing", so two sibling sections no longer share a sidebar title — @Wilian ([!7](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/7))
|
|
221
|
+
- changed: rewrote `.cursor/rules/component-principles.mdc` for Zenith and removed the duplicated predecessor lifecycle section from the MR template, adding `bun run gates` to its checklist — @Wilian ([!7](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/7))
|
|
222
|
+
- changed: **BREAKING**: `toast`, `ToastOptions` and `ToasterProps` are declared by Zenith instead of re-exported from sonner, so a sonner release can no longer change this package's public API. A build-time assertion now fails if a key Zenith declares stops existing on sonner's own type. — @wmolinari
|
|
223
|
+
- removed: **BREAKING**: `toast.custom`, `toast.message`, `toast.getHistory` and `toast.getToasts` are no longer exposed. `toast.promise` is kept but returns `unknown` rather than sonner's internally-typed handle, and its data object accepts `loading`, `success`, `error`, `description` and `finally` only. `onDismiss`/`onAutoClose` receive `unknown` instead of sonner's toast record. — @wmolinari
|
|
224
|
+
- removed: **BREAKING**: `ToasterProps` no longer accepts `invert`, `richColors`, `icons` (Zenith owns the toast's appearance), nor `hotkey` and `swipeDirections` — note that those two are keyboard and touch behaviour rather than appearance, so an application that needs to rebind Alt+T has no typed way to do it until they are reinstated deliberately. — @wmolinari
|
|
225
|
+
- fixed: The MCP catalogue is regenerated from a clean revision, replacing a published artefact that declared `isDirty: true` and `provenance: "development-worktree"`. — @wmolinari
|
|
226
|
+
- changed: Button wraps direct text runs in a diagnostic `button-label` span and trims them to the font's cap height and alphabetic baseline in supporting browsers. This adds an internal element to the rendered DOM; `asChild` and consumer element children remain unchanged — @wmolinari
|
|
227
|
+
- added: Document the read-only Zenith MCP server (`/api/mcp`) and client configuration in `packages/react/README.md` and `skills/zenith/SKILL.md` for consumers — @wmolinari
|
|
228
|
+
- fixed: `build:mcp` now pins the MCP catalogue to the published `latest` npm release via a git worktree, ensuring live tools reflect published package tokens (`accentSolid`) rather than unreleased main, with `ZENITH_MCP_SOURCE=worktree` for development — @wmolinari
|
|
229
|
+
- fixed: `build:mcp` resolves the release commit in a shallow clone. Amplify shallow-clones by default, so `git log --grep` found nothing and the deploy failed closed; the resolver now deepens and widens refs once when the clone is shallow, and still refuses to catalogue the working tree if that fails — @wmolinari
|
|
230
|
+
- changed: Both published packages now default to the `next` dist-tag. A publish lands on `next`; moving a version to `latest` — what `npm install` resolves — is an explicit `npm dist-tag add`, never a pipeline side effect. Recorded as `DECISIONS.md` D11, with the release rules in `CONTRIBUTING.md` and the consumer-facing version guidance in the package README. — @wmolinari
|
|
231
|
+
- changed: Zenith docs now use theme-aware live examples, responsive API tables, usage-first component guides and clearer catalogue navigation; production metadata becomes indexable only when its public origin is configured — @Wilian
|
|
232
|
+
- fixed: release verification now executes the real Next.js production build instead of accepting Bun's help output as a successful docs build — @Wilian
|
|
233
|
+
- added: CodeBlock presents read-only code as real `pre`/`code` markup with optional file title, language label, soft wrapping, a non-selectable line-number gutter, and a copy button that announces success and failure politely and never throws — @Wilian
|
|
234
|
+
- added: CodeEditor edits code in a textarea with a controlled or uncontrolled value, `onValueChange` alongside native `onChange`, readOnly and disabled semantics, an invalid state, and code-friendly defaults: monospace, spellcheck/autocorrect/autocapitalize off, `dir="ltr"`, tab stops matched to the indent width, and soft wrap off — @Wilian
|
|
235
|
+
- added: CodeEditor's opt-in `indentWithTab` indents at a caret and across a selection, outdents with Shift+Tab, and keeps Escape-then-Tab as the keyboard escape hatch so it is never a tab trap — @Wilian
|
|
236
|
+
- added: `family.mono` design token (`--zenith-font-mono`, accessor `fontMono`) defines Zenith's one monospace family; both code surfaces read it, so neither carries a private font stack — @Wilian
|
|
237
|
+
- changed: CodeBlock and CodeEditor are set at 12px on a 16px line with tighter padding, matching the size GitHub and GitLab set source in — a seven-line captioned snippet is 27% shorter, and both surfaces still read the same monospace token so a snippet looks identical read or edited — @Wilian
|
|
238
|
+
- changed: CodeBlock's copy button is an overlay in the top-right corner rather than a row of chrome, revealed on hover or focus and always visible where there is no hover; a header now appears only for a `title` or a `language`, so `<CodeBlock code copyable />` reserves no vertical space for controls at all — @Wilian
|
|
239
|
+
- added: CodeBlock's `showLineNumbers` gutter stays pinned while a long line scrolls under it, keeps one number against a soft-wrapped line, and gives each row a hover band on pointer devices; it is still `aria-hidden`, still unselectable, and still off by default — @Wilian
|
|
240
|
+
- added: CodeEditor takes `showLineNumbers`, which renders the same gutter and marks the line the caret is on — bundled as one property the way CodeMirror's default setup bundles them, standing aside for a range selection, and off by default so the common four-line editor keeps no per-keystroke caret state — @Wilian
|
|
241
|
+
- added: CodeEditor takes `diagnostics`, a list of `{ line, column?, severity?, message }` the caller already has — rendered as a wavy rule under the line and a coloured gutter number, and enumerated in a visually hidden list added to the control's `aria-describedby` so a screen reader gets the text rather than a colour; nothing here computes them, and there is deliberately no live region — @Wilian
|
|
242
|
+
- added: CodeEditor takes `onFormat`, a caller-supplied formatter bound to Shift+Alt+F and to a quiet button revealed on hover, with `onFormatError`, `formatLabel`, `formattedLabel` and `formatErrorLabel`; it drops a second request while one is in flight, discards a result that arrives after the author typed, restores the caret relative to its line's first non-space character, and commits through `onValueChange` like any other edit — @Wilian
|
|
243
|
+
- added: CodeEditor takes `autoSize` and `maxRows`, which grow the editor with its content between `rows` and `maxRows` using CSS `field-sizing` — no measurement, no observer, and a clean fall back to `rows` where the property is unsupported; `resize` defaults to `none` under `autoSize` — @Wilian
|
|
244
|
+
- added: `CodeDiagnostic` and `CodeDiagnosticSeverity` are exported for callers mapping a compiler's or validator's output into the diagnostics contract — @Wilian
|
|
245
|
+
- added: CodeBlock and CodeEditor syntax-highlight their `language` — bash, css, html, javascript, json, jsx, markdown, tsx, typescript and yaml, plus the usual aliases — with plain text as the fallback for anything else, so a language coming from data never needs validating first — @Wilian
|
|
246
|
+
- added: `registerCodeLanguage` teaches both code surfaces any further Prism grammar, and `isCodeLanguageSupported` reports whether a language will actually be highlighted; `CODE_LANGUAGES` lists the ten that ship — @Wilian
|
|
247
|
+
- changed: CodeEditor paints highlighting on a decorative layer behind a real `<textarea>`, so form submission, validation, `readOnly`/`disabled`, native caret and selection, IME composition and the Escape-then-Tab escape hatch all keep working unchanged — @Wilian
|
|
248
|
+
- changed: syntax colours are core design tokens rather than a theme file, so light and dark follow the token sheet with no configuration, no extra stylesheet and no hydration flash — @Wilian
|
|
249
|
+
- added: `DataTableFooter` provides a presentational footer wrapper between table records and pagination for totals, custom actions, or summary information. Ported to Griffel with slot contract and forwardRef. — @Wilian
|
|
250
|
+
- added: `DataTableResizeHandle` exposes composable column resizing for DataTable headers with Griffel styles, accessible `separator` semantics, default and customizable `aria-label`, and full keyboard navigation (ArrowLeft, ArrowRight, Home, End). — @Wilian
|
|
251
|
+
- added: `DataTableColumnOrder` and `DataTableHeaderDraggable` restore drag-and-drop column reordering to DataTable headers and toolbar dropdowns. Ported to Griffel with `@dnd-kit`, accessible drag handles, slot attributes, forwardRef, and full keyboard operation via `sortableKeyboardCoordinates`. — @Wilian
|
|
252
|
+
- added: Virtualization support restored via `DataTableVirtualizerContext`, `useDataTableVirtualizer`, and `@tanstack/react-virtual` integration in `DataTableContent` and `DataTableBody`, rendering only visible rows for large datasets with sticky header support and padding rows. — @Wilian
|
|
253
|
+
- added: named token bindings, token JSON and document-level token delivery are framework-neutral and reachable from any framework through `@epilot/zenith-react/tokens`, `@epilot/zenith-react/tokens.json` and `@epilot/zenith-react/delivery`; the `@epilot/zenith-tokens` workspace that produces them stays internal and unpublished — @Wilian
|
|
254
|
+
- added: the MCP now serves each component's authored description and its upstream primitive's documentation link, both of which were written in the docs frontmatter and discarded by the builder; a `docsPath` for every component so an agent can point a human at the page; and `requiredAncestors` from component frontmatter — @wmolinari ([!30](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/30))
|
|
255
|
+
- changed: a guideline's `appliesTo` now derives the component's reciprocal link, so guidance reaches 41 components instead of 20, and the `surfaces` guideline governs the components it describes — @wmolinari ([!30](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/30))
|
|
256
|
+
- fixed: the MCP contract tests measured `registry.json`, which !28 repointed at the published release — so every content ratchet silently stopped policing the working tree. They now generate the catalogue from source; the prop-description floor moves 265 to 288 — @wmolinari ([!30](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/30))
|
|
257
|
+
- **BREAKING**: removed: the `glass`, `tint` and `card` surface treatments. `AppearanceSurface` is now `"flat" | "bordered"`, and the 31 token definitions that existed only to paint the removed treatments are gone — @wmolinari
|
|
258
|
+
- **BREAKING**: changed: the `plain` surface treatment is renamed `flat`. `data-surface="plain"` no longer matches any generated selector; stamp `data-surface="flat"` instead. The primitives it fed are renamed to match: `gradient.plain-wash` → `gradient.flat-wash`, `color.fx.sep-plain` → `color.fx.sep-flat`, `color.fx.wash-plain` → `color.fx.wash-flat`, `shadow.plain-container` → `shadow.flat-container` — @wmolinari
|
|
259
|
+
- changed: `flat` remains the root anchor and `bordered` the inheritance base, so an unstamped page is unaffected. Note that `flat` states only where it differs from `bordered`, and only in light, so the two treatments are currently identical in dark mode — @wmolinari
|
|
260
|
+
- changed: the docs sidebar logo, standalone asset and favicon now use the Zenith apex mark — @wmolinari
|
|
261
|
+
- **BREAKING**: changed: the `sem-` prefix is removed from the semantic token layer. Every `sem-*` token was a pure alias. 44 merely restated a primitive's own name and are **deleted**; the rest are renamed (`semAccentContrast` → use `accentContrast`, `semBody` → `body`, `semLevel1` → `level1`, and the `-contrast`/`-surface` pair for every palette family). The renamed ones keep their meaning and lose the prefix: `semAccentSoft` → `accentSoft`, `semAccentSolid` → `accentSolid`, `semFocusRing` → `focusRing`. Every affected binding exported from `@epilot/zenith-tokens` changes name, so an upgrading consumer sees unresolved imports until it renames — @wmolinari
|
|
262
|
+
- fixed: `AppearanceBoundary` now paints the page background of the mode it declares. It previously stamped `data-theme`/`data-surface` and painted nothing, so a `mode="light"` region on a dark page let the page show through under light ink — 1.08:1 in the docs' own field specimens. The paint is skipped when the nearest boundary above already declares the same mode, so nested regions still compose transparently — @wmolinari
|
|
263
|
+
- fixed: Button gives no hover or press feedback while `disabled` and shows a `not-allowed` cursor. Pointer events are kept, so a Tooltip around a disabled button still opens — @wmolinari
|
|
264
|
+
- fixed: `TablePagination` now supplies default accessible names for its controls. With no `labels` prop, 4 of 8 navigation buttons and the page-size trigger previously reached assistive technology as unlabelled buttons — the design system breaking its own `actions` guideline, and inherited by anyone copying the canonical example. Caller-supplied labels still win, so localisation is unaffected. — @wmolinari
|
|
265
|
+
- fixed: `Text` helper variants use the alpha ink `grayA11` again. It was briefly switched to the solid `gray11` over a 1.07:1 reading, but the alpha was never the cause — `AppearanceBoundary` painted no canvas, so both inks landed on the wrong backdrop. The alpha tracks whatever is painted behind it, so it matches the solid on the page (5.89 vs 5.90) and beats it on any tinted surface (5.51 vs 4.82 on a gray-4 row). Pixel-neutral on white, where the two composite to the same colour — @wmolinari
|
|
266
|
+
- fixed: the Combobox docs examples clear their loading timer on unmount. Both scheduled a `setLoading(false)` in an effect with no cleanup, so navigating away inside a second updated an unmounted component — @wmolinari
|
|
267
|
+
- fixed: **BREAKING**: the core token sheet is compiled into `@epilot/zenith-react` and installed as components render, instead of fetched from the consumer's origin where nothing served it (`0.1.0-next.0` rendered a blank page). No bootstrap call or asset hosting is needed; `prepareCoreTokens()` is optional and makes no request — @wmolinari ([!21](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/21))
|
|
268
|
+
- removed: **BREAKING**: `assetBaseUrl`, `CoreTokensFetchError`, `CoreTokensTimeoutError`, `CoreTokensIntegrityError` and `CoreTokensNotPreparedError`, and the `url`/`integrity` fields of `CORE_TOKEN_ASSET` — @wmolinari ([!21](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/21))
|
|
269
|
+
- changed: `@epilot/zenith-tokens` is built into `@epilot/zenith-react` rather than published. 0.1 ships the two libraries `DECISIONS.md` D2 names — the component package and the runtime — and a consumer installs two packages, not three. Tokens remain reachable at `@epilot/zenith-react/tokens`, which is React-free and tree-shakes to 207 bytes for a sixteen-binding consumer. — @wmolinari
|
|
270
|
+
- changed: the MCP `list_guidelines` / `get_guideline` tools now serve the design guideline pages (actions, dialogs, forms, reading-layout, selection, surfaces, tables, fields) with their parsed decisions and Do/Don't pairs, plus the adoption guide as `kind: "setup"`; AGENTS.md and DECISIONS.md are no longer served — @wmolinari
|
|
271
|
+
- changed: Second prerelease. All five workspaces move together to `0.1.0-next.1` with exact internal pins, and `@epilot/zenith-react` and `@epilot/zenith-runtime` gain `release: npm publish --tag next` — publishing was hard-disabled on `main`. Two tests that asserted the literal `"0.1.0"` now assert the invariants instead: every workspace manifest shares one version, and the token package pins the runtime's actual version — @wmolinari ([!21](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/21))
|
|
272
|
+
- changed: first stable release. All five workspaces move together to `0.1.0`, and `latest` is promoted to it by hand — the deliberate act `DECISIONS.md` D11 describes, and the first time `latest` means what that decision says rather than what npm assigned on a first publish — @wmolinari ([!31](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/31))
|
|
273
|
+
- added: the component authoring contract in `packages/react/AGENTS.md` is now enforced by four test files that run over all 45 component families at once — public barrel shape, the `data-slot`/`SLOT_ATTR` DOM contract, per-family accessibility coverage, and visual-specimen coverage. Pre-existing violations are held in two-sided ratchets that fail on a new violation _and_ on a fixed one still listed, so the recorded debt can only shrink — @Wilian
|
|
274
|
+
- changed: `ComposeAsChild` and the `Command` family are marked `@internal` at their declarations, recording that neither is part of the 0.1 public API; `Toaster` is marked `@slot-foreign-dom`, recording that sonner renders and owns that DOM — @Wilian
|
|
275
|
+
- changed: detached Zenith CI from Volt UI deployment and release templates, kept AWS deployment disabled, guarded Amplify behind an exact Zenith-only enable switch, marked inherited package identities private, provisioned Playwright explicitly in CI, and added privacy-safe structured CloudWatch-ready MCP request/tool logs — @Wilian
|
|
276
|
+
- added: every one of the 200 catalogued components now carries an authored description. 134 compositional parts were served a generated placeholder — `get_component` on `PopoverContent` said "Zenith component PopoverContent with atomic CSS styling", which tells an agent nothing about it needing a `PopoverPortal`. The descriptions carry the structural facts that props cannot: which parts portal themselves and which do not, that `TablePagination` is a sibling of `Table` rather than nested in it, that `OptionCardControl` is injected when omitted, and that `CollapsibleSidebarToggle` must be rendered in both the expanded and condensed states or it disappears in one of them — @wmolinari ([!32](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/32))
|
|
277
|
+
- added: a ratchet asserting no component serves the generated placeholder, so the next component added has to be described rather than defaulted — @wmolinari ([!32](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/32))
|
|
278
|
+
- fixed: resolving a release commit used a `\b` anchor, which sits between `0` and `-` — so searching for `0.1.0` also matched `release: 0.1.0-next.1` and a stable version could resolve to one of its own prereleases. The search is end-anchored, and a commit whose subject is exactly the release line is preferred over a merge commit that quotes it — @wmolinari ([!33](https://gitlab.com/e-pilot/product/frontend/zenith/-/merge_requests/33))
|
package/codemods/README.md
CHANGED
|
@@ -79,9 +79,11 @@ when its line says so. `rg 'TODO\(zenith-migrate\)'` lists what is left.
|
|
|
79
79
|
|
|
80
80
|
### What each transform does
|
|
81
81
|
|
|
82
|
-
`from-volt` (Volt 1.7): every name Zenith kept (
|
|
83
|
-
moves onto the Zenith import unchanged; the
|
|
84
|
-
|
|
82
|
+
`from-volt` (Volt 1.7): every name Zenith kept with its API (263, the whole barrel minus the list
|
|
83
|
+
below) moves onto the Zenith import unchanged; the 16 `FieldCombobox*` parts, whose names Zenith
|
|
84
|
+
kept but whose API it changed (#106), stay on the Volt import with one TODO pointing at the Combobox
|
|
85
|
+
docs page's "Changed in this version" section, a hint rather than a rewrite; the `style.css`,
|
|
86
|
+
`preflight.css` and `font.css` imports are deleted; `ColumnFiltersState`, `ColumnOrderState`, `ColumnPinningState`, `FilterFn` and
|
|
85
87
|
`PaginationState` come from `@tanstack/react-table`; `cn`, `CloseIcon`, the `*Variants` helpers
|
|
86
88
|
and `useClientPagination` stay on the Volt import with the replacement in their TODO
|
|
87
89
|
(`mergeClasses` + `makeZenithStyles`; `calculatePagination`/`clampPage`). Then run the
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FieldCombobox kept Volt's names but not its API (#106): Zenith's runs on Base UI's Autocomplete,
|
|
3
|
+
* with the options as data. Moving the names would hand Volt markup to an API it does not fit, so
|
|
4
|
+
* they stay on the Volt import with one TODO that points at the migration. A hint, not a rewrite.
|
|
5
|
+
*/
|
|
6
|
+
import type { MigrateContext } from "../lib/types"
|
|
7
|
+
|
|
8
|
+
export const handles = [
|
|
9
|
+
"FieldCombobox",
|
|
10
|
+
"FieldComboboxGroup",
|
|
11
|
+
"FieldComboboxGroupLabel",
|
|
12
|
+
"FieldComboboxValue",
|
|
13
|
+
"FieldComboboxTrigger",
|
|
14
|
+
"FieldComboboxContent",
|
|
15
|
+
"FieldComboboxItem",
|
|
16
|
+
"FieldComboboxItemCheckIcon",
|
|
17
|
+
"FieldComboboxEmpty",
|
|
18
|
+
"FieldComboboxSeparator",
|
|
19
|
+
"FieldComboboxInput",
|
|
20
|
+
"FieldComboboxList",
|
|
21
|
+
"FieldComboboxLoading",
|
|
22
|
+
"FieldComboboxClear",
|
|
23
|
+
"FieldComboboxClearValue",
|
|
24
|
+
"FieldComboboxPlaceholder",
|
|
25
|
+
] as const
|
|
26
|
+
|
|
27
|
+
const MESSAGE =
|
|
28
|
+
'Zenith\'s FieldCombobox changed its API (options as `items` data rendered by a function, `onClick` for `onSelect`, the search text on FieldComboboxContent); port this combobox by hand as the Combobox docs page shows under "Changed in this version" (docs/components/combobox#changed-in-this-version), then import its parts from @epilot/zenith-react'
|
|
29
|
+
|
|
30
|
+
export function migrate(ctx: MigrateContext): void {
|
|
31
|
+
const imported = handles.filter((name) => name in ctx.names)
|
|
32
|
+
const [first, ...rest] = imported
|
|
33
|
+
if (!first) return
|
|
34
|
+
// One TODO for the whole family; the other parts stay on the Volt import beside it.
|
|
35
|
+
ctx.keep(first, MESSAGE)
|
|
36
|
+
for (const name of rest) ctx.keep(name)
|
|
37
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { MigrationRule } from "../lib/types"
|
|
6
6
|
import * as classHelpers from "./class-helpers"
|
|
7
|
+
import * as fieldCombobox from "./field-combobox"
|
|
7
8
|
import * as pagination from "./pagination"
|
|
8
9
|
import * as sameName from "./same-name"
|
|
9
10
|
import * as stylesheets from "./stylesheets"
|
|
@@ -11,6 +12,7 @@ import * as tanstackTypes from "./tanstack-types"
|
|
|
11
12
|
|
|
12
13
|
export const rules: readonly MigrationRule[] = [
|
|
13
14
|
sameName,
|
|
15
|
+
fieldCombobox,
|
|
14
16
|
stylesheets,
|
|
15
17
|
tanstackTypes,
|
|
16
18
|
classHelpers,
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Zenith kept Volt's names: everything listed here moves onto the Zenith import unchanged
|
|
3
3
|
* (components, hooks, helpers and their prop types). The list is Volt 1.7's barrel intersected
|
|
4
4
|
* with Zenith's public exports; `same-name.test.ts` keeps it honest against `src/index.ts`. A Volt
|
|
5
|
-
* name missing here is kept on the Volt import with a TODO, never silently mapped.
|
|
5
|
+
* name missing here is kept on the Volt import with a TODO, never silently mapped. FieldCombobox
|
|
6
|
+
* kept its names but not its API, so `field-combobox.ts` handles those.
|
|
6
7
|
*/
|
|
7
8
|
import type { MigrateContext } from "../lib/types"
|
|
8
9
|
|
|
@@ -27,22 +28,6 @@ export const handles = [
|
|
|
27
28
|
"FieldInputGroup",
|
|
28
29
|
"FieldGroupAddon",
|
|
29
30
|
"FieldTextarea",
|
|
30
|
-
"FieldCombobox",
|
|
31
|
-
"FieldComboboxGroup",
|
|
32
|
-
"FieldComboboxGroupLabel",
|
|
33
|
-
"FieldComboboxValue",
|
|
34
|
-
"FieldComboboxTrigger",
|
|
35
|
-
"FieldComboboxContent",
|
|
36
|
-
"FieldComboboxItem",
|
|
37
|
-
"FieldComboboxItemCheckIcon",
|
|
38
|
-
"FieldComboboxEmpty",
|
|
39
|
-
"FieldComboboxSeparator",
|
|
40
|
-
"FieldComboboxInput",
|
|
41
|
-
"FieldComboboxList",
|
|
42
|
-
"FieldComboboxLoading",
|
|
43
|
-
"FieldComboboxClear",
|
|
44
|
-
"FieldComboboxClearValue",
|
|
45
|
-
"FieldComboboxPlaceholder",
|
|
46
31
|
"Select",
|
|
47
32
|
"SelectContent",
|
|
48
33
|
"SelectGroup",
|
|
@@ -528,7 +528,10 @@ function expressionArity(
|
|
|
528
528
|
scope,
|
|
529
529
|
expression.name
|
|
530
530
|
)
|
|
531
|
-
if (
|
|
531
|
+
if (
|
|
532
|
+
j.TSAsExpression.check(expression) ||
|
|
533
|
+
j.TSSatisfiesExpression.check(expression)
|
|
534
|
+
)
|
|
532
535
|
return (
|
|
533
536
|
typeArity(lookup, expression.typeAnnotation) ??
|
|
534
537
|
expressionArity(lookup, scope, expression.expression)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { servedFamily as o } from "../renderer/served.js";
|
|
2
|
+
import { autocomplete as e } from "@epilot/zenith-runtime/v1";
|
|
3
|
+
const t = o("autocomplete", e), r = t.Autocomplete;
|
|
4
|
+
export {
|
|
5
|
+
r as Autocomplete
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=_zenith-runtime-family_autocomplete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_zenith-runtime-family_autocomplete.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|