create-zudo-sg 0.1.3 → 0.1.4

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,6 +4,29 @@ All notable changes to `create-zudo-sg` are documented in this file.
4
4
 
5
5
  The format is based on Keep a Changelog.
6
6
 
7
+ ## [0.1.4] - 2026-09-21
8
+
9
+ Gives a fresh starter a working preview token panel, not just the button that
10
+ opens one.
11
+
12
+ ### Added
13
+
14
+ - Scaffold the wiring the preview token panel needs: a `_body-end-islands.tsx`
15
+ that mounts `PreviewTokenPanelBootstrap` on the injected `/components/*` and
16
+ `/tokens` routes, a `_chrome-bindings.tsx`, a generated
17
+ `src/styleguide/token-manifest.ts`, and a `src/config/preview-token-panel-tabs.ts`
18
+ the panel reads its tabs from.
19
+ - Turn on `bundleZdtp` and a tabs-only `zdtpApplyProxy` in the starter's
20
+ `zfb.config.ts`, so the panel gets the real zdtp loader instead of the
21
+ throwing stub without opting into the dev-only Apply endpoint.
22
+
23
+ ### Changed
24
+
25
+ - Update the starter engine range to `@takazudo/zudo-sg ^0.3.1`, whose header
26
+ token trigger opens the panel the scaffold now wires.
27
+ - Document the new scaffold contents and the `routingFile` + `writeRoot` opt-in
28
+ for the panel's Apply step in the README.
29
+
7
30
  ## [0.1.3] - 2026-09-21
8
31
 
9
32
  Stops fresh starters from requesting favicons they never shipped, and moves
package/README.md CHANGED
@@ -49,12 +49,18 @@ overwrite an existing project.
49
49
  The template is a minimal host, not a copy of this repository's full site. It
50
50
  contains:
51
51
 
52
- - `zfb.config.ts` composing `zudoDoc()` and `withZudoSg()` with a root base.
52
+ - `zfb.config.ts` composing `zudoDoc()` and `withZudoSg()` with a root base,
53
+ `bundleZdtp: true`, and `chromeBindingsModule: "./pages/lib/_chrome-bindings.tsx"`.
53
54
  - `zfb.config.ts` uses an inline `favicon: "auto"`; replace it with a path or `FaviconConfig` when adding real icons under `public/`.
54
55
  - `zudo-sg.config.mjs` with a local `ui/` components root, registry output,
55
- preview stylesheet, category order, and token-manifest inputs.
56
- - `pages/index.tsx` and an optional `pages/lib/_zudo-sg-islands.ts` import
57
- shim for the host page.
56
+ preview stylesheet, category order, token-manifest inputs, and a tabs-only
57
+ `zdtpApplyProxy: { tabsModule }` (see "The preview token panel, out of the
58
+ box" below).
59
+ - `pages/index.tsx`, `pages/lib/_body-end-islands.tsx`, and
60
+ `pages/lib/_chrome-bindings.tsx`, plus an optional
61
+ `pages/lib/_zudo-sg-islands.ts` import shim, for the host page and for
62
+ mounting the preview token panel on the injected `/components/*` and
63
+ `/tokens` routes.
58
64
  - Three small Preact examples under `ui/`: Button, Card, and Counter, with
59
65
  co-located stories and a Button MDX document.
60
66
  - `src/styleguide/sg-registry.ts`, generated from the three example stories.
@@ -73,6 +79,37 @@ renames the package-safe `_gitignore` to `.gitignore`. The token manifest is
73
79
  generated after installation; it is intentionally not checked into the
74
80
  template seed.
75
81
 
82
+ ## The preview token panel, out of the box
83
+
84
+ `withZudoSg()` injects a header button (the tokens icon, hidden outside the
85
+ catalog routes) that opens the engine's preview token panel — the same panel
86
+ a component detail page's workbench toolbar and the `/tokens` dashboards
87
+ open. This is on by default (`headerTokenTrigger: true`); the scaffold does
88
+ not need to wire it.
89
+
90
+ What the scaffold does still wire, so the button opens a *working* panel
91
+ instead of an inert one:
92
+
93
+ - `zudo-sg.config.mjs`'s `zdtpApplyProxy: { tabsModule: "./src/config/preview-token-panel-tabs.ts" }`
94
+ gives the panel its manifest-derived tabs. Without a `tabsModule` the panel
95
+ has nothing to render.
96
+ - `pages/lib/_chrome-bindings.tsx`, wired via `chromeBindingsModule`, mounts
97
+ `pages/lib/_body-end-islands.tsx` (which bootstraps the panel) on the
98
+ injected `/components/*` and `/tokens` routes too, not just the host-owned
99
+ `/`.
100
+
101
+ This ships **tabs without the Apply write sandbox** — `zdtpApplyProxy` also
102
+ accepts `routingFile` and `writeRoot`, together, as an opt-in next step: they
103
+ let the panel's **Apply** button persist a tweak directly into your project's
104
+ CSS source under a running `pnpm dev`, instead of round-tripping through
105
+ `localStorage`/JSON export only. `routingFile` and `writeRoot` are optional
106
+ together (a tabs-only config, as shipped, omits both); supplying one without
107
+ the other is invalid. A fresh scaffold has no sensible default `writeRoot`,
108
+ which is why this step is left for you to add once your CSS source layout is
109
+ settled — see the root project's
110
+ [Design Token Panel guide](https://github.com/Takazudo/zudo-sg/blob/main/src/content/docs/overview/token-panels.mdx)
111
+ for the wiring shape.
112
+
76
113
  ## Styles
77
114
 
78
115
  zfb discovers `src/styles/global.css` automatically; no page import or config
@@ -146,7 +183,10 @@ checks each optional package independently.
146
183
  The starter targets Preact + zfb hosts. Keep these dependencies in a host
147
184
  that adopts the engine:
148
185
 
149
- - `@takazudo/zdtp` is required by the injected `/tokens` route at build time.
186
+ - `@takazudo/zdtp` is required by the injected `/tokens` route at build time,
187
+ and by the header trigger's preview token panel at runtime — `bundleZdtp: true`
188
+ in the generated `zfb.config.ts` keeps that panel on the real zdtp loader
189
+ instead of zudo-doc's throwing stub (zudolab/zudo-doc#4261). Do not drop it.
150
190
  - `diff` and `katex` are required by the published zudo-doc route dist that
151
191
  the host loads, even when the corresponding optional features are disabled.
152
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zudo-sg",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Create a new zudo-sg styleguide project",
5
5
  "license": "MIT",
6
6
  "author": "Takeshi Takatsudo",
@@ -17,7 +17,7 @@
17
17
  "@takazudo/zfb-md-wasm": "2.20.0",
18
18
  "@takazudo/zfb-runtime": "2.20.0",
19
19
  "@takazudo/zudo-doc": "5.26.2",
20
- "@takazudo/zudo-sg": "^0.3.0",
20
+ "@takazudo/zudo-sg": "^0.3.1",
21
21
  "diff": "^8.0.4",
22
22
  "katex": "^0.16.38",
23
23
  "preact": "^10.29.1",
@@ -4,6 +4,11 @@
4
4
  import type { JSX } from "preact";
5
5
  // Optional islands seed; see pages/lib/_zudo-sg-islands.ts.
6
6
  import "./lib/_zudo-sg-islands";
7
+ // Statically imported (not just reached through `chromeBindingsModule`'s
8
+ // virtual re-export) so zfb's island scanner is guaranteed to discover
9
+ // PreviewTokenPanelBootstrap and register it under its SSR marker — mirrors
10
+ // the sibling zudo-doc showcase's pages/index.tsx import of the same chain.
11
+ import { BodyEndIslands } from "./lib/_body-end-islands";
7
12
 
8
13
  export const frontmatter = { title: "Styleguide starter" };
9
14
 
@@ -34,6 +39,7 @@ export default function IndexPage(): JSX.Element {
34
39
  </a>
35
40
  </nav>
36
41
  </main>
42
+ <BodyEndIslands />
37
43
  </body>
38
44
  </html>
39
45
  );
@@ -0,0 +1,60 @@
1
+ /** @jsxRuntime automatic */
2
+ /** @jsxImportSource preact */
3
+ // Host-side body-end islands helper.
4
+ //
5
+ // Mirrors the sibling zudo-doc showcase's pages/lib/_body-end-islands.tsx,
6
+ // minus the doc-site islands this starter has no use for (AI chat, mermaid,
7
+ // doc-history, client-router bootstrap): this starter mounts only the
8
+ // engine's preview zdtp token panel bootstrap (`toggle-preview-token-panel`),
9
+ // which the header trigger `withZudoSg` injects opens.
10
+ //
11
+ // pages/index.tsx imports this file directly (as well as reaching it through
12
+ // `_chrome-bindings.tsx`'s `chromeBindingsModule` wiring), so zfb's island
13
+ // scanner is guaranteed to walk page -> this helper -> the real
14
+ // PreviewTokenPanelBootstrap component and register its constructor under the
15
+ // SSR marker name. `chromeBindingsModule` then makes the injected
16
+ // /components/* and /tokens routes render the same marker, so they hydrate
17
+ // against that registered constructor too — not just this starter's
18
+ // host-owned `/`.
19
+
20
+ import type { VNode, JSX } from "preact";
21
+ import { Island } from "@takazudo/zfb";
22
+ import PreviewTokenPanelBootstrap from "@takazudo/zudo-sg/token-tweak/preview-token-panel-bootstrap";
23
+
24
+ (PreviewTokenPanelBootstrap as { displayName?: string }).displayName = "PreviewTokenPanelBootstrap";
25
+
26
+ function prehydrationPanelToggleScript(toggleEvent: string): string {
27
+ return `(${function capturePanelToggle(channel: string) {
28
+ const script = document.currentScript as (HTMLScriptElement & {
29
+ __zdtpPrehydrateListener?: EventListener;
30
+ }) | null;
31
+ if (!script || script.dataset.bound === "1") return;
32
+ script.dataset.bound = "1";
33
+ script.dataset.pending = "0";
34
+ const listener = () => {
35
+ script.dataset.pending = String(Number(script.dataset.pending ?? "0") + 1);
36
+ };
37
+ script.__zdtpPrehydrateListener = listener;
38
+ window.addEventListener(channel, listener);
39
+ }.toString()})(${JSON.stringify(toggleEvent)});`;
40
+ }
41
+
42
+ /**
43
+ * The body-end islands this starter mounts. Currently just the preview
44
+ * token panel bootstrap the header trigger opens.
45
+ */
46
+ export function BodyEndIslands(): JSX.Element {
47
+ return (
48
+ <>
49
+ {/* Capture pre-hydration clicks; the bootstrap drains this once it loads. */}
50
+ <script
51
+ id="zdtp-preview-prehydrate"
52
+ dangerouslySetInnerHTML={{ __html: prehydrationPanelToggleScript("toggle-preview-token-panel") }}
53
+ />
54
+ {Island({
55
+ when: "load",
56
+ children: <PreviewTokenPanelBootstrap />,
57
+ }) as unknown as VNode}
58
+ </>
59
+ );
60
+ }
@@ -0,0 +1,23 @@
1
+ // Host-callables channel for the package-owned routes (#113 upstream;
2
+ // mirrors the sibling zudo-doc showcase's pages/lib/_chrome-bindings.tsx).
3
+ //
4
+ // `settings.chromeBindingsModule` points the zudo-doc routes plugin at this
5
+ // module; it re-exports `chromeBindings` into
6
+ // `virtual:zudo-doc-chrome-bindings`, which the injected chrome shim spreads
7
+ // into `createChrome(routeCtx, { ...chromeBindings })`. Every slot we omit
8
+ // keeps its package default.
9
+ //
10
+ // We override only BodyEndIslands: the package default excludes the host
11
+ // token-panel bootstrap, but this starter mounts the engine's preview zdtp
12
+ // panel (`toggle-preview-token-panel`). Without this binding, a body-end
13
+ // islands file imported only from pages/index.tsx would cover the host-owned
14
+ // `/` and leave the injected /components/* and /tokens routes on the
15
+ // package-default BodyEndIslands — i.e. the header trigger's panel would not
16
+ // mount on the pages that need it.
17
+
18
+ import type { ChromeHostBindings } from "@takazudo/zudo-doc/factory-context";
19
+ import { BodyEndIslands } from "./_body-end-islands";
20
+
21
+ export const chromeBindings: ChromeHostBindings = {
22
+ BodyEndIslands,
23
+ };
@@ -12,4 +12,4 @@ minimumReleaseAgeExclude:
12
12
  - "@takazudo/zfb-win32-x64-msvc@2.20.0"
13
13
  - "@takazudo/zfb@2.20.0"
14
14
  - "@takazudo/zudo-doc@5.26.2"
15
- - "@takazudo/zudo-sg@0.3.0"
15
+ - "@takazudo/zudo-sg@0.3.1"
@@ -0,0 +1,25 @@
1
+ // Host data for the engine's preview token panel: the manifest-derived tab set
2
+ // re-exported by `@takazudo/zudo-sg/plugins/zdtp-apply-proxy`'s
3
+ // `virtual:zudo-sg-preview-token-panel` (`zdtpApplyProxy.tabsModule` option in
4
+ // zudo-sg.config.mjs). Without a tabs module the panel bootstrap short-circuits
5
+ // and the header trigger button opens nothing (see
6
+ // preview-token-panel-bootstrap.tsx).
7
+ import { buildUiTokenTabs } from "@takazudo/zudo-sg/token-dashboard";
8
+ import type { UiDesignTokensManifest } from "@takazudo/zudo-sg/token-dashboard";
9
+ import {
10
+ UI_PALETTE_COLORS,
11
+ UI_COLOR_TOKENS,
12
+ UI_SPACING_TOKENS,
13
+ UI_FONT_TOKENS,
14
+ UI_SIZE_TOKENS,
15
+ } from "../styleguide/token-manifest.ts";
16
+
17
+ const uiDesignTokensManifest: UiDesignTokensManifest = {
18
+ paletteColors: UI_PALETTE_COLORS,
19
+ colorTokens: UI_COLOR_TOKENS,
20
+ spacingTokens: UI_SPACING_TOKENS,
21
+ fontTokens: UI_FONT_TOKENS,
22
+ sizeTokens: UI_SIZE_TOKENS,
23
+ };
24
+
25
+ export const tabs = buildUiTokenTabs(uiDesignTokensManifest);
@@ -0,0 +1,754 @@
1
+ /**
2
+ * Design-token manifest for configured UI tokens.
3
+ *
4
+ * GENERATED — do not hand-edit. Run `zudo-sg gen-token-manifest` after changing
5
+ * either configured source file, then commit the regenerated output.
6
+ * `zudo-sg gen-token-manifest --check` fails on drift.
7
+ *
8
+ * Source of truth: `src/styles/ui-tokens.css`,
9
+ * parsed by the `zudo-sg gen-token-manifest` CLI command
10
+ * (@takazudo/zudo-sg's src/cli/token-manifest/ui-token-manifest.ts). Only
11
+ * `default` values are derived from the CSS; `group`/`step`/`unit`/
12
+ * `control`/`options`/`pill` are presentation metadata with no CSS
13
+ * equivalent and are configured in that module's SPECS tables.
14
+ *
15
+ * Covers: Color / Spacing / Font / Size tabs.
16
+ * Does NOT include any --zd-* doc-chrome tokens.
17
+ */
18
+ import type { TokenDef } from "@takazudo/zdtp";
19
+
20
+ // --- Font weight select options ---
21
+ const FONT_WEIGHT_OPTIONS = [
22
+ "100", "200", "300", "400", "500", "600", "700", "800", "900",
23
+ ] as const;
24
+
25
+ /**
26
+ * Tier-1 raw palette colors from `src/styles/ui-tokens.css` (the `:root`
27
+ * `--palette-{group}-{step-or-role}` block). This is the raw material beneath the
28
+ * semantic `--color-*` tokens in UI_COLOR_TOKENS below — same three-tier
29
+ * model the doc-chrome panel exposes via `--palette-*` ramps and `--zd-*`
30
+ * semantic roles.
31
+ *
32
+ * These are plain name/value descriptors (NOT `TokenDef`) because zdtp's
33
+ * `TokenDef.control` has no `"color"` option — the preview panel builds them
34
+ * into `{ kind: "color" }` TierItems inline, mirroring the doc panel's
35
+ * ramp tiers. Rendered as a "Palette" swatch tier in the preview
36
+ * panel's Color tab; editing a swatch pushes `--palette-*` to the preview
37
+ * iframes via the sink, cascading into every semantic token that references it.
38
+ *
39
+ * Coverage: 36 colors.
40
+ */
41
+ export interface UiPaletteColor {
42
+ /** Palette key without the `--palette-` prefix, e.g. "neutral-2". */
43
+ name: string;
44
+ /** Raw oklch value, from `src/styles/ui-tokens.css`. */
45
+ value: string;
46
+ }
47
+
48
+ export const UI_PALETTE_COLORS: readonly UiPaletteColor[] = [
49
+ { name: "neutral-0", value: "#ffffff" },
50
+ { name: "neutral-1", value: "#e2e8f0" },
51
+ { name: "neutral-2", value: "#475569" },
52
+ { name: "neutral-3", value: "#0f172a" },
53
+ { name: "accent-0", value: "#dbeafe" },
54
+ { name: "accent-1", value: "#93c5fd" },
55
+ { name: "accent-2", value: "#2563eb" },
56
+ { name: "accent-3", value: "#1e40af" },
57
+ { name: "state-danger", value: "#dc2626" },
58
+ { name: "state-danger-dark", value: "#f87171" },
59
+ { name: "state-success", value: "#15803d" },
60
+ { name: "state-success-dark", value: "#4ade80" },
61
+ { name: "state-warning", value: "#a16207" },
62
+ { name: "state-warning-dark", value: "#facc15" },
63
+ { name: "state-info", value: "#0369a1" },
64
+ { name: "state-info-dark", value: "#38bdf8" },
65
+ { name: "line-vacuum-accent", value: "#2563eb" },
66
+ { name: "line-vacuum-accent-dark", value: "#93c5fd" },
67
+ { name: "line-vacuum-hover", value: "#1e40af" },
68
+ { name: "line-vacuum-hover-dark", value: "#dbeafe" },
69
+ { name: "line-process-accent", value: "#2563eb" },
70
+ { name: "line-process-accent-dark", value: "#93c5fd" },
71
+ { name: "line-process-hover", value: "#1e40af" },
72
+ { name: "line-process-hover-dark", value: "#dbeafe" },
73
+ { name: "line-laser-accent", value: "#2563eb" },
74
+ { name: "line-laser-accent-dark", value: "#93c5fd" },
75
+ { name: "line-laser-hover", value: "#1e40af" },
76
+ { name: "line-laser-hover-dark", value: "#dbeafe" },
77
+ { name: "line-meeting-accent", value: "#2563eb" },
78
+ { name: "line-meeting-accent-dark", value: "#93c5fd" },
79
+ { name: "line-meeting-hover", value: "#1e40af" },
80
+ { name: "line-meeting-hover-dark", value: "#dbeafe" },
81
+ { name: "line-beauty-accent", value: "#2563eb" },
82
+ { name: "line-beauty-accent-dark", value: "#93c5fd" },
83
+ { name: "line-beauty-hover", value: "#1e40af" },
84
+ { name: "line-beauty-hover-dark", value: "#dbeafe" },
85
+ ];
86
+
87
+ /**
88
+ * Color tokens from `src/styles/ui-tokens.css`.
89
+ *
90
+ * All values use light-dark() for dual-scheme support. Defaults here are
91
+ * the full CSS declarations including both light and dark sides.
92
+ * Stored as read-only text rows because light-dark() expressions cannot
93
+ * be driven by a single-axis slider.
94
+ *
95
+ * Coverage: 21 tokens total.
96
+ */
97
+ export const UI_COLOR_TOKENS: readonly TokenDef[] = [
98
+ {
99
+ id: "ui-color-bg",
100
+ cssVar: "--color-bg",
101
+ label: "color-bg",
102
+ group: "surface",
103
+ default: "#ffffff",
104
+ step: 1,
105
+ unit: "",
106
+ control: "text",
107
+ },
108
+ {
109
+ id: "ui-color-surface",
110
+ cssVar: "--color-surface",
111
+ label: "color-surface",
112
+ group: "surface",
113
+ default: "light-dark(#ffffff, #0f172a)",
114
+ step: 1,
115
+ unit: "",
116
+ control: "text",
117
+ },
118
+ {
119
+ id: "ui-color-surface-2",
120
+ cssVar: "--color-surface-2",
121
+ label: "color-surface-2",
122
+ group: "surface",
123
+ default: "light-dark(#e2e8f0, #475569)",
124
+ step: 1,
125
+ unit: "",
126
+ control: "text",
127
+ },
128
+ {
129
+ id: "ui-color-border",
130
+ cssVar: "--color-border",
131
+ label: "color-border",
132
+ group: "surface",
133
+ default: "#475569",
134
+ step: 1,
135
+ unit: "",
136
+ control: "text",
137
+ },
138
+ // Translucent frost scrim derived from --color-bg (SPA loading overlay).
139
+ {
140
+ id: "ui-color-loading-scrim",
141
+ cssVar: "--color-loading-scrim",
142
+ label: "color-loading-scrim",
143
+ group: "surface",
144
+ default: "#ffffff80",
145
+ step: 1,
146
+ unit: "",
147
+ control: "text",
148
+ },
149
+ {
150
+ id: "ui-color-fg",
151
+ cssVar: "--color-fg",
152
+ label: "color-fg",
153
+ group: "text",
154
+ default: "#0f172a",
155
+ step: 1,
156
+ unit: "",
157
+ control: "text",
158
+ },
159
+ {
160
+ id: "ui-color-muted",
161
+ cssVar: "--color-muted",
162
+ label: "color-muted",
163
+ group: "text",
164
+ default: "#475569",
165
+ step: 1,
166
+ unit: "",
167
+ control: "text",
168
+ },
169
+ {
170
+ id: "ui-color-accent",
171
+ cssVar: "--color-accent",
172
+ label: "color-accent",
173
+ group: "accent",
174
+ default: "#2563eb",
175
+ step: 1,
176
+ unit: "",
177
+ control: "text",
178
+ },
179
+ {
180
+ id: "ui-color-accent-hover",
181
+ cssVar: "--color-accent-hover",
182
+ label: "color-accent-hover",
183
+ group: "accent",
184
+ default: "#1e40af",
185
+ step: 1,
186
+ unit: "",
187
+ control: "text",
188
+ },
189
+ // Foreground token for text/icons on filled accent/state surfaces (consumed via `text-on-accent`).
190
+ {
191
+ id: "ui-color-on-accent",
192
+ cssVar: "--color-on-accent",
193
+ label: "color-on-accent",
194
+ group: "accent",
195
+ default: "#ffffff",
196
+ step: 1,
197
+ unit: "",
198
+ control: "text",
199
+ },
200
+ {
201
+ id: "ui-color-focus",
202
+ cssVar: "--color-focus",
203
+ label: "color-focus",
204
+ group: "accent",
205
+ default: "#2563eb",
206
+ step: 1,
207
+ unit: "",
208
+ control: "text",
209
+ },
210
+ {
211
+ id: "ui-color-rail-bg",
212
+ cssVar: "--color-rail-bg",
213
+ label: "color-rail-bg",
214
+ group: "rail",
215
+ default: "#0f172a",
216
+ step: 1,
217
+ unit: "",
218
+ control: "text",
219
+ },
220
+ {
221
+ id: "ui-color-rail-bg-strong",
222
+ cssVar: "--color-rail-bg-strong",
223
+ label: "color-rail-bg-strong",
224
+ group: "rail",
225
+ default: "#0f172a",
226
+ step: 1,
227
+ unit: "",
228
+ control: "text",
229
+ },
230
+ {
231
+ id: "ui-color-rail-fg",
232
+ cssVar: "--color-rail-fg",
233
+ label: "color-rail-fg",
234
+ group: "rail",
235
+ default: "#ffffff",
236
+ step: 1,
237
+ unit: "",
238
+ control: "text",
239
+ },
240
+ {
241
+ id: "ui-color-rail-muted",
242
+ cssVar: "--color-rail-muted",
243
+ label: "color-rail-muted",
244
+ group: "rail",
245
+ default: "#e2e8f0",
246
+ step: 1,
247
+ unit: "",
248
+ control: "text",
249
+ },
250
+ {
251
+ id: "ui-color-rail-border",
252
+ cssVar: "--color-rail-border",
253
+ label: "color-rail-border",
254
+ group: "rail",
255
+ default: "#475569",
256
+ step: 1,
257
+ unit: "",
258
+ control: "text",
259
+ },
260
+ {
261
+ id: "ui-color-rail-hover-bg",
262
+ cssVar: "--color-rail-hover-bg",
263
+ label: "color-rail-hover-bg",
264
+ group: "rail",
265
+ default: "#475569",
266
+ step: 1,
267
+ unit: "",
268
+ control: "text",
269
+ },
270
+ {
271
+ id: "ui-color-success",
272
+ cssVar: "--color-success",
273
+ label: "color-success",
274
+ group: "state",
275
+ default: "#15803d",
276
+ step: 1,
277
+ unit: "",
278
+ control: "text",
279
+ },
280
+ {
281
+ id: "ui-color-danger",
282
+ cssVar: "--color-danger",
283
+ label: "color-danger",
284
+ group: "state",
285
+ default: "#dc2626",
286
+ step: 1,
287
+ unit: "",
288
+ control: "text",
289
+ },
290
+ {
291
+ id: "ui-color-warning",
292
+ cssVar: "--color-warning",
293
+ label: "color-warning",
294
+ group: "state",
295
+ default: "#a16207",
296
+ step: 1,
297
+ unit: "",
298
+ control: "text",
299
+ },
300
+ {
301
+ id: "ui-color-info",
302
+ cssVar: "--color-info",
303
+ label: "color-info",
304
+ group: "state",
305
+ default: "#0369a1",
306
+ step: 1,
307
+ unit: "",
308
+ control: "text",
309
+ },
310
+ ];
311
+
312
+ /**
313
+ * Spacing tokens from `src/styles/ui-tokens.css`.
314
+ *
315
+ * Coverage: 15 tokens total.
316
+ */
317
+ export const UI_SPACING_TOKENS: readonly TokenDef[] = [
318
+ {
319
+ id: "ui-hsp-2xs",
320
+ cssVar: "--spacing-hsp-2xs",
321
+ label: "hsp-2xs",
322
+ group: "hsp",
323
+ default: "0.125rem",
324
+ step: 0.025,
325
+ unit: "rem",
326
+ },
327
+ {
328
+ id: "ui-hsp-xs",
329
+ cssVar: "--spacing-hsp-xs",
330
+ label: "hsp-xs",
331
+ group: "hsp",
332
+ default: "0.25rem",
333
+ step: 0.025,
334
+ unit: "rem",
335
+ },
336
+ {
337
+ id: "ui-hsp-sm",
338
+ cssVar: "--spacing-hsp-sm",
339
+ label: "hsp-sm",
340
+ group: "hsp",
341
+ default: "0.5rem",
342
+ step: 0.025,
343
+ unit: "rem",
344
+ },
345
+ {
346
+ id: "ui-hsp-md",
347
+ cssVar: "--spacing-hsp-md",
348
+ label: "hsp-md",
349
+ group: "hsp",
350
+ default: "1rem",
351
+ step: 0.025,
352
+ unit: "rem",
353
+ },
354
+ {
355
+ id: "ui-hsp-lg",
356
+ cssVar: "--spacing-hsp-lg",
357
+ label: "hsp-lg",
358
+ group: "hsp",
359
+ default: "1.5rem",
360
+ step: 0.025,
361
+ unit: "rem",
362
+ },
363
+ {
364
+ id: "ui-hsp-xl",
365
+ cssVar: "--spacing-hsp-xl",
366
+ label: "hsp-xl",
367
+ group: "hsp",
368
+ default: "2rem",
369
+ step: 0.025,
370
+ unit: "rem",
371
+ },
372
+ {
373
+ id: "ui-hsp-2xl",
374
+ cssVar: "--spacing-hsp-2xl",
375
+ label: "hsp-2xl",
376
+ group: "hsp",
377
+ default: "3rem",
378
+ step: 0.025,
379
+ unit: "rem",
380
+ },
381
+ {
382
+ id: "ui-vsp-3xs",
383
+ cssVar: "--spacing-vsp-3xs",
384
+ label: "vsp-3xs",
385
+ group: "vsp",
386
+ default: "0.0625rem",
387
+ step: 0.025,
388
+ unit: "rem",
389
+ },
390
+ {
391
+ id: "ui-vsp-2xs",
392
+ cssVar: "--spacing-vsp-2xs",
393
+ label: "vsp-2xs",
394
+ group: "vsp",
395
+ default: "0.125rem",
396
+ step: 0.025,
397
+ unit: "rem",
398
+ },
399
+ {
400
+ id: "ui-vsp-xs",
401
+ cssVar: "--spacing-vsp-xs",
402
+ label: "vsp-xs",
403
+ group: "vsp",
404
+ default: "0.25rem",
405
+ step: 0.025,
406
+ unit: "rem",
407
+ },
408
+ {
409
+ id: "ui-vsp-sm",
410
+ cssVar: "--spacing-vsp-sm",
411
+ label: "vsp-sm",
412
+ group: "vsp",
413
+ default: "0.5rem",
414
+ step: 0.025,
415
+ unit: "rem",
416
+ },
417
+ {
418
+ id: "ui-vsp-md",
419
+ cssVar: "--spacing-vsp-md",
420
+ label: "vsp-md",
421
+ group: "vsp",
422
+ default: "1rem",
423
+ step: 0.025,
424
+ unit: "rem",
425
+ },
426
+ {
427
+ id: "ui-vsp-lg",
428
+ cssVar: "--spacing-vsp-lg",
429
+ label: "vsp-lg",
430
+ group: "vsp",
431
+ default: "1.5rem",
432
+ step: 0.025,
433
+ unit: "rem",
434
+ },
435
+ {
436
+ id: "ui-vsp-xl",
437
+ cssVar: "--spacing-vsp-xl",
438
+ label: "vsp-xl",
439
+ group: "vsp",
440
+ default: "2rem",
441
+ step: 0.025,
442
+ unit: "rem",
443
+ },
444
+ {
445
+ id: "ui-vsp-2xl",
446
+ cssVar: "--spacing-vsp-2xl",
447
+ label: "vsp-2xl",
448
+ group: "vsp",
449
+ default: "3rem",
450
+ step: 0.025,
451
+ unit: "rem",
452
+ },
453
+ ];
454
+
455
+ /**
456
+ * Font tokens from `src/styles/ui-tokens.css`.
457
+ *
458
+ * Coverage: 22 tokens total.
459
+ */
460
+ export const UI_FONT_TOKENS: readonly TokenDef[] = [
461
+ {
462
+ id: "ui-text-xs",
463
+ cssVar: "--text-xs",
464
+ label: "text-xs",
465
+ group: "font-size",
466
+ default: "0.75rem",
467
+ step: 0.05,
468
+ unit: "rem",
469
+ },
470
+ {
471
+ id: "ui-text-sm",
472
+ cssVar: "--text-sm",
473
+ label: "text-sm",
474
+ group: "font-size",
475
+ default: "0.875rem",
476
+ step: 0.05,
477
+ unit: "rem",
478
+ },
479
+ {
480
+ id: "ui-text-base",
481
+ cssVar: "--text-base",
482
+ label: "text-base",
483
+ group: "font-size",
484
+ default: "1rem",
485
+ step: 0.05,
486
+ unit: "rem",
487
+ },
488
+ {
489
+ id: "ui-text-lg",
490
+ cssVar: "--text-lg",
491
+ label: "text-lg",
492
+ group: "font-size",
493
+ default: "1.125rem",
494
+ step: 0.05,
495
+ unit: "rem",
496
+ },
497
+ {
498
+ id: "ui-text-xl",
499
+ cssVar: "--text-xl",
500
+ label: "text-xl",
501
+ group: "font-size",
502
+ default: "1.25rem",
503
+ step: 0.05,
504
+ unit: "rem",
505
+ },
506
+ {
507
+ id: "ui-text-2xl",
508
+ cssVar: "--text-2xl",
509
+ label: "text-2xl",
510
+ group: "font-size",
511
+ default: "1.5rem",
512
+ step: 0.05,
513
+ unit: "rem",
514
+ },
515
+ {
516
+ id: "ui-text-xs--line-height",
517
+ cssVar: "--text-xs--line-height",
518
+ label: "text-xs / lh",
519
+ group: "font-size-lh",
520
+ default: "1.5",
521
+ step: 0.05,
522
+ unit: "",
523
+ },
524
+ {
525
+ id: "ui-text-sm--line-height",
526
+ cssVar: "--text-sm--line-height",
527
+ label: "text-sm / lh",
528
+ group: "font-size-lh",
529
+ default: "1.5",
530
+ step: 0.05,
531
+ unit: "",
532
+ },
533
+ {
534
+ id: "ui-text-base--line-height",
535
+ cssVar: "--text-base--line-height",
536
+ label: "text-base / lh",
537
+ group: "font-size-lh",
538
+ default: "1.5",
539
+ step: 0.05,
540
+ unit: "",
541
+ },
542
+ {
543
+ id: "ui-text-lg--line-height",
544
+ cssVar: "--text-lg--line-height",
545
+ label: "text-lg / lh",
546
+ group: "font-size-lh",
547
+ default: "1.5",
548
+ step: 0.05,
549
+ unit: "",
550
+ },
551
+ {
552
+ id: "ui-text-xl--line-height",
553
+ cssVar: "--text-xl--line-height",
554
+ label: "text-xl / lh",
555
+ group: "font-size-lh",
556
+ default: "1.5",
557
+ step: 0.05,
558
+ unit: "",
559
+ },
560
+ {
561
+ id: "ui-text-2xl--line-height",
562
+ cssVar: "--text-2xl--line-height",
563
+ label: "text-2xl / lh",
564
+ group: "font-size-lh",
565
+ default: "1.5",
566
+ step: 0.05,
567
+ unit: "",
568
+ },
569
+ {
570
+ id: "ui-font-weight-normal",
571
+ cssVar: "--font-weight-normal",
572
+ label: "font-weight-normal",
573
+ group: "font-weight",
574
+ default: "400",
575
+ step: 1,
576
+ unit: "",
577
+ control: "select",
578
+ options: FONT_WEIGHT_OPTIONS,
579
+ },
580
+ {
581
+ id: "ui-font-weight-medium",
582
+ cssVar: "--font-weight-medium",
583
+ label: "font-weight-medium",
584
+ group: "font-weight",
585
+ default: "500",
586
+ step: 1,
587
+ unit: "",
588
+ control: "select",
589
+ options: FONT_WEIGHT_OPTIONS,
590
+ },
591
+ {
592
+ id: "ui-font-weight-semibold",
593
+ cssVar: "--font-weight-semibold",
594
+ label: "font-weight-semibold",
595
+ group: "font-weight",
596
+ default: "600",
597
+ step: 1,
598
+ unit: "",
599
+ control: "select",
600
+ options: FONT_WEIGHT_OPTIONS,
601
+ },
602
+ {
603
+ id: "ui-font-weight-bold",
604
+ cssVar: "--font-weight-bold",
605
+ label: "font-weight-bold",
606
+ group: "font-weight",
607
+ default: "700",
608
+ step: 1,
609
+ unit: "",
610
+ control: "select",
611
+ options: FONT_WEIGHT_OPTIONS,
612
+ },
613
+ {
614
+ id: "ui-leading-tight",
615
+ cssVar: "--leading-tight",
616
+ label: "leading-tight",
617
+ group: "line-height",
618
+ default: "1.25",
619
+ step: 0.05,
620
+ unit: "",
621
+ },
622
+ {
623
+ id: "ui-leading-snug",
624
+ cssVar: "--leading-snug",
625
+ label: "leading-snug",
626
+ group: "line-height",
627
+ default: "1.375",
628
+ step: 0.05,
629
+ unit: "",
630
+ },
631
+ {
632
+ id: "ui-leading-normal",
633
+ cssVar: "--leading-normal",
634
+ label: "leading-normal",
635
+ group: "line-height",
636
+ default: "1.5",
637
+ step: 0.05,
638
+ unit: "",
639
+ },
640
+ {
641
+ id: "ui-leading-relaxed",
642
+ cssVar: "--leading-relaxed",
643
+ label: "leading-relaxed",
644
+ group: "line-height",
645
+ default: "1.625",
646
+ step: 0.05,
647
+ unit: "",
648
+ },
649
+ {
650
+ id: "ui-font-sans",
651
+ cssVar: "--font-sans",
652
+ label: "font-sans",
653
+ group: "font-family",
654
+ default: "sans-serif",
655
+ step: 1,
656
+ unit: "",
657
+ control: "text",
658
+ },
659
+ {
660
+ id: "ui-font-mono",
661
+ cssVar: "--font-mono",
662
+ label: "font-mono",
663
+ group: "font-family",
664
+ default: "monospace",
665
+ step: 1,
666
+ unit: "",
667
+ control: "text",
668
+ },
669
+ ];
670
+
671
+ /**
672
+ * Size tokens from `src/styles/ui-tokens.css`.
673
+ *
674
+ * Coverage: 8 tokens total.
675
+ * `--radius-full` carries a pill toggle (sentinel 9999px).
676
+ */
677
+ export const UI_SIZE_TOKENS: readonly TokenDef[] = [
678
+ {
679
+ id: "ui-radius-DEFAULT",
680
+ cssVar: "--radius-DEFAULT",
681
+ label: "radius-DEFAULT",
682
+ group: "radius",
683
+ default: "0.25rem",
684
+ step: 0.05,
685
+ unit: "rem",
686
+ },
687
+ {
688
+ id: "ui-radius-sm",
689
+ cssVar: "--radius-sm",
690
+ label: "radius-sm",
691
+ group: "radius",
692
+ default: "0.25rem",
693
+ step: 0.05,
694
+ unit: "rem",
695
+ },
696
+ {
697
+ id: "ui-radius-md",
698
+ cssVar: "--radius-md",
699
+ label: "radius-md",
700
+ group: "radius",
701
+ default: "0.5rem",
702
+ step: 0.05,
703
+ unit: "rem",
704
+ },
705
+ {
706
+ id: "ui-radius-lg",
707
+ cssVar: "--radius-lg",
708
+ label: "radius-lg",
709
+ group: "radius",
710
+ default: "1rem",
711
+ step: 0.05,
712
+ unit: "rem",
713
+ },
714
+ {
715
+ id: "ui-radius-full",
716
+ cssVar: "--radius-full",
717
+ label: "radius-full",
718
+ group: "radius",
719
+ default: "9999px",
720
+ step: 1,
721
+ unit: "px",
722
+ pill: { value: "9999px", customDefault: "16px" },
723
+ },
724
+ {
725
+ id: "ui-shadow-card",
726
+ cssVar: "--shadow-card",
727
+ label: "shadow-card",
728
+ group: "shadow",
729
+ default: "0 1px 2px #0000001a",
730
+ step: 1,
731
+ unit: "",
732
+ control: "text",
733
+ },
734
+ {
735
+ id: "ui-shadow-raised",
736
+ cssVar: "--shadow-raised",
737
+ label: "shadow-raised",
738
+ group: "shadow",
739
+ default: "0 2px 4px #0000001a",
740
+ step: 1,
741
+ unit: "",
742
+ control: "text",
743
+ },
744
+ {
745
+ id: "ui-shadow-overlay",
746
+ cssVar: "--shadow-overlay",
747
+ label: "shadow-overlay",
748
+ group: "shadow",
749
+ default: "0 4px 8px #0000001a",
750
+ step: 1,
751
+ unit: "",
752
+ control: "text",
753
+ },
754
+ ];
@@ -22,6 +22,16 @@ export default defineConfig(
22
22
  // minimal (see package.json).
23
23
  mermaid: false,
24
24
  strictContentBridge: true,
25
+ // Bundling follows `bundleZdtp ?? designTokenPanel`; this starter never
26
+ // sets `designTokenPanel`, so without this the zdtp loader resolves to
27
+ // zudo-doc's throwing stub and the engine's PREVIEW token panel (opened
28
+ // from the header trigger `withZudoSg` injects) rejects at runtime
29
+ // (zudolab/zudo-doc#4261).
30
+ bundleZdtp: true,
31
+ // Mounts the preview token panel bootstrap on every package-owned
32
+ // route (/components/*, /tokens), not just this starter's host-owned
33
+ // `/`. See pages/lib/_chrome-bindings.tsx.
34
+ chromeBindingsModule: "./pages/lib/_chrome-bindings.tsx",
25
35
  }),
26
36
  zudoSgConfig,
27
37
  ),
@@ -20,4 +20,11 @@ export default {
20
20
  cssFiles: ["./src/styles/ui-tokens.css", "./src/styles/ui-tokens.css"],
21
21
  manifestOut: "./src/styleguide/token-manifest.ts",
22
22
  },
23
+ // Tabs-only shape (routingFile/writeRoot omitted): the header trigger's
24
+ // preview token panel gets the manifest-derived tabs, but no dev-only Apply
25
+ // write sandbox — a fresh scaffold has no sensible default path for one.
26
+ // Wiring the Apply sandbox is an opt-in next step (#818).
27
+ zdtpApplyProxy: {
28
+ tabsModule: "./src/config/preview-token-panel-tabs.ts",
29
+ },
23
30
  };