create-zudo-sg 0.1.2 → 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,50 @@ 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
+
30
+ ## [0.1.3] - 2026-09-21
31
+
32
+ Stops fresh starters from requesting favicons they never shipped, and moves
33
+ them onto the engine's new chrome token namespace.
34
+
35
+ ### Fixed
36
+
37
+ - Set the starter's favicon to the self-contained inline icon instead of
38
+ advertising four `/favicon*` files the template does not create, which made a
39
+ clean build log four missing-asset errors on first load.
40
+ - Verify after every packed build that each local head asset a generated page
41
+ links to actually exists, so a reintroduced missing icon or stylesheet fails
42
+ the release gate rather than the adopter's browser console.
43
+
44
+ ### Changed
45
+
46
+ - Update the starter engine range to `@takazudo/zudo-sg ^0.3.0`, which reads its
47
+ chrome colors from the `--sg-*` namespace. The starter's own stylesheet now
48
+ sets those roles explicitly, so the catalog chrome renders correctly whatever
49
+ order a project imports its component theme in.
50
+
7
51
  ## [0.1.2] - 2026-09-20
8
52
 
9
53
  Gives fresh starter projects a styled homepage and usable default navigation.
package/README.md CHANGED
@@ -49,11 +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"`.
54
+ - `zfb.config.ts` uses an inline `favicon: "auto"`; replace it with a path or `FaviconConfig` when adding real icons under `public/`.
53
55
  - `zudo-sg.config.mjs` with a local `ui/` components root, registry output,
54
- preview stylesheet, category order, and token-manifest inputs.
55
- - `pages/index.tsx` and an optional `pages/lib/_zudo-sg-islands.ts` import
56
- 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.
57
64
  - Three small Preact examples under `ui/`: Button, Card, and Counter, with
58
65
  co-located stories and a Button MDX document.
59
66
  - `src/styleguide/sg-registry.ts`, generated from the three example stories.
@@ -72,6 +79,37 @@ renames the package-safe `_gitignore` to `.gitignore`. The token manifest is
72
79
  generated after installation; it is intentionally not checked into the
73
80
  template seed.
74
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
+
75
113
  ## Styles
76
114
 
77
115
  zfb discovers `src/styles/global.css` automatically; no page import or config
@@ -100,6 +138,32 @@ host defines consumer-only color tokens before zudo-doc's theme, replace
100
138
  omits the color-token reset; tokens defined by both stylesheets still follow
101
139
  source order.
102
140
 
141
+ ### Three token worlds and engine overrides
142
+
143
+ The generated host has three color-token worlds: zudo-doc's `--zd-*` roles for the
144
+ documentation shell, zudo-sg's raw `--sg-*` roles for catalog chrome, and the host
145
+ component library's own `@theme` color tokens (usually `--color-*`) for previewed
146
+ components. Keep the engine and component-library roles separate.
147
+
148
+ The engine defaults are plain `--sg-*` properties under `:where(:root)`, so zudo-doc's
149
+ `theme.css` `--color-*` reset cannot erase them. Retheme engine chrome with an ordinary,
150
+ unlayered override in any import position:
151
+
152
+ ```css
153
+ :root {
154
+ --sg-border: oklch(0.72 0.02 65);
155
+ }
156
+ ```
157
+
158
+ If the host previously used bare `--color-border` to style engine chrome, migrate that
159
+ declaration to `--sg-border` (and migrate each other chrome role to its corresponding
160
+ `--sg-*` role). `--color-border` can still style the host's own components. The
161
+ order-proof engine hook does not change the import-order contract for those host-owned
162
+ `@theme` colors: import `theme.css` before them, or deliberately use
163
+ `theme-no-reset.css` when their earlier declaration must be preserved. This is the
164
+ zudo-doc namespace contract for host-owned color tokens; the engine's raw `--sg-*`
165
+ namespace is separate.
166
+
103
167
  The preview document uses `src/styles/preview-entry.css` independently. Add
104
168
  your component package's styles and source scan there too when previews need
105
169
  them; its `@source` paths are relative to that stylesheet. Keep dashboard and
@@ -119,7 +183,10 @@ checks each optional package independently.
119
183
  The starter targets Preact + zfb hosts. Keep these dependencies in a host
120
184
  that adopts the engine:
121
185
 
122
- - `@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.
123
190
  - `diff` and `katex` are required by the published zudo-doc route dist that
124
191
  the host loads, even when the corresponding optional features are disabled.
125
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zudo-sg",
3
- "version": "0.1.2",
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.2.2",
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.2.2"
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
+ ];
@@ -21,6 +21,16 @@
21
21
  @import "@takazudo/zudo-sg/styles.css";
22
22
  @import "@takazudo/zudo-sg/safelist.css";
23
23
 
24
+ /* The engine chrome owns raw --sg-* tokens and no longer needs the host's
25
+ * bare --color-* values. To theme chrome intentionally, add an unlayered
26
+ * override after these imports, for example:
27
+ *
28
+ * :root {
29
+ * --sg-border: #94a3b8;
30
+ * --sg-accent: #7c3aed;
31
+ * }
32
+ */
33
+
24
34
  /* zfb resolves these global-entry globs from the project root.
25
35
  * Add your component package's @source here; package chrome uses the safelists. */
26
36
  @source "pages/**/*.{tsx,ts,jsx,js}";
@@ -1,6 +1,15 @@
1
1
  /* Self-contained input for the packed gen-token-manifest CLI. The current
2
- * generator requires this complete vocabulary; none comes from demo-ui or
3
- * the workspace. The Button/Card still use only the original five tokens. */
2
+ * generator requires this complete host vocabulary; none comes from demo-ui
3
+ * or the workspace. The engine chrome owns raw --sg-* tokens and no longer
4
+ * depends on these bare --color-* values. Keep the vocabulary for host
5
+ * components and the manifest; override chrome separately in global.css, e.g.:
6
+ *
7
+ * :root {
8
+ * --sg-border: #94a3b8;
9
+ * --sg-accent: #7c3aed;
10
+ * }
11
+ *
12
+ * The Button/Card still use only the original five tokens. */
4
13
  :root {
5
14
  --palette-neutral-0: #ffffff;
6
15
  --palette-neutral-1: #e2e8f0;
@@ -15,11 +15,23 @@ export default defineConfig(
15
15
  siteName: "Styleguide Starter",
16
16
  base: "/",
17
17
  port: 4397,
18
+ // The starter ships no public/ icons; replace "auto" with a path or FaviconConfig once real icons exist.
19
+ favicon: "auto",
18
20
  // No mermaid diagrams anywhere in this starter's one seed doc; turned
19
21
  // off rather than leaving the default on to keep the dependency set
20
22
  // minimal (see package.json).
21
23
  mermaid: false,
22
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",
23
35
  }),
24
36
  zudoSgConfig,
25
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
  };