@takazudo/zdtp 0.6.0 → 0.6.1
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 +25 -0
- package/PORTABLE-CONTRACT.md +102 -0
- package/README.md +82 -0
- package/dist/dashboard/index.d.ts +4 -0
- package/dist/dashboard/index.js +407 -0
- package/dist/dashboard/model.d.ts +7 -0
- package/dist/dashboard/styles.css +294 -0
- package/dist/dashboard/token-dashboard.d.ts +23 -0
- package/dist/dashboard/types.d.ts +62 -0
- package/package.json +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,31 @@ All notable changes to `@takazudo/zdtp` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
|
|
6
6
|
|
|
7
|
+
## [0.6.1] - 2026-09-08
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- feat: model declared token defaults for static dashboards (422643b)
|
|
12
|
+
- feat: expose a static Preact token dashboard (5b6c0f8)
|
|
13
|
+
- feat(playground): add static token dashboard demo and docs (2edd98b)
|
|
14
|
+
- feat(dashboard): render rulers, palette strips and wide type specimens (7cdf01f)
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- fix: preserve semantic literals alongside legacy palette references (b813c8a)
|
|
19
|
+
|
|
20
|
+
### Other Changes
|
|
21
|
+
|
|
22
|
+
- test: account for standalone dashboard stylesheet delivery (a00541a)
|
|
23
|
+
- test: verify packed dashboard SSR and public stylesheet consumption (6cd8157)
|
|
24
|
+
- test: cover static dashboard list-view geometry in browser (e07d69e)
|
|
25
|
+
- chore: normalize dashboard browser test file mode (0ec8217)
|
|
26
|
+
- docs: demonstrate dashboard rulers ramps and reading specimens (859d3a4)
|
|
27
|
+
- test: verify packed dashboard list views and preview text (7ebf4cf)
|
|
28
|
+
- test: prove isolated dashboard package consumption (fdb3906)
|
|
29
|
+
- fix(test): compare Preact package identity across module conditions (bb280ee)
|
|
30
|
+
- Fix static browser harness loading and polling (3fe6203)
|
|
31
|
+
|
|
7
32
|
## [0.6.0] - 2026-09-08
|
|
8
33
|
|
|
9
34
|
### Breaking Changes
|
package/PORTABLE-CONTRACT.md
CHANGED
|
@@ -1741,6 +1741,107 @@ on across a release bump.
|
|
|
1741
1741
|
|
|
1742
1742
|
---
|
|
1743
1743
|
|
|
1744
|
+
## 12. Static token dashboard
|
|
1745
|
+
|
|
1746
|
+
This entry is available from v0.6.1. It does
|
|
1747
|
+
not change the `configurePanel` or persistence contracts above.
|
|
1748
|
+
|
|
1749
|
+
`@takazudo/zdtp/dashboard` exports the Preact `TokenDashboard` component and
|
|
1750
|
+
`TokenDashboardProps`, `DashboardMode`, `DashboardPreviewKind`, `TabConfig`,
|
|
1751
|
+
`TierConfig`, and `TierItem` types. The internal value model is not a public API.
|
|
1752
|
+
|
|
1753
|
+
```ts
|
|
1754
|
+
export type DashboardMode = 'light' | 'dark';
|
|
1755
|
+
export type DashboardPreviewKind =
|
|
1756
|
+
| 'bar' | 'size' | 'line-height' | 'family' | 'weight' | 'radius' | 'duration'
|
|
1757
|
+
| 'color' | 'shadow' | 'text';
|
|
1758
|
+
|
|
1759
|
+
export interface TokenDashboardProps {
|
|
1760
|
+
tabs: readonly TabConfig[];
|
|
1761
|
+
mode?: DashboardMode; // default: light
|
|
1762
|
+
title?: string; // default: Token dashboard
|
|
1763
|
+
previewText?: string; // static typography passage; default English/Japanese prose
|
|
1764
|
+
id?: string; // optional root ID; caller owns uniqueness
|
|
1765
|
+
previewOverrides?: Readonly<Record<string, DashboardPreviewKind>>;
|
|
1766
|
+
}
|
|
1767
|
+
```
|
|
1768
|
+
|
|
1769
|
+
### Rendering and package boundary
|
|
1770
|
+
|
|
1771
|
+
- Ordinary Preact rendering and server rendering are supported. The component
|
|
1772
|
+
has no browser-global, panel-runtime, persistence, host-adapter, bin-server,
|
|
1773
|
+
or browser Tailwind compiler dependency. No hydration is required.
|
|
1774
|
+
- `@takazudo/zdtp/dashboard/styles.css` is a separate public asset. Dashboard
|
|
1775
|
+
JavaScript has no CSS side-effect import; the host must include this asset
|
|
1776
|
+
through its CSS build or a static stylesheet link. It is not appended to the
|
|
1777
|
+
existing panel stylesheet, which is unnecessary for a dashboard-only page.
|
|
1778
|
+
- Each instance declares known token variables inside its inventory and sets
|
|
1779
|
+
`color-scheme` to its explicit `mode`. It never writes to `:root` or host
|
|
1780
|
+
state. Dashboard chrome styles are independent of inventory variables.
|
|
1781
|
+
- Tabs, tiers, and rows render in input order. All token rows remain visible;
|
|
1782
|
+
no collapsed sections or automatic IDs are introduced. Empty input is valid.
|
|
1783
|
+
Notes-only tabs are omitted, and arbitrary notes HTML is never injected.
|
|
1784
|
+
|
|
1785
|
+
### Layouts for spacing, color, and typography
|
|
1786
|
+
|
|
1787
|
+
Spacing (`bar`) uses wide rows with aligned ruler origins. Nonnegative `px` or
|
|
1788
|
+
`rem` lengths, unitless zero, and resolved direct local aliases get an actual-size
|
|
1789
|
+
sample. Ticks mark 8 CSS px, with major ticks every 64 CSS px. The browser sizes
|
|
1790
|
+
`rem` against the host root font size; the dashboard does not assume a conversion.
|
|
1791
|
+
Large lengths scroll inside the focusable ruler instead of shrinking or clamping.
|
|
1792
|
+
Zero has zero width. Negative lengths, `em`, percentages, `calc()` and other
|
|
1793
|
+
unsupported expressions retain their declaration with a ruler-unavailable
|
|
1794
|
+
explanation. Missing references also retain their diagnostics.
|
|
1795
|
+
|
|
1796
|
+
A nonsemantic, non-reference tier whose source items are all colors becomes a
|
|
1797
|
+
discrete palette strip. Stops stay in declared order, with every label and value
|
|
1798
|
+
visible; narrow containers scroll horizontally. Grouping uses tier structure,
|
|
1799
|
+
not name matching or generated intermediate colors. Semantic and reference tiers
|
|
1800
|
+
keep independent color samples. Per-variable preview overrides remain authoritative
|
|
1801
|
+
and retain each token's slot and order, including mixed overrides within a strip.
|
|
1802
|
+
|
|
1803
|
+
`size`, `family`, `weight`, and `line-height` use wide, multiline specimens with
|
|
1804
|
+
auto height outside palette strips. Overrides inside a palette retain their stop slot. The baseline is 18px text with 1.6 line height; each sample changes
|
|
1805
|
+
only its intended property. A resolved `previewBase` can supply the font size of
|
|
1806
|
+
a line-height sample. Font families use locally available fonts and their CSS
|
|
1807
|
+
fallbacks; the dashboard does not download fonts.
|
|
1808
|
+
|
|
1809
|
+
`previewText` supplies one static passage for all typography samples in an
|
|
1810
|
+
instance. Its default is longer English/Japanese prose. Newlines and white space
|
|
1811
|
+
are preserved, text is escaped, and an explicit empty string stays empty.
|
|
1812
|
+
Long text wraps; unusually large glyphs can scroll locally without scaling.
|
|
1813
|
+
There is no built-in editable text workbench or connection to panel state.
|
|
1814
|
+
|
|
1815
|
+
### Values and previews
|
|
1816
|
+
|
|
1817
|
+
- Inputs describe **declared defaults**: item defaults, selected-mode
|
|
1818
|
+
`colorExtras.semanticDefaults`, and explicit `colorExtras.baseDefaults` for
|
|
1819
|
+
base-role rows. Named presets, panel initialization, and saved/current panel
|
|
1820
|
+
state are not applied. Pill rows preserve `item.default`, not `customDefault`.
|
|
1821
|
+
- Reference tiers, cross-tab ramps, aliases, and legacy palette indices resolve
|
|
1822
|
+
against actual declared IDs and `cssVar` names. Rows retain declared values
|
|
1823
|
+
and display CSS/reference text. Missing/ambiguous mappings, duplicate
|
|
1824
|
+
variables, cycles, and unsafe declarations remain visible with diagnostics
|
|
1825
|
+
and no misleading sample.
|
|
1826
|
+
- CSS expressions remain expressions. Resolution is not universal CSS grammar
|
|
1827
|
+
validation or browser computed-style inspection. External dependencies may
|
|
1828
|
+
be context-dependent; unresolved dependencies suppress samples and expose
|
|
1829
|
+
diagnostics. Browser layout, units, and available fonts affect previews.
|
|
1830
|
+
- Preview choice is `previewOverrides[cssVar]`, then `TierConfig.preview`, then
|
|
1831
|
+
color for color-editor rows or text for other rows. `duration` and `text`
|
|
1832
|
+
display values without animated/visual samples. A resolved `previewBase`
|
|
1833
|
+
token can supply font size for a line-height sample. Subsets must include
|
|
1834
|
+
referenced tiers to retain resolved samples.
|
|
1835
|
+
- Preact escapes text. URL/mask assets and unsafe resource-bearing expressions
|
|
1836
|
+
are not automatically loaded through samples.
|
|
1837
|
+
|
|
1838
|
+
Keep shared token definitions in a build-safe data module using type-only
|
|
1839
|
+
imports. Pass the same tabs to `PanelConfig` and `TokenDashboard`; no automatic
|
|
1840
|
+
state synchronization follows from sharing them. A React adapter and an
|
|
1841
|
+
HTML-export function or CLI are outside this checkpoint.
|
|
1842
|
+
|
|
1843
|
+
---
|
|
1844
|
+
|
|
1744
1845
|
## Appendix A — section index
|
|
1745
1846
|
|
|
1746
1847
|
Cross-reference table — what each section pins down.
|
|
@@ -1772,6 +1873,7 @@ Cross-reference table — what each section pins down.
|
|
|
1772
1873
|
| `data-zdtp-action` stable header-action hook (ids stable, labels not; two matches while the compact popover is open) | §7.6 |
|
|
1773
1874
|
| v4 envelope precedence, v1/v2/v3 storage migration, and typography-id rename map | §2, §8.3, §8.4 |
|
|
1774
1875
|
| JSON export/import schemas v1/v2/v3 (serde) | §9 |
|
|
1876
|
+
| Static Preact dashboard props, defaults, previews, isolation, and CSS export | §12 |
|
|
1775
1877
|
| Out-of-scope / deferred concerns | §10 |
|
|
1776
1878
|
| Versioning promise, breaking-change signalling, and consumer guidance for mirroring the eager-load gate registries | §11 |
|
|
1777
1879
|
| Feature walkthrough and shortcut table | [Panel UX tour](/docs/recipes/panel-ux-tour) |
|
package/README.md
CHANGED
|
@@ -32,12 +32,94 @@ The authoritative API spec is [`PORTABLE-CONTRACT.md`](./PORTABLE-CONTRACT.md).
|
|
|
32
32
|
partial reconciliation, and JSON export/import when disk apply is not
|
|
33
33
|
configured.
|
|
34
34
|
- Owner autoload for developer-only loading across page visits.
|
|
35
|
+
- A separate Preact `TokenDashboard` for a static, read-only token inventory
|
|
36
|
+
(available from v0.6.1).
|
|
35
37
|
|
|
36
38
|
See the [Panel UX tour](/docs/recipes/panel-ux-tour) for a feature-by-feature
|
|
37
39
|
walkthrough and the shortcut table.
|
|
38
40
|
|
|
39
41
|
---
|
|
40
42
|
|
|
43
|
+
## Static token dashboard
|
|
44
|
+
|
|
45
|
+
Import `TokenDashboard` from `@takazudo/zdtp/dashboard` to render the same tab
|
|
46
|
+
manifest on a plain page. This entry is available from v0.6.1. The [live workspace demo](https://zdtp-playground.zudolab.dev/dashboard/)
|
|
47
|
+
shows both modes and a compact embedded instance.
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { TokenDashboard } from '@takazudo/zdtp/dashboard';
|
|
51
|
+
import { tokenTabs } from './token-tabs'; // Shared data module; type-only imports.
|
|
52
|
+
|
|
53
|
+
export default function TokenPage() {
|
|
54
|
+
return <TokenDashboard tabs={tokenTabs} mode="light" title="Site tokens" />;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For example, keep the definitions in a module that is safe to import at build time:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// token-tabs.ts — data only; safe to import during a site build.
|
|
62
|
+
import type { TabConfig } from '@takazudo/zdtp/dashboard';
|
|
63
|
+
|
|
64
|
+
export const tokenTabs: readonly TabConfig[] = [{
|
|
65
|
+
id: 'space',
|
|
66
|
+
label: 'Spacing',
|
|
67
|
+
tiers: [{
|
|
68
|
+
id: 'scale',
|
|
69
|
+
label: 'Scale',
|
|
70
|
+
preview: 'bar',
|
|
71
|
+
items: [{
|
|
72
|
+
id: 'space-md',
|
|
73
|
+
cssVar: '--site-space-md',
|
|
74
|
+
label: 'Medium',
|
|
75
|
+
default: '1rem',
|
|
76
|
+
type: { kind: 'length', step: 0.25, unit: 'rem' },
|
|
77
|
+
}],
|
|
78
|
+
}],
|
|
79
|
+
}];
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Also pass `tokenTabs` as `tabs` in your panel's config. Keep panel initialization
|
|
83
|
+
and browser/storage code out of this data module.
|
|
84
|
+
The component requires Preact and can render during the host's static build
|
|
85
|
+
without hydration; no panel initialization is needed.
|
|
86
|
+
|
|
87
|
+
Include **`@takazudo/zdtp/dashboard/styles.css`** separately. The dashboard JS
|
|
88
|
+
does not import CSS and does not need the panel stylesheet. A CSS-emitting
|
|
89
|
+
bundler can import this asset from its layout; strict static hosts should use
|
|
90
|
+
a supported global CSS `@import` or copy the resolved public export to their
|
|
91
|
+
public assets directory and add a stylesheet link. The
|
|
92
|
+
[static dashboard recipe](https://zdtp.zudolab.dev/docs/recipes/static-token-dashboard/)
|
|
93
|
+
provides complete data and CSS-copy examples.
|
|
94
|
+
|
|
95
|
+
Optional props are `mode` (`light` by default), `title`, caller-owned unique
|
|
96
|
+
`id`, `previewText`, and `previewOverrides` keyed by CSS variable (for example,
|
|
97
|
+
a text-editor shadow can use `'shadow'`). Existing `TierConfig.preview` metadata is reused.
|
|
98
|
+
|
|
99
|
+
Spacing uses actual-size rulers with local scrolling. Nonnegative px/rem, zero,
|
|
100
|
+
and resolved direct aliases are supported; other lengths keep a readable fallback.
|
|
101
|
+
Color-only nonsemantic, non-reference tiers form discrete strips in source order.
|
|
102
|
+
Typography uses wide multiline specimens; `previewText` supplies your static
|
|
103
|
+
passage (default: English/Japanese prose; an explicit empty string stays empty).
|
|
104
|
+
The baseline is 18px/1.6, with only the intended property changed and resolved
|
|
105
|
+
`previewBase` honored for line-height. Fonts use local availability and CSS
|
|
106
|
+
fallbacks. There is no editable text workbench.
|
|
107
|
+
|
|
108
|
+
The inventory uses **declared defaults**: item defaults, per-mode
|
|
109
|
+
`semanticDefaults`, and explicit base-role `baseDefaults`. It does not apply
|
|
110
|
+
named color presets, panel initialization, or persisted edits. CSS expressions
|
|
111
|
+
remain expressions; samples depend on browser layout and available fonts.
|
|
112
|
+
Missing/context-dependent references and invalid declarations remain readable
|
|
113
|
+
with diagnostics rather than misleading samples. Preview variables and
|
|
114
|
+
`color-scheme` are local to each instance; the component never writes to
|
|
115
|
+
`:root`. Arbitrary notes HTML and URL/mask assets are not rendered or loaded.
|
|
116
|
+
|
|
117
|
+
This checkpoint provides a Preact component. An HTML-export API/CLI, React
|
|
118
|
+
adapter, and automatic panel-state synchronization are outside its scope.
|
|
119
|
+
See [`PORTABLE-CONTRACT.md` §12](./PORTABLE-CONTRACT.md#12-static-token-dashboard).
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
41
123
|
## 0. Architecture at a glance
|
|
42
124
|
|
|
43
125
|
The design-token panel is a browser-based UI that writes token overrides to
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { TokenDashboard } from "./token-dashboard.js";
|
|
2
|
+
export type { TokenDashboardProps, DashboardPreviewKind } from "./token-dashboard.js";
|
|
3
|
+
export type { DashboardMode } from "./types.js";
|
|
4
|
+
export type { TabConfig, TierConfig, TierItem } from "../tokens/tier-model.js";
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { jsxs as m, jsx as y } from "preact/jsx-runtime";
|
|
2
|
+
const q = new Set("aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen transparent currentcolor inherit initial unset revert revert-layer".split(" "));
|
|
3
|
+
function C(l) {
|
|
4
|
+
return l.startsWith("#") || l.includes("(") || q.has(l.toLowerCase());
|
|
5
|
+
}
|
|
6
|
+
const O = [
|
|
7
|
+
"background",
|
|
8
|
+
"foreground",
|
|
9
|
+
"cursor",
|
|
10
|
+
"selectionBg",
|
|
11
|
+
"selectionFg"
|
|
12
|
+
], j = /^--[\w\u0080-\uffff-]+$/u;
|
|
13
|
+
function A(l) {
|
|
14
|
+
let u = "", o = 0;
|
|
15
|
+
for (let a = 0; a < l.length; a++) {
|
|
16
|
+
const g = l[a];
|
|
17
|
+
if (g === '"' || g === "'") {
|
|
18
|
+
const f = g;
|
|
19
|
+
let r = !1;
|
|
20
|
+
for (a++; a < l.length; a++)
|
|
21
|
+
if (l[a] === "\\") a++;
|
|
22
|
+
else if (l[a] === f) {
|
|
23
|
+
r = !0;
|
|
24
|
+
break;
|
|
25
|
+
} else if (/[\n\r\f]/.test(l[a])) return { syntax: u, safe: !1 };
|
|
26
|
+
if (!r) return { syntax: u, safe: !1 };
|
|
27
|
+
u += '""';
|
|
28
|
+
} else if (g === "/" && l[a + 1] === "*") {
|
|
29
|
+
const f = l.indexOf("*/", a + 2);
|
|
30
|
+
if (f === -1) return { syntax: u, safe: !1 };
|
|
31
|
+
u += " ", a = f + 1;
|
|
32
|
+
} else {
|
|
33
|
+
const f = g.charCodeAt(0);
|
|
34
|
+
if (/[;{}\\@]/.test(g) || f <= 8 || f === 11 || f >= 14 && f <= 31 || f === 127)
|
|
35
|
+
return { syntax: u, safe: !1 };
|
|
36
|
+
if (g === "(" && o++, g === ")" && --o < 0) return { syntax: u, safe: !1 };
|
|
37
|
+
u += g;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
syntax: u,
|
|
42
|
+
safe: o === 0 && !/\b(?:url|(?:-webkit-)?image-set|image|cross-fade|element|paint|src)\s*\(/i.test(u) && !/!\s*important\b/i.test(u)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function D(l, u) {
|
|
46
|
+
const o = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const a of l) {
|
|
48
|
+
const g = u(a), f = o.get(g) ?? [];
|
|
49
|
+
f.push(a), o.set(g, f);
|
|
50
|
+
}
|
|
51
|
+
return o;
|
|
52
|
+
}
|
|
53
|
+
function B(l, u = "light") {
|
|
54
|
+
const o = {
|
|
55
|
+
mode: u,
|
|
56
|
+
tabs: [],
|
|
57
|
+
rows: [],
|
|
58
|
+
declarations: /* @__PURE__ */ Object.create(null),
|
|
59
|
+
diagnostics: []
|
|
60
|
+
}, a = [], g = l.filter((e) => e.id !== "notes"), f = D(g, (e) => e.id);
|
|
61
|
+
function r(e, t, s, n = "error") {
|
|
62
|
+
const d = { code: t, severity: n, message: s, ...e ? { rowKey: e.row.key } : {} };
|
|
63
|
+
e?.row.diagnostics.some((i) => i.code === t && i.message === s) || (e?.row.diagnostics.push(d), o.diagnostics.push(d));
|
|
64
|
+
}
|
|
65
|
+
function _(e, t, s, n, d, i) {
|
|
66
|
+
t.rows.push(s), o.rows.push(s), a.push({ row: s, tab: e, tier: d, item: n, baseRole: i, dependencies: [], contextDependent: !1 });
|
|
67
|
+
}
|
|
68
|
+
g.forEach((e, t) => {
|
|
69
|
+
const s = { key: `tab-${t}`, id: e.id, label: e.label, tiers: [] };
|
|
70
|
+
o.tabs.push(s), e.tiers.forEach((i, w) => {
|
|
71
|
+
const b = {
|
|
72
|
+
key: `${s.key}-tier-${w}`,
|
|
73
|
+
id: i.id,
|
|
74
|
+
label: i.label,
|
|
75
|
+
palette: !i.semantic && !i.referencesTier && !i.referencesRamps?.length && i.items.length > 0 && i.items.every((h) => h.type.kind === "color"),
|
|
76
|
+
preview: i.preview,
|
|
77
|
+
previewBase: i.previewBase,
|
|
78
|
+
rows: []
|
|
79
|
+
};
|
|
80
|
+
s.tiers.push(b), i.items.forEach((h, V) => _(e, b, {
|
|
81
|
+
key: `${b.key}-item-${V}`,
|
|
82
|
+
tabId: e.id,
|
|
83
|
+
tierId: i.id,
|
|
84
|
+
itemId: h.id,
|
|
85
|
+
label: h.label,
|
|
86
|
+
cssVar: h.cssVar,
|
|
87
|
+
kind: h.type.kind,
|
|
88
|
+
source: "item",
|
|
89
|
+
defaultValue: h.default,
|
|
90
|
+
declaredValue: h.default,
|
|
91
|
+
cssValue: null,
|
|
92
|
+
resolvedValue: null,
|
|
93
|
+
references: [],
|
|
94
|
+
diagnostics: []
|
|
95
|
+
}, h, i));
|
|
96
|
+
});
|
|
97
|
+
const n = e.colorExtras;
|
|
98
|
+
if (!n) return;
|
|
99
|
+
const d = {
|
|
100
|
+
key: `${s.key}-base-roles`,
|
|
101
|
+
id: "base-roles",
|
|
102
|
+
label: "Base roles",
|
|
103
|
+
rows: []
|
|
104
|
+
};
|
|
105
|
+
for (const i of O) {
|
|
106
|
+
const w = n.baseRoles[i];
|
|
107
|
+
if (w === void 0) continue;
|
|
108
|
+
const b = n.baseDefaults[i];
|
|
109
|
+
_(e, d, {
|
|
110
|
+
key: `${d.key}-${i}`,
|
|
111
|
+
tabId: e.id,
|
|
112
|
+
tierId: d.id,
|
|
113
|
+
itemId: i,
|
|
114
|
+
label: i,
|
|
115
|
+
cssVar: w,
|
|
116
|
+
kind: "color",
|
|
117
|
+
source: "base-role",
|
|
118
|
+
defaultValue: b === void 0 ? "" : String(b),
|
|
119
|
+
declaredValue: b === void 0 ? "(no declared default)" : `palette[${b}]`,
|
|
120
|
+
cssValue: null,
|
|
121
|
+
resolvedValue: null,
|
|
122
|
+
references: [],
|
|
123
|
+
diagnostics: []
|
|
124
|
+
}, void 0, void 0, i);
|
|
125
|
+
}
|
|
126
|
+
d.rows.length > 0 && s.tiers.push(d);
|
|
127
|
+
});
|
|
128
|
+
const v = D(a, (e) => e.row.cssVar);
|
|
129
|
+
for (const e of a)
|
|
130
|
+
j.test(e.row.cssVar) || r(e, "invalid-variable", `Unsupported CSS custom property name: ${e.row.cssVar}.`), v.get(e.row.cssVar).length > 1 && r(e, "duplicate-variable", `Multiple declarations use ${e.row.cssVar}; none is selected.`), f.get(e.tab.id).length > 1 && r(e, "duplicate-tab", `Multiple tabs use the id ${e.tab.id}.`), e.tier && (e.tab.tiers.filter((t) => t.id === e.tier.id).length > 1 && r(e, "duplicate-tier", `Multiple tiers use the id ${e.tier.id} in tab ${e.tab.id}.`), e.tier.items.filter((t) => t.id === e.item.id).length > 1 && r(e, "duplicate-item", `Multiple items use the id ${e.item.id} in tier ${e.tier.id}.`));
|
|
131
|
+
function c(e, t) {
|
|
132
|
+
e.directReference = t, e.row.cssValue = `var(${t.row.cssVar})`;
|
|
133
|
+
}
|
|
134
|
+
function p(e, t, s, n) {
|
|
135
|
+
const d = f.get(t) ?? [];
|
|
136
|
+
if (d.length !== 1) {
|
|
137
|
+
r(e, d.length ? "ambiguous-reference" : "missing-reference", `Reference tab ${t} ${d.length ? "is ambiguous" : "does not exist"}.`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const i = d[0].tiers.filter((b) => b.id === s);
|
|
141
|
+
if (i.length !== 1) {
|
|
142
|
+
r(e, i.length ? "ambiguous-reference" : "missing-reference", `Reference tier ${t}/${s} ${i.length ? "is ambiguous" : "does not exist"}.`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const w = a.filter((b) => b.tab === d[0] && b.tier === i[0] && b.item?.id === n);
|
|
146
|
+
if (w.length !== 1) {
|
|
147
|
+
r(e, w.length ? "ambiguous-reference" : "missing-reference", `Reference item ${t}/${s}/${n} ${w.length ? "is ambiguous" : "does not exist"}.`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
return w[0];
|
|
151
|
+
}
|
|
152
|
+
function $(e) {
|
|
153
|
+
return e.tab.tiers.filter((t) => !t.semantic && !t.referencesTier && t.items.length > 0 && t.items.every((s) => s.type.kind === "color"));
|
|
154
|
+
}
|
|
155
|
+
function z(e, t) {
|
|
156
|
+
const s = $(e), n = e.tier?.referencesTier ? s.filter((w) => w.id === e.tier.referencesTier) : s;
|
|
157
|
+
if (n.length !== 1) {
|
|
158
|
+
r(e, "unsupported-palette", `A palette index requires one unambiguous declared palette tier; found ${n.length}.`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!Number.isInteger(t) || t < 0 || t >= n[0].items.length) {
|
|
162
|
+
r(e, "invalid-palette-index", `Palette index ${t} is outside the declared palette.`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const d = n[0].items[t], i = p(e, e.tab.id, n[0].id, d.id);
|
|
166
|
+
i && c(e, i);
|
|
167
|
+
}
|
|
168
|
+
function k(e, t) {
|
|
169
|
+
const s = e.tab.colorExtras?.baseDefaults[t];
|
|
170
|
+
if (s === void 0) {
|
|
171
|
+
r(e, "missing-base-default", `Base role ${t} has no explicit baseDefaults mapping.`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
z(e, s);
|
|
175
|
+
}
|
|
176
|
+
function S(e, t) {
|
|
177
|
+
if (typeof t == "number")
|
|
178
|
+
e.row.declaredValue = `palette[${t}]`, z(e, t);
|
|
179
|
+
else if (t === "bg" || t === "fg")
|
|
180
|
+
e.row.declaredValue = t, k(e, t === "bg" ? "background" : "foreground");
|
|
181
|
+
else if (typeof t == "object" && t !== null && "literal" in t) {
|
|
182
|
+
const s = typeof t.literal == "string" ? t.literal : t.literal[u];
|
|
183
|
+
e.row.declaredValue = s, e.row.cssValue = s;
|
|
184
|
+
} else if (typeof t == "object" && t !== null && "ref" in t) {
|
|
185
|
+
const { tab: s = e.tab.id, tier: n, item: d } = t.ref;
|
|
186
|
+
e.row.declaredValue = `${s}/${n}/${d}`;
|
|
187
|
+
const i = p(e, s, n, d);
|
|
188
|
+
i && c(e, i);
|
|
189
|
+
} else
|
|
190
|
+
r(e, "invalid-semantic-default", "Unsupported semantic default mapping.");
|
|
191
|
+
}
|
|
192
|
+
for (const e of a) {
|
|
193
|
+
if (e.baseRole) {
|
|
194
|
+
k(e, e.baseRole);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const t = e.tier, s = e.item, n = $(e), d = t.semantic || e.tab.colorExtras && n.some((h) => h.id === t.referencesTier), w = (t.referencesTier === void 0 ? n : n.filter((h) => h.id === t.referencesTier)).flatMap((h) => h.items.filter((V) => V.id === s.default).map(() => h)), b = e.tab.colorExtras?.semanticDefaults;
|
|
198
|
+
if (d && b && Object.hasOwn(b, s.id))
|
|
199
|
+
S(e, b[s.id]);
|
|
200
|
+
else if (d && w.length > 0)
|
|
201
|
+
if (w.length > 1)
|
|
202
|
+
r(e, "ambiguous-reference", `Default ${s.default} names multiple palette items.`);
|
|
203
|
+
else {
|
|
204
|
+
const h = p(e, e.tab.id, w[0].id, s.default);
|
|
205
|
+
h && c(e, h);
|
|
206
|
+
}
|
|
207
|
+
else if (d && (s.default === "bg" || s.default === "fg"))
|
|
208
|
+
k(e, s.default === "bg" ? "background" : "foreground");
|
|
209
|
+
else if (t.referencesTier !== void 0 && !(d && t.referencesRamps?.length) && !(t.semantic && C(s.default))) {
|
|
210
|
+
const h = p(e, e.tab.id, t.referencesTier, s.default);
|
|
211
|
+
h && c(e, h);
|
|
212
|
+
} else if (d)
|
|
213
|
+
if (t.referencesRamps?.length && !C(s.default)) {
|
|
214
|
+
const h = s.default.indexOf(":"), V = h === -1 ? [t.referencesRamps[0]] : t.referencesRamps.filter((N) => N.tier === s.default.slice(0, h));
|
|
215
|
+
if (V.length !== 1)
|
|
216
|
+
r(e, V.length ? "ambiguous-reference" : "missing-reference", `Ramp source for ${s.default} ${V.length ? "is ambiguous" : "does not exist"}.`);
|
|
217
|
+
else {
|
|
218
|
+
const N = p(e, V[0].tab ?? e.tab.id, V[0].tier, h === -1 ? s.default : s.default.slice(h + 1));
|
|
219
|
+
N && c(e, N);
|
|
220
|
+
}
|
|
221
|
+
} else
|
|
222
|
+
e.row.cssValue = s.default;
|
|
223
|
+
else
|
|
224
|
+
e.row.cssValue = s.default;
|
|
225
|
+
}
|
|
226
|
+
for (const e of a) {
|
|
227
|
+
const t = e.row.cssValue;
|
|
228
|
+
if (t === null) continue;
|
|
229
|
+
const s = A(t);
|
|
230
|
+
if (!s.safe) {
|
|
231
|
+
r(e, "unsafe-css", "This declaration contains unsupported CSS syntax or a resource-loading function; preview omitted.");
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
e.directReference && e.dependencies.push(e.directReference);
|
|
235
|
+
for (const n of s.syntax.matchAll(/\bvar\(\s*(--[\w\u0080-\uffff-]+)\s*(?=[,)])/giu)) {
|
|
236
|
+
const d = v.get(n[1]) ?? [];
|
|
237
|
+
d.length > 1 ? r(e, "ambiguous-reference", `CSS reference ${n[1]} has multiple declarations.`) : d.length === 1 ? e.dependencies.push(d[0]) : (e.contextDependent = !0, r(e, "external-reference", `CSS reference ${n[1]} is not in this inventory; its value depends on the host context or fallback.`, "warning"));
|
|
238
|
+
}
|
|
239
|
+
e.dependencies = [...new Set(e.dependencies)], e.row.references = e.dependencies.map(({ row: n }) => ({ key: n.key, cssVar: n.cssVar, label: n.label })), /(?:^|[^\w-])(?:currentcolor|initial|inherit|unset|revert|revert-layer)(?=$|[^\w-])|\b(?:env|attr)\s*\(/i.test(s.syntax) && (e.contextDependent = !0, r(e, "context-dependent", "This CSS value depends on the surrounding document; no computed preview is claimed.", "warning"));
|
|
240
|
+
}
|
|
241
|
+
const R = /* @__PURE__ */ new Set(), x = [];
|
|
242
|
+
function T(e) {
|
|
243
|
+
if (R.has(e)) return;
|
|
244
|
+
const t = x.indexOf(e);
|
|
245
|
+
if (t !== -1) {
|
|
246
|
+
for (const s of x.slice(t)) r(s, "cyclic-reference", "Declared references form a cycle.");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
x.push(e);
|
|
250
|
+
for (const s of e.dependencies)
|
|
251
|
+
T(s), s.row.diagnostics.some((n) => n.severity === "error") ? r(e, "invalid-reference", `Referenced declaration ${s.row.cssVar} is invalid.`) : s.contextDependent && (e.contextDependent = !0, r(e, "context-dependent", `Referenced declaration ${s.row.cssVar} depends on the host context.`, "warning"));
|
|
252
|
+
x.pop(), R.add(e), e.row.diagnostics.some((s) => s.severity === "error") ? e.row.cssValue = null : e.contextDependent || (e.row.resolvedValue = e.directReference?.row.resolvedValue ?? e.row.cssValue);
|
|
253
|
+
}
|
|
254
|
+
for (const e of a) T(e);
|
|
255
|
+
return o.declarations = Object.fromEntries(a.filter((e) => e.row.cssValue !== null).map((e) => [e.row.cssVar, e.row.cssValue])), o;
|
|
256
|
+
}
|
|
257
|
+
function L(l, u, o) {
|
|
258
|
+
return o?.[l.cssVar] ?? u.preview ?? (l.kind === "color" ? "color" : "text");
|
|
259
|
+
}
|
|
260
|
+
const M = `Good typography gives every idea room to breathe. Compare the rhythm of several lines, the shapes of letters, and the distance between words. A token becomes useful when you can see it in context.
|
|
261
|
+
|
|
262
|
+
読みやすさは、文字の大きさだけでは決まりません。行間と余白を比べて、心地よいリズムを探しましょう。0123456789`;
|
|
263
|
+
function E(l) {
|
|
264
|
+
return ["size", "family", "weight", "line-height"].includes(l);
|
|
265
|
+
}
|
|
266
|
+
function I(l, u) {
|
|
267
|
+
const o = /* @__PURE__ */ new Set();
|
|
268
|
+
let a = l;
|
|
269
|
+
for (; a.cssValue !== null && a.resolvedValue !== null && !o.has(a.key); ) {
|
|
270
|
+
o.add(a.key);
|
|
271
|
+
const g = a.resolvedValue.trim();
|
|
272
|
+
if (/^(?:0|\+?(?:\d+(?:\.\d+)?|\.\d+)(?:px|rem))$/i.test(g)) return g;
|
|
273
|
+
const f = /^var\(\s*(--[a-zA-Z0-9_-]+)\s*\)$/.exec(g);
|
|
274
|
+
if (!f) return null;
|
|
275
|
+
const r = u.rows.filter((_) => _.cssVar === f[1]);
|
|
276
|
+
if (r.length !== 1) return null;
|
|
277
|
+
a = r[0];
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
function P({ row: l, tier: u, kind: o, model: a, previewText: g }) {
|
|
282
|
+
if (o === "bar") {
|
|
283
|
+
const c = I(l, a);
|
|
284
|
+
return c === null ? /* @__PURE__ */ y("div", { className: "zdtp-dashboard__preview-unavailable", children: "Ruler unavailable: requires a nonnegative px/rem length or a resolved direct alias." }) : /* @__PURE__ */ m("div", { className: "zdtp-dashboard__ruler-wrap", children: [
|
|
285
|
+
/* @__PURE__ */ y("div", { className: "zdtp-dashboard__ruler-caption", children: "Actual size · ticks every 8 CSS px · major ticks every 64 CSS px" }),
|
|
286
|
+
/* @__PURE__ */ y("div", { className: "zdtp-dashboard__scroll zdtp-dashboard__ruler-scroll", role: "region", tabIndex: 0, "aria-label": `${l.label} (${l.cssVar}) actual-size ruler`, children: /* @__PURE__ */ m("div", { className: "zdtp-dashboard__ruler", style: { inlineSize: `max(100%, ${c})` }, children: [
|
|
287
|
+
/* @__PURE__ */ y("div", { className: "zdtp-dashboard__ruler-origin", children: "0" }),
|
|
288
|
+
/* @__PURE__ */ y("span", { className: "zdtp-dashboard__sample zdtp-dashboard__sample--bar", style: { inlineSize: c }, "aria-hidden": "true" })
|
|
289
|
+
] }) })
|
|
290
|
+
] });
|
|
291
|
+
}
|
|
292
|
+
if (l.cssValue === null || l.resolvedValue === null || o === "text" || o === "duration") return null;
|
|
293
|
+
const f = l.resolvedValue.trim().toLowerCase();
|
|
294
|
+
if (!f || (o === "radius" || o === "size") && (/^(?:auto|none|normal|(?:min|max)-content|stretch|content)$|^fit-content(?:\(|$)/.test(f) || /^[+-]?(?:\d*\.)?\d+(?:ms|s)$/.test(f) || /^[+-]?(?:\d*\.)?\d+$/.test(f) && Number(f) !== 0))
|
|
295
|
+
return null;
|
|
296
|
+
const r = l.cssValue;
|
|
297
|
+
let _;
|
|
298
|
+
switch (o) {
|
|
299
|
+
case "color":
|
|
300
|
+
_ = { backgroundColor: r };
|
|
301
|
+
break;
|
|
302
|
+
case "radius":
|
|
303
|
+
_ = { borderRadius: r };
|
|
304
|
+
break;
|
|
305
|
+
case "shadow":
|
|
306
|
+
_ = { boxShadow: r };
|
|
307
|
+
break;
|
|
308
|
+
case "size":
|
|
309
|
+
_ = { fontSize: r };
|
|
310
|
+
break;
|
|
311
|
+
case "family":
|
|
312
|
+
_ = { fontFamily: r };
|
|
313
|
+
break;
|
|
314
|
+
case "weight":
|
|
315
|
+
_ = { fontWeight: r };
|
|
316
|
+
break;
|
|
317
|
+
case "line-height": {
|
|
318
|
+
const c = a.rows.find((p) => p.cssVar === u.previewBase);
|
|
319
|
+
_ = { lineHeight: r, ...c?.cssValue !== null && c?.resolvedValue != null ? { fontSize: `var(${c.cssVar})` } : {} };
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const v = E(o);
|
|
324
|
+
return /* @__PURE__ */ y(
|
|
325
|
+
"div",
|
|
326
|
+
{
|
|
327
|
+
className: `zdtp-dashboard__preview zdtp-dashboard__preview--${o}${v ? " zdtp-dashboard__preview--typography zdtp-dashboard__scroll" : ""}`,
|
|
328
|
+
"aria-hidden": v ? void 0 : "true",
|
|
329
|
+
role: v ? "region" : void 0,
|
|
330
|
+
tabIndex: v ? 0 : void 0,
|
|
331
|
+
"aria-label": v ? `${l.label} (${l.cssVar}) typography specimen` : void 0,
|
|
332
|
+
children: /* @__PURE__ */ y("span", { className: `zdtp-dashboard__sample zdtp-dashboard__sample--${o}`, style: _, children: v ? g : "" })
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
function W({
|
|
337
|
+
tabs: l,
|
|
338
|
+
mode: u = "light",
|
|
339
|
+
title: o = "Token dashboard",
|
|
340
|
+
id: a,
|
|
341
|
+
previewOverrides: g,
|
|
342
|
+
previewText: f = M
|
|
343
|
+
}) {
|
|
344
|
+
const r = B(l, u), _ = r.rows.filter((v) => v.diagnostics.length > 0).length;
|
|
345
|
+
return /* @__PURE__ */ m("div", { id: a, className: "zdtp-dashboard", role: "region", "aria-label": o, "data-mode": u, children: [
|
|
346
|
+
/* @__PURE__ */ m("div", { className: "zdtp-dashboard__header", children: [
|
|
347
|
+
/* @__PURE__ */ y("div", { role: "heading", "aria-level": 2, className: "zdtp-dashboard__title", children: o }),
|
|
348
|
+
/* @__PURE__ */ m("div", { className: "zdtp-dashboard__summary", children: [
|
|
349
|
+
/* @__PURE__ */ m("span", { className: "zdtp-dashboard__count", children: [
|
|
350
|
+
r.rows.length,
|
|
351
|
+
" tokens"
|
|
352
|
+
] }),
|
|
353
|
+
/* @__PURE__ */ m("span", { children: [
|
|
354
|
+
"Declared defaults · ",
|
|
355
|
+
u,
|
|
356
|
+
" mode"
|
|
357
|
+
] }),
|
|
358
|
+
_ > 0 && /* @__PURE__ */ m("span", { children: [
|
|
359
|
+
_,
|
|
360
|
+
" with diagnostics"
|
|
361
|
+
] })
|
|
362
|
+
] })
|
|
363
|
+
] }),
|
|
364
|
+
/* @__PURE__ */ m("div", { className: "zdtp-dashboard__inventory", style: { ...r.declarations, colorScheme: u }, children: [
|
|
365
|
+
r.rows.length === 0 && /* @__PURE__ */ y("div", { className: "zdtp-dashboard__empty", children: "No tokens declared." }),
|
|
366
|
+
r.tabs.map((v) => /* @__PURE__ */ m("div", { className: "zdtp-dashboard__tab", role: "group", "aria-label": v.label, children: [
|
|
367
|
+
/* @__PURE__ */ m("div", { className: "zdtp-dashboard__tab-header", children: [
|
|
368
|
+
/* @__PURE__ */ y("div", { role: "heading", "aria-level": 3, className: "zdtp-dashboard__tab-title", children: v.label }),
|
|
369
|
+
/* @__PURE__ */ m("span", { className: "zdtp-dashboard__tab-count", children: [
|
|
370
|
+
v.tiers.reduce((c, p) => c + p.rows.length, 0),
|
|
371
|
+
" tokens"
|
|
372
|
+
] })
|
|
373
|
+
] }),
|
|
374
|
+
v.tiers.map((c) => /* @__PURE__ */ m("div", { className: "zdtp-dashboard__tier", children: [
|
|
375
|
+
/* @__PURE__ */ y("div", { role: "heading", "aria-level": 4, className: "zdtp-dashboard__tier-title", children: c.label }),
|
|
376
|
+
/* @__PURE__ */ y("div", { className: c.palette ? "zdtp-dashboard__scroll zdtp-dashboard__palette-scroll" : void 0, role: c.palette ? "region" : void 0, tabIndex: c.palette ? 0 : void 0, "aria-label": c.palette ? `${c.label} palette stops` : void 0, children: /* @__PURE__ */ y("div", { className: `zdtp-dashboard__grid${c.palette ? " zdtp-dashboard__palette" : ""}`, role: "list", "aria-label": c.label, children: c.rows.map((p) => {
|
|
377
|
+
const $ = L(p, c, g), z = !c.palette && ($ === "bar" || E($));
|
|
378
|
+
return /* @__PURE__ */ m("div", { className: `zdtp-dashboard__token${z ? " zdtp-dashboard__token--wide" : ""}`, role: "listitem", "data-css-var": p.cssVar, children: [
|
|
379
|
+
/* @__PURE__ */ y(P, { row: p, tier: c, kind: $, model: r, previewText: f }),
|
|
380
|
+
/* @__PURE__ */ m("div", { className: "zdtp-dashboard__token-content", children: [
|
|
381
|
+
/* @__PURE__ */ y("div", { className: "zdtp-dashboard__name", children: p.label }),
|
|
382
|
+
/* @__PURE__ */ y("div", { className: "zdtp-dashboard__variable", children: p.cssVar }),
|
|
383
|
+
/* @__PURE__ */ y("div", { className: "zdtp-dashboard__value", children: p.declaredValue || "(empty)" }),
|
|
384
|
+
p.cssValue !== null && p.cssValue !== p.declaredValue && /* @__PURE__ */ m("div", { className: "zdtp-dashboard__reference", children: [
|
|
385
|
+
"CSS: ",
|
|
386
|
+
p.cssValue
|
|
387
|
+
] }),
|
|
388
|
+
p.references.length > 0 && /* @__PURE__ */ m("div", { className: "zdtp-dashboard__reference", children: [
|
|
389
|
+
"References: ",
|
|
390
|
+
p.references.map((k) => k.cssVar).join(", ")
|
|
391
|
+
] }),
|
|
392
|
+
p.diagnostics.map((k, S) => /* @__PURE__ */ m("div", { className: "zdtp-dashboard__diagnostic", "data-diagnostic": k.code, children: [
|
|
393
|
+
k.severity === "error" ? "Unavailable" : "Context needed",
|
|
394
|
+
": ",
|
|
395
|
+
k.message
|
|
396
|
+
] }, S))
|
|
397
|
+
] })
|
|
398
|
+
] }, p.key);
|
|
399
|
+
}) }) })
|
|
400
|
+
] }, c.key))
|
|
401
|
+
] }, v.key))
|
|
402
|
+
] })
|
|
403
|
+
] });
|
|
404
|
+
}
|
|
405
|
+
export {
|
|
406
|
+
W as TokenDashboard
|
|
407
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TabConfig } from "../tokens/tier-model.js";
|
|
2
|
+
import type { DashboardMode, DashboardModel } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Normalize the authored inventory for a static dashboard. Named color schemes,
|
|
5
|
+
* stored changes, editor fallback values and pill.customDefault are not inputs.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildDashboardModel(tabs: readonly TabConfig[], mode?: DashboardMode): DashboardModel;
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/* Static dashboard chrome deliberately uses no inventory custom properties.
|
|
2
|
+
The namespace and div/span markup protect documentation hosts from tag resets. */
|
|
3
|
+
.zdtp-dashboard,
|
|
4
|
+
.zdtp-dashboard * {
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.zdtp-dashboard {
|
|
9
|
+
min-inline-size: 0;
|
|
10
|
+
max-inline-size: 100%;
|
|
11
|
+
border: 1px solid #dce1e8;
|
|
12
|
+
border-radius: 10px;
|
|
13
|
+
background: #f8fafc;
|
|
14
|
+
color: #182131;
|
|
15
|
+
color-scheme: light;
|
|
16
|
+
font: 400 14px/1.5 system-ui, sans-serif;
|
|
17
|
+
letter-spacing: normal;
|
|
18
|
+
text-align: start;
|
|
19
|
+
text-transform: none;
|
|
20
|
+
white-space: normal;
|
|
21
|
+
overflow-wrap: anywhere;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.zdtp-dashboard :where(div, span) {
|
|
25
|
+
min-inline-size: 0;
|
|
26
|
+
margin: 0;
|
|
27
|
+
padding: 0;
|
|
28
|
+
border: 0;
|
|
29
|
+
font: inherit;
|
|
30
|
+
color: inherit;
|
|
31
|
+
letter-spacing: inherit;
|
|
32
|
+
text-transform: none;
|
|
33
|
+
box-shadow: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.zdtp-dashboard__header {
|
|
37
|
+
display: grid;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
padding: 20px;
|
|
40
|
+
border-block-end: 1px solid #dce1e8;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.zdtp-dashboard__title {
|
|
44
|
+
font-size: 22px;
|
|
45
|
+
font-weight: 700;
|
|
46
|
+
line-height: 1.25;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.zdtp-dashboard__summary {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-wrap: wrap;
|
|
52
|
+
align-items: baseline;
|
|
53
|
+
gap: 4px 16px;
|
|
54
|
+
color: #536174;
|
|
55
|
+
font-size: 12px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.zdtp-dashboard__count {
|
|
59
|
+
color: #182131;
|
|
60
|
+
font-weight: 600;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.zdtp-dashboard__inventory {
|
|
64
|
+
display: grid;
|
|
65
|
+
gap: 32px;
|
|
66
|
+
padding: 20px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.zdtp-dashboard__tab {
|
|
70
|
+
display: grid;
|
|
71
|
+
gap: 20px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.zdtp-dashboard__tab-header {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-wrap: wrap;
|
|
77
|
+
align-items: baseline;
|
|
78
|
+
gap: 4px 12px;
|
|
79
|
+
padding-block-end: 8px;
|
|
80
|
+
border-block-end: 1px solid #dce1e8;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.zdtp-dashboard__tab-title {
|
|
84
|
+
font-size: 18px;
|
|
85
|
+
font-weight: 700;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.zdtp-dashboard__tab-count {
|
|
89
|
+
color: #536174;
|
|
90
|
+
font-size: 12px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.zdtp-dashboard__tier {
|
|
94
|
+
display: grid;
|
|
95
|
+
gap: 10px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.zdtp-dashboard__tier-title {
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.zdtp-dashboard__grid {
|
|
103
|
+
display: grid;
|
|
104
|
+
grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
|
|
105
|
+
gap: 12px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.zdtp-dashboard__token {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
border: 1px solid #dce1e8;
|
|
112
|
+
border-radius: 6px;
|
|
113
|
+
background: #fff;
|
|
114
|
+
color: #182131;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.zdtp-dashboard__token-content {
|
|
118
|
+
display: grid;
|
|
119
|
+
align-content: start;
|
|
120
|
+
gap: 5px;
|
|
121
|
+
padding: 12px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.zdtp-dashboard__name {
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.zdtp-dashboard__variable,
|
|
129
|
+
.zdtp-dashboard__value,
|
|
130
|
+
.zdtp-dashboard__reference {
|
|
131
|
+
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
132
|
+
font-size: 12px;
|
|
133
|
+
white-space: pre-wrap;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.zdtp-dashboard__variable,
|
|
137
|
+
.zdtp-dashboard__reference {
|
|
138
|
+
color: #536174;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.zdtp-dashboard__value {
|
|
142
|
+
padding-block-start: 5px;
|
|
143
|
+
color: #182131;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.zdtp-dashboard__diagnostic {
|
|
147
|
+
margin-block-start: 3px;
|
|
148
|
+
padding: 8px;
|
|
149
|
+
border-inline-start: 2px solid #9d641a;
|
|
150
|
+
background: #fff5e6;
|
|
151
|
+
color: #704612;
|
|
152
|
+
font-size: 12px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.zdtp-dashboard__preview {
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
padding: 16px;
|
|
159
|
+
block-size: 88px;
|
|
160
|
+
border-block-end: 1px solid #dce1e8;
|
|
161
|
+
border-start-start-radius: 5px;
|
|
162
|
+
border-start-end-radius: 5px;
|
|
163
|
+
background: #eef1f5;
|
|
164
|
+
color: #182131;
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.zdtp-dashboard[data-mode='dark'] .zdtp-dashboard__preview {
|
|
169
|
+
background: #222936;
|
|
170
|
+
color: #f1f5f9;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.zdtp-dashboard__sample {
|
|
174
|
+
display: block;
|
|
175
|
+
flex: none;
|
|
176
|
+
max-inline-size: 100%;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.zdtp-dashboard__preview--color {
|
|
180
|
+
padding: 0;
|
|
181
|
+
background: repeating-conic-gradient(#e2e6eb 0% 25%, #fff 0% 50%) 0 / 16px 16px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.zdtp-dashboard[data-mode='dark'] .zdtp-dashboard__preview--color {
|
|
185
|
+
background: repeating-conic-gradient(#3e4652 0% 25%, #252c37 0% 50%) 0 / 16px 16px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.zdtp-dashboard__sample--color {
|
|
189
|
+
inline-size: 100%;
|
|
190
|
+
block-size: 100%;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.zdtp-dashboard__sample--bar {
|
|
194
|
+
block-size: 16px;
|
|
195
|
+
background: currentColor;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.zdtp-dashboard__sample--radius,
|
|
199
|
+
.zdtp-dashboard__sample--shadow {
|
|
200
|
+
inline-size: 64px;
|
|
201
|
+
block-size: 48px;
|
|
202
|
+
background: #fff;
|
|
203
|
+
border: 1px solid #8290a3;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.zdtp-dashboard__sample--family,
|
|
207
|
+
.zdtp-dashboard__sample--weight,
|
|
208
|
+
.zdtp-dashboard__sample--line-height {
|
|
209
|
+
font-size: 18px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.zdtp-dashboard__sample--line-height {
|
|
213
|
+
white-space: pre-line;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.zdtp-dashboard__empty {
|
|
217
|
+
color: #536174;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Wide rows use their embedding width, including narrow documentation columns. */
|
|
221
|
+
.zdtp-dashboard { container: zdtp-dashboard / inline-size; }
|
|
222
|
+
.zdtp-dashboard__token--wide {
|
|
223
|
+
grid-column: 1 / -1;
|
|
224
|
+
display: grid;
|
|
225
|
+
grid-template-columns: minmax(0, 1fr);
|
|
226
|
+
border-radius: 0;
|
|
227
|
+
}
|
|
228
|
+
.zdtp-dashboard__token--wide > .zdtp-dashboard__token-content { order: -1; }
|
|
229
|
+
.zdtp-dashboard__scroll {
|
|
230
|
+
max-inline-size: 100%;
|
|
231
|
+
overflow: auto;
|
|
232
|
+
overscroll-behavior-inline: contain;
|
|
233
|
+
}
|
|
234
|
+
.zdtp-dashboard__scroll:focus-visible {
|
|
235
|
+
outline: 2px solid #245caa;
|
|
236
|
+
outline-offset: -2px;
|
|
237
|
+
}
|
|
238
|
+
.zdtp-dashboard__ruler-wrap { padding: 12px; }
|
|
239
|
+
.zdtp-dashboard__ruler-caption,
|
|
240
|
+
.zdtp-dashboard__preview-unavailable {
|
|
241
|
+
color: #536174;
|
|
242
|
+
font-size: 12px;
|
|
243
|
+
padding: 12px;
|
|
244
|
+
}
|
|
245
|
+
.zdtp-dashboard__ruler-caption { padding: 0 0 8px; }
|
|
246
|
+
.zdtp-dashboard__ruler {
|
|
247
|
+
position: relative;
|
|
248
|
+
block-size: 64px;
|
|
249
|
+
padding-block-start: 36px;
|
|
250
|
+
background:
|
|
251
|
+
repeating-linear-gradient(to right, #8290a3 0 1px, transparent 1px 64px) left 24px / 100% 12px no-repeat,
|
|
252
|
+
repeating-linear-gradient(to right, #c4ccd7 0 1px, transparent 1px 8px) left 30px / 100% 6px no-repeat;
|
|
253
|
+
}
|
|
254
|
+
.zdtp-dashboard__ruler-origin { position: absolute; inset-block-start: 0; inset-inline-start: 0; font-size: 12px; }
|
|
255
|
+
.zdtp-dashboard__sample--bar {
|
|
256
|
+
max-inline-size: none;
|
|
257
|
+
min-inline-size: 0;
|
|
258
|
+
border: 0;
|
|
259
|
+
padding: 0;
|
|
260
|
+
background: #526c92;
|
|
261
|
+
}
|
|
262
|
+
.zdtp-dashboard__palette {
|
|
263
|
+
display: flex;
|
|
264
|
+
gap: 0;
|
|
265
|
+
inline-size: max-content;
|
|
266
|
+
min-inline-size: 100%;
|
|
267
|
+
}
|
|
268
|
+
.zdtp-dashboard__palette > .zdtp-dashboard__token {
|
|
269
|
+
display: grid;
|
|
270
|
+
grid-template-rows: minmax(88px, auto) 1fr;
|
|
271
|
+
align-content: start;
|
|
272
|
+
flex: 1 0 156px;
|
|
273
|
+
inline-size: 156px;
|
|
274
|
+
border-radius: 0;
|
|
275
|
+
}
|
|
276
|
+
.zdtp-dashboard__palette > .zdtp-dashboard__token > .zdtp-dashboard__token-content { grid-row: 2; }
|
|
277
|
+
.zdtp-dashboard__palette .zdtp-dashboard__preview { border-radius: 0; }
|
|
278
|
+
.zdtp-dashboard__preview--typography {
|
|
279
|
+
display: block;
|
|
280
|
+
block-size: auto;
|
|
281
|
+
padding: 20px;
|
|
282
|
+
border: 0;
|
|
283
|
+
border-radius: 0;
|
|
284
|
+
}
|
|
285
|
+
.zdtp-dashboard__preview--typography > .zdtp-dashboard__sample {
|
|
286
|
+
font: 400 18px/1.6 system-ui, sans-serif;
|
|
287
|
+
letter-spacing: normal;
|
|
288
|
+
white-space: pre-wrap;
|
|
289
|
+
overflow-wrap: anywhere;
|
|
290
|
+
max-inline-size: 100%;
|
|
291
|
+
}
|
|
292
|
+
@container zdtp-dashboard (min-width: 640px) {
|
|
293
|
+
.zdtp-dashboard__token--wide { grid-template-columns: 220px minmax(0, 1fr); }
|
|
294
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JSX } from 'preact';
|
|
2
|
+
import type { TabConfig, TierConfig } from "../tokens/tier-model.js";
|
|
3
|
+
import type { DashboardMode } from "./types.js";
|
|
4
|
+
/** Visual samples are independent from the panel's editor kind. */
|
|
5
|
+
export type DashboardPreviewKind = NonNullable<TierConfig['preview']> | 'color' | 'shadow' | 'text';
|
|
6
|
+
export interface TokenDashboardProps {
|
|
7
|
+
tabs: readonly TabConfig[];
|
|
8
|
+
/** Selects declared per-mode defaults. Does not read the host or panel mode. */
|
|
9
|
+
mode?: DashboardMode;
|
|
10
|
+
title?: string;
|
|
11
|
+
/** Static multiline typography specimen. Empty strings are preserved. */
|
|
12
|
+
previewText?: string;
|
|
13
|
+
/** Optional root ID. Supply distinct IDs when rendering multiple instances. */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** Per-variable sample choices, including text-editor colors and shadows. */
|
|
16
|
+
previewOverrides?: Readonly<Record<string, DashboardPreviewKind>>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A complete, read-only inventory of declared defaults. Works with Preact's
|
|
20
|
+
* server renderer without hydration, browser globals, or panel configuration.
|
|
21
|
+
* Include @takazudo/zdtp/dashboard/styles.css through the host's CSS pipeline.
|
|
22
|
+
*/
|
|
23
|
+
export declare function TokenDashboard({ tabs, mode, title, id, previewOverrides, previewText, }: TokenDashboardProps): JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { TierConfig, TierValueKind } from "../tokens/tier-model.js";
|
|
2
|
+
/** Internal, render-independent description of declared token defaults. */
|
|
3
|
+
export type DashboardMode = 'light' | 'dark';
|
|
4
|
+
export interface DashboardDiagnostic {
|
|
5
|
+
code: string;
|
|
6
|
+
severity: 'warning' | 'error';
|
|
7
|
+
message: string;
|
|
8
|
+
rowKey?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DashboardReference {
|
|
11
|
+
key: string;
|
|
12
|
+
cssVar: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
15
|
+
export interface DashboardRow {
|
|
16
|
+
/** Positional identity stays unique even when a manifest repeats identifiers. */
|
|
17
|
+
key: string;
|
|
18
|
+
tabId: string;
|
|
19
|
+
tierId: string;
|
|
20
|
+
itemId: string;
|
|
21
|
+
label: string;
|
|
22
|
+
cssVar: string;
|
|
23
|
+
kind: TierValueKind['kind'];
|
|
24
|
+
source: 'item' | 'base-role';
|
|
25
|
+
defaultValue: string;
|
|
26
|
+
/** Human-readable selected-mode declaration, including explicit semantic overrides. */
|
|
27
|
+
declaredValue: string;
|
|
28
|
+
/** Safe local CSS declaration; null for an invalid or ambiguous declaration. */
|
|
29
|
+
cssValue: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Follows direct declared references only, never computes CSS. Expressions
|
|
32
|
+
* remain expressions. Null means invalid or dependent on unknown context;
|
|
33
|
+
* the renderer must not show a value preview in that case.
|
|
34
|
+
*/
|
|
35
|
+
resolvedValue: string | null;
|
|
36
|
+
references: DashboardReference[];
|
|
37
|
+
diagnostics: DashboardDiagnostic[];
|
|
38
|
+
}
|
|
39
|
+
export interface DashboardTier {
|
|
40
|
+
key: string;
|
|
41
|
+
id: string;
|
|
42
|
+
label: string;
|
|
43
|
+
/** Nonsemantic, non-reference, color-only source tier. */
|
|
44
|
+
palette?: boolean;
|
|
45
|
+
preview?: TierConfig['preview'];
|
|
46
|
+
previewBase?: string;
|
|
47
|
+
rows: DashboardRow[];
|
|
48
|
+
}
|
|
49
|
+
export interface DashboardTab {
|
|
50
|
+
key: string;
|
|
51
|
+
id: string;
|
|
52
|
+
label: string;
|
|
53
|
+
tiers: DashboardTier[];
|
|
54
|
+
}
|
|
55
|
+
export interface DashboardModel {
|
|
56
|
+
mode: DashboardMode;
|
|
57
|
+
tabs: DashboardTab[];
|
|
58
|
+
rows: DashboardRow[];
|
|
59
|
+
/** Declare these on each preview scope, never on :root or the document. */
|
|
60
|
+
declarations: Readonly<Record<string, string>>;
|
|
61
|
+
diagnostics: DashboardDiagnostic[];
|
|
62
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takazudo/zdtp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Dynamic design-token tweak panel — host-config-driven, framework-agnostic Preact UI.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -71,6 +71,12 @@
|
|
|
71
71
|
"import": "./dist/constants.js",
|
|
72
72
|
"default": "./dist/constants.js"
|
|
73
73
|
},
|
|
74
|
+
"./dashboard": {
|
|
75
|
+
"types": "./dist/dashboard/index.d.ts",
|
|
76
|
+
"import": "./dist/dashboard/index.js",
|
|
77
|
+
"default": "./dist/dashboard/index.js"
|
|
78
|
+
},
|
|
79
|
+
"./dashboard/styles.css": "./dist/dashboard/styles.css",
|
|
74
80
|
"./styles": "./dist/zdtp.css",
|
|
75
81
|
"./styles.css": "./dist/zdtp.css",
|
|
76
82
|
"./package.json": "./package.json"
|
|
@@ -102,6 +108,7 @@
|
|
|
102
108
|
"jsdom": "^25.0.0",
|
|
103
109
|
"playwright": "^1.60.0",
|
|
104
110
|
"preact": "^10.29.1",
|
|
111
|
+
"preact-render-to-string": "^6.6.6",
|
|
105
112
|
"typescript": "^5.6.0",
|
|
106
113
|
"vite": "^7.3.5",
|
|
107
114
|
"vitest": "^4.1.8",
|