@massimo-cassandro/minimo 1.34.0 → 1.35.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/dev-tools/design-tokens-builder/README.md +6 -0
- package/dev-tools/design-tokens-builder/build-tokens-src/build-source-modes.mjs +13 -0
- package/dev-tools/design-tokens-builder/build-tokens-src/formats/css.mjs +13 -1
- package/dev-tools/design-tokens-builder/build-tokens-src/formats/json.mjs +5 -1
- package/dev-tools/design-tokens-builder/build-tokens-src/legacy-tokens-parser.mjs +105 -0
- package/dev-tools/design-tokens-builder/build-tokens.mjs +13 -1
- package/dev-tools/design-tokens-builder/check-unresolved-custom-props.mjs +12 -4
- package/dev-tools/design-tokens-builder/tokens-config-sample.mjs +23 -2
- package/dev-tools/starter-kit/source_files/root/__project__.code-workspace +4 -0
- package/package.json +1 -1
|
@@ -66,6 +66,10 @@ The `source` array accepts any combination of:
|
|
|
66
66
|
- `.json` / `.jsonc` — parsed directly as token data
|
|
67
67
|
- `.mjs` / `.js` — imported as ES modules; must export a W3C DTCG-compliant object as `default`
|
|
68
68
|
|
|
69
|
+
**Legacy token syntax (auto-detected, `.json` only):** `.json` source files written in the older, non-DTCG Style Dictionary syntax (`value`/`type` instead of `$value`/`$type`, references as `{group.token.value}` instead of `{group.token}` — e.g. a file exported from [Open Props](https://open-props.style/)) are converted to DTCG v5 syntax automatically at parse time, node by node. Files already using `$value`/`$type` are left untouched, so no config flag or file list is needed — mixing legacy and DTCG sources in the same `source` array just works. `.jsonc` is not covered (reserved for hand-authored DTCG sources, parsed by Style Dictionary's own loader) and these `.json` files are parsed with plain `JSON.parse` (no comments, no trailing commas). See `build-tokens-src/legacy-tokens-parser.mjs`.
|
|
70
|
+
|
|
71
|
+
Known limitation: a legacy node that is at the same time a token (own `value`/`type`) *and* a group with further nested children (e.g. Open Props' `other.ease.out`, which has its own value plus `out.1` ... `out.5`) can't be represented in DTCG v5 — a node with `$value` is always a leaf, Style Dictionary doesn't descend into its children. Only the node's own value is converted; the nested children are silently lost.
|
|
72
|
+
|
|
69
73
|
|
|
70
74
|
#### Token file format (JS example)
|
|
71
75
|
|
|
@@ -157,6 +161,8 @@ When set, each mode has its own `source` array, and the generated CSS composes a
|
|
|
157
161
|
- the base mode's props go in a top-level `:root { ... }` block, with `color-scheme: <all modes>;` prepended
|
|
158
162
|
- every other mode's props go in `@media (prefers-color-scheme: <mode>) { :root { ... } }`, with `color-scheme: <mode>;` prepended
|
|
159
163
|
|
|
164
|
+
Tokens of a non-base mode can reference tokens defined in the base mode (e.g. a dark token using `{color.1}` defined in light, output as `var(--color-1)`). References to a non-base mode's tokens (from the base mode or from another non-base mode) fail with a "reference not defined" error, since the target would be undefined outside its own `@media` block.
|
|
165
|
+
|
|
160
166
|
`customPropsGroups`, `mergeCustomProps`, `addLayer`, `pxToRem` etc. all apply the same way as with a single `source`. The JSON output (when `jsonBuildPath` is set) is produced once per mode, with the mode name appended to each filename (e.g. `tokens-light.jsonc` / `tokens-dark.jsonc`).
|
|
161
167
|
|
|
162
168
|
---
|
|
@@ -29,6 +29,7 @@ import { CSS_TRANSFORMS, JSON_TRANSFORMS } from './platforms.mjs';
|
|
|
29
29
|
import { customPropsCount } from './formats/css.mjs';
|
|
30
30
|
import { buildJsonFiles, collectConcreteFilePaths } from './formats/json.mjs';
|
|
31
31
|
import { loadExistingCustomPropsScoped } from './merge-css.mjs';
|
|
32
|
+
import { LEGACY_TOKENS_PARSER_NAME } from './legacy-tokens-parser.mjs';
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* @param {object} opts
|
|
@@ -79,8 +80,18 @@ export const buildSourceModes = async ({
|
|
|
79
80
|
const jsonFilesByMode = {};
|
|
80
81
|
|
|
81
82
|
for (const mode of modeNames) {
|
|
83
|
+
// Non-base modes may reference base-mode tokens (always defined, since
|
|
84
|
+
// the base block is the unconditional top-level `:root`). The base
|
|
85
|
+
// sources are passed as `include`: available for reference resolution
|
|
86
|
+
// but flagged isSource: false, and skipped by the css/json formats.
|
|
87
|
+
// Base-mode-only on purpose: a reference to a token that exists only in
|
|
88
|
+
// another `@media` block would build fine but be undefined at runtime.
|
|
89
|
+
const include = mode === baseMode ? [] : sourceModes[baseMode];
|
|
90
|
+
|
|
82
91
|
const sd = new StyleDictionary({
|
|
92
|
+
include,
|
|
83
93
|
source: sourceModes[mode],
|
|
94
|
+
parsers: [LEGACY_TOKENS_PARSER_NAME],
|
|
84
95
|
log: { verbosity: 'verbose' },
|
|
85
96
|
platforms: {
|
|
86
97
|
css: {
|
|
@@ -114,7 +125,9 @@ export const buildSourceModes = async ({
|
|
|
114
125
|
const files = buildJsonFiles(concreteFilePaths, jsonDestFile, jsonFormat, jsonExpression, `-${mode}`);
|
|
115
126
|
|
|
116
127
|
const jsonSd = new StyleDictionary({
|
|
128
|
+
include,
|
|
117
129
|
source: sourceModes[mode],
|
|
130
|
+
parsers: [LEGACY_TOKENS_PARSER_NAME],
|
|
118
131
|
log: { verbosity: 'silent' },
|
|
119
132
|
platforms: {
|
|
120
133
|
json: {
|
|
@@ -189,8 +189,11 @@ StyleDictionary.registerFormat({
|
|
|
189
189
|
return a.name.localeCompare(b.name, 'en', { numeric: true, sensitivity: 'base' });
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
+
// Tokens loaded via `include` (sourceModes: base-mode tokens made
|
|
193
|
+
// available to other modes) stay in tokenByPath, so references to them
|
|
194
|
+
// resolve to var(--name), but are not declared in this mode's block.
|
|
192
195
|
const lines = dictionary.allTokens
|
|
193
|
-
.
|
|
196
|
+
.filter((token) => token.isSource !== false)
|
|
194
197
|
// .sort((a, b) => a.name.localeCompare(b.name, 'en', {numeric: true, sensitivity: 'base'}))
|
|
195
198
|
.sort((a, b) => sortFunction(a,b))
|
|
196
199
|
.flatMap((token) => {
|
|
@@ -247,6 +250,15 @@ StyleDictionary.registerFormat({
|
|
|
247
250
|
value = String(token.$value ?? token.value);
|
|
248
251
|
}
|
|
249
252
|
|
|
253
|
+
// Multi-line values (e.g. Open Props' linear() easings) are collapsed
|
|
254
|
+
// to a single line: declarations are parsed one line at a time (see
|
|
255
|
+
// parseCustomProps in ../merge-css.mjs), so a multi-line value would
|
|
256
|
+
// be truncated to its first line.
|
|
257
|
+
value = String(value)
|
|
258
|
+
.replace(/\s*\n\s*/g, ' ')
|
|
259
|
+
.replace(/\(\s+/g, '(')
|
|
260
|
+
.replace(/\s+\)/g, ')');
|
|
261
|
+
|
|
250
262
|
return [` --${token.name}: ${value};${commentSuffix}`];
|
|
251
263
|
});
|
|
252
264
|
|
|
@@ -136,6 +136,10 @@ StyleDictionary.registerFormat({
|
|
|
136
136
|
const tokenMap = (expressionMode === 'resolve') ? makeTokenMap(dictionary) : null;
|
|
137
137
|
|
|
138
138
|
for (const token of dictionary.allTokens) {
|
|
139
|
+
// Tokens loaded via `include` (sourceModes: base-mode tokens made
|
|
140
|
+
// available to other modes for reference resolution) are not output.
|
|
141
|
+
if (token.isSource === false) continue;
|
|
142
|
+
|
|
139
143
|
// Rebuild the nested tree from the token path
|
|
140
144
|
let node = root;
|
|
141
145
|
for (let i = 0; i < token.path.length - 1; i++) {
|
|
@@ -205,7 +209,7 @@ export const collectConcreteFilePaths = async (sd) => {
|
|
|
205
209
|
await sd.hasInitialized;
|
|
206
210
|
const paths = new Set();
|
|
207
211
|
for (const token of sd.allTokens) {
|
|
208
|
-
if (token.filePath) paths.add(token.filePath);
|
|
212
|
+
if (token.filePath && token.isSource !== false) paths.add(token.filePath);
|
|
209
213
|
}
|
|
210
214
|
return [...paths].sort();
|
|
211
215
|
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// build-tokens-src/legacy-tokens-parser.mjs
|
|
2
|
+
// Registers a Style Dictionary parser that bridges "legacy" token files
|
|
3
|
+
// (Style Dictionary v3/v4-style syntax: no `$` prefix on `value`/`type`,
|
|
4
|
+
// references written as `{group.token.value}`) into the W3C DTCG syntax
|
|
5
|
+
// (`$value`/`$type`, references as `{group.token}`) required by Style
|
|
6
|
+
// Dictionary v5.
|
|
7
|
+
//
|
|
8
|
+
// Why this is needed: Style Dictionary v5 decides whether a build "usesDtcg"
|
|
9
|
+
// ONCE per build, from whichever source file is processed first (see
|
|
10
|
+
// detectDtcgSyntax() called inside combineJSON.js in style-dictionary) — it
|
|
11
|
+
// is a single global flag, not a per-file check. Mixing a legacy-syntax file
|
|
12
|
+
// (e.g. a file exported from Open Props, or from an old Style Dictionary
|
|
13
|
+
// v3/v4 project) together with DTCG sources therefore causes the legacy
|
|
14
|
+
// file's tokens to be silently dropped regardless of file order: they never
|
|
15
|
+
// carry the `$value` key combineJSON's own tagging (and the rest of the
|
|
16
|
+
// pipeline) expects.
|
|
17
|
+
//
|
|
18
|
+
// This parser intercepts every `.json` source file (not `.jsonc` — that
|
|
19
|
+
// extension is reserved in this project for hand-authored DTCG sources,
|
|
20
|
+
// already handled correctly by Style Dictionary's own built-in loader) and
|
|
21
|
+
// converts, node by node:
|
|
22
|
+
// value -> $value, type -> $type, comment/description -> $description
|
|
23
|
+
// "{group.token.value}" -> "{group.token}" inside reference strings
|
|
24
|
+
// Nodes that already use `$value` (DTCG v5 syntax) are left untouched, so
|
|
25
|
+
// the parser is a safe no-op for files that don't need conversion — no
|
|
26
|
+
// config flag or file list is required, detection is fully automatic.
|
|
27
|
+
//
|
|
28
|
+
// Limitation: `.json` files handled by this parser are parsed with plain
|
|
29
|
+
// JSON.parse (no comments, no trailing commas), unlike Style Dictionary's
|
|
30
|
+
// own built-in loader (which uses JSON5 for .json/.jsonc/.json5). This keeps
|
|
31
|
+
// the builder dependency-free — legacy token exports (Open Props, old Style
|
|
32
|
+
// Dictionary v3/v4 projects) are virtually always strict JSON anyway. Use
|
|
33
|
+
// `.jsonc` or `.mjs` for hand-authored DTCG sources that need comments.
|
|
34
|
+
|
|
35
|
+
import StyleDictionary from 'style-dictionary';
|
|
36
|
+
|
|
37
|
+
export const LEGACY_TOKENS_PARSER_NAME = 'minimo/legacy-tokens';
|
|
38
|
+
|
|
39
|
+
// Matches "{some.token.path.value}" -> captures "some.token.path"
|
|
40
|
+
const LEGACY_REF = /\{([^}]+)\.value\}/g;
|
|
41
|
+
|
|
42
|
+
/** @param {unknown} value @returns {unknown} */
|
|
43
|
+
const convertLegacyRefs = (value) => {
|
|
44
|
+
if (typeof value === 'string') {
|
|
45
|
+
return value.replace(LEGACY_REF, '{$1}');
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
return value.map(convertLegacyRefs);
|
|
49
|
+
}
|
|
50
|
+
if (value !== null && typeof value === 'object') {
|
|
51
|
+
return Object.fromEntries(
|
|
52
|
+
Object.entries(value).map(([key, v]) => [key, convertLegacyRefs(v)])
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** @param {unknown} node @returns {node is Record<string, unknown>} */
|
|
59
|
+
const isPlainObject = (node) => node !== null && typeof node === 'object' && !Array.isArray(node);
|
|
60
|
+
|
|
61
|
+
/** @param {unknown} node @returns {unknown} */
|
|
62
|
+
const convertLegacyNode = (node) => {
|
|
63
|
+
if (!isPlainObject(node)) return node;
|
|
64
|
+
|
|
65
|
+
// Already DTCG v5 syntax: leave untouched.
|
|
66
|
+
if (Object.hasOwn(node, '$value')) return node;
|
|
67
|
+
|
|
68
|
+
if (Object.hasOwn(node, 'value')) {
|
|
69
|
+
// Legacy token leaf. Some legacy files (e.g. Open Props) also attach
|
|
70
|
+
// sibling numbered/named children to a node that carries its own
|
|
71
|
+
// `value`/`type` (a group that is also a default token) — those
|
|
72
|
+
// children are converted too, even though Style Dictionary itself
|
|
73
|
+
// cannot represent "group + own $value" ambiguity natively.
|
|
74
|
+
const { value, type, comment, description, ...rest } = node;
|
|
75
|
+
const convertedRest = Object.fromEntries(
|
|
76
|
+
Object.entries(rest).map(([key, child]) => [key, convertLegacyNode(child)])
|
|
77
|
+
);
|
|
78
|
+
const desc = comment ?? description;
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
...convertedRest,
|
|
82
|
+
$value: convertLegacyRefs(value),
|
|
83
|
+
...(type !== undefined ? { $type: type } : {}),
|
|
84
|
+
...(desc !== undefined ? { $description: desc } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Group node: recurse into children.
|
|
89
|
+
return Object.fromEntries(
|
|
90
|
+
Object.entries(node).map(([key, child]) => [key, convertLegacyNode(child)])
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Registers the parser globally on the StyleDictionary class. To actually
|
|
95
|
+
// run it, a Style Dictionary instance must also opt in via
|
|
96
|
+
// `parsers: [LEGACY_TOKENS_PARSER_NAME]` in its config (registering alone
|
|
97
|
+
// does not activate it for every instance — see Style Dictionary's parser
|
|
98
|
+
// hooks docs).
|
|
99
|
+
export const registerLegacyTokensParser = () => {
|
|
100
|
+
StyleDictionary.registerParser({
|
|
101
|
+
name: LEGACY_TOKENS_PARSER_NAME,
|
|
102
|
+
pattern: /\.json$/,
|
|
103
|
+
parser: ({ contents }) => convertLegacyNode(JSON.parse(contents)),
|
|
104
|
+
});
|
|
105
|
+
};
|
|
@@ -44,6 +44,12 @@ import {
|
|
|
44
44
|
// ── 2. Custom transforms ─────────────────────────────────────────────────────
|
|
45
45
|
import './build-tokens-src/transforms.mjs';
|
|
46
46
|
|
|
47
|
+
// ── 2b. Legacy token syntax bridge ───────────────────────────────────────────
|
|
48
|
+
// Converts legacy (non-DTCG, e.g. Open Props) .json/.jsonc source files to
|
|
49
|
+
// DTCG v5 syntax at parse time — see build-tokens-src/legacy-tokens-parser.mjs.
|
|
50
|
+
import { LEGACY_TOKENS_PARSER_NAME, registerLegacyTokensParser } from './build-tokens-src/legacy-tokens-parser.mjs';
|
|
51
|
+
registerLegacyTokensParser();
|
|
52
|
+
|
|
47
53
|
// ── 3. CSS format ─────────────────────────────────────────────────────────────
|
|
48
54
|
// customPropsCount is updated by the format at render time
|
|
49
55
|
// loadExistingCustomProps supports the mergeCustomProps option (see below)
|
|
@@ -127,12 +133,18 @@ if (sourceModes) {
|
|
|
127
133
|
const singleSource = /** @type {string[]} */ (source);
|
|
128
134
|
|
|
129
135
|
if (jsonBuildPath && !jsonDestFile) {
|
|
130
|
-
const sdInit = new StyleDictionary({
|
|
136
|
+
const sdInit = new StyleDictionary({
|
|
137
|
+
source: singleSource,
|
|
138
|
+
parsers: [LEGACY_TOKENS_PARSER_NAME],
|
|
139
|
+
log: { verbosity: 'silent' },
|
|
140
|
+
platforms: {},
|
|
141
|
+
});
|
|
131
142
|
concreteFilePaths = await collectConcreteFilePaths(sdInit);
|
|
132
143
|
}
|
|
133
144
|
|
|
134
145
|
const sd = new StyleDictionary({
|
|
135
146
|
source: singleSource,
|
|
147
|
+
parsers: [LEGACY_TOKENS_PARSER_NAME],
|
|
136
148
|
log: { verbosity: 'verbose' },
|
|
137
149
|
platforms: buildPlatforms({
|
|
138
150
|
buildPath,
|
|
@@ -26,6 +26,9 @@ import './build-tokens-src/transforms.mjs';
|
|
|
26
26
|
import { CSS_TRANSFORMS } from './build-tokens-src/platforms.mjs';
|
|
27
27
|
import { resolveSourcePaths } from './build-tokens-src/resolve-source-paths.mjs';
|
|
28
28
|
import { findMediaModeBlocks } from './build-tokens-src/merge-css.mjs';
|
|
29
|
+
import { LEGACY_TOKENS_PARSER_NAME, registerLegacyTokensParser } from './build-tokens-src/legacy-tokens-parser.mjs';
|
|
30
|
+
|
|
31
|
+
registerLegacyTokensParser();
|
|
29
32
|
|
|
30
33
|
// const __filename = fileURLToPath(import.meta.url);
|
|
31
34
|
// const __dirname = dirname(__filename);
|
|
@@ -199,21 +202,25 @@ async function run() {
|
|
|
199
202
|
/** @type {Map<string, Map<string, string>>} */
|
|
200
203
|
const sourceFileByPropByMode = new Map();
|
|
201
204
|
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
// includePatterns: base-mode sources, passed as `include` for non-base
|
|
206
|
+
// modes so cross-mode references resolve (same as build-source-modes.mjs).
|
|
207
|
+
/** @param {string[]} sourcePatterns @param {string[]} [includePatterns] @returns {Promise<Map<string,string>>} */
|
|
208
|
+
const resolveSourceFileMap = async (sourcePatterns, includePatterns = []) => {
|
|
204
209
|
/** @type {Map<string, string>} */
|
|
205
210
|
const map = new Map();
|
|
206
211
|
const resolvedPaths = resolveSourcePaths(sourcePatterns, configDir);
|
|
207
212
|
if (!resolvedPaths.length) return map;
|
|
208
213
|
try {
|
|
209
214
|
const sd = new StyleDictionary({
|
|
215
|
+
include: resolveSourcePaths(includePatterns, configDir),
|
|
210
216
|
source: resolvedPaths,
|
|
217
|
+
parsers: [LEGACY_TOKENS_PARSER_NAME],
|
|
211
218
|
log: { verbosity: 'silent' },
|
|
212
219
|
platforms: { css: { transforms: CSS_TRANSFORMS } },
|
|
213
220
|
});
|
|
214
221
|
const dictionary = await sd.getPlatformTokens('css');
|
|
215
222
|
for (const token of dictionary.allTokens) {
|
|
216
|
-
if (token.filePath) map.set(`--${token.name}`, token.filePath);
|
|
223
|
+
if (token.filePath && token.isSource !== false) map.set(`--${token.name}`, token.filePath);
|
|
217
224
|
}
|
|
218
225
|
} catch {
|
|
219
226
|
// Non-fatal: unused properties will be reported by name only.
|
|
@@ -223,7 +230,8 @@ async function run() {
|
|
|
223
230
|
|
|
224
231
|
if (checkUnused && config.sourceModes) {
|
|
225
232
|
for (const [mode, modeSource] of Object.entries(config.sourceModes)) {
|
|
226
|
-
|
|
233
|
+
const includePatterns = mode === sourceModesBase ? [] : config.sourceModes[sourceModesBase];
|
|
234
|
+
sourceFileByPropByMode.set(mode, await resolveSourceFileMap(modeSource, includePatterns));
|
|
227
235
|
}
|
|
228
236
|
} else if (checkUnused && Array.isArray(config.source) && config.source.length) {
|
|
229
237
|
const map = await resolveSourceFileMap(config.source);
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
// minimo package path in node_modules
|
|
16
|
-
const
|
|
16
|
+
const node_modules_path = '../../node_modules',
|
|
17
|
+
minimo_path = `${node_modules_path}/@massimo-cassandro/minimo`;
|
|
17
18
|
|
|
18
19
|
const config = {
|
|
19
20
|
|
|
@@ -33,6 +34,19 @@ const config = {
|
|
|
33
34
|
// Source token files — accept paths, globs, or a mix of both.
|
|
34
35
|
// Supported formats: .json, .jsonc, .mjs (must export a DTCG-compliant default object)
|
|
35
36
|
//
|
|
37
|
+
// Legacy (non-DTCG) .json files — e.g. exported from Open Props — are
|
|
38
|
+
// auto-detected and converted to DTCG v5 syntax at parse time, so they can
|
|
39
|
+
// be freely mixed with regular DTCG sources (see README.md). Not covered:
|
|
40
|
+
// .jsonc, reserved for hand-authored DTCG sources. These .json files are
|
|
41
|
+
// parsed with plain JSON.parse (no comments, no trailing commas).
|
|
42
|
+
//
|
|
43
|
+
// LIMITATION: a legacy node that is at the same time a token (own
|
|
44
|
+
// value/type) AND a group with further nested children (e.g. Open Props'
|
|
45
|
+
// other.ease.out, which has its own value plus out.1 ... out.5) cannot be
|
|
46
|
+
// represented in DTCG v5 — a node with $value is always a leaf, Style
|
|
47
|
+
// Dictionary does not descend into its children. In that case only the
|
|
48
|
+
// node's own value is converted; the nested children are silently lost.
|
|
49
|
+
//
|
|
36
50
|
// WARNING: do not use path.join() to build glob patterns — it may corrupt
|
|
37
51
|
// the pattern syntax. Use template literals instead:
|
|
38
52
|
// OK: `${minimo_path}/**/*.{json,mjs}`
|
|
@@ -42,7 +56,7 @@ const config = {
|
|
|
42
56
|
`${minimo_path}/src/**/*.tokens.{mjs,jsonc}`, // components tokens
|
|
43
57
|
|
|
44
58
|
// optional extra token source (in this example, openProps, https://open-props.style/)
|
|
45
|
-
//
|
|
59
|
+
// `${node_modules_path}/open-props/open-props.style-dictionary-tokens.json`,
|
|
46
60
|
|
|
47
61
|
// your project tokens
|
|
48
62
|
'./project-tokens/*.{js,mjs,jsonc,json}',
|
|
@@ -63,6 +77,13 @@ const config = {
|
|
|
63
77
|
// customPropsGroups, mergeCustomProps, addLayer, pxToRem etc. all apply
|
|
64
78
|
// the same way as with a single `source`.
|
|
65
79
|
//
|
|
80
|
+
// Cross-mode references: tokens of a non-base mode can reference tokens
|
|
81
|
+
// defined in the base mode (e.g. a dark token using {color.1} defined in
|
|
82
|
+
// light); it resolves to var(--color-1). The reverse (or between two
|
|
83
|
+
// non-base modes) is not supported and fails with a "reference not
|
|
84
|
+
// defined" error, since the target would be undefined outside its own
|
|
85
|
+
// @media block.
|
|
86
|
+
//
|
|
66
87
|
// The JSON output (see JSON TOKENS OUTPUT below) is produced once per
|
|
67
88
|
// mode, with the mode name appended to each filename, e.g. with
|
|
68
89
|
// jsonDestFile: 'tokens' -> "tokens-light.jsonc", "tokens-dark.jsonc".
|