@halftone-ds/tokens 1.3.0 → 1.3.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/lib/theme-cli.mjs +25 -7
- package/package.json +1 -1
- package/targets/ts-theme.mjs +5 -2
package/lib/theme-cli.mjs
CHANGED
|
@@ -126,17 +126,20 @@ function readRecipe(arg, root) {
|
|
|
126
126
|
fail(`"${arg}" is not a theme code, a preset (${PRESETS.map((p) => p.id).join(', ')}), a recipe file or a shadcn preset code`, 2);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
/** A recipe in a few lines. `families`: a system's own fonts (any Google Fonts family), which win over the recipe's. */
|
|
130
|
+
const describe = (input, families = {}) => {
|
|
130
131
|
// A recipe recorded by an older halftone-tokens has no typeset or styles: it gets the defaults.
|
|
131
132
|
const recipe = normalizeRecipe(input);
|
|
132
|
-
const font = (
|
|
133
|
+
const font = (role) => {
|
|
134
|
+
if (families[role]) return families[role];
|
|
135
|
+
const id = recipe.font[role];
|
|
133
136
|
const f = fontById(id);
|
|
134
137
|
return f?.system ? (f.category === 'mono' ? 'system mono' : 'system') : (f?.family ?? id);
|
|
135
138
|
};
|
|
136
139
|
return [
|
|
137
140
|
`brand ${recipe.brand} · neutral ${recipe.neutral} · radius ${recipe.radius} · controls ${recipe.controls}`,
|
|
138
|
-
`type ${font(
|
|
139
|
-
`typeset ${recipe.size}px · leading ${recipe.leading} · flow ${recipe.flow} · measure ${recipe.measure}ch · mono ${font(
|
|
141
|
+
`type ${font('heading')} / ${font('body')} · stroke ${recipe.stroke} · depth ${recipe.depth} · density ${recipe.density} · border ${recipe.border}`,
|
|
142
|
+
`typeset ${recipe.size}px · leading ${recipe.leading} · flow ${recipe.flow} · measure ${recipe.measure}ch · mono ${font('mono')}`,
|
|
140
143
|
`styles: ${recipe.button} buttons · ${recipe.field} fields`,
|
|
141
144
|
].join('\n ');
|
|
142
145
|
};
|
|
@@ -167,6 +170,18 @@ function installFonts(root, config, theme, flags) {
|
|
|
167
170
|
if (unused.length) console.log(`\nNo longer used by the theme (remove when you like): npm uninstall ${unused.join(' ')}`);
|
|
168
171
|
}
|
|
169
172
|
|
|
173
|
+
/**
|
|
174
|
+
* A kit's registry:build writes which components draw which token (registry/generated/token-usage.json and the docs
|
|
175
|
+
* from it), and that follows the colour roles a system maps, so applying one rebuilds it: its --check stays green.
|
|
176
|
+
*/
|
|
177
|
+
function rebuildRegistry(root) {
|
|
178
|
+
const file = join(root, 'package.json');
|
|
179
|
+
if (!existsSync(file) || !JSON.parse(readFileSync(file, 'utf8')).scripts?.['registry:build']) return;
|
|
180
|
+
console.log('\nregistry:build (token usage follows the colour roles)…');
|
|
181
|
+
const r = spawnSync('npm', ['run', '--silent', 'registry:build'], { cwd: root, stdio: 'inherit' });
|
|
182
|
+
if (r.status !== 0) console.log('registry:build failed: run `npm run registry:build` yourself.');
|
|
183
|
+
}
|
|
184
|
+
|
|
170
185
|
export async function themeCommand({ positional, flags, root, sourcePath, config, build }) {
|
|
171
186
|
const [sub = 'show', arg] = positional;
|
|
172
187
|
const tokens = () => JSON.parse(readFileSync(sourcePath, 'utf8'));
|
|
@@ -253,7 +268,7 @@ export async function themeCommand({ positional, flags, root, sourcePath, config
|
|
|
253
268
|
if (status.system) {
|
|
254
269
|
const { system } = status;
|
|
255
270
|
const overrides = describeOverrides(system);
|
|
256
|
-
console.log(`System "${system.name}"${status.code ? ` ${status.code}` : ' (no theme code: it has overrides)'}\n ${describe(status.recipe)}`);
|
|
271
|
+
console.log(`System "${system.name}"${status.code ? ` ${status.code}` : ' (no theme code: it has overrides)'}\n ${describe(status.recipe, system.fonts)}`);
|
|
257
272
|
if (overrides.length) console.log(`\nOverrides:\n${overrides.map((o) => ` ${o}`).join('\n')}`);
|
|
258
273
|
printWarnings(tokens().$extensions?.[THEME_EXTENSION]?.warnings);
|
|
259
274
|
} else {
|
|
@@ -282,7 +297,7 @@ export async function themeCommand({ positional, flags, root, sourcePath, config
|
|
|
282
297
|
if (system) {
|
|
283
298
|
const code = systemCode(system);
|
|
284
299
|
const overrides = describeOverrides(system);
|
|
285
|
-
console.log(`System "${system.name}"${code ? ` ${code}` : ''}\n ${describe(theme.recipe)}`);
|
|
300
|
+
console.log(`System "${system.name}"${code ? ` ${code}` : ''}\n ${describe(theme.recipe, system.fonts)}`);
|
|
286
301
|
if (overrides.length) console.log(`\nOverrides (no theme code carries these; tokens.json records the document):\n${overrides.map((o) => ` ${o}`).join('\n')}`);
|
|
287
302
|
} else console.log(`Theme ${theme.code}${recipe.preset ? ` (${presetById(recipe.preset)?.name})` : ''}\n ${describe(theme.recipe)}`);
|
|
288
303
|
if (theme.adjustments.length) {
|
|
@@ -292,7 +307,9 @@ export async function themeCommand({ positional, flags, root, sourcePath, config
|
|
|
292
307
|
printWarnings(theme.warnings);
|
|
293
308
|
const cvd = cvdWarnings(theme.resolved);
|
|
294
309
|
if (cvd.length) {
|
|
295
|
-
|
|
310
|
+
// Counted as the Studio's Accessibility tab counts them: pairs of tones, each listed in every view it's close in.
|
|
311
|
+
const pairs = new Set(cvd.map((w) => `${w.a}/${w.b}`)).size;
|
|
312
|
+
console.log(`\nColour vision: ${pairs} pair(s) of tones close together, in ${cvd.length} view(s) below ΔE ${MIN_DELTA_E} (a warning; give them an icon or a label as well):`);
|
|
296
313
|
console.log(describeCvdWarnings(cvd).join('\n'));
|
|
297
314
|
}
|
|
298
315
|
if (flags['dry-run']) {
|
|
@@ -303,6 +320,7 @@ export async function themeCommand({ positional, flags, root, sourcePath, config
|
|
|
303
320
|
console.log(`\nWrote ${sourcePath.slice(root.length + 1)}.`);
|
|
304
321
|
installFonts(root, config, theme, flags);
|
|
305
322
|
await build();
|
|
323
|
+
rebuildRegistry(root);
|
|
306
324
|
return;
|
|
307
325
|
}
|
|
308
326
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@halftone-ds/tokens",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Design tokens (W3C DTCG) to NativeWind, Tailwind 4 / shadcn, TypeScript, DESIGN.md and Figma, with a WCAG AA contrast gate. The token build behind Halftone.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/targets/ts-theme.mjs
CHANGED
|
@@ -12,7 +12,8 @@ function themeInfo(source) {
|
|
|
12
12
|
const status = themeStatus(source);
|
|
13
13
|
if (!status.applied) return 'null';
|
|
14
14
|
const preset = presetById(status.recipe.preset ?? '');
|
|
15
|
-
|
|
15
|
+
// A system is named after itself; its code is null when it has overrides a code can't carry.
|
|
16
|
+
const name = status.system?.name ?? (preset && sameRecipe(preset.recipe, status.recipe) ? preset.name : 'Custom');
|
|
16
17
|
return JSON.stringify({ code: status.code, name, edited: status.drift.length > 0, recipe: status.recipe }, null, 2);
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -137,7 +138,9 @@ export const COMPONENT_VARS = ${JSON.stringify(componentVarNames())} as const;
|
|
|
137
138
|
export const THEME_INFO: ThemeInfo | null = ${themeInfo(ctx.source)};
|
|
138
139
|
|
|
139
140
|
export type ThemeInfo = {
|
|
140
|
-
code
|
|
141
|
+
/** null for a system (\`.halftone.json\`) with overrides no theme code carries. */
|
|
142
|
+
code: string | null;
|
|
143
|
+
/** The preset's name, the system's name, or 'Custom'. */
|
|
141
144
|
name: string;
|
|
142
145
|
edited: boolean;
|
|
143
146
|
recipe: {
|