@takazudo/zdtp 0.4.13 → 0.4.15
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 +143 -0
- package/PORTABLE-CONTRACT.md +366 -94
- package/README.md +218 -96
- package/dist/apply/build-apply-overrides.d.ts +2 -2
- package/dist/apply/compute-hunks.d.ts +23 -0
- package/dist/apply/last-applied.d.ts +5 -0
- package/dist/apply/reconcile-applied.d.ts +19 -0
- package/dist/apply-modal.d.ts +2 -48
- package/dist/astro/host-adapter.js +3 -3
- package/dist/astro/index.js +2 -2
- package/dist/{autoload-state-CfL2VUe1.js → autoload-state-7BQYXI7Y.js} +1 -1
- package/dist/bin/server.js +52 -50
- package/dist/bulk/bulk-actions.d.ts +45 -0
- package/dist/bulk/index.d.ts +2 -0
- package/dist/chain/index.d.ts +2 -0
- package/dist/chain/token-chain-context.d.ts +19 -0
- package/dist/chain/token-chain-popover.d.ts +22 -0
- package/dist/changed/contribution.d.ts +32 -0
- package/dist/changed/footer-content.d.ts +16 -0
- package/dist/changed/index.d.ts +4 -0
- package/dist/changed/tab-badge.d.ts +8 -0
- package/dist/changed/tab-filter.d.ts +8 -0
- package/dist/config/panel-config.d.ts +22 -25
- package/dist/controls/actions-menu-popover.d.ts +2 -0
- package/dist/controls/role-button.d.ts +5 -2
- package/dist/controls/tooltip.d.ts +8 -4
- package/dist/element-inspect/element-inspect-context.d.ts +22 -0
- package/dist/element-inspect/element-inspect-orchestrator.d.ts +17 -0
- package/dist/element-inspect/element-inspect-overlay.d.ts +16 -0
- package/dist/element-inspect/element-inspect-toggle-button.d.ts +3 -0
- package/dist/element-inspect/element-inspect-view.d.ts +6 -0
- package/dist/element-inspect/find-tokens-for-element.d.ts +23 -0
- package/dist/element-inspect/index.d.ts +8 -0
- package/dist/highlight/find-elements.d.ts +14 -3
- package/dist/highlight/highlight-orchestrator.d.ts +7 -3
- package/dist/highlight/highlight-state.d.ts +3 -2
- package/dist/highlight/highlight-toggle-button.d.ts +3 -0
- package/dist/highlight/walk-css-rules.d.ts +2 -0
- package/dist/history/buttons.d.ts +28 -0
- package/dist/history/index.d.ts +3 -0
- package/dist/history/rail.d.ts +13 -0
- package/dist/history/snapshots.d.ts +57 -0
- package/dist/host/host-mutations.d.ts +23 -0
- package/dist/index-BOOmFtJh.js +12165 -0
- package/dist/{index-BAY1JOUl.js → index-C2DIYD0d.js} +2 -2
- package/dist/index.js +14 -14
- package/dist/load-routing-BtCE1hGI.js +547 -0
- package/dist/{manifest-DCReQE0k.js → manifest-DvuKi7I4.js} +11 -7
- package/dist/{panel-config-COHesW2B.js → panel-config-CRf-ONSV.js} +404 -308
- package/dist/picker/alt-click-picker.d.ts +10 -2
- package/dist/picker/arming-coordinator.d.ts +2 -0
- package/dist/picker/index.d.ts +2 -2
- package/dist/search/command-palette.d.ts +21 -0
- package/dist/search/contribution.d.ts +7 -0
- package/dist/search/fuzzy.d.ts +10 -0
- package/dist/search/index.d.ts +4 -0
- package/dist/search/match-bar.d.ts +14 -0
- package/dist/search/search-header.d.ts +7 -0
- package/dist/search/token-search.d.ts +36 -0
- package/dist/server/create-apply-handler.d.ts +33 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +1 -1
- package/dist/shell/dock-mode-switch.d.ts +6 -0
- package/dist/shell/footer.d.ts +1 -0
- package/dist/shell/ghost-idle.d.ts +24 -0
- package/dist/shell/header.d.ts +5 -0
- package/dist/shell/layer-activity.d.ts +6 -0
- package/dist/shell/mini-pill.d.ts +21 -0
- package/dist/shell/regions.d.ts +26 -0
- package/dist/shell/shortcut-dispatcher.d.ts +17 -0
- package/dist/shell/tab-bar.d.ts +17 -0
- package/dist/shell/tab-overflow-popover.d.ts +13 -0
- package/dist/specimen/on-page-specimen.d.ts +19 -0
- package/dist/specimen/preview-glyphs.d.ts +2 -0
- package/dist/specimen/specimen-state.d.ts +12 -0
- package/dist/specimen/specimen-tab-body.d.ts +18 -0
- package/dist/specimen/specimen-toolbar.d.ts +10 -0
- package/dist/specimen/specimen-values.d.ts +9 -0
- package/dist/state/history.d.ts +64 -0
- package/dist/state/persist.d.ts +10 -14
- package/dist/state/transaction.d.ts +24 -0
- package/dist/state/tweak-state.d.ts +16 -0
- package/dist/styles/z-index-tokens.d.ts +2 -0
- package/dist/tabs/color-tab.d.ts +10 -1
- package/dist/tabs/flat/flat-tab.d.ts +27 -0
- package/dist/tabs/flat/index.d.ts +8 -0
- package/dist/tabs/flat/scroll-to-token-row.d.ts +3 -0
- package/dist/tabs/flat/tier-section.d.ts +17 -0
- package/dist/tabs/flat/token-controller.d.ts +23 -0
- package/dist/tabs/flat/token-row.d.ts +12 -0
- package/dist/tabs/flat/types.d.ts +25 -0
- package/dist/tabs/font-tab.d.ts +13 -12
- package/dist/tabs/generic-tab.d.ts +8 -38
- package/dist/tabs/palette/palette-check-view.d.ts +5 -1
- package/dist/tabs/palette/palette-edit-view.d.ts +9 -1
- package/dist/tabs/palette/palette-tab.d.ts +12 -1
- package/dist/tabs/size-tab.d.ts +7 -10
- package/dist/tabs/spacing-tab.d.ts +7 -13
- package/dist/testing.js +3 -3
- package/dist/tokens/tier-model.d.ts +6 -0
- package/dist/{tweak-state-BKNxtHzb.js → tweak-state-BGp_7Tfh.js} +473 -423
- package/dist/utils/numeric-transform.d.ts +32 -0
- package/dist/utils/token-diff.d.ts +25 -0
- package/dist/utils/token-graph.d.ts +23 -0
- package/dist/utils/token-index.d.ts +30 -0
- package/dist/zdtp.css +1 -1
- package/package.json +3 -1
- package/dist/controls/pill-slider-row.d.ts +0 -36
- package/dist/controls/select-row.d.ts +0 -25
- package/dist/controls/slider-row.d.ts +0 -29
- package/dist/controls/text-row.d.ts +0 -27
- package/dist/index-U7Ol7tgA.js +0 -7591
- package/dist/load-routing-D4H2VOl5.js +0 -426
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @takazudo/zdtp
|
|
2
2
|
|
|
3
|
-
A live-tweak design-token panel for Astro sites. Drop a single `<DesignTokenPanelHost>` component into your layout, hand it a `PanelConfig`, and your users get an in-page UI for adjusting CSS custom properties (spacing, typography, sizing, color palette + semantic roles). Changes apply to `:root`
|
|
3
|
+
A live-tweak design-token panel for Astro sites. Drop a single `<DesignTokenPanelHost>` component into your layout, hand it a `PanelConfig`, and your users get an in-page UI for adjusting CSS custom properties (spacing, typography, sizing, color palette + semantic roles). Changes apply instantly to the default `:root` target (or to a configured `applySink`), persist to `localStorage`, and survive view transitions and hard reloads.
|
|
4
4
|
|
|
5
|
-
The package is portable: every project-specific identifier is driven by the host's `PanelConfig`. Storage keys, console namespace, modal class prefix, schema id, and the entire tab configuration (tiers, items, color cluster extras) are all configured by the consumer. Every config field is JSON-serializable so the configuration crosses the Astro frontmatter → client island boundary without losing fidelity.
|
|
5
|
+
The package is portable: every project-specific identifier is driven by the host's `PanelConfig`. Storage keys, console namespace, modal class prefix, schema id, and the entire tab configuration (tiers, items, color cluster extras) are all configured by the consumer. Every config field except the function-valued `applySink` is JSON-serializable, so the configuration crosses the Astro frontmatter → client island boundary without losing fidelity.
|
|
6
6
|
|
|
7
7
|
The panel uses an **abstract token tier model**: all token categories — spacing, typography, size, and color — are expressed as `TabConfig` / `TierConfig` / `TierItem` structures on `PanelConfig.tabs`. A "ref tier" mechanism lets semantic tokens reference base tokens, and the apply pipeline emits `var(--base-cssvar)` for ref-tier items. See `PORTABLE-CONTRACT.md` §3 for the full tier model spec.
|
|
8
8
|
|
|
@@ -10,23 +10,53 @@ The authoritative API spec is [`PORTABLE-CONTRACT.md`](./PORTABLE-CONTRACT.md).
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Token tabs and tiers for spacing, typography, sizing, color, and host-defined
|
|
16
|
+
token families, with reference-tier aliases and kind-specific editors.
|
|
17
|
+
- Fuzzy token search, cross-tab counts, and a `Cmd/Ctrl+K` command palette.
|
|
18
|
+
- Changed-row markers, per-row revert, tab badges, **Changed only**, **Copy
|
|
19
|
+
diff**, and **Revert all**.
|
|
20
|
+
- Undo/redo history plus persisted A/B snapshots.
|
|
21
|
+
- Numeric ramp selection and bulk multiply, add, round-to-step, or set-all
|
|
22
|
+
operations.
|
|
23
|
+
- Resolution-chain popovers with dependents, ramp siblings, blast radius, and
|
|
24
|
+
on-demand used-on-page counts.
|
|
25
|
+
- Ten-slot token-user highlights, element inspect, element path, and the
|
|
26
|
+
optional DOM Tweaker, coordinated around one Alt+click owner.
|
|
27
|
+
- In-panel type, line-height, family, weight, bar, radius, and duration
|
|
28
|
+
previews, plus a host-page specimen rendered with the site's real font.
|
|
29
|
+
- Float, right-dock, bottom-dock, mini-pill, ghost-idle, and optional
|
|
30
|
+
body-margin reflow modes.
|
|
31
|
+
- Debounced apply previews with file digests and hunks, stale-write protection,
|
|
32
|
+
partial reconciliation, and JSON export/import when disk apply is not
|
|
33
|
+
configured.
|
|
34
|
+
- Owner autoload for developer-only loading across page visits.
|
|
35
|
+
|
|
36
|
+
See the [Panel UX tour](/docs/recipes/panel-ux-tour) for a feature-by-feature
|
|
37
|
+
walkthrough and the shortcut table.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
13
41
|
## 0. Architecture at a glance
|
|
14
42
|
|
|
15
|
-
The design-token panel is a browser-based UI that writes token overrides to
|
|
43
|
+
The design-token panel is a browser-based UI that writes token overrides to
|
|
44
|
+
`:root` by default (or through `PanelConfig.applySink`), with an optional
|
|
45
|
+
**apply pipeline** for persisting those overrides back to disk source files.
|
|
16
46
|
|
|
17
47
|
```
|
|
18
48
|
┌─ Your dev server (Astro / Vite / any host) ──────┐
|
|
19
49
|
│ │
|
|
20
50
|
│ Panel UI (browser) ←─────────────────────────> │ Host adapter (side-effect import)
|
|
21
51
|
│ ↓ (user tweaks) │
|
|
22
|
-
│ POST /apply (
|
|
52
|
+
│ POST /apply (preview, then confirmed write) │ Apply endpoint (routes tokens to files)
|
|
23
53
|
│ ↓ │
|
|
24
54
|
└──────────────────────────────────────────────────┘
|
|
25
55
|
│
|
|
26
56
|
│ (HTTP)
|
|
27
57
|
↓
|
|
28
58
|
┌─ design-token-panel bin server ───────────────┐
|
|
29
|
-
│ Receives POST /apply
|
|
59
|
+
│ Receives POST /apply preview/write requests │
|
|
30
60
|
│ Validates tokens & paths │
|
|
31
61
|
│ Rewrites source CSS files atomically │
|
|
32
62
|
│ (respects --write-root sandbox) │
|
|
@@ -44,7 +74,7 @@ A Preact-rendered side panel that:
|
|
|
44
74
|
- Reads a host-supplied **tab configuration** (`PanelConfig.tabs`) — an array of `TabConfig` entries where each tab owns one or more `TierConfig` objects, each holding an array of `TierItem` entries. Sliders (`length` / `number`), selects, text inputs, color pickers, and pill toggles are all supported via a discriminated `TierValueKind`.
|
|
45
75
|
- Supports **abstract tier references**: a `TierConfig` can carry `referencesTier` to point at a base tier; the apply pipeline emits `var(--base-cssvar)` for each ref-tier item, encoding semantic → base token aliasing in the config data model.
|
|
46
76
|
- Renders a **color tab** (id `'color'`) from the same tab model — palette and semantic tokens are `TierItem` arrays, and the structural metadata (base roles, scheme registry, panel settings) lives in `TabConfig.colorExtras`.
|
|
47
|
-
- Writes every override to `document.documentElement.style.setProperty(...)` against the consumer-supplied CSS-var names — so your stylesheet can be plain CSS, CSS Modules, Tailwind, or anything else.
|
|
77
|
+
- Writes every override to the default `document.documentElement.style.setProperty(...)` target (or to the configured `applySink`) against the consumer-supplied CSS-var names — so your stylesheet can be plain CSS, CSS Modules, Tailwind, or anything else.
|
|
48
78
|
- Persists state to `localStorage` under a host-chosen prefix and re-applies overrides synchronously on next page load (no FOUT — this is a hard requirement of the contract).
|
|
49
79
|
- Exposes a small console API (`window.<namespace>.showDesignPanel()` etc.) so a developer can pop the panel without it being mounted on every page.
|
|
50
80
|
- Plugs into Astro's view-transition lifecycle (`astro:before-swap` / `astro:page-load`) so soft navigation does not double-mount the panel.
|
|
@@ -111,13 +141,13 @@ pnpm exec zdtp-server --help
|
|
|
111
141
|
|
|
112
142
|
| Flag | Required | Purpose | Default |
|
|
113
143
|
|---|---|---|---|
|
|
114
|
-
| `--routing <path>` | yes | Path to the routing JSON file (
|
|
144
|
+
| `--routing <path>` | yes | Path to the routing JSON file (CSS-var prefix family → repo-relative CSS file path). Absolute, or relative to `--root`. See §3.2. | — |
|
|
115
145
|
| `--write-root <dir>` | yes | Required write boundary: the only directory tree the bin is allowed to write into. Absolute, or relative to `--root`. | — |
|
|
116
146
|
| `--root <dir>` | no | CWD/resolution base used to resolve `--routing` and `--write-root`. | `process.cwd()` |
|
|
117
147
|
| `--port <number>` | no | TCP port to bind. `0` asks the OS for an ephemeral port (the bin logs the assigned port on startup). | `24681` |
|
|
118
148
|
| `--host <addr>` | no | Bind address. Use `0.0.0.0` to expose on the LAN (off by default). | `127.0.0.1` |
|
|
119
149
|
| `--allow-origin <origin>` | repeatable | Origin allowed to POST to `/apply` (scheme + host + port, no trailing slash). At least one is required for any browser to apply. | none (all origins denied) |
|
|
120
|
-
| `--quiet` | no | Suppress the startup banner and
|
|
150
|
+
| `--quiet` | no | Suppress the startup banner, no-allow-origin warning, and per-request summary logs. | off |
|
|
121
151
|
| `--help`, `-h` | no | Print usage and exit 0. | — |
|
|
122
152
|
|
|
123
153
|
Generic invocation, run from your consumer repo root:
|
|
@@ -133,7 +163,7 @@ The bin reads the routing JSON once at startup and does not hot-reload it. Resta
|
|
|
133
163
|
|
|
134
164
|
### 3.2 Routing configuration
|
|
135
165
|
|
|
136
|
-
The routing JSON is a top-level object mapping **
|
|
166
|
+
The routing JSON is a top-level object mapping a **CSS-var prefix family** (without the leading `--` and trailing `-`) to a **repo-relative CSS file path**. Each path receives the apply pipeline's serialised writes for that prefix family. The keys must match the keys used in your `PanelConfig.applyRouting` (the panel UI loads from this same JSON file — see §5).
|
|
137
167
|
|
|
138
168
|
Generic example:
|
|
139
169
|
|
|
@@ -181,7 +211,7 @@ While the bin is running it exposes a tiny HTTP surface:
|
|
|
181
211
|
|
|
182
212
|
- **`GET /healthz`** — returns `200 OK` with `{"ok":true,"writeRoot":"…","routing":"…","port":…}` once the listener is up. Useful for dev-server readiness checks.
|
|
183
213
|
- **`OPTIONS /apply`** — CORS preflight. Returns `204` with `Access-Control-Allow-{Origin,Methods,Headers,Max-Age}` headers when the request's `Origin` is on the allow-list, and `403` otherwise.
|
|
184
|
-
- **`POST /apply`** — applies a token-overrides payload via the apply pipeline. The body is `application/json` with a top-level `tokens` object whose keys are CSS-var names (e.g. `--brand-primary`) and whose values are CSS values. See §6 for the full token-manifest schema and §6.
|
|
214
|
+
- **`POST /apply`** — applies a token-overrides payload via the apply pipeline. The body is `application/json` with a top-level `tokens` object whose keys are CSS-var names (e.g. `--brand-primary`) and whose values are CSS values. See §6 for the full token-manifest schema and §6.5 for apply-time behaviour. A non-JSON content type returns `415`; an unallowed origin returns `403`.
|
|
185
215
|
- **Anything else** — `404` for unknown paths and `405` for unsupported methods on `/apply`.
|
|
186
216
|
|
|
187
217
|
The bin is intended to run as a subprocess of your dev server (`concurrently`, `npm-run-all`, a custom Node wrapper, etc.) and exits cleanly under host control:
|
|
@@ -260,7 +290,6 @@ const spacingTier: TierConfig = {
|
|
|
260
290
|
id: 'spacing-md',
|
|
261
291
|
cssVar: '--myapp-spacing-md',
|
|
262
292
|
label: 'Spacing M',
|
|
263
|
-
group: 'hsp',
|
|
264
293
|
default: '1rem',
|
|
265
294
|
type: { kind: 'length', step: 0.0625, unit: 'rem' },
|
|
266
295
|
},
|
|
@@ -313,7 +342,7 @@ import { myPanelConfig } from '../lib/my-panel-config';
|
|
|
313
342
|
</html>
|
|
314
343
|
|
|
315
344
|
<script>
|
|
316
|
-
// Required side-effect load — see §
|
|
345
|
+
// Required side-effect load — see §12.1 for the rationale. Use a dynamic
|
|
317
346
|
// `void import(...)` here, NOT a top-level `import '...';` statement.
|
|
318
347
|
void import('@takazudo/zdtp/astro/host-adapter');
|
|
319
348
|
</script>
|
|
@@ -377,7 +406,7 @@ The Astro recipe above shows the case where a host owns the config import and th
|
|
|
377
406
|
|
|
378
407
|
**Worked example:** the [Astro](https://github.com/Takazudo/zudo-design-token-panel-example-astro), [Vite + React](https://github.com/Takazudo/zudo-design-token-panel-example-vite-react), and [Next.js](https://github.com/Takazudo/zudo-design-token-panel-example-nextjs) example repos prove the contract end-to-end. Each ships:
|
|
379
408
|
|
|
380
|
-
- A host-side config file with deliberately different names (e.g. `--astro-palette-
|
|
409
|
+
- A host-side config file with deliberately different names (e.g. explicit `--astro-palette-0` / `--astro-palette-1` items, `astro` namespace).
|
|
381
410
|
- A routing JSON file at the example's root.
|
|
382
411
|
- A bin invocation via `concurrently` in the dev script, pointing at that routing file.
|
|
383
412
|
|
|
@@ -504,12 +533,17 @@ default instance only.
|
|
|
504
533
|
| `storagePrefix` | `string` | Base for every derived `localStorage` key. Also the instance id. See §9. |
|
|
505
534
|
| `consoleNamespace` | `string` | Global object the package installs `showDesignPanel` / `hideDesignPanel` / `toggleDesignPanel` on (e.g. `consoleNamespace: 'myapp'` → `window.myapp.showDesignPanel`). |
|
|
506
535
|
| `modalClassPrefix` | `string` | BEM root class for every modal the panel owns (export, import, apply). Emits `${prefix}__overlay`, `${prefix}__panel`, etc. |
|
|
507
|
-
| `schemaId` | `string` | **Display-only** label
|
|
536
|
+
| `schemaId` | `string` | **Display-only** label returned by `getDesignTokenSchema()`. The built-in import/export UI does not read it; `serialize()` emits canonical `SCHEMA_V2`/`SCHEMA_V3` and `deserialize()` validates canonical `SCHEMA_V1`/`V2`/`V3`. See §14's migration recipe. |
|
|
508
537
|
| `exportFilenameBase` | `string` | Default download filename base — exports save as `${exportFilenameBase}.json`. |
|
|
509
538
|
| `toggleEvent` | `string` (optional) | Window-event name that toggles THIS instance. Defaults to `toggle-${storagePrefix}` for non-default instances; the default instance keeps `toggle-design-token-panel`. |
|
|
510
539
|
| `tabs` | `readonly TabConfig[]` | **Required.** Tab strip data — each entry is a tab with one or more `TierConfig` objects. The color tab (id `'color'`) additionally requires `colorExtras`. See §6. |
|
|
511
540
|
| `colorPresets` | `Record<string, ColorScheme>` (optional) | Optional named scheme presets surfaced in the Color tab "Scheme..." dropdown. Defaults to `{}`. See §7.5. |
|
|
541
|
+
| `applyEndpoint` | `string` (optional) | Browser POST target for apply previews and confirmed writes. See §3 and [Apply pipeline reference](/docs/reference/apply-pipeline). |
|
|
542
|
+
| `applyRouting` | `Record<string, string>` (optional) | CSS-var prefix-family to repo-relative CSS-file routing map used by the apply endpoint. |
|
|
512
543
|
| `applySink` | `ApplySink` (optional) | Optional sink that routes this instance's CSS-var writes off `:root`. See §5.4. Not JSON-serializable — do not include in Astro inline config. |
|
|
544
|
+
| `domTweaker` | `DomTweakerConfig` (optional) | Enables the development-only Tailwind class editor and its Alt+click owner. See [DOM Tweaker reference](/docs/reference/dom-tweaker). |
|
|
545
|
+
| `dock` | `PanelDockConfig` (optional) | Controls right/bottom host reflow. Defaults to `{ reflow: 'body-margin' }`; see [configurePanel](/docs/reference/configure-panel#dock--optional-host-reflow). |
|
|
546
|
+
| `legacyIdRenameMap` | `Record<string, string \| null>` (optional) | Maps renamed persisted ids to new ids, or drops ids with `null`, during load. |
|
|
513
547
|
| `autoRememberOnOpen` | `boolean` (optional) | Whether opening the panel (any of the auto-remember call sites — see §10.1) writes the `:autoload` flag with `'auto'` provenance. Defaults to `true`. Set `false` for a public site that wants a panel-open button visible to every visitor without arming owner-mode for whoever clicks it. `enableAutoload()`'s explicit `'1'` write is unaffected either way. See §10.1's Auto-remember footgun. |
|
|
514
548
|
|
|
515
549
|
### 5.4 `applySink` — optional write target
|
|
@@ -561,10 +595,10 @@ const handle = configurePanel({
|
|
|
561
595
|
The Astro entry point (`<DesignTokenPanelHost>`) handles mounting for you. Internally:
|
|
562
596
|
|
|
563
597
|
- The console API (`showDesignPanel` etc.) is **always installed eagerly**, even when the panel module has not loaded — calling them is what triggers the lazy import for cold-start users.
|
|
564
|
-
- The panel module is **dynamically imported on first need**: when the user calls a console helper, OR when first-paint detects any of these gate signals in `localStorage` — `${storagePrefix}:visible` set to `1` or its `${storagePrefix}-open` mirror set to `1`, persisted overrides (a content check across the `${storagePrefix}-state` family — `-state` (v1) through every `-state-vN` — rather than a presence check on a specific version key; see §9), the owner-autoload flag (`${storagePrefix}:autoload` set to `'1'` or `'auto'`), or the element-path inspector enabled.
|
|
565
|
-
- This gating keeps the panel out of the initial JS
|
|
598
|
+
- The panel module is **dynamically imported on first need**: when the user calls a console helper, OR when first-paint detects any of these gate signals in `localStorage` — `${storagePrefix}:visible` set to `1` or its `${storagePrefix}-open` mirror set to `1`, persisted overrides (a content check across the `${storagePrefix}-state` family — `-state` (v1) through every `-state-vN` — rather than a presence check on a specific version key; see §9), the owner-autoload flag (`${storagePrefix}:autoload` set to `'1'` or `'auto'`), or the element-path inspector enabled. `${storagePrefix}-domtweaker-enabled` is also a gate signal when `domTweaker` is configured.
|
|
599
|
+
- This gating keeps the panel bundle out of the initial JS payload for first-time visitors while still re-applying overrides on hard reload for users who have tweaked things. **General visitors** (none of these signals set) pay no panel-bundle cost; the small host adapter/config bootstrap still runs.
|
|
566
600
|
|
|
567
|
-
For a Vite-only / non-Astro host, mount it yourself by importing the adapter module after `configurePanel(...)`. See §
|
|
601
|
+
For a Vite-only / non-Astro host, mount it yourself by importing the adapter module after `configurePanel(...)`. See §8.5.
|
|
568
602
|
|
|
569
603
|
### 5.6 First-open geometry
|
|
570
604
|
|
|
@@ -589,24 +623,22 @@ spec; this section is the consumer-oriented summary.
|
|
|
589
623
|
|
|
590
624
|
```ts
|
|
591
625
|
export type TierValueKind =
|
|
592
|
-
| { kind: 'length'; step: number; unit: string }
|
|
593
|
-
| { kind: 'number'; step: number }
|
|
626
|
+
| { kind: 'length'; step: number; unit: string; units?: readonly string[] }
|
|
627
|
+
| { kind: 'number'; step: number; unit?: string }
|
|
594
628
|
| { kind: 'select'; options: readonly string[] }
|
|
595
629
|
| { kind: 'text' }
|
|
596
630
|
| { kind: 'cursor' }
|
|
597
631
|
| { kind: 'content' }
|
|
598
632
|
| { kind: 'mask-image' }
|
|
599
|
-
| { kind: 'color' };
|
|
633
|
+
| { kind: 'color'; format?: 'hex' | 'oklch' };
|
|
600
634
|
|
|
601
635
|
export interface TierItem {
|
|
602
636
|
/** Stable id used as the key in persisted state (e.g. `hsp-2xs`). */
|
|
603
637
|
id: string;
|
|
604
|
-
/** CSS custom property written to
|
|
638
|
+
/** CSS custom property written to the default root or configured apply sink (e.g. `--myapp-spacing-md`). */
|
|
605
639
|
cssVar: string;
|
|
606
640
|
/** Display label shown in the panel row. */
|
|
607
641
|
label: string;
|
|
608
|
-
/** Optional manifest group — tab components use this for section headers. */
|
|
609
|
-
group?: string;
|
|
610
642
|
/** Default value as a CSS string (`0.125rem`, `12px`, etc.). */
|
|
611
643
|
default: string;
|
|
612
644
|
/** Discriminated union describing the control kind and its metadata. */
|
|
@@ -631,16 +663,29 @@ export interface TierConfig {
|
|
|
631
663
|
* apply pipeline emits `var(--target-cssvar)` for ref-tier items.
|
|
632
664
|
*/
|
|
633
665
|
referencesTier?: string;
|
|
666
|
+
/** Marks a color tier as semantic data rather than a palette tier. */
|
|
667
|
+
semantic?: true;
|
|
668
|
+
/** Allowed ramp sources for per-row semantic references on color tabs. */
|
|
669
|
+
referencesRamps?: readonly { tab?: string; tier: string }[];
|
|
670
|
+
/** Optional visual preview rendered for this tier in the panel. */
|
|
671
|
+
preview?: 'size' | 'line-height' | 'family' | 'weight' | 'bar' | 'radius' | 'duration';
|
|
672
|
+
/** CSS variable used as the base for a preview, when supplied. */
|
|
673
|
+
previewBase?: string;
|
|
634
674
|
}
|
|
635
675
|
|
|
636
676
|
export interface TabConfig {
|
|
637
677
|
id: string;
|
|
638
678
|
label: string;
|
|
639
679
|
tiers: readonly TierConfig[];
|
|
640
|
-
/** Tier ids hidden behind an Advanced disclosure. */
|
|
641
|
-
advancedTiers?: readonly string[];
|
|
642
680
|
/** Required on color tabs (id 'color' / 'color-secondary'). */
|
|
643
681
|
colorExtras?: ColorClusterExtras;
|
|
682
|
+
/** Required on the reserved 'notes' tab; forbidden on other tabs. */
|
|
683
|
+
notesExtras?: NotesExtras;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export interface NotesExtras {
|
|
687
|
+
title: string;
|
|
688
|
+
html: string;
|
|
644
689
|
}
|
|
645
690
|
```
|
|
646
691
|
|
|
@@ -662,7 +707,6 @@ export const spacingTab: TabConfig = {
|
|
|
662
707
|
id: 'spacing-md',
|
|
663
708
|
cssVar: '--myapp-spacing-md',
|
|
664
709
|
label: 'Spacing M',
|
|
665
|
-
group: 'hsp',
|
|
666
710
|
default: '1rem',
|
|
667
711
|
type: { kind: 'length', step: 0.0625, unit: 'rem' },
|
|
668
712
|
},
|
|
@@ -747,8 +791,8 @@ export interface ColorScheme {
|
|
|
747
791
|
cursor: number | string;
|
|
748
792
|
selectionBg: number | string;
|
|
749
793
|
selectionFg: number | string;
|
|
750
|
-
palette: readonly string[]; //
|
|
751
|
-
shikiTheme
|
|
794
|
+
palette: readonly string[]; // the public type requires exactly 16 entries
|
|
795
|
+
shikiTheme?: string;
|
|
752
796
|
semantic?: Record<string, number | string>;
|
|
753
797
|
}
|
|
754
798
|
```
|
|
@@ -798,9 +842,13 @@ the JSON round-trip.
|
|
|
798
842
|
|
|
799
843
|
For each palette `TierItem` in the palette tier, the panel writes
|
|
800
844
|
`item.cssVar` ← `palette[i]` from the active scheme / user override. For
|
|
801
|
-
each declared base role,
|
|
802
|
-
|
|
803
|
-
|
|
845
|
+
each declared base role, the live DOM apply path writes the role's CSS-var ←
|
|
846
|
+
`palette[state[roleKey]]`. The disk `buildApplyOverrides` payload
|
|
847
|
+
intentionally contains palette and semantic CSS variables only; base roles are
|
|
848
|
+
runtime wiring and are not emitted into source-file rewrites.
|
|
849
|
+
For each semantic `TierItem`, it resolves the mapping and writes the emitted
|
|
850
|
+
CSS value: `var(...)` for a palette/reference mapping, a literal string for a
|
|
851
|
+
literal mapping, or `light-dark(light, dark)` for a per-mode literal.
|
|
804
852
|
|
|
805
853
|
Roles absent from `colorExtras.baseRoles` are not written, so a minimalist
|
|
806
854
|
cluster (just `background` + `foreground`) is fine.
|
|
@@ -809,7 +857,7 @@ cluster (just `background` + `foreground`) is fine.
|
|
|
809
857
|
|
|
810
858
|
The Color tab's "Scheme..." dropdown surfaces named `ColorScheme` entries. Two sources feed it:
|
|
811
859
|
|
|
812
|
-
1. **`
|
|
860
|
+
1. **`colorExtras.colorSchemes`** — the color tab's bundled scheme registry. Always present, typically holds your default scheme(s) (`"Default"`, `"Default Light"` / `"Default Dark"`).
|
|
813
861
|
2. **`PanelConfig.colorPresets`** — an optional, host-supplied preset map for an additional, larger preset library. Defaults to `{}` — the package itself ships zero presets.
|
|
814
862
|
|
|
815
863
|
This split exists so a host that just wants the panel for a single scheme (zero or one cluster scheme) does not pay for a long preset blob, while a host that wants to ship a "playground" of curated schemes (Dracula / Solarized / Tokyo Night / etc.) drops them into a single config field.
|
|
@@ -829,7 +877,7 @@ const myPresets: Record<string, ColorScheme> = {
|
|
|
829
877
|
selectionBg: '#44475a',
|
|
830
878
|
selectionFg: '#ffffff',
|
|
831
879
|
palette: [
|
|
832
|
-
// 16 hex strings —
|
|
880
|
+
// 16 hex strings — the ColorScheme type's fixed palette length
|
|
833
881
|
'#21222c',
|
|
834
882
|
'#ff5555',
|
|
835
883
|
'#50fa7b',
|
|
@@ -854,7 +902,7 @@ const myPresets: Record<string, ColorScheme> = {
|
|
|
854
902
|
};
|
|
855
903
|
|
|
856
904
|
export const myPanelConfig: PanelConfig = {
|
|
857
|
-
// ... storagePrefix,
|
|
905
|
+
// ... storagePrefix, tabs (including the color tab), etc.
|
|
858
906
|
colorPresets: myPresets,
|
|
859
907
|
};
|
|
860
908
|
```
|
|
@@ -868,7 +916,7 @@ export const myPanelConfig: PanelConfig = {
|
|
|
868
916
|
|
|
869
917
|
**Key collision** — if a `colorPresets` key matches a `colorExtras.colorSchemes` key, the bundled scheme wins for `handleLoadPreset`. Rename one of the keys if you want both to be selectable.
|
|
870
918
|
|
|
871
|
-
**JSON-serializable** — every `ColorScheme` is plain JSON, same as the rest of the config (§
|
|
919
|
+
**JSON-serializable** — every `ColorScheme` is plain JSON, same as the rest of the config (§8.2). The host-supplied preset map crosses the Astro frontmatter → island boundary as part of the serialised `PanelConfig`.
|
|
872
920
|
|
|
873
921
|
> **Note on preset libraries.** The package ships zero baked-in scheme presets — the long preset blob (Dracula / Solarized / Tokyo Night / etc.) historically baked into earlier internal versions has been moved out of the package so consumers do not pay for a preset library they do not use. Hosts that want a curated preset list ship it themselves through `panelConfig.colorPresets`.
|
|
874
922
|
|
|
@@ -903,7 +951,7 @@ Astro stringifies props at render time. **Functions, class instances, and `undef
|
|
|
903
951
|
When the consumer site renders Astro's `<ClientRouter />`, the panel's host adapter automatically wires:
|
|
904
952
|
|
|
905
953
|
- `astro:before-swap` → unmount the Preact tree (`render(null, root)`), remove the host node, snapshot visibility intent so the remount decision survives the body swap.
|
|
906
|
-
- `astro:page-load` → re-apply persisted overrides + re-materialise the shell when
|
|
954
|
+
- `astro:page-load` → re-apply persisted overrides + re-materialise the shell when any visibility, autoload, enabled-feature, or non-empty persisted-state signal is set.
|
|
907
955
|
|
|
908
956
|
No additional wiring needed in your layout beyond importing `<ClientRouter />` from `astro:transitions`.
|
|
909
957
|
|
|
@@ -962,10 +1010,24 @@ Behaviour notes:
|
|
|
962
1010
|
| `state-v3` | `${storagePrefix}-state-v3` | Legacy pre-v4 format (flat, single-slot `color`). Migrated into `state-v4` on first load; the v3 key itself is left in place (not deleted) so a downgrade can still read it. |
|
|
963
1011
|
| `state-v2` | `${storagePrefix}-state-v2` | Legacy pre-v3 format. Migrated into `state-v3` (and from there into `state-v4`) on first load, then deleted. |
|
|
964
1012
|
| `state-v1` | `${storagePrefix}-state` | Legacy pre-v2 flat-state format (Color-only). Migrated into `state-v3` (and from there into `state-v4`) on first load, then deleted. |
|
|
965
|
-
| `open` | `${storagePrefix}-open` | Mirror of the panel's `open` boolean
|
|
966
|
-
| `position` | `${storagePrefix}-position` | Drag position `{ top, left }` so the panel reappears where the user left it.
|
|
967
|
-
| `
|
|
968
|
-
| `
|
|
1013
|
+
| `open` | `${storagePrefix}-open` | Mirror of the panel's `open` boolean, read synchronously at mount so user intent survives reloads. |
|
|
1014
|
+
| `position` | `${storagePrefix}-position` | Drag position `{ top, left }` so the panel reappears where the user left it. |
|
|
1015
|
+
| `size` | `${storagePrefix}-size` | Floating shell dimensions `{ width, height }` in pixels. |
|
|
1016
|
+
| `dock` | `${storagePrefix}-dock` | Presentation mode: `'float'`, `'right'`, `'bottom'`, or `'mini'`. |
|
|
1017
|
+
| `dock-size` | `${storagePrefix}-dock-size` | Right/bottom dock dimensions `{ right, bottom }`, defaulting to `{ right: 440, bottom: 340 }`. |
|
|
1018
|
+
| `density` | `${storagePrefix}-density` | Tab-grid density preference (`0`, `1`, or `2`). |
|
|
1019
|
+
| `ghost` | `${storagePrefix}-ghost` | Ghost-when-idle preference (`'1'` when enabled). |
|
|
1020
|
+
| `specimen` | `${storagePrefix}-specimen` | Font specimen toolbar JSON `{ text, preset, overridden, width }`; width is clamped to 240–720. |
|
|
1021
|
+
| `snapshot-a` | `${storagePrefix}-snapshot-a` | Persisted A snapshot `{ state, identity, savedAt, edits }`. |
|
|
1022
|
+
| `snapshot-b` | `${storagePrefix}-snapshot-b` | Persisted B snapshot `{ state, identity, savedAt, edits }`. |
|
|
1023
|
+
| `last-applied` | `${storagePrefix}-last-applied` | Flat comparison baseline; a successful apply resets it to `{}` while unconfirmed overrides remain in live state. |
|
|
1024
|
+
| `visible` | `${storagePrefix}:visible` | Adapter-level visibility-intent flag, owned by the lazy-load gate. |
|
|
1025
|
+
| `autoload` | `${storagePrefix}:autoload` | Owner-mode autoload flag: `'1'` for explicit opt-in or `'auto'` for auto-remember. |
|
|
1026
|
+
| `elpath-enabled` | `${storagePrefix}-elpath-enabled` | Element-path picker enabled bit. |
|
|
1027
|
+
| `domtweaker-enabled` | `${storagePrefix}-domtweaker-enabled` | DOM Tweaker enabled bit; meaningful only when `domTweaker` is configured. |
|
|
1028
|
+
| `highlight-slots` | `${storagePrefix}-highlight-slots` | Ten highlight slot colors in local storage. |
|
|
1029
|
+
| `highlight-outline-width` | `${storagePrefix}-highlight-outline-width` | Global highlight outline width in local storage, clamped to 1–20. |
|
|
1030
|
+
| `highlight-active` | `${storagePrefix}-highlight-active` | Active CSS-variable-to-slot map in session storage. |
|
|
969
1031
|
|
|
970
1032
|
For example, with `storagePrefix: 'myapp-design-token-panel'`:
|
|
971
1033
|
|
|
@@ -976,8 +1038,22 @@ myapp-design-token-panel-state-v2
|
|
|
976
1038
|
myapp-design-token-panel-state
|
|
977
1039
|
myapp-design-token-panel-open
|
|
978
1040
|
myapp-design-token-panel-position
|
|
1041
|
+
myapp-design-token-panel-size
|
|
1042
|
+
myapp-design-token-panel-dock
|
|
1043
|
+
myapp-design-token-panel-dock-size
|
|
1044
|
+
myapp-design-token-panel-density
|
|
1045
|
+
myapp-design-token-panel-ghost
|
|
1046
|
+
myapp-design-token-panel-specimen
|
|
1047
|
+
myapp-design-token-panel-snapshot-a
|
|
1048
|
+
myapp-design-token-panel-snapshot-b
|
|
1049
|
+
myapp-design-token-panel-last-applied
|
|
979
1050
|
myapp-design-token-panel:visible
|
|
980
1051
|
myapp-design-token-panel:autoload
|
|
1052
|
+
myapp-design-token-panel-elpath-enabled
|
|
1053
|
+
myapp-design-token-panel-domtweaker-enabled
|
|
1054
|
+
myapp-design-token-panel-highlight-slots
|
|
1055
|
+
myapp-design-token-panel-highlight-outline-width
|
|
1056
|
+
myapp-design-token-panel-highlight-active # sessionStorage
|
|
981
1057
|
```
|
|
982
1058
|
|
|
983
1059
|
### Note: colon vs dash — `visible` and `autoload`
|
|
@@ -1008,11 +1084,11 @@ When the host dispatches a `color-scheme-changed` event (e.g. a light/dark toggl
|
|
|
1008
1084
|
- If that identity already has a persisted slot, the slot's stored `color` (and `secondary`) state is loaded and re-applied — a per-scheme tweak now **survives** a round-trip through another scheme and back.
|
|
1009
1085
|
- If the identity has no slot yet (never tweaked under this scheme before), the panel cold-seeds from that scheme's defaults instead — the pre-existing "adopt the new scheme's palette" behavior for a scheme you haven't touched.
|
|
1010
1086
|
|
|
1011
|
-
Editing color under scheme A only ever overwrites scheme A's slot (`writeMergedV4` merge-saves: it reads the existing envelope, replaces just the active identity's slot, and preserves every other identity's slot untouched) — it never mutates scheme B's stored state. A full panel reset (Reset
|
|
1087
|
+
Editing color under scheme A only ever overwrites scheme A's slot (`writeMergedV4` merge-saves: it reads the existing envelope, replaces just the active identity's slot, and preserves every other identity's slot untouched) — it never mutates scheme B's stored state. A full panel reset (Reset) still wipes every slice via `clearAppliedStyles`; a disk Apply only reconciles confirmed-written variables and does not reset other identities or slices.
|
|
1012
1088
|
|
|
1013
|
-
**Migration.** On first load with no `state-v4` key, the legacy v1/v2/v3 chain runs unchanged (v3 wins over v2 over v1, exactly as before) and the resulting single flat `TweakState` is filed into `state-v4` under whichever identity is active at that moment — every subsequent load reads `state-v4` first. The legacy v3
|
|
1089
|
+
**Migration.** On first load with no `state-v4` key, the legacy v1/v2/v3 chain runs unchanged (v3 wins over v2 over v1, exactly as before); v2 and v1 are normalised to v3 as that chain requires, and the resulting single flat `TweakState` is filed into `state-v4` under whichever identity is active at that moment — every subsequent load reads `state-v4` first. The legacy v3 key is left in place by the v4 migration step so a host that needs to downgrade can still read it; the lower v2/v1 keys follow the legacy chain's deletion rules.
|
|
1014
1090
|
|
|
1015
|
-
**Host-owned `color-scheme` is never touched.** A host that manages its own `<html style="color-scheme">` (e.g. a site-level light/dark toggle) has that inline style tracked separately from the panel's own writes; the panel's mount/clear paths only ever remove a `color-scheme` value that the panel itself applied, never a host-owned one
|
|
1091
|
+
**Host-owned `color-scheme` is never touched.** A host that manages its own `<html style="color-scheme">` (e.g. a site-level light/dark toggle) has that inline style tracked separately from the panel's own writes; the panel's mount/clear paths only ever remove a `color-scheme` value that the panel itself applied, never a host-owned one.
|
|
1016
1092
|
|
|
1017
1093
|
Two consequences worth knowing as a host integrator:
|
|
1018
1094
|
|
|
@@ -1053,7 +1129,7 @@ zdtp.toggle(); // toggle open/closed
|
|
|
1053
1129
|
- **Available on both integration paths:**
|
|
1054
1130
|
- Non-Astro hosts get it as soon as `@takazudo/zdtp`'s package-root module has loaded (it installs the alias at module init).
|
|
1055
1131
|
- Astro hosts get it as soon as the host-adapter `<script>` has run — **before** the panel bundle itself has loaded. The first `zdtp.*` call lazy-imports the bundle, exactly like `window[consoleNamespace].*`.
|
|
1056
|
-
- **Never clobbers a host-defined `window.zdtp`.** If
|
|
1132
|
+
- **Never clobbers a host-defined `window.zdtp`.** If the existing value exposes callable `show`, `hide`, and `toggle` methods, the package treats it as an intentional host alias and stays silent — enabling hosts to pre-claim the alias before lazily loading the panel bundle. Other existing values are still left untouched and produce a `console.warn`, including the edge case of choosing `consoleNamespace: 'zdtp'` yourself.
|
|
1057
1133
|
- **Auto-remember applies too** — `zdtp.show()` arms the `:autoload` flag with `'auto'` provenance exactly like `showDesignPanel()` (§10.1's Auto-remember footgun note applies here as well).
|
|
1058
1134
|
|
|
1059
1135
|
See `PORTABLE-CONTRACT.md` §6.5 for the full install-site and no-clobber/no-double-install contract.
|
|
@@ -1074,7 +1150,7 @@ There is no default `consoleNamespace` exposed to consumers — the field is req
|
|
|
1074
1150
|
|
|
1075
1151
|
### 10.1 Owner-autoload — loading the panel only for you
|
|
1076
1152
|
|
|
1077
|
-
**Use case:** you are deploying a public site and want the design-token panel available to yourself (the site owner) without
|
|
1153
|
+
**Use case:** you are deploying a public site and want the design-token panel available to yourself (the site owner) without fetching the panel bundle or injecting its stylesheet for general visitors. The small host adapter/config bootstrap still runs. Once armed, every page load fetches the bundle eagerly and mounts the panel CLOSED — the Alt+click element-path inspector is ready immediately, and you can open the panel UI at any time.
|
|
1078
1154
|
|
|
1079
1155
|
#### API surface
|
|
1080
1156
|
|
|
@@ -1083,14 +1159,14 @@ Both the Astro host-adapter (`window[consoleNamespace].*`) and the package-root
|
|
|
1083
1159
|
| Call | Effect |
|
|
1084
1160
|
|------|--------|
|
|
1085
1161
|
| `window.myapp.enableAutoload()` | Sets `${storagePrefix}:autoload` to `'1'`, arms the element-path inspector (`-elpath-enabled`), loads the panel bundle, mounts CLOSED. |
|
|
1086
|
-
| `window.myapp.disableAutoload()` |
|
|
1162
|
+
| `window.myapp.disableAutoload()` | Removes `:autoload`, writes `'0'` to `:visible` and `-elpath-enabled`, removes the open-state key, and unmounts the panel. |
|
|
1087
1163
|
| `enableAutoload()` (package-root export) | Same as the console form — for non-Astro hosts. |
|
|
1088
1164
|
| `disableAutoload()` (package-root export) | Same as the console form — for non-Astro hosts. |
|
|
1089
1165
|
| `shouldAutoload()` (package-root export) | Returns `true` iff the flag is currently set. |
|
|
1090
1166
|
|
|
1091
1167
|
#### The `${storagePrefix}:autoload` flag
|
|
1092
1168
|
|
|
1093
|
-
Stored in `localStorage` as `'1'` (explicit — set by `enableAutoload()`) or `'auto'` (auto-remembered — set by opening the panel; see "Auto-remember on open" below), removed or `'0'` otherwise. The host-adapter's lazy-load gate honours **either** value — when the flag is `'1'` or `'auto'`, the panel bundle loads eagerly (the same as when the panel was previously visible, via either the `:visible` flag or its `-open` mirror, or overrides are persisted, checked as a content check across the `${storagePrefix}-state` family rather than a presence check on a specific version key — see §9). **General visitors** who have never opened the panel or called `enableAutoload()` have no flag and pay
|
|
1169
|
+
Stored in `localStorage` as `'1'` (explicit — set by `enableAutoload()`) or `'auto'` (auto-remembered — set by opening the panel; see "Auto-remember on open" below), removed or `'0'` otherwise. The host-adapter's lazy-load gate honours **either** value — when the flag is `'1'` or `'auto'`, the panel bundle loads eagerly (the same as when the panel was previously visible, via either the `:visible` flag or its `-open` mirror, or overrides are persisted, checked as a content check across the `${storagePrefix}-state` family rather than a presence check on a specific version key — see §9). **General visitors** who have never opened the panel or called `enableAutoload()` have no flag and pay no panel-bundle cost; the small host adapter/config bootstrap still runs.
|
|
1094
1170
|
|
|
1095
1171
|
**Downstream-host recipe.** A host that writes its own lazy-load probe (e.g. to decide whether to eagerly fetch something else alongside the panel) can read the flag directly and test `=== '1'` to match only the explicit-owner population, excluding visitors who merely opened the panel once.
|
|
1096
1172
|
|
|
@@ -1098,11 +1174,11 @@ Stored in `localStorage` as `'1'` (explicit — set by `enableAutoload()`) or `'
|
|
|
1098
1174
|
|
|
1099
1175
|
#### Auto-remember on open
|
|
1100
1176
|
|
|
1101
|
-
Opening the panel by any means — `showDesignPanel()`, `toggleDesignPanel()`,
|
|
1177
|
+
Opening the panel by any means — `showDesignPanel()`, `toggleDesignPanel()`, the instance handle's `open()` / `toggle()`, the fixed-name `zdtp.show()` / `zdtp.toggle()` global, or an instance toggle event — **automatically sets the `:autoload` flag to `'auto'`** (auto-remembered provenance, distinct from the `'1'` that `enableAutoload()` writes). This means once you open the panel you will be in owner-mode on subsequent page loads without calling `enableAutoload()` explicitly. An existing explicit `'1'` is never downgraded — an owner who armed autoload deliberately keeps that provenance even after opening the panel again.
|
|
1102
1178
|
|
|
1103
1179
|
#### Element-path coupling
|
|
1104
1180
|
|
|
1105
|
-
`enableAutoload()` arms the Alt+click element-path inspector by writing `-elpath-enabled = '1'`. The inspector runs inside the Preact shell; mounting the shell CLOSED (rather than unmounted) keeps the inspector functional even while the panel UI is hidden. You can still turn the inspector off via the in-panel toggle — `disableAutoload()`
|
|
1181
|
+
`enableAutoload()` arms the Alt+click element-path inspector by writing `-elpath-enabled = '1'`. The inspector runs inside the Preact shell; mounting the shell CLOSED (rather than unmounted) keeps the inspector functional even while the panel UI is hidden. You can still turn the inspector off via the in-panel toggle — `disableAutoload()` writes `'0'` as part of full teardown.
|
|
1106
1182
|
|
|
1107
1183
|
#### Auto-remember footgun
|
|
1108
1184
|
|
|
@@ -1116,48 +1192,95 @@ Because **any open trigger sets `:autoload`** (to `'auto'`), a visible "open pan
|
|
|
1116
1192
|
|
|
1117
1193
|
---
|
|
1118
1194
|
|
|
1195
|
+
### 10.2 Shared Alt+click picker ownership
|
|
1196
|
+
|
|
1197
|
+
Element path, DOM Tweaker, and element inspect use one per-window coordinator
|
|
1198
|
+
for the Alt+click gesture. Enabling or arming one feature revokes the previous
|
|
1199
|
+
owner; the three features do not run simultaneously. Panel surfaces and the
|
|
1200
|
+
on-page specimen are excluded from picker hits.
|
|
1201
|
+
|
|
1202
|
+
| Feature | Activation | Result |
|
|
1203
|
+
| --- | --- | --- |
|
|
1204
|
+
| Element path | Owner autoload or its panel toggle, then `Alt+click` a host element | Copies the annotated selector/path block. |
|
|
1205
|
+
| DOM Tweaker | Enable the configured DOM Tweaker, then `Alt+click` a host element | Opens the Tailwind class editor and live utility preview. |
|
|
1206
|
+
| Element inspect | Toggle inspect or press `I`, then click a host element; `Alt` also arms the shared picker | Opens the reserved inspect tab with token-backed and inherited rows. |
|
|
1207
|
+
|
|
1208
|
+
The most recently armed feature owns the gesture and receives subsequent page
|
|
1209
|
+
clicks. Owner autoload's element-path arm therefore does not block a later DOM
|
|
1210
|
+
Tweaker or element-inspect arm.
|
|
1211
|
+
|
|
1212
|
+
---
|
|
1213
|
+
|
|
1119
1214
|
## 11. Tailwind not required
|
|
1120
1215
|
|
|
1121
|
-
The panel ships its own bundled CSS scoped under a panel-private namespace
|
|
1216
|
+
The panel ships its own bundled CSS scoped under a panel-private namespace. The
|
|
1217
|
+
chrome palette is a self-contained OKLCH ramp; it does not read host
|
|
1218
|
+
`--color-*` or `--font-mono` variables. A host can opt into a different chrome
|
|
1219
|
+
theme by assigning the same `--tokentweak-*` variables on a listed scope.
|
|
1122
1220
|
|
|
1123
1221
|
```css
|
|
1124
1222
|
:where(.tokenpanel-shell, [data-design-token-panel-modal]) {
|
|
1125
|
-
|
|
1126
|
-
--tokentweak-
|
|
1127
|
-
--tokentweak-
|
|
1128
|
-
--tokentweak-
|
|
1129
|
-
--tokentweak-
|
|
1130
|
-
--tokentweak-
|
|
1131
|
-
--tokentweak-
|
|
1132
|
-
--tokentweak-
|
|
1133
|
-
--
|
|
1134
|
-
|
|
1223
|
+
/* base-0 is the darkest ground; stops ascend toward the foreground. */
|
|
1224
|
+
--tokentweak-palette-base-0: oklch(0.18 0 0);
|
|
1225
|
+
--tokentweak-palette-base-1: oklch(0.25 0 0);
|
|
1226
|
+
--tokentweak-palette-base-2: oklch(0.34 0 0);
|
|
1227
|
+
--tokentweak-palette-base-3: oklch(0.536 0 0);
|
|
1228
|
+
--tokentweak-palette-base-4: oklch(0.66 0 0);
|
|
1229
|
+
--tokentweak-palette-base-5: oklch(0.8 0 0);
|
|
1230
|
+
--tokentweak-palette-base-6: oklch(0.91 0 0);
|
|
1231
|
+
--tokentweak-color-fg: var(--tokentweak-palette-base-5);
|
|
1232
|
+
--tokentweak-color-bg: var(--tokentweak-palette-base-0);
|
|
1233
|
+
--tokentweak-color-muted: var(--tokentweak-palette-base-4);
|
|
1234
|
+
--tokentweak-color-border: var(--tokentweak-palette-base-3);
|
|
1235
|
+
--tokentweak-color-surface: var(--tokentweak-palette-base-1);
|
|
1236
|
+
--tokentweak-color-accent: #d69a66;
|
|
1237
|
+
--tokentweak-color-accent-bar: #efb477;
|
|
1238
|
+
--tokentweak-color-accent-hover: #a7c0e3;
|
|
1239
|
+
--tokentweak-color-code-bg: var(--tokentweak-palette-base-2);
|
|
1240
|
+
--tokentweak-color-code-fg: var(--tokentweak-palette-base-6);
|
|
1241
|
+
--tokentweak-color-success: #93bb77;
|
|
1242
|
+
--tokentweak-color-danger: #da6871;
|
|
1243
|
+
--tokentweak-color-warning: #dfbb77;
|
|
1244
|
+
--tokentweak-font-mono: Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
|
1245
|
+
/* pad-*, gap-*, text-*, radius, and z-* tokens are also declared here. */
|
|
1135
1246
|
}
|
|
1136
1247
|
```
|
|
1137
1248
|
|
|
1138
|
-
|
|
1249
|
+
Hosts may override a `--tokentweak-palette-base-*` stop to move all semantic
|
|
1250
|
+
roles that alias it, or continue assigning a `--tokentweak-color-*` role
|
|
1251
|
+
directly. The direct semantic assignment still wins. Borders now use
|
|
1252
|
+
`--tokentweak-color-border`; a host that previously assigned
|
|
1253
|
+
`--tokentweak-color-muted` to recolor both secondary text and 1px dividers must
|
|
1254
|
+
also assign the border role.
|
|
1255
|
+
|
|
1256
|
+
- **Naming:** panel-private color, font, spacing, typography, and z-index variables use the `--tokentweak-*` prefix; the shared radius token is `--radius-tokentweak`. Consumer-namespaced identifiers do not appear in the panel chrome — `panel.css` reads only package-owned tokens and component-local layout hints.
|
|
1139
1257
|
- **Files:** `panel.css` (chrome layout / typography / controls) + `panel-tokens.css` (the `--tokentweak-*` declarations). Both ship from the package and the consumer pulls them in via `sideEffects`.
|
|
1140
1258
|
- **No Tailwind dependency in the consumer.** The panel chrome uses hand-authored CSS classes backed by `--tokentweak-*` variables. You can integrate the panel into a Tailwind site, a CSS Modules site, a vanilla CSS site, or anything in between.
|
|
1141
1259
|
|
|
1142
|
-
### 11.1
|
|
1260
|
+
### 11.1 Chrome scopes and host overrides
|
|
1143
1261
|
|
|
1144
|
-
The
|
|
1262
|
+
The declarations cover `.tokenpanel-shell`, `.tokenpanel-mini-pill`,
|
|
1263
|
+
`[data-design-token-panel-modal]`, the highlight-settings and chain popovers,
|
|
1264
|
+
the color picker, tooltip, element-path label/toast, and element-inspect box,
|
|
1265
|
+
label, and view. `:where()` keeps their specificity at zero, so a host can
|
|
1266
|
+
override a token with a single-class rule on the relevant surface. The host
|
|
1267
|
+
theme variables are never read by the panel chrome.
|
|
1145
1268
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
| `--tokentweak-color-fg` directly | Panel-only override that bypasses the host's `--color-*` theme. Useful when you want the panel to look different from your site shell (e.g., a brand-neutral inspector overlay). |
|
|
1269
|
+
The CSS variables the panel **writes to** (the `cssVar` field on each
|
|
1270
|
+
`TierItem`, palette/base-role names, and semantic CSS names) are entirely
|
|
1271
|
+
consumer-controlled. The package only writes those variables through the
|
|
1272
|
+
default `document.documentElement` target or a configured `applySink`.
|
|
1151
1273
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1274
|
+
Dock integration publishes `--zdtp-dock-inset-right` and
|
|
1275
|
+
`--zdtp-dock-inset-bottom` on the host root while a claim is active. The
|
|
1276
|
+
page-level specimen uses the `.tokenpanel-on-page-specimen` class and
|
|
1277
|
+
`[data-zdtp-specimen]` marker; it is host-page content, not panel chrome.
|
|
1155
1278
|
|
|
1156
1279
|
### 11.2 Apply-to-disk for a Tailwind v4 `@theme` block
|
|
1157
1280
|
|
|
1158
1281
|
The section above is about the panel's OWN chrome — it never needed Tailwind. This section is about the opposite direction: a Tailwind v4 **consumer** whose design tokens live inside `@theme { ... }` (so Tailwind can generate utility classes from them) rather than `:root`.
|
|
1159
1282
|
|
|
1160
|
-
The apply-to-disk rewriter (§3.2) scans the first top-level `:root` block AND the first top-level `@theme` block, so a Tailwind v4 token file like this applies out of the box
|
|
1283
|
+
The apply-to-disk rewriter (§3.2) scans the first top-level `:root` block AND the first top-level `@theme` block, so a Tailwind v4 token file like this applies out of the box:
|
|
1161
1284
|
|
|
1162
1285
|
```css
|
|
1163
1286
|
:root {
|
|
@@ -1224,7 +1347,7 @@ Net effect: the consumer MUST own the host-adapter import in their wrapper layou
|
|
|
1224
1347
|
</script>
|
|
1225
1348
|
```
|
|
1226
1349
|
|
|
1227
|
-
This is the second half of the paired-unit contract from §
|
|
1350
|
+
This is the second half of the paired-unit contract from §4.1.2 (`<DesignTokenPanelHost>` AND the host-adapter `<script>` block — always together). If you forget it, the `<DesignTokenPanelHost>` JSON config payload still ships, but no JS reads it, so calling `window.<consoleNamespace>.showDesignPanel()` throws `ReferenceError`.
|
|
1228
1351
|
|
|
1229
1352
|
For the regression-guard tests that pin this contract, see `package-exports.test.ts` under the package's test suite.
|
|
1230
1353
|
|
|
@@ -1236,25 +1359,25 @@ For the regression-guard tests that pin this contract, see `package-exports.test
|
|
|
1236
1359
|
|
|
1237
1360
|
**Symptom:** on first paint after a hard reload, the page renders with the consumer's default token values for a beat before snapping to the user's saved overrides.
|
|
1238
1361
|
|
|
1239
|
-
**Resolution:** the host adapter eagerly re-applies persisted overrides during the lazy-load gate (
|
|
1362
|
+
**Resolution:** the host adapter eagerly re-applies persisted overrides during the lazy-load gate (it content-checks the exact `${storagePrefix}-state` / `${storagePrefix}-state-vN` family and the visibility, autoload, and enabled-feature signals synchronously from `localStorage`). If you still see a flash, your `<DesignTokenPanelHost>` is being rendered too late in the document (e.g. inside a deferred island) — move it to the layout's `<body>` and verify the inline `<script type="application/json" id="tokenpanel-config">` is in the initial HTML.
|
|
1240
1363
|
|
|
1241
1364
|
### 13.2 Auto-mount race on first reload
|
|
1242
1365
|
|
|
1243
1366
|
**Symptom:** the panel does not re-open on the first reload after the user closed it, even though `${storagePrefix}-open` is set in `localStorage`.
|
|
1244
1367
|
|
|
1245
|
-
**Resolution:** the open boolean is mirrored to `localStorage` synchronously and read at mount time so the next mount opens directly into the user's last state without a post-render toggle dispatch. If the symptom persists, confirm the storage key matches what the contract derives (§
|
|
1368
|
+
**Resolution:** the open boolean is mirrored to `localStorage` synchronously and read at mount time so the next mount opens directly into the user's last state without a post-render toggle dispatch. If the symptom persists, confirm the storage key matches what the contract derives (§9) and that nothing else in the page is clearing the key on load.
|
|
1246
1369
|
|
|
1247
1370
|
### 13.3 Live-apply regression test approach
|
|
1248
1371
|
|
|
1249
|
-
**Symptom:** after a panel-package change, you want to confirm the live-apply pipeline (storage → adapter → `:root`) is unbroken end-to-end.
|
|
1372
|
+
**Symptom:** after a panel-package change, you want to confirm the live-apply pipeline (storage → adapter → default `:root` or `applySink`) is unbroken end-to-end.
|
|
1250
1373
|
|
|
1251
|
-
**Resolution:** the canonical regression test is each external example repo's `apply-roundtrip.spec.ts` Playwright spec under `tests/e2e/`. It boots the example's preview build, seeds a
|
|
1374
|
+
**Resolution:** the canonical regression test is each external example repo's `apply-roundtrip.spec.ts` Playwright spec under `tests/e2e/`. It boots the example's preview build, seeds a compatible state envelope under the example's storage prefix, hard-reloads, and asserts the adapter rehydrated and applied the override against the example's palette and semantic CSS variable names. The contract: storage prefix, `TierItem.cssVar` names, semantic CSS names — change one of those and this spec fails first. See §15 for links to the five external example repos.
|
|
1252
1375
|
|
|
1253
1376
|
---
|
|
1254
1377
|
|
|
1255
1378
|
## 14. Migration recipe — adopting the panel into an existing consumer
|
|
1256
1379
|
|
|
1257
|
-
This recipe walks through wiring the panel into a project that does not currently use it. If you previously consumed an internal pre-OSS snapshot of the panel where storage keys, console namespace, modal class prefix, and
|
|
1380
|
+
This recipe walks through wiring the panel into a project that does not currently use it. If you previously consumed an internal pre-OSS snapshot of the panel where storage keys, console namespace, modal class prefix, and token identifiers were hardcoded literals, the same steps apply — lift those literals into a `PanelConfig` value and a host-owned tab manifest.
|
|
1258
1381
|
|
|
1259
1382
|
1. **Install the package.**
|
|
1260
1383
|
|
|
@@ -1264,9 +1387,9 @@ This recipe walks through wiring the panel into a project that does not currentl
|
|
|
1264
1387
|
|
|
1265
1388
|
2. **Define your `PanelConfig` literals.**
|
|
1266
1389
|
|
|
1267
|
-
Pick identifiers for `storagePrefix`, `consoleNamespace`, `modalClassPrefix`, `schemaId`,
|
|
1390
|
+
Pick identifiers for `storagePrefix`, `consoleNamespace`, `modalClassPrefix`, `schemaId`, and `exportFilenameBase`. Pull these into a host-side config file (e.g. `src/lib/panel-config.ts`). If you are migrating from an internal snapshot fork and want to preserve users' saved state across the migration, keep the legacy values verbatim; otherwise pick fresh, neutral identifiers (e.g. `myapp-design-token-panel`).
|
|
1268
1391
|
|
|
1269
|
-
`schemaId` is display-only (see §5.3) — it never gates import/export, so picking any string here is safe. If
|
|
1392
|
+
`schemaId` is display-only (see §5.3) — it never gates import/export, so picking any string here is safe. If a host-facing label should name the actual schema your export/import round-trips through, import `SCHEMA_V1` / `SCHEMA_V2` / `SCHEMA_V3` from the package root and set `schemaId` to one of them instead of an arbitrary string:
|
|
1270
1393
|
|
|
1271
1394
|
```ts
|
|
1272
1395
|
import { SCHEMA_V3 } from '@takazudo/zdtp';
|
|
@@ -1279,21 +1402,20 @@ This recipe walks through wiring the panel into a project that does not currentl
|
|
|
1279
1402
|
|
|
1280
1403
|
3. **Author your token manifest in the host project.**
|
|
1281
1404
|
|
|
1282
|
-
The package itself ships zero baked-in manifest data. Define `
|
|
1283
|
-
|
|
1284
|
-
|
|
1405
|
+
The package itself ships zero baked-in manifest data. Define `TabConfig`
|
|
1406
|
+
values in your project and place them in `PanelConfig.tabs`. Every
|
|
1407
|
+
`TierItem.cssVar` is an explicit string, including one string per palette
|
|
1408
|
+
slot; there is no palette-name callback to survive the Astro JSON boundary.
|
|
1409
|
+
Add `preview` / `previewBase` to tiers that should render specimen or glyph
|
|
1410
|
+
previews.
|
|
1285
1411
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
```ts
|
|
1289
|
-
// wrong (function — does not survive Astro frontmatter → island handoff)
|
|
1290
|
-
// paletteCssVar: (i) => `--myapp-p${i}`,
|
|
1291
|
-
|
|
1292
|
-
// right (string template, JSON-serializable)
|
|
1293
|
-
paletteCssVarTemplate: '--myapp-p{n}',
|
|
1294
|
-
```
|
|
1412
|
+
4. **Author the color tab in the host project.**
|
|
1295
1413
|
|
|
1296
|
-
|
|
1414
|
+
Build a `TabConfig` with `id: 'color'`, palette and semantic `TierItem`
|
|
1415
|
+
entries, and the `colorExtras` structural metadata (base roles, scheme
|
|
1416
|
+
registry, and panel settings). If you need a secondary color cluster, add
|
|
1417
|
+
a companion `TabConfig` with `id: 'color-secondary'` and its own
|
|
1418
|
+
`colorExtras`. Keep all of this metadata plain JSON data.
|
|
1297
1419
|
|
|
1298
1420
|
5. **Drop `<DesignTokenPanelHost>` into your layout.**
|
|
1299
1421
|
|
|
@@ -1310,11 +1432,11 @@ This recipe walks through wiring the panel into a project that does not currentl
|
|
|
1310
1432
|
|
|
1311
1433
|
6. **Verify storage keys derive to the expected literals.**
|
|
1312
1434
|
|
|
1313
|
-
Open devtools → Application → Local Storage. Confirm you see keys derived under your `storagePrefix` and that any pre-existing user state (under the legacy prefix, if you preserved it) is migrated
|
|
1435
|
+
Open devtools → Application → Local Storage. Confirm you see keys derived under your `storagePrefix` and that any pre-existing user state (under the legacy prefix, if you preserved it) is migrated through the v1/v2/v3 → v4 compatibility path on first load.
|
|
1314
1436
|
|
|
1315
1437
|
7. **Run the live-apply e2e spec.**
|
|
1316
1438
|
|
|
1317
|
-
Use any of the external example repos' Playwright spec at `tests/e2e/apply-roundtrip.spec.ts` (see §15 for the five repo links) as a template: seed a `${storagePrefix}-state-
|
|
1439
|
+
Use any of the external example repos' Playwright spec at `tests/e2e/apply-roundtrip.spec.ts` (see §15 for the five repo links) as a template: seed a compatible `${storagePrefix}-state-v4` payload, hard-reload, and assert your palette and semantic CSS variables on the default `:root` (or your configured `applySink`) reflect the seeded values.
|
|
1318
1440
|
|
|
1319
1441
|
For edge cases hit during the migration, see CONTRIBUTING and the doc-site reference pages for each `PanelConfig` field.
|
|
1320
1442
|
|
|
@@ -1324,7 +1446,7 @@ For edge cases hit during the migration, see CONTRIBUTING and the doc-site refer
|
|
|
1324
1446
|
|
|
1325
1447
|
The canonical worked examples live in five dedicated sibling repos. Each is an independent consumer app that demonstrates the panel against a different host framework. Each renders a tiny page with cards, buttons, and palette swatches whose styles reference its own demo CSS variables, and each ships a Playwright spec at `tests/e2e/apply-roundtrip.spec.ts` that asserts the live-apply pipeline.
|
|
1326
1448
|
|
|
1327
|
-
- [`zudo-design-token-panel-example-astro`](https://github.com/Takazudo/zudo-design-token-panel-example-astro) — Astro + Preact island. Uses `storagePrefix: 'astro-example-tokens'`, `consoleNamespace: 'astro'`, `
|
|
1449
|
+
- [`zudo-design-token-panel-example-astro`](https://github.com/Takazudo/zudo-design-token-panel-example-astro) — Astro + Preact island. Uses `storagePrefix: 'astro-example-tokens'`, `consoleNamespace: 'astro'`, and explicit `TierItem.cssVar` names such as `--astro-palette-0`.
|
|
1328
1450
|
- [`zudo-design-token-panel-example-vite-react`](https://github.com/Takazudo/zudo-design-token-panel-example-vite-react) — Vite + React (panel mounted as a Preact island, React tree untouched). Uses the `vr` namespace.
|
|
1329
1451
|
- [`zudo-design-token-panel-example-nextjs`](https://github.com/Takazudo/zudo-design-token-panel-example-nextjs) — Next.js (App Router) + React, panel as a `'use client'` boundary. Uses the `nx` namespace.
|
|
1330
1452
|
- [`zudo-design-token-panel-example-zfb`](https://github.com/Takazudo/zudo-design-token-panel-example-zfb) — [zfb](https://github.com/Takazudo/zudo-front-builder) (Preact host). Uses the `devMiddleware` plugin hook for the apply proxy.
|