@conduction/theme 1.1.61 → 1.1.63
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/.claude/commands/nlds-reference.md +1216 -0
- package/.claude/skills/nlds-new/SKILL.md +48 -0
- package/.claude/skills/nlds-update/SKILL.md +53 -0
- package/.gitattributes +64 -0
- package/README.md +2 -0
- package/conduction-design-tokens/src/component/utrecht/extra-tokens/link.tokens.json +2 -1
- package/municipalities/hof-van-twente-design-tokens/src/component/conduction/logo.tokens.json +7 -7
- package/municipalities/vaals-design-tokens/LICENSE.md +17 -0
- package/municipalities/vaals-design-tokens/README.md +23 -0
- package/municipalities/vaals-design-tokens/documentation/color.stories.mdx +17 -0
- package/municipalities/vaals-design-tokens/documentation/components.stories.mdx +11 -0
- package/municipalities/vaals-design-tokens/documentation/design-tokens.stories.mdx +14 -0
- package/municipalities/vaals-design-tokens/documentation/readme.stories.mdx +7 -0
- package/municipalities/vaals-design-tokens/package.json +37 -0
- package/municipalities/vaals-design-tokens/src/brand/vaals/color.tokens.json +129 -0
- package/municipalities/vaals-design-tokens/src/brand/vaals/font-size.tokens.json +54 -0
- package/municipalities/vaals-design-tokens/src/brand/vaals/size.tokens.json +17 -0
- package/municipalities/vaals-design-tokens/src/brand/vaals/typography.tokens.json +40 -0
- package/municipalities/vaals-design-tokens/src/common/utrecht/action.tokens.json +9 -0
- package/municipalities/vaals-design-tokens/src/common/utrecht/space.tokens.json +28 -0
- package/municipalities/vaals-design-tokens/src/component/conduction/card-header.tokens.json +34 -0
- package/municipalities/vaals-design-tokens/src/component/conduction/card-wrapper.tokens.json +33 -0
- package/municipalities/vaals-design-tokens/src/component/conduction/logo.tokens.json +27 -0
- package/municipalities/vaals-design-tokens/src/component/conduction/pagination.tokens.json +95 -0
- package/municipalities/vaals-design-tokens/src/component/conduction/select.tokens.json +39 -0
- package/municipalities/vaals-design-tokens/src/component/conduction/table-wrapper.tokens.json +21 -0
- package/municipalities/vaals-design-tokens/src/component/conduction/tooltip.tokens.json +17 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/accordion.tokens.json +68 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/alert.tokens.json +46 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/badge-counter.tokens.json +13 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/badge-status.tokens.json +8 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/badge.tokens.json +15 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/blockquote.tokens.json +27 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/breadcrumb.tokens.json +44 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/button.tokens.json +152 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/calendar.tokens.json +80 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/checkbox.tokens.json +57 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/code.tokens.json +26 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/data-list.tokens.json +28 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/document.tokens.json +12 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/accordion.tokens.json +36 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/alert.tokens.json +9 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/badge-counter.tokens.json +11 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/breadcrumb.tokens.json +21 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/form-field.tokens.json +13 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/form-input.tokens.json +26 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/heading.tokens.json +28 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/icon.tokens.json +7 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/link.tokens.json +11 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/page-footer.tokens.json +46 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/page-header.tokens.json +15 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/radio-button.tokens.json +14 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/skip-link.tokens.json +24 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/table.tokens.json +37 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/extra-tokens/textbox.tokens.json +26 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/focus.tokens.json +15 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/form-field.tokens.json +17 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/form-input.tokens.json +31 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/form-label.tokens.json +23 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/heading.tokens.json +49 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/icon.tokens.json +12 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/link.tokens.json +33 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/list.tokens.json +31 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/page-footer.tokens.json +13 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/page-header.tokens.json +10 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/page.tokens.json +11 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/paragraph.tokens.json +25 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/radio-button.tokens.json +68 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/select.tokens.json +47 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/separator.tokens.json +10 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/skip-link.tokens.json +16 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/spotlight-section.tokens.json +24 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/surface.tokens.json +8 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/table.tokens.json +60 -0
- package/municipalities/vaals-design-tokens/src/component/utrecht/textbox.tokens.json +32 -0
- package/municipalities/vaals-design-tokens/src/config.json +73 -0
- package/municipalities/vaals-design-tokens/src/font/GT-Walsheim-Bold.woff2 +0 -0
- package/municipalities/vaals-design-tokens/src/font/GT-Walsheim-Light.woff2 +0 -0
- package/municipalities/vaals-design-tokens/src/font/GT-Walsheim-Medium.woff2 +0 -0
- package/municipalities/vaals-design-tokens/src/font/GT-Walsheim-Regular.woff2 +0 -0
- package/municipalities/vaals-design-tokens/src/font.scss +31 -0
- package/municipalities/vaals-design-tokens/src/index.scss +8 -0
- package/municipalities/vaals-design-tokens/style-dictionary.config.js +6 -0
- package/package.json +7 -3
|
@@ -0,0 +1,1216 @@
|
|
|
1
|
+
# NL Design System Token Set — Shared Reference
|
|
2
|
+
|
|
3
|
+
This document is the shared reference for both `/nlds:new` and `/nlds:update`. It contains all extraction scripts, file templates, component mappings, and build/test procedures.
|
|
4
|
+
|
|
5
|
+
**Do not invoke this file directly** — it is read by the skill wrappers which provide the organisation name, slug, URLs, and mode (new vs update).
|
|
6
|
+
|
|
7
|
+
## Variables provided by the calling skill
|
|
8
|
+
|
|
9
|
+
| Variable | Example | Description |
|
|
10
|
+
| ---------------- | --------------------------------------------------- | -------------------------------------------------- |
|
|
11
|
+
| `<slug>` | `den-haag` | Lowercase, hyphenated identifier |
|
|
12
|
+
| `<prefix>` | `den-haag` | Same as slug |
|
|
13
|
+
| `<fullName>` | `Den Haag` | Proper display name |
|
|
14
|
+
| `<packageName>` | `@nl-design-system-unstable/den-haag-design-tokens` | npm package name |
|
|
15
|
+
| `<folderName>` | `municipalities/den-haag-design-tokens` | Directory path |
|
|
16
|
+
| `<websiteUrl>` | `https://www.denhaag.nl/` | Organisation website (may be empty) |
|
|
17
|
+
| `<huisstijlUrl>` | (url) | Style guide URL (may be empty) |
|
|
18
|
+
| `<mode>` | `new` or `update` | Whether creating from scratch or updating existing |
|
|
19
|
+
|
|
20
|
+
> **Notation convention:**
|
|
21
|
+
>
|
|
22
|
+
> - `<slug>`, `<fullName>`, `<fontKey>` etc. in angle brackets = **template variables** — substitute with the actual derived value
|
|
23
|
+
> - `{token.reference}` in curly braces = **Style Dictionary token references** — write literally into the output file (after substituting any `<angle-bracket>` parts inside them)
|
|
24
|
+
> - Example: `"{<slug>.size.xs}"` → written as `"{gouda.size.xs}"` in the output file for organisation "Gouda"
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Part A — Extract design info
|
|
29
|
+
|
|
30
|
+
If a website URL or huisstijlhandboek URL was provided, use **Playwright MCP** to extract the organisation's visual design. If neither was provided, skip to Part B and use placeholder values.
|
|
31
|
+
|
|
32
|
+
### A1 — Navigate and screenshot
|
|
33
|
+
|
|
34
|
+
1. Navigate to the homepage using `mcp__browser-{N}__browser_navigate`.
|
|
35
|
+
2. Take a **full-page screenshot** (`mcp__browser-{N}__browser_take_screenshot` with `fullPage: true`).
|
|
36
|
+
|
|
37
|
+
### A2 — Run the extraction script on the homepage
|
|
38
|
+
|
|
39
|
+
Run the following JavaScript via `mcp__browser-{N}__browser_evaluate` **exactly as written** — do not modify or simplify it. Copy-paste the entire function:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
() => {
|
|
43
|
+
const rgbToHex = (rgb) => {
|
|
44
|
+
if (!rgb || rgb === "transparent" || rgb === "rgba(0, 0, 0, 0)")
|
|
45
|
+
return null;
|
|
46
|
+
const m = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
47
|
+
if (!m) return rgb;
|
|
48
|
+
const hex =
|
|
49
|
+
"#" +
|
|
50
|
+
[m[1], m[2], m[3]]
|
|
51
|
+
.map((x) => parseInt(x).toString(16).padStart(2, "0"))
|
|
52
|
+
.join("");
|
|
53
|
+
if (m[4] !== undefined && parseFloat(m[4]) < 1) {
|
|
54
|
+
const alpha = Math.round(parseFloat(m[4]) * 255)
|
|
55
|
+
.toString(16)
|
|
56
|
+
.padStart(2, "0");
|
|
57
|
+
return hex + alpha;
|
|
58
|
+
}
|
|
59
|
+
return hex;
|
|
60
|
+
};
|
|
61
|
+
const hslLightness = (hex) => {
|
|
62
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255,
|
|
63
|
+
g = parseInt(hex.slice(3, 5), 16) / 255,
|
|
64
|
+
b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
65
|
+
return Math.round(((Math.max(r, g, b) + Math.min(r, g, b)) / 2) * 100);
|
|
66
|
+
};
|
|
67
|
+
const getStyles = (el) => {
|
|
68
|
+
if (!el) return null;
|
|
69
|
+
const cs = getComputedStyle(el);
|
|
70
|
+
return {
|
|
71
|
+
backgroundColor: rgbToHex(cs.backgroundColor),
|
|
72
|
+
color: rgbToHex(cs.color),
|
|
73
|
+
fontFamily: cs.fontFamily,
|
|
74
|
+
fontSize: cs.fontSize,
|
|
75
|
+
fontWeight: cs.fontWeight,
|
|
76
|
+
lineHeight: cs.lineHeight,
|
|
77
|
+
borderColor: rgbToHex(cs.borderColor),
|
|
78
|
+
borderRadius: cs.borderRadius,
|
|
79
|
+
borderWidth: cs.borderWidth,
|
|
80
|
+
paddingBlockStart: cs.paddingTop,
|
|
81
|
+
paddingBlockEnd: cs.paddingBottom,
|
|
82
|
+
paddingInlineStart: cs.paddingLeft,
|
|
83
|
+
paddingInlineEnd: cs.paddingRight,
|
|
84
|
+
textDecoration: cs.textDecoration,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
const first = (sels) => {
|
|
88
|
+
for (const s of sels) {
|
|
89
|
+
const el = document.querySelector(s);
|
|
90
|
+
if (el) return el;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const result = {
|
|
96
|
+
components: {},
|
|
97
|
+
allColors: [],
|
|
98
|
+
allFonts: [],
|
|
99
|
+
loadedFonts: [],
|
|
100
|
+
fontFaceRules: [],
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Component extraction — multiple selectors per component for robustness
|
|
104
|
+
const componentMap = {
|
|
105
|
+
h1: ["h1"],
|
|
106
|
+
h2: ["h2"],
|
|
107
|
+
h3: ["h3"],
|
|
108
|
+
h4: ["h4"],
|
|
109
|
+
paragraph: ["article p", "main p", ".content p", "p"],
|
|
110
|
+
link: [
|
|
111
|
+
"article a",
|
|
112
|
+
"main a",
|
|
113
|
+
".content a",
|
|
114
|
+
'a:not([class*="logo"]):not([class*="nav"])',
|
|
115
|
+
],
|
|
116
|
+
button: [
|
|
117
|
+
'button[type="submit"]',
|
|
118
|
+
".btn-primary",
|
|
119
|
+
"button.primary",
|
|
120
|
+
"form button",
|
|
121
|
+
"button",
|
|
122
|
+
],
|
|
123
|
+
searchInput: [
|
|
124
|
+
'input[type="search"]',
|
|
125
|
+
'input[name*="search"]',
|
|
126
|
+
'input[name*="zoek"]',
|
|
127
|
+
'input[type="text"]',
|
|
128
|
+
],
|
|
129
|
+
pageHeader: [
|
|
130
|
+
"header",
|
|
131
|
+
'[class*="header"]:not(th)',
|
|
132
|
+
'nav[role="navigation"]',
|
|
133
|
+
],
|
|
134
|
+
pageFooter: ["footer", '[class*="footer"]'],
|
|
135
|
+
breadcrumb: [
|
|
136
|
+
'nav[aria-label*="breadcrumb"] a',
|
|
137
|
+
'[class*="breadcrumb"] a',
|
|
138
|
+
'ol[class*="breadcrumb"] li a',
|
|
139
|
+
],
|
|
140
|
+
card: [
|
|
141
|
+
'[class*="card"]',
|
|
142
|
+
'[class*="tile"]',
|
|
143
|
+
'[class*="teaser"]',
|
|
144
|
+
"article",
|
|
145
|
+
],
|
|
146
|
+
separator: ["hr", '[class*="separator"]', '[class*="divider"]'],
|
|
147
|
+
};
|
|
148
|
+
for (const [name, sels] of Object.entries(componentMap)) {
|
|
149
|
+
const el = first(sels);
|
|
150
|
+
result.components[name] = getStyles(el);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Collect ALL unique colors on the page
|
|
154
|
+
const uniqueColors = new Set();
|
|
155
|
+
document.querySelectorAll("*").forEach((el) => {
|
|
156
|
+
const cs = getComputedStyle(el);
|
|
157
|
+
[cs.backgroundColor, cs.color, cs.borderColor].forEach((v) => {
|
|
158
|
+
const h = rgbToHex(v);
|
|
159
|
+
if (h && h.length >= 7) uniqueColors.add(h.slice(0, 7));
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
result.allColors = [...uniqueColors].map((hex) => ({
|
|
163
|
+
hex,
|
|
164
|
+
lightness: hslLightness(hex),
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
// Font families
|
|
168
|
+
const uniqueFonts = new Set();
|
|
169
|
+
document
|
|
170
|
+
.querySelectorAll("h1,h2,h3,h4,p,a,button,input,body")
|
|
171
|
+
.forEach((el) => {
|
|
172
|
+
uniqueFonts.add(getComputedStyle(el).fontFamily);
|
|
173
|
+
});
|
|
174
|
+
result.allFonts = [...uniqueFonts];
|
|
175
|
+
|
|
176
|
+
// Loaded font faces
|
|
177
|
+
document.fonts.forEach((f) => {
|
|
178
|
+
result.loadedFonts.push({
|
|
179
|
+
family: f.family,
|
|
180
|
+
weight: f.weight,
|
|
181
|
+
style: f.style,
|
|
182
|
+
status: f.status,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// @font-face rules from stylesheets
|
|
187
|
+
try {
|
|
188
|
+
for (const sheet of document.styleSheets) {
|
|
189
|
+
try {
|
|
190
|
+
for (const rule of sheet.cssRules) {
|
|
191
|
+
if (rule instanceof CSSFontFaceRule) {
|
|
192
|
+
const src = rule.style.getPropertyValue("src");
|
|
193
|
+
const family = rule.style.getPropertyValue("font-family");
|
|
194
|
+
const weight = rule.style.getPropertyValue("font-weight");
|
|
195
|
+
result.fontFaceRules.push({
|
|
196
|
+
family: family.replace(/['"]/g, ""),
|
|
197
|
+
weight,
|
|
198
|
+
src: src.substring(0, 300),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch (e) {}
|
|
203
|
+
}
|
|
204
|
+
} catch (e) {}
|
|
205
|
+
|
|
206
|
+
// Google Fonts links
|
|
207
|
+
result.googleFontLinks = [];
|
|
208
|
+
document
|
|
209
|
+
.querySelectorAll(
|
|
210
|
+
'link[href*="fonts.googleapis"], link[href*="fonts.gstatic"]',
|
|
211
|
+
)
|
|
212
|
+
.forEach((l) => {
|
|
213
|
+
result.googleFontLinks.push(l.href);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return result;
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Record the returned JSON** — you will use it to set token values.
|
|
221
|
+
|
|
222
|
+
### A3 — Visit additional pages and re-run extraction
|
|
223
|
+
|
|
224
|
+
Navigate to **each** of the following pages (skip if not found), take a full-page screenshot, and re-run the same extraction script:
|
|
225
|
+
|
|
226
|
+
1. A **content/service page** — click on the first article/service link visible on the homepage. This reveals body text, heading hierarchy, and link styles in context.
|
|
227
|
+
2. The **search page** — try these URLs in order: `/zoeken`, `/search`, `?s=test`. If none work, look for a search icon/link in the header. This reveals form inputs, result cards, and pagination.
|
|
228
|
+
3. A **contact page** — try `/contact`, `/over-ons`, or look in the footer. This reveals forms with multiple input types.
|
|
229
|
+
|
|
230
|
+
After each page, **merge the results**: if a component was `null` on the homepage but found on a later page, use the later value. If both have values, keep the homepage value (it's the primary design).
|
|
231
|
+
|
|
232
|
+
### A4 — Resolve font file URLs
|
|
233
|
+
|
|
234
|
+
From the `fontFaceRules` array in the extraction result, resolve each `src` URL to an absolute URL:
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
() => {
|
|
238
|
+
const fonts = [];
|
|
239
|
+
for (const sheet of document.styleSheets) {
|
|
240
|
+
try {
|
|
241
|
+
for (const rule of sheet.cssRules) {
|
|
242
|
+
if (rule instanceof CSSFontFaceRule) {
|
|
243
|
+
const family = rule.style
|
|
244
|
+
.getPropertyValue("font-family")
|
|
245
|
+
.replace(/['"]/g, "");
|
|
246
|
+
const weight = rule.style.getPropertyValue("font-weight") || "400";
|
|
247
|
+
const src = rule.style.getPropertyValue("src");
|
|
248
|
+
// Extract URLs from src
|
|
249
|
+
const urls = [...src.matchAll(/url\("?([^")\s]+)"?\)/g)].map((m) => {
|
|
250
|
+
try {
|
|
251
|
+
return new URL(m[1], document.baseURI).href;
|
|
252
|
+
} catch (e) {
|
|
253
|
+
return m[1];
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
// Prefer woff2 > woff > ttf > otf
|
|
257
|
+
const preferred =
|
|
258
|
+
urls.find((u) => u.endsWith(".woff2")) ||
|
|
259
|
+
urls.find((u) => u.endsWith(".woff")) ||
|
|
260
|
+
urls.find((u) => u.endsWith(".ttf")) ||
|
|
261
|
+
urls.find((u) => u.endsWith(".otf")) ||
|
|
262
|
+
urls[0];
|
|
263
|
+
if (preferred)
|
|
264
|
+
fonts.push({
|
|
265
|
+
family,
|
|
266
|
+
weight,
|
|
267
|
+
url: preferred,
|
|
268
|
+
ext: preferred.split(".").pop().split("?")[0],
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} catch (e) {}
|
|
273
|
+
}
|
|
274
|
+
return fonts;
|
|
275
|
+
};
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Record the returned array** — you will download these font files later.
|
|
279
|
+
|
|
280
|
+
### A5 — Extract from huisstijlhandboek (if applicable)
|
|
281
|
+
|
|
282
|
+
If a huisstijlhandboek URL was provided:
|
|
283
|
+
|
|
284
|
+
1. Navigate to the URL using `mcp__browser-{N}__browser_navigate`.
|
|
285
|
+
2. Take a screenshot.
|
|
286
|
+
3. Look for: colour palettes (hex values), typography specifications (font names, sizes, weights), logo usage guidelines.
|
|
287
|
+
4. Extract any values not already found from the website. The huisstijlhandboek takes precedence for brand palette definitions (exact hex codes) and font specifications.
|
|
288
|
+
|
|
289
|
+
### A6 — Build the design summary
|
|
290
|
+
|
|
291
|
+
From the extraction results, build this structured summary (write it out as text in your response so it's clear and traceable):
|
|
292
|
+
|
|
293
|
+
| Property | Value | Source |
|
|
294
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
295
|
+
| **Primary brand color** | (hex) — the most prominent non-black, non-white, non-grey color used for headings, links, or the header | components.h3.color or components.link.color |
|
|
296
|
+
| **Primary hover color** | (hex) — slightly darker variant, OR darken primary by 10% lightness | Observed or calculated |
|
|
297
|
+
| **Body font family** | (font stack) | components.paragraph.fontFamily |
|
|
298
|
+
| **Heading font family** | (font stack) — may be the same as body | components.h1.fontFamily |
|
|
299
|
+
| **Body font size** | (px → rem) | components.paragraph.fontSize |
|
|
300
|
+
| **Body font weight** | (number) | components.paragraph.fontWeight |
|
|
301
|
+
| **Heading font weight** | (number) | components.h1.fontWeight |
|
|
302
|
+
| **H1 font size** | (px → rem) | components.h1.fontSize |
|
|
303
|
+
| **H2 font size** | (px → rem) | components.h2.fontSize |
|
|
304
|
+
| **H3 font size** | (px → rem) | components.h3.fontSize |
|
|
305
|
+
| **H4 font size** | (px → rem, estimated if not found) | components.h4.fontSize |
|
|
306
|
+
| **Page header bg** | (hex) | components.pageHeader.backgroundColor |
|
|
307
|
+
| **Page header text** | (hex) | components.pageHeader.color |
|
|
308
|
+
| **Page footer bg** | (hex) | components.pageFooter.backgroundColor |
|
|
309
|
+
| **Page footer text** | (hex) | components.pageFooter.color |
|
|
310
|
+
| **Link color** | (hex) | components.link.color |
|
|
311
|
+
| **Button bg** | (hex) | components.button.backgroundColor |
|
|
312
|
+
| **Button text** | (hex) | components.button.color |
|
|
313
|
+
| **Button border-radius** | (px) | components.button.borderRadius |
|
|
314
|
+
| **Input border-color** | (hex) | components.searchInput.borderColor |
|
|
315
|
+
| **Input border-radius** | (px) | components.searchInput.borderRadius |
|
|
316
|
+
| **Separator color** | (hex) | components.separator.backgroundColor or borderColor |
|
|
317
|
+
| **Font file URLs** | (list) | From Step A4 |
|
|
318
|
+
|
|
319
|
+
**This table is your source of truth for all token values.** Every token value you write must trace back to a row in this table. If a property could not be extracted (returned `null`), note it as "not found — using conduction default" and do NOT change that token from the conduction baseline.
|
|
320
|
+
|
|
321
|
+
If no URL was provided, use placeholder values and inform the user they should update the color and font values.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Part B — Create all files (new mode only)
|
|
326
|
+
|
|
327
|
+
> **Skip this part in `update` mode** — go directly to Part C.
|
|
328
|
+
|
|
329
|
+
Create the full directory structure at `municipalities/<slug>-design-tokens/` modeled on `conduction-design-tokens/`. Create every file listed below.
|
|
330
|
+
|
|
331
|
+
> **Do not create or copy the `dist/` folder.** This folder is generated by `npm run build` and will always be overwritten.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
### `municipalities/<slug>-design-tokens/package.json`
|
|
336
|
+
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"version": "1.0.0-alpha.1",
|
|
340
|
+
"author": "Community for NL Design System",
|
|
341
|
+
"description": "NL Design System design tokens for <fullName>",
|
|
342
|
+
"website": "<websiteUrl>",
|
|
343
|
+
"keywords": ["nl-design-system", "conduction"],
|
|
344
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
345
|
+
"name": "<packageName>",
|
|
346
|
+
"private": false,
|
|
347
|
+
"publishConfig": {
|
|
348
|
+
"access": "public"
|
|
349
|
+
},
|
|
350
|
+
"repository": {
|
|
351
|
+
"type": "git+ssh",
|
|
352
|
+
"url": "git@github.com:nl-design-system/themes.git"
|
|
353
|
+
},
|
|
354
|
+
"scripts": {
|
|
355
|
+
"clean": "rimraf -rf dist/",
|
|
356
|
+
"prebuild": "npm run clean",
|
|
357
|
+
"watch": "npm-run-all watch:**",
|
|
358
|
+
"watch:style-dictionary": "chokidar --follow-symlinks --command 'npm run --ignore-scripts build' 'src/**/*.tokens.json'",
|
|
359
|
+
"build": "npm-run-all build:**",
|
|
360
|
+
"build:scss": "sass --no-source-map src/:dist/",
|
|
361
|
+
"build:style-dictionary": "style-dictionary build --config ./style-dictionary.config.js"
|
|
362
|
+
},
|
|
363
|
+
"devDependencies": {
|
|
364
|
+
"@nl-design-system-unstable/theme-toolkit": "workspace:*",
|
|
365
|
+
"chokidar-cli": "3.0.0",
|
|
366
|
+
"npm-run-all": "4.1.5",
|
|
367
|
+
"rimraf": "3.0.2",
|
|
368
|
+
"style-dictionary": "3.8.0"
|
|
369
|
+
},
|
|
370
|
+
"bugs": {
|
|
371
|
+
"url": "https://github.com/ConductionNL/conduction-theme/issues"
|
|
372
|
+
},
|
|
373
|
+
"homepage": "https://github.com/ConductionNL/conduction-theme#readme"
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
### `municipalities/<slug>-design-tokens/style-dictionary.config.js`
|
|
380
|
+
|
|
381
|
+
```js
|
|
382
|
+
const config = require("./src/config.json");
|
|
383
|
+
const { createConfig } = require("../../style-dictionary-config");
|
|
384
|
+
|
|
385
|
+
module.exports = createConfig({
|
|
386
|
+
selector: `.${config.prefix}-theme`,
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### `municipalities/<slug>-design-tokens/src/config.json`
|
|
393
|
+
|
|
394
|
+
Read `conduction-design-tokens/src/config.json`, copy the full `stories` array, and replace only the top-level fields:
|
|
395
|
+
|
|
396
|
+
```json
|
|
397
|
+
{
|
|
398
|
+
"fullName": "<fullName>",
|
|
399
|
+
"name": "<fullName>",
|
|
400
|
+
"prefix": "<slug>",
|
|
401
|
+
"npm": "@conduction/theme",
|
|
402
|
+
"stories": [
|
|
403
|
+
"react-utrecht-alert--default",
|
|
404
|
+
"react-utrecht-alert--warning",
|
|
405
|
+
"react-utrecht-alert--error",
|
|
406
|
+
"react-utrecht-alert--ok",
|
|
407
|
+
"react-utrecht-badge-counter--default",
|
|
408
|
+
"react-utrecht-breadcrumb-nac--default",
|
|
409
|
+
"react-utrecht-breadcrumb-nac--separator",
|
|
410
|
+
"react-utrecht-button--default",
|
|
411
|
+
"react-utrecht-button--hover",
|
|
412
|
+
"react-utrecht-button--primary-action-button",
|
|
413
|
+
"react-utrecht-button--secondary-action-button",
|
|
414
|
+
"react-utrecht-calendar--default",
|
|
415
|
+
"react-utrecht-calendar--limited-range-calendar",
|
|
416
|
+
"react-utrecht-checkbox--default",
|
|
417
|
+
"react-utrecht-checkbox--checked",
|
|
418
|
+
"react-utrecht-checkbox--disabled",
|
|
419
|
+
"react-utrecht-checkbox--checked-and-disabled",
|
|
420
|
+
"react-utrecht-checkbox--hover",
|
|
421
|
+
"react-utrecht-checkbox--focus",
|
|
422
|
+
"react-utrecht-checkbox--focus-visible",
|
|
423
|
+
"react-utrecht-code--default",
|
|
424
|
+
"react-utrecht-code-block--default",
|
|
425
|
+
"react-utrecht-data-badge--default",
|
|
426
|
+
"react-utrecht-document--default",
|
|
427
|
+
"react-utrecht-heading-1--default",
|
|
428
|
+
"react-utrecht-heading-2--default",
|
|
429
|
+
"react-utrecht-heading-3--default",
|
|
430
|
+
"react-utrecht-heading-4--default",
|
|
431
|
+
"react-utrecht-heading-5--default",
|
|
432
|
+
"react-utrecht-link--default",
|
|
433
|
+
"react-utrecht-link--hover",
|
|
434
|
+
"react-utrecht-link--focus",
|
|
435
|
+
"react-utrecht-ordered-list--default",
|
|
436
|
+
"react-utrecht-unordered-list--default",
|
|
437
|
+
"react-utrecht-page--default",
|
|
438
|
+
"react-utrecht-page-header--default",
|
|
439
|
+
"react-utrecht-page-footer--default",
|
|
440
|
+
"react-utrecht-paragraph--default",
|
|
441
|
+
"react-utrecht-radio-button--default",
|
|
442
|
+
"react-utrecht-radio-button--hover",
|
|
443
|
+
"react-utrecht-radio-button--focus",
|
|
444
|
+
"react-utrecht-radio-button--checked",
|
|
445
|
+
"react-utrecht-radio-button--checked-and-disabled",
|
|
446
|
+
"react-utrecht-radio-button--disabled",
|
|
447
|
+
"react-utrecht-separator--default",
|
|
448
|
+
"react-utrecht-skip-link--default",
|
|
449
|
+
"react-utrecht-spotlicht-section--default",
|
|
450
|
+
"react-utrecht-spotlicht-section--info",
|
|
451
|
+
"react-utrecht-spotlicht-section--warning",
|
|
452
|
+
"react-utrecht-surface--default",
|
|
453
|
+
"react-utrecht-table--default",
|
|
454
|
+
"react-utrecht-textbox--default",
|
|
455
|
+
"react-conduction-card-header--default",
|
|
456
|
+
"react-conduction-card-header--hover",
|
|
457
|
+
"react-conduction-card-wrapper--default",
|
|
458
|
+
"react-conduction-card-wrapper--hover",
|
|
459
|
+
"react-conduction-pagination--default",
|
|
460
|
+
"react-conduction-input-select--default",
|
|
461
|
+
"react-conduction-input-select--list-option",
|
|
462
|
+
"react-conduction-input-select--placeholder",
|
|
463
|
+
"react-conduction-tabs--default",
|
|
464
|
+
"react-conduction-tabs--selected",
|
|
465
|
+
"react-conduction-tabs--hover",
|
|
466
|
+
"react-conduction-tabs--list",
|
|
467
|
+
"react-conduction-tabs--panel"
|
|
468
|
+
]
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
### `municipalities/<slug>-design-tokens/src/index.scss`
|
|
475
|
+
|
|
476
|
+
```scss
|
|
477
|
+
/**
|
|
478
|
+
* @license SEE LICENSE.md
|
|
479
|
+
* Copyright (c) 2021 NL Design System Community
|
|
480
|
+
* All rights reserved
|
|
481
|
+
*/
|
|
482
|
+
|
|
483
|
+
@import "./design-tokens.css";
|
|
484
|
+
@import "./font.css";
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
### `municipalities/<slug>-design-tokens/src/font/` — Download font files
|
|
490
|
+
|
|
491
|
+
For every font file URL found in Part A (`.woff2`, `.woff`, `.ttf`, `.otf`):
|
|
492
|
+
|
|
493
|
+
1. Download the file using the Bash tool (`curl -L -o ...`) into `municipalities/<slug>-design-tokens/src/font/`.
|
|
494
|
+
2. Use a clean filename: `<FontName>-<weight>.<ext>` (e.g. `RijksoverheidSans-Regular.woff2`, `RijksoverheidSans-Bold.woff2`).
|
|
495
|
+
3. If the font is served via Google Fonts CSS (a `<link>` to `fonts.googleapis.com`), fetch that CSS URL with `WebFetch` to get the individual `@font-face` blocks, then download each `.woff2` URL from those blocks.
|
|
496
|
+
4. If no font files were found via Part A, try to find them by:
|
|
497
|
+
- Searching for `@font-face` rules in the page's `<style>` tags or linked CSS files using `mcp__browser-{N}__browser_evaluate`
|
|
498
|
+
- Looking up the font name on Google Fonts (`fonts.google.com`) or the font foundry website
|
|
499
|
+
- If found from an external source, download and store them the same way
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
### `municipalities/<slug>-design-tokens/src/font.scss`
|
|
504
|
+
|
|
505
|
+
Write `@font-face` declarations for every font file downloaded above. The `font.scss` file lives in `src/` but is compiled to `dist/font.css`, so paths must be relative to `dist/` — use `url("../src/font/<FileName>-<weight>.<ext>")` (e.g. `url("../src/font/RijksoverheidSans-Regular.woff2")`). Model the structure after `conduction-design-tokens/src/font.scss`. Include the correct `font-weight` value for each file.
|
|
506
|
+
|
|
507
|
+
If no font files could be found or downloaded anywhere, leave a placeholder comment:
|
|
508
|
+
|
|
509
|
+
```scss
|
|
510
|
+
/* TODO: Add @font-face declarations for <fontName> */
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
### Brand token files
|
|
516
|
+
|
|
517
|
+
#### `municipalities/<slug>-design-tokens/src/brand/<slug>/color.tokens.json`
|
|
518
|
+
|
|
519
|
+
Use colors extracted from the website. If not found, use these placeholders:
|
|
520
|
+
|
|
521
|
+
- primary: `#000000`
|
|
522
|
+
- primary-hover: `#333333`
|
|
523
|
+
|
|
524
|
+
**Always read `conduction-design-tokens/src/brand/conduction/color.tokens.json` first.** Component tokens reference specific named shades by path (e.g. `<slug>.color.grey.82`). If any referenced path is missing the build fails with "Reference doesn't exist". Start with the full conduction baseline below, replacing `conduction` with `<slug>`, then add the organisation's own brand palette groups on top. After all component files are written, scan every `*.tokens.json` for references into `<slug>.color.*` and remove any palette entries that are not referenced by any file.
|
|
525
|
+
|
|
526
|
+
##### Color naming convention
|
|
527
|
+
|
|
528
|
+
**Lightness percentage** — The number in a color key represents the HSL lightness of that color. Calculate it with this formula:
|
|
529
|
+
|
|
530
|
+
```
|
|
531
|
+
R, G, B = hex channels / 255
|
|
532
|
+
lightness = round( (max(R,G,B) + min(R,G,B)) / 2 × 100 )
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
The extraction script in A2 already returns `lightness` for every color in the `allColors` array — use those values directly.
|
|
536
|
+
|
|
537
|
+
Examples:
|
|
538
|
+
|
|
539
|
+
- `#ffffff` (white) = `100`
|
|
540
|
+
- `#000000` (black) = `0`
|
|
541
|
+
- `#808080` (mid grey) = `50`
|
|
542
|
+
- `#e10a17` (bright red) = `44`
|
|
543
|
+
- `#012c9d` (dark blue) = `31`
|
|
544
|
+
|
|
545
|
+
So a color group key like `"31"` means that color sits at 31% on the lightness scale.
|
|
546
|
+
|
|
547
|
+
**Transparency suffix** — When a color has opacity, append `-{n}t` to the key where `{n}` is the opacity percentage. The hex value gets a 2-digit alpha suffix appended:
|
|
548
|
+
|
|
549
|
+
| Opacity | Hex suffix |
|
|
550
|
+
| ------- | -------------------------- |
|
|
551
|
+
| 100% | `FF` (omit — fully opaque) |
|
|
552
|
+
| 80% | `CC` |
|
|
553
|
+
| 60% | `99` |
|
|
554
|
+
| 50% | `80` |
|
|
555
|
+
| 10% | `1A` |
|
|
556
|
+
|
|
557
|
+
Full reference: https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4
|
|
558
|
+
|
|
559
|
+
Only add transparency variants when they are actually used on the organisation website.
|
|
560
|
+
|
|
561
|
+
**Color palette ordering inside the `color` object:**
|
|
562
|
+
|
|
563
|
+
1. Semantic aliases (`primary`, `primary-hover`, `error`, alert variants, etc.)
|
|
564
|
+
2. Brand-specific palette groups from the website (e.g. `blue`, `green`, `red`, `yellow`) — ordered lightest group last
|
|
565
|
+
3. `grey` (full baseline)
|
|
566
|
+
4. `lightgrey` (if present — sits between grey and white)
|
|
567
|
+
5. `white` (full baseline)
|
|
568
|
+
6. `black` (full baseline)
|
|
569
|
+
|
|
570
|
+
**Building the brand palette from extracted colors:**
|
|
571
|
+
|
|
572
|
+
1. Look at `allColors` from A2. Filter out greys (R≈G≈B), white (#ffffff-area), and black (#000000-area).
|
|
573
|
+
2. Group the remaining colors by dominant hue: red, blue, green, orange, etc.
|
|
574
|
+
3. For each group, create a palette object keyed by lightness percentage.
|
|
575
|
+
4. The **primary brand color** (from the design summary) must be in one of these groups.
|
|
576
|
+
5. Set `"primary"` to reference that color: `"{<slug>.color.<group>.<lightness>}"`.
|
|
577
|
+
6. Set `"primary-hover"` to a darker shade in the same group, or darken by ~10% lightness.
|
|
578
|
+
|
|
579
|
+
Start with this full baseline (replace `conduction` with `<slug>`), then insert the organisation's own brand palette groups between the semantic aliases and `grey`:
|
|
580
|
+
|
|
581
|
+
```json
|
|
582
|
+
{
|
|
583
|
+
"<slug>": {
|
|
584
|
+
"color": {
|
|
585
|
+
"primary": {
|
|
586
|
+
"value": "{<slug>.color.<paletteGroup>.<shade>}"
|
|
587
|
+
},
|
|
588
|
+
"primary-hover": {
|
|
589
|
+
"value": "{<slug>.color.<paletteGroup>.<darkerShade>}"
|
|
590
|
+
},
|
|
591
|
+
"error": { "value": "#dc3545" },
|
|
592
|
+
"alert-error": { "value": "#721c24" },
|
|
593
|
+
"alert-error-background": { "value": "#f8d7da" },
|
|
594
|
+
"warning": { "value": "#ffc107" },
|
|
595
|
+
"alert-warning": { "value": "#856404" },
|
|
596
|
+
"alert-warning-background": { "value": "#fff3cd" },
|
|
597
|
+
"succes": { "value": "#28a745" },
|
|
598
|
+
"alert-succes": { "value": "#155724" },
|
|
599
|
+
"alert-succes-background": { "value": "#d4edda" },
|
|
600
|
+
"info": { "value": "{<slug>.color.primary}" },
|
|
601
|
+
"alert-info": { "value": "#004085" },
|
|
602
|
+
"alert-info-background": { "value": "#cce5ff" },
|
|
603
|
+
"grey": {
|
|
604
|
+
"27": {
|
|
605
|
+
"value": "#444444"
|
|
606
|
+
},
|
|
607
|
+
"29": {
|
|
608
|
+
"value": "#4a4a4a"
|
|
609
|
+
},
|
|
610
|
+
"31": {
|
|
611
|
+
"value": "#4f4f4f"
|
|
612
|
+
},
|
|
613
|
+
"46": {
|
|
614
|
+
"value": "#767676"
|
|
615
|
+
},
|
|
616
|
+
"48": {
|
|
617
|
+
"value": "#7a7a7a"
|
|
618
|
+
},
|
|
619
|
+
"50": {
|
|
620
|
+
"value": "#808080",
|
|
621
|
+
"comment": "Base/Grey"
|
|
622
|
+
},
|
|
623
|
+
"70": {
|
|
624
|
+
"value": "#b3b3b3"
|
|
625
|
+
},
|
|
626
|
+
"82": {
|
|
627
|
+
"value": "#d1d1d1"
|
|
628
|
+
},
|
|
629
|
+
"87": {
|
|
630
|
+
"value": "#dddddd"
|
|
631
|
+
},
|
|
632
|
+
"90": {
|
|
633
|
+
"value": "#e6e6e6"
|
|
634
|
+
},
|
|
635
|
+
"95": {
|
|
636
|
+
"value": "#f2f2f2"
|
|
637
|
+
},
|
|
638
|
+
"97": {
|
|
639
|
+
"value": "#f7f7f7"
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
"lightgrey": {
|
|
643
|
+
"96": {
|
|
644
|
+
"value": "#f5f5f5",
|
|
645
|
+
"comment": "Base/LightGrey"
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
"white": {
|
|
649
|
+
"98": {
|
|
650
|
+
"value": "#fafafa"
|
|
651
|
+
},
|
|
652
|
+
"100": {
|
|
653
|
+
"value": "#ffffff",
|
|
654
|
+
"comment": "Base/White"
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
"black": {
|
|
658
|
+
"0": {
|
|
659
|
+
"value": "#000000",
|
|
660
|
+
"comment": "Base/Black"
|
|
661
|
+
},
|
|
662
|
+
"0-60t": {
|
|
663
|
+
"value": "#00000099",
|
|
664
|
+
"comment": "Black with 60% transparency"
|
|
665
|
+
},
|
|
666
|
+
"30": {
|
|
667
|
+
"value": "#4d4d4d"
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
After all component files are written, scan every `*.tokens.json` for which `<slug>.color.*` paths are actually referenced. Remove any individual shade keys from the palette that are not referenced anywhere. Do not remove semantic aliases (`primary`, `error`, etc.) even if unreferenced.
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
679
|
+
#### `municipalities/<slug>-design-tokens/src/brand/<slug>/font-size.tokens.json`
|
|
680
|
+
|
|
681
|
+
Set the values based on the computed font sizes extracted from the website. The mapping between font-size keys and components is:
|
|
682
|
+
|
|
683
|
+
| Key | Used by component |
|
|
684
|
+
| ----- | -------------------------------------------------------------------------------------- |
|
|
685
|
+
| `md` | `paragraph`, `heading-5` — **set to the most-used paragraph font-size on the website** |
|
|
686
|
+
| `lg` | `heading-4` |
|
|
687
|
+
| `xl` | `heading-3` |
|
|
688
|
+
| `2xl` | `heading-2` |
|
|
689
|
+
| `3xl` | `heading-1` |
|
|
690
|
+
|
|
691
|
+
Extract the computed `font-size` of `<p>`, `<h1>`, `<h2>`, `<h3>`, `<h4>` from the website and set the corresponding keys. Convert px values to rem (divide by 16). If you find additional distinct sizes used on the website (e.g. for captions, labels, small text), map them to the nearest smaller key.
|
|
692
|
+
|
|
693
|
+
**Scale validation** — after assigning all heading sizes, verify that the scale is well-spaced (each step should be meaningfully larger than the previous). If sizes are too close together (e.g. `3xl` = 52px, `2xl` = 50px, `xl` = 38px) this indicates a scale problem — set them to better-distributed values, keep the heading sizes as close as possible to the website, and include a note in the summary telling the user which sizes were adjusted and why.
|
|
694
|
+
|
|
695
|
+
```json
|
|
696
|
+
{
|
|
697
|
+
"<slug>": {
|
|
698
|
+
"font-size": {
|
|
699
|
+
"4xs": { "value": "0.625rem", "comment": "10px" },
|
|
700
|
+
"3xs": { "value": "0.75rem", "comment": "12px" },
|
|
701
|
+
"2xs": { "value": "0.875rem", "comment": "14px" },
|
|
702
|
+
"xs": { "value": "1rem", "comment": "16px" },
|
|
703
|
+
"sm": { "value": "1.125rem", "comment": "18px" },
|
|
704
|
+
"md": { "value": "1.25rem", "comment": "20px" },
|
|
705
|
+
"lg": { "value": "1.5rem", "comment": "24px" },
|
|
706
|
+
"xl": { "value": "1.75rem", "comment": "28px" },
|
|
707
|
+
"2xl": { "value": "2rem", "comment": "32px" },
|
|
708
|
+
"3xl": { "value": "2.5rem", "comment": "40px" },
|
|
709
|
+
"4xl": { "value": "3rem", "comment": "48px" },
|
|
710
|
+
"5xl": { "value": "3.625rem", "comment": "58px" }
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
#### `municipalities/<slug>-design-tokens/src/brand/<slug>/size.tokens.json`
|
|
719
|
+
|
|
720
|
+
```json
|
|
721
|
+
{
|
|
722
|
+
"<slug>": {
|
|
723
|
+
"size": {
|
|
724
|
+
"4xs": { "value": "1px" },
|
|
725
|
+
"3xs": { "value": "2px" },
|
|
726
|
+
"2xs": { "value": "4px" },
|
|
727
|
+
"xs": { "value": "8px" },
|
|
728
|
+
"sm": { "value": "14px" },
|
|
729
|
+
"md": { "value": "18px" },
|
|
730
|
+
"lg": { "value": "24px" },
|
|
731
|
+
"xl": { "value": "32px" },
|
|
732
|
+
"2xl": { "value": "48px" },
|
|
733
|
+
"3xl": { "value": "72px" },
|
|
734
|
+
"4xl": { "value": "96px" }
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
#### `municipalities/<slug>-design-tokens/src/brand/<slug>/typography.tokens.json`
|
|
743
|
+
|
|
744
|
+
Use the font family found on the website, or `Arial, sans-serif` as fallback.
|
|
745
|
+
`<fontKey>` = lowercase font name with hyphens (e.g. `open-sans`, `source-sans-pro`).
|
|
746
|
+
|
|
747
|
+
**`sans-serif` is required** — component tokens (button, paragraph, document, form inputs, etc.) reference `<slug>.typography.sans-serif.font-family`. Set it to the body font of the organisation.
|
|
748
|
+
|
|
749
|
+
```json
|
|
750
|
+
{
|
|
751
|
+
"<slug>": {
|
|
752
|
+
"typography": {
|
|
753
|
+
"<fontKey>": {
|
|
754
|
+
"font-family": {
|
|
755
|
+
"value": "\"<FontName>\", Arial, sans-serif"
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
"sans-serif": {
|
|
759
|
+
"font-family": {
|
|
760
|
+
"value": "\"<FontName>\", Arial, sans-serif"
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
"monospace": {
|
|
764
|
+
"font-family": {
|
|
765
|
+
"value": "Monospace, \"Lucida Console\""
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
"font-weight": {
|
|
769
|
+
"bold": { "value": "700" },
|
|
770
|
+
"semibold": { "value": "600" },
|
|
771
|
+
"normal": { "value": "400" },
|
|
772
|
+
"light": { "value": "100" }
|
|
773
|
+
},
|
|
774
|
+
"scale": {
|
|
775
|
+
"4xs": { "value": "{<slug>.font-size.4xs}" },
|
|
776
|
+
"3xs": { "value": "{<slug>.font-size.3xs}" },
|
|
777
|
+
"2xs": { "value": "{<slug>.font-size.2xs}" },
|
|
778
|
+
"xs": { "value": "{<slug>.font-size.xs}" },
|
|
779
|
+
"sm": { "value": "{<slug>.font-size.sm}" },
|
|
780
|
+
"md": { "value": "{<slug>.font-size.md}" },
|
|
781
|
+
"lg": { "value": "{<slug>.font-size.lg}" },
|
|
782
|
+
"xl": { "value": "{<slug>.font-size.xl}" },
|
|
783
|
+
"2xl": { "value": "{<slug>.font-size.2xl}" },
|
|
784
|
+
"3xl": { "value": "{<slug>.font-size.3xl}" },
|
|
785
|
+
"4xl": { "value": "{<slug>.font-size.4xl}" }
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
---
|
|
793
|
+
|
|
794
|
+
### Common token files
|
|
795
|
+
|
|
796
|
+
#### `municipalities/<slug>-design-tokens/src/common/utrecht/action.tokens.json`
|
|
797
|
+
|
|
798
|
+
```json
|
|
799
|
+
{
|
|
800
|
+
"utrecht": {
|
|
801
|
+
"action": {
|
|
802
|
+
"busy": { "cursor": { "value": "wait" } },
|
|
803
|
+
"disabled": { "cursor": { "value": "not-allowed" } },
|
|
804
|
+
"submit": { "cursor": { "value": "pointer" } }
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
---
|
|
811
|
+
|
|
812
|
+
#### `municipalities/<slug>-design-tokens/src/common/utrecht/space.tokens.json`
|
|
813
|
+
|
|
814
|
+
Read `conduction-design-tokens/src/common/utrecht/space.tokens.json` and replace `conduction` with `<slug>`:
|
|
815
|
+
|
|
816
|
+
```json
|
|
817
|
+
{
|
|
818
|
+
"utrecht": {
|
|
819
|
+
"space": {
|
|
820
|
+
"block": {
|
|
821
|
+
"3xs": { "value": "{<slug>.size.3xs}" },
|
|
822
|
+
"2xs": { "value": "{<slug>.size.2xs}" },
|
|
823
|
+
"xs": { "value": "{<slug>.size.xs}" },
|
|
824
|
+
"sm": { "value": "{<slug>.size.sm}" },
|
|
825
|
+
"md": { "value": "{<slug>.size.md}" },
|
|
826
|
+
"lg": { "value": "{<slug>.size.lg}" },
|
|
827
|
+
"xl": { "value": "{<slug>.size.xl}" },
|
|
828
|
+
"2xl": { "value": "{<slug>.size.2xl}" },
|
|
829
|
+
"3xl": { "value": "{<slug>.size.3xl}" }
|
|
830
|
+
},
|
|
831
|
+
"inline": {
|
|
832
|
+
"3xs": { "value": "{<slug>.size.3xs}" },
|
|
833
|
+
"2xs": { "value": "{<slug>.size.2xs}" },
|
|
834
|
+
"xs": { "value": "{<slug>.size.xs}" },
|
|
835
|
+
"sm": { "value": "{<slug>.size.sm}" },
|
|
836
|
+
"md": { "value": "{<slug>.size.md}" },
|
|
837
|
+
"lg": { "value": "{<slug>.size.lg}" },
|
|
838
|
+
"xl": { "value": "{<slug>.size.xl}" },
|
|
839
|
+
"2xl": { "value": "{<slug>.size.2xl}" },
|
|
840
|
+
"3xl": { "value": "{<slug>.size.3xl}" }
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
---
|
|
848
|
+
|
|
849
|
+
### Component token files
|
|
850
|
+
|
|
851
|
+
For each file listed below:
|
|
852
|
+
|
|
853
|
+
1. **Read** the source file from `conduction-design-tokens/src/` using the Read tool.
|
|
854
|
+
2. **Copy the full file content verbatim** into memory.
|
|
855
|
+
3. Apply only these text substitutions to the copied content:
|
|
856
|
+
- `conduction` → `<slug>`
|
|
857
|
+
- `aldritch` → `<fontKey>`
|
|
858
|
+
4. **Write** the result to the equivalent path under `municipalities/<slug>-design-tokens/src/` using the Write tool.
|
|
859
|
+
|
|
860
|
+
> **CRITICAL — Indentation must be preserved exactly.**
|
|
861
|
+
> The JSON files use a specific indent style (spaces or tabs) that is intentional.
|
|
862
|
+
> Do **not** reformat, reindent, pretty-print, or restructure the JSON in any way.
|
|
863
|
+
> The only changes allowed are the text substitutions listed above.
|
|
864
|
+
> If the source uses 2-space indentation, the output must also use 2-space indentation — character for character.
|
|
865
|
+
|
|
866
|
+
**Known reference fix:** The conduction baseline has `{conduction.color.blue.95}` in `calendar.tokens.json` (used for the "today" highlight). After replacement this becomes `{<slug>.color.blue.95}` which will fail if the organisation doesn't have a `blue` palette. **Replace this reference** with `{<slug>.color.grey.95}` (a neutral light highlight) unless the organisation's palette has a light brand color shade that makes more sense.
|
|
867
|
+
|
|
868
|
+
Create these files:
|
|
869
|
+
|
|
870
|
+
**`src/component/utrecht/`** (read from conduction and replace prefix):
|
|
871
|
+
|
|
872
|
+
- `accordion.tokens.json`
|
|
873
|
+
- `alert.tokens.json`
|
|
874
|
+
- `badge-counter.tokens.json`
|
|
875
|
+
- `badge-status.tokens.json`
|
|
876
|
+
- `badge.tokens.json`
|
|
877
|
+
- `blockquote.tokens.json`
|
|
878
|
+
- `breadcrumb.tokens.json`
|
|
879
|
+
- `button.tokens.json`
|
|
880
|
+
- `calendar.tokens.json`
|
|
881
|
+
- `checkbox.tokens.json`
|
|
882
|
+
- `code.tokens.json`
|
|
883
|
+
- `data-list.tokens.json`
|
|
884
|
+
- `document.tokens.json`
|
|
885
|
+
- `focus.tokens.json`
|
|
886
|
+
- `form-field.tokens.json`
|
|
887
|
+
- `form-input.tokens.json`
|
|
888
|
+
- `form-label.tokens.json`
|
|
889
|
+
- `heading.tokens.json`
|
|
890
|
+
- `icon.tokens.json`
|
|
891
|
+
- `link.tokens.json`
|
|
892
|
+
- `list.tokens.json`
|
|
893
|
+
- `page.tokens.json`
|
|
894
|
+
- `page-footer.tokens.json`
|
|
895
|
+
- `page-header.tokens.json`
|
|
896
|
+
- `paragraph.tokens.json`
|
|
897
|
+
- `radio-button.tokens.json`
|
|
898
|
+
- `select.tokens.json`
|
|
899
|
+
- `separator.tokens.json`
|
|
900
|
+
- `skip-link.tokens.json`
|
|
901
|
+
- `spotlight-section.tokens.json`
|
|
902
|
+
- `surface.tokens.json`
|
|
903
|
+
- `table.tokens.json`
|
|
904
|
+
- `textbox.tokens.json`
|
|
905
|
+
|
|
906
|
+
**`src/component/utrecht/extra-tokens/`** (read from conduction and replace prefix):
|
|
907
|
+
|
|
908
|
+
- `accordion.tokens.json`
|
|
909
|
+
- `alert.tokens.json`
|
|
910
|
+
- `badge-counter.tokens.json`
|
|
911
|
+
- `breadcrumb.tokens.json`
|
|
912
|
+
- `form-field.tokens.json`
|
|
913
|
+
- `form-input.tokens.json`
|
|
914
|
+
- `heading.tokens.json`
|
|
915
|
+
- `icon.tokens.json`
|
|
916
|
+
- `link.tokens.json`
|
|
917
|
+
- `page-footer.tokens.json`
|
|
918
|
+
- `page-header.tokens.json`
|
|
919
|
+
- `radio-button.tokens.json`
|
|
920
|
+
- `skip-link.tokens.json`
|
|
921
|
+
- `table.tokens.json`
|
|
922
|
+
- `textbox.tokens.json`
|
|
923
|
+
|
|
924
|
+
**`src/component/conduction/`** — these are Conduction framework components. The destination folder is **always** `municipalities/<slug>-design-tokens/src/component/conduction/` — **never rename this folder to `<slug>`**.
|
|
925
|
+
|
|
926
|
+
For file contents: replace only brand token references inside curly braces (`{conduction.` → `{<slug>.`), but **keep the root JSON key `"conduction"` unchanged** so that CSS variables remain `--conduction-*` as expected by the Conduction framework components.
|
|
927
|
+
|
|
928
|
+
- `card-header.tokens.json`
|
|
929
|
+
- `card-wrapper.tokens.json`
|
|
930
|
+
- `logo.tokens.json`
|
|
931
|
+
- `pagination.tokens.json`
|
|
932
|
+
- `select.tokens.json`
|
|
933
|
+
- `table-wrapper.tokens.json`
|
|
934
|
+
- `tooltip.tokens.json`
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
938
|
+
### Documentation files
|
|
939
|
+
|
|
940
|
+
#### `municipalities/<slug>-design-tokens/documentation/color.stories.mdx`
|
|
941
|
+
|
|
942
|
+
```mdx
|
|
943
|
+
import { Meta, ColorPalette, ColorItem } from "@storybook/addon-docs";
|
|
944
|
+
import tokens from "../dist/tokens.json";
|
|
945
|
+
import { ColorSearch } from "@nl-design-system-unstable/theme-toolkit/src/ColorSearch";
|
|
946
|
+
import { ColorTable } from "@nl-design-system-unstable/theme-toolkit/src/ColorTable";
|
|
947
|
+
import config from "../src/config.json";
|
|
948
|
+
|
|
949
|
+
<Meta title={`${config.name}/Color`} />
|
|
950
|
+
|
|
951
|
+
# Color
|
|
952
|
+
|
|
953
|
+
## Find a color
|
|
954
|
+
|
|
955
|
+
<ColorSearch tokens={tokens[config.prefix]["color"]}></ColorSearch>
|
|
956
|
+
|
|
957
|
+
## Color palette
|
|
958
|
+
|
|
959
|
+
<ColorTable tokens={tokens[config.prefix]["color"]}></ColorTable>
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
#### `municipalities/<slug>-design-tokens/documentation/components.stories.mdx`
|
|
963
|
+
|
|
964
|
+
Read `conduction-design-tokens/documentation/components.stories.mdx` and copy its content verbatim (it uses `config.json` dynamically).
|
|
965
|
+
|
|
966
|
+
#### `municipalities/<slug>-design-tokens/documentation/design-tokens.stories.mdx`
|
|
967
|
+
|
|
968
|
+
Read `conduction-design-tokens/documentation/design-tokens.stories.mdx` and copy its content verbatim.
|
|
969
|
+
|
|
970
|
+
#### `municipalities/<slug>-design-tokens/documentation/readme.stories.mdx`
|
|
971
|
+
|
|
972
|
+
Read `conduction-design-tokens/documentation/readme.stories.mdx` and copy its content verbatim.
|
|
973
|
+
|
|
974
|
+
---
|
|
975
|
+
|
|
976
|
+
### `municipalities/<slug>-design-tokens/LICENSE.md`
|
|
977
|
+
|
|
978
|
+
Generate a new LICENSE.md file for the organisation. If the organisation is a municipality, use `Gemeente <fullName>` as the name; otherwise use `<fullName>`. Use this template:
|
|
979
|
+
|
|
980
|
+
```md
|
|
981
|
+
# Auteursrecht <OrgName>
|
|
982
|
+
|
|
983
|
+
Copyright (c) <currentYear> <OrgName>
|
|
984
|
+
|
|
985
|
+
## Logo en huisstijl
|
|
986
|
+
|
|
987
|
+
Op het huisstijl en logo zijn auteursrechten van toepassing. Het gebruik van logo en huisstijl is alleen toegestaan voor gebruik door <OrgName>.
|
|
988
|
+
|
|
989
|
+
Wanneer je een bewerking van de software wilt gebruiken voor andere doeleinden, mag je niet het logo van <OrgName> gebruiken en je ontwerpt een eigen huisstijl.
|
|
990
|
+
|
|
991
|
+
## Lettertype
|
|
992
|
+
|
|
993
|
+
Lettertypes die worden gebruikt voor de huisstijl zijn niet allemaal gratis en open source. Let op dat bij gebruik van die bijgeleverde lettertypes je een (betaalde) licentie regelt. Pas anders de configuratie aan om minder of andere lettertypes te gebruiken.
|
|
994
|
+
|
|
995
|
+
## Toestemming
|
|
996
|
+
|
|
997
|
+
Wanneer je het logo of de huisstijl wilt gebruiken kun je voor toestemming contact opnemen met <OrgName>.
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
Where `<OrgName>` = `Gemeente <fullName>` for municipalities, or `<fullName>` for other organisation types.
|
|
1001
|
+
|
|
1002
|
+
### `municipalities/<slug>-design-tokens/README.md`
|
|
1003
|
+
|
|
1004
|
+
```md
|
|
1005
|
+
# <fullName> Design Tokens
|
|
1006
|
+
|
|
1007
|
+
NL Design System design tokens for <fullName>.
|
|
1008
|
+
|
|
1009
|
+
## Usage
|
|
1010
|
+
|
|
1011
|
+
Install the package:
|
|
1012
|
+
|
|
1013
|
+
\`\`\`sh
|
|
1014
|
+
npm install <packageName>
|
|
1015
|
+
\`\`\`
|
|
1016
|
+
|
|
1017
|
+
Apply the theme class to your root element:
|
|
1018
|
+
|
|
1019
|
+
\`\`\`html
|
|
1020
|
+
|
|
1021
|
+
<html class="<slug>-theme">
|
|
1022
|
+
\`\`\`
|
|
1023
|
+
|
|
1024
|
+
## Building
|
|
1025
|
+
|
|
1026
|
+
\`\`\`sh
|
|
1027
|
+
npm run build
|
|
1028
|
+
\`\`\`
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
---
|
|
1032
|
+
|
|
1033
|
+
## Part C — Update component tokens to match the website
|
|
1034
|
+
|
|
1035
|
+
This part applies in **both** `new` and `update` modes. In update mode, read the existing token files and update only the values that differ from the extraction.
|
|
1036
|
+
|
|
1037
|
+
**Rules:**
|
|
1038
|
+
|
|
1039
|
+
- **Only change values you have data for** — if the design summary says "not found — using conduction default", do NOT change that token.
|
|
1040
|
+
- **Always use palette references** (e.g. `{<slug>.color.red.44}`) rather than hardcoded hex values. If the color is not yet in the palette, add it to `color.tokens.json` first with the correct lightness key.
|
|
1041
|
+
- **Never empty a token that had a value** — only replace values with different values.
|
|
1042
|
+
|
|
1043
|
+
**Use this exact mapping from design summary → token files:**
|
|
1044
|
+
|
|
1045
|
+
### `heading.tokens.json`
|
|
1046
|
+
|
|
1047
|
+
| Token path | Set to |
|
|
1048
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
1049
|
+
| `heading-1.color` | Design summary: **Primary brand color** if headings are colored, or leave empty if black |
|
|
1050
|
+
| `heading-1.font-family` | `{<slug>.typography.<fontKey>.font-family}` using heading font |
|
|
1051
|
+
| `heading-1.font-weight` | `{<slug>.typography.font-weight.bold}` or `.semibold` or `.normal` — match **Heading font weight** |
|
|
1052
|
+
| `heading-2.color` | Same as h1, or different if h2 has a distinct color |
|
|
1053
|
+
| `heading-2.font-family` | Same as h1 |
|
|
1054
|
+
| `heading-3.color` | From design summary **H3** — often the primary brand color for links/headings |
|
|
1055
|
+
| `heading-3.font-family` | Same as h1 (or body font if h3 uses body font) |
|
|
1056
|
+
| All `heading-*.font-weight` | Match the extracted weight. Map: 300→light, 400→normal, 500/600→semibold, 700→bold |
|
|
1057
|
+
|
|
1058
|
+
### `font-size.tokens.json`
|
|
1059
|
+
|
|
1060
|
+
| Token key | Set to |
|
|
1061
|
+
| --------- | --------------------------------------------------------------------------- |
|
|
1062
|
+
| `md` | **Body font size** (px / 16 = rem) — this is the paragraph/h5 size |
|
|
1063
|
+
| `lg` | **H4 font size** in rem. If H4 not found, set to midpoint between md and xl |
|
|
1064
|
+
| `xl` | **H3 font size** in rem |
|
|
1065
|
+
| `2xl` | **H2 font size** in rem |
|
|
1066
|
+
| `3xl` | **H1 font size** in rem |
|
|
1067
|
+
| `4xl` | H1 x 1.25 (for oversized display headings if needed) |
|
|
1068
|
+
|
|
1069
|
+
Validate the scale is well-spaced: each step should be >= 20% larger than the previous. Adjust if needed.
|
|
1070
|
+
|
|
1071
|
+
### `paragraph.tokens.json`
|
|
1072
|
+
|
|
1073
|
+
| Token path | Set to |
|
|
1074
|
+
| ----------------------- | --------------------------------------------------------------------------------- |
|
|
1075
|
+
| `paragraph.font-weight` | `{<slug>.typography.font-weight.light}` or `.normal` — match **Body font weight** |
|
|
1076
|
+
|
|
1077
|
+
### `link.tokens.json`
|
|
1078
|
+
|
|
1079
|
+
| Token path | Set to |
|
|
1080
|
+
| ------------------ | ----------------------------------------------------------- |
|
|
1081
|
+
| `link.color` | `{<slug>.color.primary}` (which references the brand color) |
|
|
1082
|
+
| `link.hover.color` | `{<slug>.color.primary-hover}` |
|
|
1083
|
+
|
|
1084
|
+
### `page-header.tokens.json`
|
|
1085
|
+
|
|
1086
|
+
| Token path | Set to |
|
|
1087
|
+
| ------------------------------ | ------------------------------------------------------------------- |
|
|
1088
|
+
| `page-header.background-color` | From **Page header bg**. Use palette ref if a matching color exists |
|
|
1089
|
+
| `page-header.color` | From **Page header text** |
|
|
1090
|
+
|
|
1091
|
+
### `page-footer.tokens.json`
|
|
1092
|
+
|
|
1093
|
+
| Token path | Set to |
|
|
1094
|
+
| ------------------------------ | ------------------------- |
|
|
1095
|
+
| `page-footer.background-color` | From **Page footer bg** |
|
|
1096
|
+
| `page-footer.color` | From **Page footer text** |
|
|
1097
|
+
|
|
1098
|
+
### `button.tokens.json`
|
|
1099
|
+
|
|
1100
|
+
| Token path | Set to |
|
|
1101
|
+
| ------------------------------- | ------------------------------------------------------- |
|
|
1102
|
+
| `button.background-color` | From **Button bg** — typically `{<slug>.color.primary}` |
|
|
1103
|
+
| `button.border-color` | Same as background, or from extraction |
|
|
1104
|
+
| `button.border-radius` | From **Button border-radius** |
|
|
1105
|
+
| `button.color` | From **Button text** |
|
|
1106
|
+
| `button.hover.background-color` | `{<slug>.color.primary-hover}` |
|
|
1107
|
+
| `button.hover.border-color` | `{<slug>.color.primary-hover}` |
|
|
1108
|
+
|
|
1109
|
+
### `textbox.tokens.json`
|
|
1110
|
+
|
|
1111
|
+
| Token path | Set to |
|
|
1112
|
+
| ----------------------- | ---------------------------- |
|
|
1113
|
+
| `textbox.border-color` | From **Input border-color** |
|
|
1114
|
+
| `textbox.border-radius` | From **Input border-radius** |
|
|
1115
|
+
|
|
1116
|
+
### `document.tokens.json` and `surface.tokens.json`
|
|
1117
|
+
|
|
1118
|
+
| Token path | Set to |
|
|
1119
|
+
| --------------------------- | ---------------------------------------------------------------- |
|
|
1120
|
+
| `document.background-color` | `{<slug>.color.white.100}` or the page's background if not white |
|
|
1121
|
+
| `surface.background-color` | Same as document |
|
|
1122
|
+
|
|
1123
|
+
### `separator.tokens.json`
|
|
1124
|
+
|
|
1125
|
+
| Token path | Set to |
|
|
1126
|
+
| ----------------- | ------------------------ |
|
|
1127
|
+
| `separator.color` | From **Separator color** |
|
|
1128
|
+
|
|
1129
|
+
### `focus.tokens.json`
|
|
1130
|
+
|
|
1131
|
+
| Token path | Set to |
|
|
1132
|
+
| --------------------- | --------------------------------------------------------- |
|
|
1133
|
+
| `focus.outline-color` | `{<slug>.color.primary}` (common pattern) or keep default |
|
|
1134
|
+
|
|
1135
|
+
### `breadcrumb.tokens.json`
|
|
1136
|
+
|
|
1137
|
+
| Token path | Set to |
|
|
1138
|
+
| --------------------------------- | ------------------------------ |
|
|
1139
|
+
| `breadcrumb-nav.link.color` | `{<slug>.color.primary}` |
|
|
1140
|
+
| `breadcrumb-nav.link.hover.color` | `{<slug>.color.primary-hover}` |
|
|
1141
|
+
|
|
1142
|
+
**For all other component files** (alert, badge, calendar, checkbox, code, etc.) — leave the conduction defaults with only the prefix replaced. These components are rarely visible on the organisation's public website, so the defaults are acceptable.
|
|
1143
|
+
|
|
1144
|
+
---
|
|
1145
|
+
|
|
1146
|
+
## Part D — Build and test
|
|
1147
|
+
|
|
1148
|
+
### D1 — Build the token set
|
|
1149
|
+
|
|
1150
|
+
Run the build:
|
|
1151
|
+
|
|
1152
|
+
```sh
|
|
1153
|
+
cd municipalities/<slug>-design-tokens && npm run build
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
Capture all output. If the build fails:
|
|
1157
|
+
|
|
1158
|
+
- Parse every "Reference doesn't exist" error — note the exact token path that is missing
|
|
1159
|
+
- Check whether the missing token exists in `conduction-design-tokens/src/brand/conduction/color.tokens.json` or another conduction token file
|
|
1160
|
+
- If it does, add it to the corresponding token file for this organisation
|
|
1161
|
+
- Retry the build until it succeeds or you have identified all unresolvable errors
|
|
1162
|
+
|
|
1163
|
+
### D2 — Test against woo-website-template-apiv2
|
|
1164
|
+
|
|
1165
|
+
After a successful build, test the theme against the `woo-website-template-apiv2` repository:
|
|
1166
|
+
|
|
1167
|
+
1. Check if `../woo-website-template-apiv2` exists relative to the current repository root. If not, report that the repository was not found at the expected path and skip the remaining steps.
|
|
1168
|
+
2. If found, check out (or confirm) the `Theme-test-branch` branch in that repository.
|
|
1169
|
+
3. Install the new package into the template repo (or copy the built dist files from `municipalities/<slug>-design-tokens/dist/`).
|
|
1170
|
+
4. **Update `pwa/src/styling/index.css`**: Check this file to confirm the theme CSS is imported. If the new theme is not yet imported, add an import for the built CSS file.
|
|
1171
|
+
5. **Update `pwa/src/layout/head.tsx`**: Update the theme class applied to the root element to use `<slug>-theme` so the new theme is applied.
|
|
1172
|
+
6. Start the development server or run a build (`npm run build` or `npm run dev`) in the template repository.
|
|
1173
|
+
7. **Navigate to `/theme`** using Playwright MCP to see all available components rendered with the new theme.
|
|
1174
|
+
8. Take screenshots of the `/theme` page and each visible component section.
|
|
1175
|
+
9. Compare the rendered components against the component token files you created. Note any visual discrepancies.
|
|
1176
|
+
|
|
1177
|
+
### D3 — Error report
|
|
1178
|
+
|
|
1179
|
+
Produce a **detailed error report** with these sections:
|
|
1180
|
+
|
|
1181
|
+
#### Build errors
|
|
1182
|
+
|
|
1183
|
+
- List every build error with the exact error message and file
|
|
1184
|
+
- For "Reference doesn't exist" errors: state the missing token path and whether it was fixable
|
|
1185
|
+
- State whether the build ultimately succeeded or failed
|
|
1186
|
+
|
|
1187
|
+
#### Theme integration errors (woo-website-template-apiv2)
|
|
1188
|
+
|
|
1189
|
+
- State whether the repository was found and the branch was checked out successfully
|
|
1190
|
+
- List any errors encountered when updating config files
|
|
1191
|
+
- List any build or server errors
|
|
1192
|
+
|
|
1193
|
+
#### Visual component review (from /theme page)
|
|
1194
|
+
|
|
1195
|
+
- For each component visible at `/theme` that is part of the created token set: state whether it looks correct or note the specific visual issue
|
|
1196
|
+
|
|
1197
|
+
#### Summary of issues requiring manual attention
|
|
1198
|
+
|
|
1199
|
+
- List any colors or fonts that could not be determined and were left as placeholders
|
|
1200
|
+
- List any build, integration, or visual issues that could not be auto-resolved
|
|
1201
|
+
|
|
1202
|
+
---
|
|
1203
|
+
|
|
1204
|
+
## Part E — Summary
|
|
1205
|
+
|
|
1206
|
+
After creating/updating all files and completing the build and test, output a summary:
|
|
1207
|
+
|
|
1208
|
+
1. List all files created or modified
|
|
1209
|
+
2. Note which colors were extracted from the website (or indicate placeholders were used)
|
|
1210
|
+
3. Note which font was found (or indicate placeholder was used)
|
|
1211
|
+
4. State the build result (success / failed with N errors)
|
|
1212
|
+
5. State the test result against `woo-website-template-apiv2` (success / failed / repo not found)
|
|
1213
|
+
6. Remind the user to:
|
|
1214
|
+
- Verify and refine colors in `src/brand/<slug>/color.tokens.json`
|
|
1215
|
+
- Add `@font-face` rules in `src/font.scss` if not auto-generated
|
|
1216
|
+
- Add a build script entry in the root `package.json`: `"build:<slug>": "cd ./municipalities/<slug>-design-tokens && npm run build"`
|