@takazudo/zdtp 0.5.1 → 0.5.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 CHANGED
@@ -4,10 +4,40 @@ All notable changes to `@takazudo/zdtp` are documented in this file.
4
4
 
5
5
  The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
6
6
 
7
+ ## [0.5.2] - 2026-09-08
8
+
9
+ ### Features
10
+
11
+ - Header actions now carry a stable `data-zdtp-action` attribute on both affordances — the inline header link and the compact kebab popover — with the ids `export`, `import`, `apply`, and `reset`. The ids are stable across releases; the display labels are not. This gives consumer tests a selector that survives a label change, which is what broke in [#831](https://github.com/Takazudo/zudo-design-token-panel/issues/831). The hook removes the dependence on the label; it does **not** remove the responsive collapse, so a locator still needs to be visibility-qualified and panel-scoped — see the recipe [Reaching header actions](https://zdtp.zudolab.dev/docs/recipes/reaching-header-actions/) and `PORTABLE-CONTRACT.md` §7.6. (91916b4, [#840](https://github.com/Takazudo/zudo-design-token-panel/issues/840))
12
+
13
+ ### Fixed
14
+
15
+ - The popover action items and the header region items are now keyed by their stable action id rather than by their display label. Two actions sharing a label previously produced duplicate keys, so reconciliation could reuse the wrong node and leave an item with a stale handler or `aria-disabled` state. (c3384ae)
16
+ - The 0.5.1 changelog said the old header-action selector "worked through 0.4.14". It worked through **0.5.0** — `v0.4.15` and `v0.5.0` both shipped before the collapse landed. (c3384ae)
17
+
18
+ ### Other Changes
19
+
20
+ - The documentation site moved to `https://zdtp.zudolab.dev/`, and the playground and minimal example to `https://zdtp-playground.zudolab.dev/` and `https://zdtp-minimal.zudolab.dev/`. The package `homepage` field follows. (0b9e596, ef41bf2, [#835](https://github.com/Takazudo/zudo-design-token-panel/issues/835))
21
+ - The 0.5.1 release notes now flag the header-action collapse as consumer-affecting, with the exact `1135px` container-query threshold and the fact that it measures the shell's content-box inline size rather than the viewport. (6215888, [#841](https://github.com/Takazudo/zudo-design-token-panel/issues/841))
22
+ - New bilingual recipe, [Reaching header actions](https://zdtp.zudolab.dev/docs/recipes/reaching-header-actions/), covering both a label-based pattern for existing releases and the id-based pattern from this one. (c701369, [#842](https://github.com/Takazudo/zudo-design-token-panel/issues/842))
23
+ - Twenty dead `*.pages.dev` example-demo links on the Examples page were repointed to their live `zudolab.dev` hosts. (c83ffa3, [#836](https://github.com/Takazudo/zudo-design-token-panel/issues/836))
24
+
7
25
  ## [0.5.1] - 2026-09-06
8
26
 
9
27
  The panel now fits its token grids, card controls, and header controls at the minimum panel width instead of overflowing. Multiple panels keep stable spawn positions: spawn ordinals are persisted and survive Astro client-side navigation. Eager loading is bounded to state keys the panel can actually read, and the specimen stylesheet gained the boundaries the docs-adoption check required.
10
28
 
29
+ ### Breaking Changes
30
+
31
+ - **Narrowed the accepted-key set of `EAGER_LOAD_GATE_STATE_FAMILY.matchesKey`, a public exported API.** 0.5.0 accepted `${storagePrefix}-state` optionally followed by `-v` and one or more digits. 0.5.1 accepts only exact matches against the new `READABLE_STATE_KEY_SUFFIXES` registry — `-state`, `-state-v2`, `-state-v3`, `-state-v4`. So `${storagePrefix}-state` and `${storagePrefix}-state-v2` still match, while `${storagePrefix}-state-v1`, `${storagePrefix}-state-v0`, `${storagePrefix}-state-v01`, and `${storagePrefix}-state-v123` matched in 0.5.0 and do not in 0.5.1. The narrowing is deliberate — the synchronous eager gate must probe only the key versions the loader can actually read — but it is consumer-observable, the TypeScript signature is unchanged so a type check will not surface it, and it should have ridden a minor release rather than this patch. **Migration**: hosts asserting on `-state-vN` keys should derive them from the new `READABLE_STATE_KEY_SUFFIXES` export instead of matching a numeric pattern, so their assertions self-update at the next storage-format bump. (bcc55fd, [#790](https://github.com/Takazudo/zudo-design-token-panel/issues/790), [#793](https://github.com/Takazudo/zudo-design-token-panel/issues/793), [#814](https://github.com/Takazudo/zudo-design-token-panel/issues/814))
32
+
33
+ - **Header action links now collapse behind the kebab menu at a panel content width of 1135px and below, making previously-clickable controls unreachable by their old selectors.** _(Retroactive correction added 2026-09-08. This behaviour shipped in 0.5.1 but was recorded only under **Fixed**, as `fix(panel): yield header controls before floating panel overflow`; it belongs here.)_ The header action links — Export, Load from JSON…, Apply, Reset — are hidden and served instead from the kebab trigger `.tokenpanel-actions-menu-btn` (`aria-label="Panel actions"`), which opens `.tokenpanel-actions-popover`. The rule is `@container tokenpanel (max-width: 1135px)` against `.tokenpanel-shell` (`container: tokenpanel / inline-size`). Two properties of that threshold matter: it engages at **1135px and below** — exactly 1135px collapses, 1136px does not — and it measures the **shell's own content-box inline size, not the viewport**, so the panel's borders count against the measured width. 1135px is wide enough that an ordinary desktop panel is already past it; a shell at 1024px is not a narrow or mobile case. The surprise is not that a responsive collapse exists but that it engages at a width most people would call desktop. Collapsed links are `display: none` and therefore leave the accessibility tree, so a query that worked through 0.5.0 times out on 0.5.1:
34
+
35
+ ```js
36
+ await page.getByRole('button', { name: 'Reset', exact: true }).click();
37
+ ```
38
+
39
+ Like the `matchesKey` narrowing above, this is consumer-observable, the TypeScript signature is unchanged so a type check will not surface it, and it should have ridden a minor release rather than this patch. **Migration**: open the kebab menu before reaching for an action, or drive the panel at a content width above the threshold — the recipe [Reaching header actions](https://zdtp.zudolab.dev/docs/recipes/reaching-header-actions/) covers both, with the boundary arithmetic. A stable `data-zdtp-action` attribute for these controls landed in 0.5.2; it does not exist in 0.5.1. (06cbb0e, [#787](https://github.com/Takazudo/zudo-design-token-panel/issues/787), [#831](https://github.com/Takazudo/zudo-design-token-panel/issues/831))
40
+
11
41
  ### Fixed
12
42
 
13
43
  - fix: fit token grids and card controls at minimum panel width (8db4235)
@@ -25,7 +55,6 @@ The panel now fits its token grids, card controls, and header controls at the mi
25
55
  - Merge branch 'topic/790-storage-bootstrap-confirm' into base/sweep-260906-2-storage-bootstrap (aca17d0)
26
56
  - Confirm storage bootstrap regressions (1fdb75e)
27
57
  - Merge branch 'topic/790-readable-gate-probe' into base/sweep-260906-2-storage-bootstrap (be49753)
28
- - Bound eager loading to readable state keys (bcc55fd)
29
58
  - Merge branch 'topic/790-astro-ordinal-lifecycle' into base/sweep-260906-2-storage-bootstrap (48a76ab)
30
59
  - Keep spawn positions stable across Astro navigation (2a6672b)
31
60
  - Merge branch 'topic/790-persisted-spawn-ordinal' into base/sweep-260906-2-storage-bootstrap (03deeaa)
@@ -515,7 +515,7 @@ export interface TierConfig {
515
515
  }
516
516
 
517
517
  For the full `SemanticValue` mapping and emission behavior, see the maintained
518
- [Color-cluster reference](https://zudo-design-token-panel.takazudomodular.com/docs/reference/color-cluster/).
518
+ [Color-cluster reference](https://zdtp.zudolab.dev/docs/reference/color-cluster/).
519
519
 
520
520
  /**
521
521
  * Color-cluster extras — the non-tier fields required for the color tab.
@@ -1515,6 +1515,47 @@ pipeline:
1515
1515
  </script>
1516
1516
  ```
1517
1517
 
1518
+ ### 7.6 `data-zdtp-action` — stable header-action DOM hook
1519
+
1520
+ The four header actions carry a stable per-action attribute so a consumer test
1521
+ can select one without matching its display label:
1522
+
1523
+ ```html
1524
+ <div role="button" class="tokenpanel-action-link" data-zdtp-action="export">Export</div>
1525
+ ```
1526
+
1527
+ | Action label | `data-zdtp-action` |
1528
+ | ----------------- | ------------------ |
1529
+ | `Export` | `export` |
1530
+ | `Load from JSON…` | `import` |
1531
+ | `Apply` | `apply` |
1532
+ | `Reset` | `reset` |
1533
+
1534
+ **The ids are stable; the labels are not.** Labels are display strings and may
1535
+ change at any minor version (note the ellipsis in `Load from JSON…`, which is
1536
+ `…` U+2026, not three periods). Select on the attribute, never on the text.
1537
+
1538
+ Both affordances emit it with identical values: the header row and the compact
1539
+ kebab popover that takes over visually below the header's container-query
1540
+ threshold.
1541
+
1542
+ **The hook does not remove the visibility branch.** `ShellHeader` *always*
1543
+ renders the header action row; the container query only hides it with
1544
+ `display: none`. So while the compact popover is open, `[data-zdtp-action="reset"]`
1545
+ matches **two** elements — the hidden header control and the popover item. A
1546
+ bare attribute selector is therefore not unambiguous at narrow widths; qualify
1547
+ it by visibility (Playwright's `:visible` / `.filter({ visible: true })`, or a
1548
+ non-zero bounding box) to land on exactly one.
1549
+
1550
+ Scope — this hook covers the header actions and their compact popover only,
1551
+ which means every layout sharing `ShellHeader`: floating, right-docked, and
1552
+ bottom-docked. Deliberately **not** covered:
1553
+
1554
+ - The **command palette**, which renders its own Export / Import / Apply entries
1555
+ and whose reset entries are per-tab (`Reset {tab.label}`), not the header's
1556
+ reset-all.
1557
+ - **Mini mode**, which renders Apply and has no header.
1558
+
1518
1559
  ---
1519
1560
 
1520
1561
  ## 8. Storage-key continuity & migration paths
@@ -1660,6 +1701,28 @@ Items this contract deliberately does NOT pin down:
1660
1701
 
1661
1702
  ---
1662
1703
 
1704
+ ## 11. Versioning & compatibility
1705
+
1706
+ The package is pre-1.0 (`0.x`). This section states what a consumer may rely
1707
+ on across a release bump.
1708
+
1709
+ - **The versioning promise.** On 0.x, patch releases preserve documented
1710
+ public contracts and accepted consumer inputs. Consumer-breaking contract
1711
+ changes, including input narrowing, require a minor release. Patches remain
1712
+ free to fix bugs, and a bug fix can change observable behavior; the promise
1713
+ only bounds what a patch may take *away* from a consumer.
1714
+ - **How breaking changes are signalled.** A consumer-breaking contract change
1715
+ gets its own heading in the release notes, with a migration pointer naming
1716
+ the new adoption path.
1717
+ - **What consumers should do.** A host mirroring the eager-load gate should
1718
+ derive its checks from the exported registries
1719
+ (`READABLE_STATE_KEY_SUFFIXES`, `EAGER_LOAD_GATE_KEY_SUFFIXES`) documented
1720
+ in §6.2, rather than matching a pattern of its own — that way the host's
1721
+ assertions self-update on the next storage-format bump instead of silently
1722
+ drifting from the package's actual behavior.
1723
+
1724
+ ---
1725
+
1663
1726
  ## Appendix A — section index
1664
1727
 
1665
1728
  Cross-reference table — what each section pins down.
@@ -1688,7 +1751,9 @@ Cross-reference table — what each section pins down.
1688
1751
  | Modal class prefix and `data-design-token-panel-modal` selector contract | §7.3 |
1689
1752
  | Self-contained panel chrome palette (no host theme reads) | §7.4 |
1690
1753
  | Host-adapter side-effect import (paired-unit obligation) | §7.5 |
1754
+ | `data-zdtp-action` stable header-action hook (ids stable, labels not; two matches while the compact popover is open) | §7.6 |
1691
1755
  | v4 envelope precedence, v1/v2/v3 storage migration, and typography-id rename map | §2, §8.3, §8.4 |
1692
1756
  | JSON export/import schemas v1/v2/v3 (serde) | §9 |
1693
1757
  | Out-of-scope / deferred concerns | §10 |
1758
+ | Versioning promise, breaking-change signalling, and consumer guidance for mirroring the eager-load gate registries | §11 |
1694
1759
  | Feature walkthrough and shortcut table | [Panel UX tour](/docs/recipes/panel-ux-tour) |
@@ -11,6 +11,10 @@
11
11
  */
12
12
  import type { ComponentChildren, JSX } from 'preact';
13
13
  export interface ActionsMenuAction {
14
+ /** Stable action id, emitted as `data-zdtp-action` on the rendered item.
15
+ * Required so the DOM hook can never silently go missing; labels are
16
+ * display strings and must not be used as the key (PORTABLE-CONTRACT §7.6). */
17
+ id: string;
14
18
  label: string;
15
19
  onSelect: () => void;
16
20
  disabled?: boolean;
@@ -16,5 +16,8 @@ export interface RoleButtonProps {
16
16
  title?: string;
17
17
  'aria-label'?: string;
18
18
  'data-testid'?: string;
19
+ /** Stable per-action DOM hook for consumer tests (see PORTABLE-CONTRACT §7.6).
20
+ * Keyed on an action id, never on the display label. */
21
+ 'data-zdtp-action'?: string;
19
22
  }
20
- export declare function RoleButton({ children, onClick, className, 'aria-disabled': ariaDisabled, ariaProps, tabIndex, elementRef, id, title, 'aria-label': ariaLabel, 'data-testid': testId, }: RoleButtonProps): JSX.Element;
23
+ export declare function RoleButton({ children, onClick, className, 'aria-disabled': ariaDisabled, ariaProps, tabIndex, elementRef, id, title, 'aria-label': ariaLabel, 'data-testid': testId, 'data-zdtp-action': zdtpAction, }: RoleButtonProps): JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { jsx as y, jsxs as K, Fragment as kt } from "preact/jsx-runtime";
2
2
  import { useEffect as P, useState as Y, useRef as O, useMemo as J, useCallback as X } from "preact/hooks";
3
- import { b as ft, p as wt, u as ht, a as Ne, A as bt, Z as gt } from "./index-D4NR_eYT.js";
3
+ import { b as ft, p as wt, u as ht, a as Ne, A as bt, Z as gt } from "./index-ChsJ5u4d.js";
4
4
  import { g as vt, m as j } from "./panel-config-CqbuB0nh.js";
5
5
  const xt = (e, o) => {
6
6
  const t = new Array(e.length + o.length);