style-dictionary 5.1.4 → 5.3.0
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/examples/advanced/assets-base64-embed/package.json +1 -1
- package/examples/advanced/create-react-app/package.json +1 -1
- package/examples/advanced/create-react-native-app/package.json +1 -1
- package/examples/advanced/custom-parser/package.json +1 -1
- package/examples/advanced/custom-transforms/package.json +1 -1
- package/examples/advanced/font-face-rules/package.json +1 -1
- package/examples/advanced/format-helpers/package.json +2 -1
- package/examples/advanced/matching-build-files/README.md +24 -22
- package/examples/advanced/matching-build-files/package.json +1 -1
- package/examples/advanced/matching-build-files/tokens/index.js +8 -4
- package/examples/advanced/multi-brand-multi-platform/package.json +1 -1
- package/examples/advanced/node-modules-as-config-and-properties/package.json +1 -1
- package/examples/advanced/npm-module/package.json +1 -1
- package/examples/advanced/referencing_aliasing/package.json +1 -1
- package/examples/advanced/s3/package.json +1 -1
- package/examples/advanced/tailwind-preset/package.json +1 -1
- package/examples/advanced/tokens-deprecation/package.json +1 -1
- package/examples/advanced/transitive-transforms/package.json +1 -1
- package/examples/advanced/variables-in-outputs/package.json +1 -1
- package/examples/advanced/yaml-tokens/package.json +1 -1
- package/lib/StyleDictionary.js +1 -1
- package/lib/common/formatHelpers/fileHeader.js +11 -7
- package/lib/common/formatHelpers/formattedVariables.d.ts +5 -1
- package/lib/common/formatHelpers/formattedVariables.js +59 -10
- package/lib/common/formats.js +10 -5
- package/lib/common/transforms.d.ts +108 -0
- package/lib/common/transforms.js +311 -17
- package/lib/enums/index.d.ts +6 -5
- package/lib/enums/index.js +6 -5
- package/lib/enums/sorts.d.ts +3 -0
- package/lib/enums/sorts.js +3 -0
- package/lib/enums/transforms.d.ts +4 -0
- package/lib/enums/transforms.js +4 -0
- package/package.json +7 -5
- package/types/Config.d.ts +2 -0
- package/types/DesignToken.d.ts +15 -0
- package/types/Sort.d.ts +17 -0
- package/types/index.d.ts +1 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"name": "format-helpers",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"description": "",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "sd.config.js",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"build": "style-dictionary build --config ./sd.config.js"
|
|
@@ -10,6 +11,6 @@
|
|
|
10
11
|
"author": "",
|
|
11
12
|
"license": "Apache-2.0",
|
|
12
13
|
"devDependencies": {
|
|
13
|
-
"style-dictionary": "^5.
|
|
14
|
+
"style-dictionary": "^5.3.0"
|
|
14
15
|
}
|
|
15
16
|
}
|
|
@@ -18,25 +18,25 @@ At this point, you can run `npm run build`. This command will generate the outpu
|
|
|
18
18
|
|
|
19
19
|
The "build" command processes the JSON files in the `tokens` folder. The `index.js` file adds each folder, allowing you to map through them in `config.js`. The script goes through each folder and generates a file for each folder and populates it with tokens that match the filter.
|
|
20
20
|
|
|
21
|
-
```
|
|
21
|
+
```json
|
|
22
22
|
# tokens/color/base.json
|
|
23
23
|
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
"color": {
|
|
25
|
+
"red": {
|
|
26
|
+
"value": "#FF0000"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
```
|
|
32
|
+
```json
|
|
33
33
|
# tokens/size/base.json
|
|
34
34
|
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
"size": {
|
|
36
|
+
"small": {
|
|
37
|
+
"value": "2px"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
40
|
}
|
|
41
41
|
```
|
|
42
42
|
|
|
@@ -46,16 +46,18 @@ Because the folder name matches the category, the output would automatically gen
|
|
|
46
46
|
|
|
47
47
|
Open the `config.js` file and see how the script first looks within the `tokens` directory to map through each folder. The destination then outputs a file that would match the name, and fill that file with the tokens that match the filter criteria.
|
|
48
48
|
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
```js
|
|
50
|
+
{
|
|
51
|
+
files: tokens.map((tokenCategory) => ({
|
|
52
|
+
destination: `${tokenCategory}.js`,
|
|
53
|
+
format: 'format/js',
|
|
54
|
+
filter: {
|
|
55
|
+
attributes: {
|
|
56
|
+
category: tokenCategory,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
Now each new folder that gets added will automatically generate a corresponding file filled with tokens that match the category!
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { readdirSync
|
|
2
|
-
|
|
3
|
-
const dirs = (p) =>
|
|
4
|
-
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
const dirs = (p) =>
|
|
4
|
+
readdirSync(p, { withFileTypes: true })
|
|
5
|
+
.filter((d) => d.isDirectory())
|
|
6
|
+
.map((d) => d.name);
|
|
7
|
+
|
|
8
|
+
export default dirs(import.meta.dirname);
|
package/lib/StyleDictionary.js
CHANGED
|
@@ -67,7 +67,7 @@ export default class StyleDictionary extends Register {
|
|
|
67
67
|
// Placeholder is transformed on prepublish -> see scripts/inject-version.js
|
|
68
68
|
// Another option might be import pkg from './package.json' with { "type": "json" } which would work in both browser and node, but support is not there yet.
|
|
69
69
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
|
|
70
|
-
static VERSION = '5.
|
|
70
|
+
static VERSION = '5.3.0';
|
|
71
71
|
|
|
72
72
|
/** @returns {Config} */
|
|
73
73
|
get options() {
|
|
@@ -61,11 +61,15 @@ export default async function fileHeader({ file, commentStyle, formatting = {},
|
|
|
61
61
|
fn = file.options.fileHeader;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
let {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
let {
|
|
65
|
+
prefix,
|
|
66
|
+
lineSeparator,
|
|
67
|
+
header,
|
|
68
|
+
commentStyle: _commentStyle,
|
|
69
|
+
footer,
|
|
70
|
+
fileHeaderTimestamp,
|
|
71
|
+
} = Object.assign({}, defaultFormatting, formatting);
|
|
72
|
+
const commentStyleMerged = commentStyle ?? _commentStyle;
|
|
69
73
|
|
|
70
74
|
// default header
|
|
71
75
|
const defaultHeader = [
|
|
@@ -73,11 +77,11 @@ export default async function fileHeader({ file, commentStyle, formatting = {},
|
|
|
73
77
|
...(fileHeaderTimestamp ? [`Generated on ${new Date().toUTCString()}`] : []),
|
|
74
78
|
];
|
|
75
79
|
|
|
76
|
-
if (
|
|
80
|
+
if (commentStyleMerged === commentStyles.short) {
|
|
77
81
|
prefix = `// `;
|
|
78
82
|
header = `${lineSeparator}`;
|
|
79
83
|
footer = `${lineSeparator}${lineSeparator}`;
|
|
80
|
-
} else if (
|
|
84
|
+
} else if (commentStyleMerged === 'xml') {
|
|
81
85
|
prefix = ` `;
|
|
82
86
|
header = `<!--${lineSeparator}`;
|
|
83
87
|
footer = `${lineSeparator}-->`;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* @param {Formatting} [options.formatting] - Custom formatting properties that define parts of a declaration line in code. This will get passed to `formatHelpers` -> `createPropertyformat` and used for the `lineSeparator` between lines of code.
|
|
12
12
|
* @param {Boolean} [options.themeable] [false] - Whether tokens should default to being themeable.
|
|
13
13
|
* @param {boolean} [options.usesDtcg] [false] - Whether DTCG token syntax should be uses.
|
|
14
|
+
* @param {SortOption} [options.sort] - Optional sorting strategy.
|
|
14
15
|
* @returns {String}
|
|
15
16
|
* @example
|
|
16
17
|
* ```js
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
* });
|
|
29
30
|
* ```
|
|
30
31
|
*/
|
|
31
|
-
export default function formattedVariables({ format, dictionary, outputReferences, outputReferenceFallbacks, formatting, themeable, usesDtcg, }: {
|
|
32
|
+
export default function formattedVariables({ format, dictionary, outputReferences, outputReferenceFallbacks, formatting, themeable, usesDtcg, sort, }: {
|
|
32
33
|
format: string;
|
|
33
34
|
dictionary: Dictionary;
|
|
34
35
|
outputReferences?: import("../../../types/Format.d.ts").OutputReferences | undefined;
|
|
@@ -36,9 +37,12 @@ export default function formattedVariables({ format, dictionary, outputReference
|
|
|
36
37
|
formatting?: import("../../../types/File.d.ts").FormattingOptions | undefined;
|
|
37
38
|
themeable?: boolean | undefined;
|
|
38
39
|
usesDtcg?: boolean | undefined;
|
|
40
|
+
sort?: import("../../../types/Sort.d.ts").SortOption | undefined;
|
|
39
41
|
}): string;
|
|
40
42
|
export type Token = import("../../../types/DesignToken.d.ts").TransformedToken;
|
|
41
43
|
export type Tokens = import("../../../types/DesignToken.d.ts").TransformedTokens;
|
|
42
44
|
export type Formatting = import("../../../types/File.d.ts").FormattingOptions;
|
|
43
45
|
export type OutputReferences = import("../../../types/Format.d.ts").OutputReferences;
|
|
46
|
+
export type SortFn = import("../../../types/Sort.d.ts").SortFn;
|
|
47
|
+
export type SortOption = import("../../../types/Sort.d.ts").SortOption;
|
|
44
48
|
export type Dictionary = import("../../../types/DesignToken.d.ts").Dictionary;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import createPropertyFormatter from './createPropertyFormatter.js';
|
|
2
2
|
import sortByReference from './sortByReference.js';
|
|
3
|
+
import sortByName from './sortByName.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @typedef {import('../../../types/DesignToken.d.ts').TransformedToken} Token
|
|
6
7
|
* @typedef {import('../../../types/DesignToken.d.ts').TransformedTokens} Tokens
|
|
7
8
|
* @typedef {import('../../../types/File.d.ts').FormattingOptions} Formatting
|
|
8
9
|
* @typedef {import('../../../types/Format.d.ts').OutputReferences} OutputReferences
|
|
10
|
+
* @typedef {import('../../../types/Sort.d.ts').SortFn} SortFn
|
|
11
|
+
* @typedef {import('../../../types/Sort.d.ts').SortOption} SortOption
|
|
9
12
|
* @typedef {import('../../../types/DesignToken.d.ts').Dictionary} Dictionary
|
|
10
13
|
*/
|
|
11
14
|
|
|
@@ -26,6 +29,7 @@ const defaultFormatting = {
|
|
|
26
29
|
* @param {Formatting} [options.formatting] - Custom formatting properties that define parts of a declaration line in code. This will get passed to `formatHelpers` -> `createPropertyformat` and used for the `lineSeparator` between lines of code.
|
|
27
30
|
* @param {Boolean} [options.themeable] [false] - Whether tokens should default to being themeable.
|
|
28
31
|
* @param {boolean} [options.usesDtcg] [false] - Whether DTCG token syntax should be uses.
|
|
32
|
+
* @param {SortOption} [options.sort] - Optional sorting strategy.
|
|
29
33
|
* @returns {String}
|
|
30
34
|
* @example
|
|
31
35
|
* ```js
|
|
@@ -51,6 +55,7 @@ export default function formattedVariables({
|
|
|
51
55
|
formatting = {},
|
|
52
56
|
themeable = false,
|
|
53
57
|
usesDtcg = false,
|
|
58
|
+
sort,
|
|
54
59
|
}) {
|
|
55
60
|
// typecast, we know that by know the tokens have been transformed
|
|
56
61
|
let allTokens = /** @type {Token[]} */ (dictionary.allTokens);
|
|
@@ -59,18 +64,62 @@ export default function formattedVariables({
|
|
|
59
64
|
|
|
60
65
|
let { lineSeparator } = Object.assign({}, defaultFormatting, formatting);
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const sortInputs = sort ? (Array.isArray(sort) ? sort : [sort]) : [];
|
|
68
|
+
|
|
69
|
+
// reference-safety sorter is an internal concern:
|
|
70
|
+
// we only compute it when outputReferences=true
|
|
71
|
+
const byReference = outputReferences
|
|
72
|
+
? sortByReference(tokens, { unfilteredTokens: dictionary.unfilteredTokens, usesDtcg })
|
|
73
|
+
: null;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {SortFn} keyOrFn
|
|
77
|
+
* @returns {((a: Token, b: Token) => number) | null}
|
|
78
|
+
*/
|
|
79
|
+
const normalize = (keyOrFn) => {
|
|
80
|
+
if (typeof keyOrFn === 'function') return keyOrFn;
|
|
81
|
+
|
|
82
|
+
if (keyOrFn === 'name') return sortByName;
|
|
83
|
+
|
|
84
|
+
// Fail loudly for invalid values (esp. typos)
|
|
85
|
+
if (typeof keyOrFn === 'string') {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Invalid "sort" option: "${keyOrFn}". ` +
|
|
88
|
+
`Use "name", a comparator function, or an array of those.`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Non-string non-function values are also invalid
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Invalid "sort" option type: ${typeof keyOrFn}. ` +
|
|
95
|
+
`Use "name", a comparator function, or an array of those.`,
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {((a: Token, b: Token) => number) | null} s
|
|
101
|
+
* @returns {s is ((a: Token, b: Token) => number)}
|
|
102
|
+
*/
|
|
103
|
+
const isComparator = (s) => s !== null;
|
|
104
|
+
|
|
105
|
+
const requestedSorters = sortInputs.map(normalize).filter(isComparator);
|
|
106
|
+
|
|
107
|
+
// If outputReferences=true, enforce reference-safe ordering first (define-before-use safety).
|
|
108
|
+
// The user-provided sort acts as a tie-breaker on top.
|
|
109
|
+
const comparators = outputReferences
|
|
110
|
+
? [byReference, ...requestedSorters].filter(isComparator)
|
|
111
|
+
: requestedSorters.filter(isComparator);
|
|
112
|
+
|
|
113
|
+
if (comparators.length > 0) {
|
|
69
114
|
// note: using the spread operator here so we get a new array rather than
|
|
70
115
|
// mutating the original
|
|
71
|
-
allTokens = [...allTokens].sort(
|
|
72
|
-
|
|
73
|
-
|
|
116
|
+
allTokens = [...allTokens].sort((a, b) => {
|
|
117
|
+
for (const cmp of comparators) {
|
|
118
|
+
const r = cmp(a, b);
|
|
119
|
+
if (r !== 0) return r;
|
|
120
|
+
}
|
|
121
|
+
return 0;
|
|
122
|
+
});
|
|
74
123
|
}
|
|
75
124
|
|
|
76
125
|
return allTokens
|
package/lib/common/formats.js
CHANGED
|
@@ -193,7 +193,7 @@ const formats = {
|
|
|
193
193
|
: options.selector
|
|
194
194
|
? [options.selector]
|
|
195
195
|
: [`:root`];
|
|
196
|
-
const { outputReferences, outputReferenceFallbacks, usesDtcg, formatting } = options;
|
|
196
|
+
const { outputReferences, outputReferenceFallbacks, usesDtcg, formatting, sort } = options;
|
|
197
197
|
const header = await fileHeader({
|
|
198
198
|
file,
|
|
199
199
|
formatting: getFormattingCloneWithoutPrefix(formatting),
|
|
@@ -219,6 +219,7 @@ const formats = {
|
|
|
219
219
|
indentation: indentation.repeat(selector.length),
|
|
220
220
|
},
|
|
221
221
|
usesDtcg,
|
|
222
|
+
sort,
|
|
222
223
|
});
|
|
223
224
|
|
|
224
225
|
return (
|
|
@@ -289,7 +290,7 @@ const formats = {
|
|
|
289
290
|
*/
|
|
290
291
|
[scssMapDeep]: async function ({ dictionary, options, file }) {
|
|
291
292
|
// Default the "themeable" option to true for backward compatibility.
|
|
292
|
-
const { outputReferences, themeable = true, formatting, usesDtcg } = options;
|
|
293
|
+
const { outputReferences, themeable = true, formatting, usesDtcg, sort } = options;
|
|
293
294
|
const header = await fileHeader({
|
|
294
295
|
file,
|
|
295
296
|
commentStyle: long,
|
|
@@ -306,6 +307,7 @@ const formats = {
|
|
|
306
307
|
themeable,
|
|
307
308
|
formatting,
|
|
308
309
|
usesDtcg,
|
|
310
|
+
sort,
|
|
309
311
|
}) +
|
|
310
312
|
'\n' +
|
|
311
313
|
scssMapDeepTemplate({ dictionary, options })
|
|
@@ -326,7 +328,7 @@ const formats = {
|
|
|
326
328
|
* ```
|
|
327
329
|
*/
|
|
328
330
|
[scssVariables]: async function ({ dictionary, options, file }) {
|
|
329
|
-
const { outputReferences, themeable = false, formatting, usesDtcg } = options;
|
|
331
|
+
const { outputReferences, themeable = false, formatting, usesDtcg, sort } = options;
|
|
330
332
|
const header = await fileHeader({
|
|
331
333
|
file,
|
|
332
334
|
commentStyle: short,
|
|
@@ -342,6 +344,7 @@ const formats = {
|
|
|
342
344
|
themeable,
|
|
343
345
|
formatting,
|
|
344
346
|
usesDtcg,
|
|
347
|
+
sort,
|
|
345
348
|
}) +
|
|
346
349
|
'\n'
|
|
347
350
|
);
|
|
@@ -381,7 +384,7 @@ const formats = {
|
|
|
381
384
|
* ```
|
|
382
385
|
*/
|
|
383
386
|
[lessVariables]: async function ({ dictionary, options, file }) {
|
|
384
|
-
const { outputReferences, formatting, usesDtcg } = options;
|
|
387
|
+
const { outputReferences, formatting, usesDtcg, sort } = options;
|
|
385
388
|
const header = await fileHeader({
|
|
386
389
|
file,
|
|
387
390
|
commentStyle: short,
|
|
@@ -396,6 +399,7 @@ const formats = {
|
|
|
396
399
|
outputReferences,
|
|
397
400
|
formatting,
|
|
398
401
|
usesDtcg,
|
|
402
|
+
sort,
|
|
399
403
|
}) +
|
|
400
404
|
'\n'
|
|
401
405
|
);
|
|
@@ -435,7 +439,7 @@ const formats = {
|
|
|
435
439
|
* ```
|
|
436
440
|
*/
|
|
437
441
|
[stylusVariables]: async function ({ dictionary, options, file, platform }) {
|
|
438
|
-
const { formatting, usesDtcg } = options;
|
|
442
|
+
const { formatting, usesDtcg, sort } = options;
|
|
439
443
|
const outputReferences = !!platform.options?.outputReferences;
|
|
440
444
|
const header = await fileHeader({
|
|
441
445
|
file,
|
|
@@ -451,6 +455,7 @@ const formats = {
|
|
|
451
455
|
outputReferences,
|
|
452
456
|
formatting,
|
|
453
457
|
usesDtcg,
|
|
458
|
+
sort,
|
|
454
459
|
}) +
|
|
455
460
|
'\n'
|
|
456
461
|
);
|
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a value is a DTCG color object
|
|
3
|
+
* @param {unknown} val
|
|
4
|
+
* @returns {val is DTCGColorValue}
|
|
5
|
+
*/
|
|
6
|
+
export function isDTCGColorObject(val: unknown): val is DTCGColorValue;
|
|
7
|
+
/**
|
|
8
|
+
* Get color RGB values from either legacy string format or DTCG object format
|
|
9
|
+
* @param {Token} token
|
|
10
|
+
* @param {Config} options
|
|
11
|
+
* @returns {{ r: number, g: number, b: number, a: number }}
|
|
12
|
+
*/
|
|
13
|
+
export function getColorRgb(token: Token, options: Config): {
|
|
14
|
+
r: number;
|
|
15
|
+
g: number;
|
|
16
|
+
b: number;
|
|
17
|
+
a: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Get a tinycolor2 Color instance from either legacy string format or DTCG object format
|
|
21
|
+
* This provides backward compatibility for transformers that use tinycolor2 methods
|
|
22
|
+
* @param {Token} token
|
|
23
|
+
* @param {Config} options
|
|
24
|
+
* @returns {ReturnType<typeof Color>}
|
|
25
|
+
*/
|
|
26
|
+
export function getColor(token: Token, options: Config): ReturnType<typeof Color>;
|
|
27
|
+
/**
|
|
28
|
+
* Get a ColorJS Color instance from either legacy string format or DTCG object format
|
|
29
|
+
* @param {Token} token
|
|
30
|
+
* @param {Config} options
|
|
31
|
+
* @returns {ColorJS}
|
|
32
|
+
*/
|
|
33
|
+
export function getColorJS(token: Token, options: Config): ColorJS;
|
|
1
34
|
/**
|
|
2
35
|
* @param {Token} token
|
|
3
36
|
* @param {Config} options
|
|
@@ -354,6 +387,78 @@ declare const _default: {
|
|
|
354
387
|
alpha: number;
|
|
355
388
|
};
|
|
356
389
|
};
|
|
390
|
+
/**
|
|
391
|
+
* Transforms the value into an OKLCH CSS color function string.
|
|
392
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
393
|
+
*
|
|
394
|
+
* ```css
|
|
395
|
+
* // Matches: token.type === 'color'
|
|
396
|
+
* // Returns:
|
|
397
|
+
* "oklch(0.7 0.15 180)"
|
|
398
|
+
* "oklch(0.7 0.15 180 / 0.5)"
|
|
399
|
+
* ```
|
|
400
|
+
*
|
|
401
|
+
* @memberof Transforms
|
|
402
|
+
*/
|
|
403
|
+
"color/oklch": {
|
|
404
|
+
type: "value";
|
|
405
|
+
filter: typeof isColor;
|
|
406
|
+
transform: (token: import("../../types/DesignToken.d.ts").TransformedToken, _: import("../../types/Config.d.ts").PlatformConfig, options: import("../../types/Config.d.ts").Config) => string;
|
|
407
|
+
};
|
|
408
|
+
/**
|
|
409
|
+
* Transforms the value into an OKLAB CSS color function string.
|
|
410
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
411
|
+
*
|
|
412
|
+
* ```css
|
|
413
|
+
* // Matches: token.type === 'color'
|
|
414
|
+
* // Returns:
|
|
415
|
+
* "oklab(0.7 -0.1 0.1)"
|
|
416
|
+
* "oklab(0.7 -0.1 0.1 / 0.5)"
|
|
417
|
+
* ```
|
|
418
|
+
*
|
|
419
|
+
* @memberof Transforms
|
|
420
|
+
*/
|
|
421
|
+
"color/oklab": {
|
|
422
|
+
type: "value";
|
|
423
|
+
filter: typeof isColor;
|
|
424
|
+
transform: (token: import("../../types/DesignToken.d.ts").TransformedToken, _: import("../../types/Config.d.ts").PlatformConfig, options: import("../../types/Config.d.ts").Config) => string;
|
|
425
|
+
};
|
|
426
|
+
/**
|
|
427
|
+
* Transforms the value into a Display P3 CSS color function string.
|
|
428
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
429
|
+
*
|
|
430
|
+
* ```css
|
|
431
|
+
* // Matches: token.type === 'color'
|
|
432
|
+
* // Returns:
|
|
433
|
+
* "color(display-p3 0.5 0.5 0.5)"
|
|
434
|
+
* "color(display-p3 0.5 0.5 0.5 / 0.5)"
|
|
435
|
+
* ```
|
|
436
|
+
*
|
|
437
|
+
* @memberof Transforms
|
|
438
|
+
*/
|
|
439
|
+
"color/p3": {
|
|
440
|
+
type: "value";
|
|
441
|
+
filter: typeof isColor;
|
|
442
|
+
transform: (token: import("../../types/DesignToken.d.ts").TransformedToken, _: import("../../types/Config.d.ts").PlatformConfig, options: import("../../types/Config.d.ts").Config) => string;
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* Transforms the value into an LCH CSS color function string.
|
|
446
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
447
|
+
*
|
|
448
|
+
* ```css
|
|
449
|
+
* // Matches: token.type === 'color'
|
|
450
|
+
* // Returns:
|
|
451
|
+
* "lch(50% 30 180)"
|
|
452
|
+
* "lch(50% 30 180 / 0.5)"
|
|
453
|
+
* ```
|
|
454
|
+
*
|
|
455
|
+
* @memberof Transforms
|
|
456
|
+
*/
|
|
457
|
+
"color/lch": {
|
|
458
|
+
type: "value";
|
|
459
|
+
filter: typeof isColor;
|
|
460
|
+
transform: (token: import("../../types/DesignToken.d.ts").TransformedToken, _: import("../../types/Config.d.ts").PlatformConfig, options: import("../../types/Config.d.ts").Config) => string;
|
|
461
|
+
};
|
|
357
462
|
/**
|
|
358
463
|
* Transforms the value into a scale-independent pixel (sp) value for font sizes on Android. It will not scale the number.
|
|
359
464
|
*
|
|
@@ -975,9 +1080,12 @@ declare const _default: {
|
|
|
975
1080
|
export default _default;
|
|
976
1081
|
export type Transform = import("../../types/Transform.d.ts").Transform;
|
|
977
1082
|
export type Token = import("../../types/DesignToken.d.ts").TransformedToken;
|
|
1083
|
+
export type DTCGColorValue = import("../../types/DesignToken.d.ts").DTCGColorValue;
|
|
1084
|
+
export type DTCGColorSpace = import("../../types/DesignToken.d.ts").DTCGColorSpace;
|
|
978
1085
|
export type PlatformConfig = import("../../types/Config.d.ts").PlatformConfig;
|
|
979
1086
|
export type Config = import("../../types/Config.d.ts").Config;
|
|
980
1087
|
import Color from 'tinycolor2';
|
|
1088
|
+
import ColorJS from 'colorjs.io';
|
|
981
1089
|
/**
|
|
982
1090
|
* @param {Token} token
|
|
983
1091
|
* @param {Config} options
|
package/lib/common/transforms.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Color from 'tinycolor2';
|
|
2
|
+
import ColorJS from 'colorjs.io';
|
|
2
3
|
import { join } from 'path-unified/posix';
|
|
3
4
|
import { snakeCase, kebabCase, camelCase } from 'change-case';
|
|
4
5
|
import convertToBase64 from '../utils/convertToBase64.js';
|
|
@@ -8,6 +9,8 @@ import { transforms, transformTypes } from '../enums/index.js';
|
|
|
8
9
|
/**
|
|
9
10
|
* @typedef {import('../../types/Transform.d.ts').Transform} Transform
|
|
10
11
|
* @typedef {import('../../types/DesignToken.d.ts').TransformedToken} Token
|
|
12
|
+
* @typedef {import('../../types/DesignToken.d.ts').DTCGColorValue} DTCGColorValue
|
|
13
|
+
* @typedef {import('../../types/DesignToken.d.ts').DTCGColorSpace} DTCGColorSpace
|
|
11
14
|
* @typedef {import('../../types/Config.d.ts').PlatformConfig} PlatformConfig
|
|
12
15
|
* @typedef {import('../../types/Config.d.ts').Config} Config
|
|
13
16
|
*/
|
|
@@ -15,6 +18,169 @@ import { transforms, transformTypes } from '../enums/index.js';
|
|
|
15
18
|
const UNKNOWN_CSS_FONT_PROPS_WARNINGS = GroupMessages.GROUP.UnknownCSSFontProperties;
|
|
16
19
|
const { value, name, attribute } = transformTypes;
|
|
17
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Valid DTCG v2025.10 color spaces
|
|
23
|
+
* @type {Set<string>}
|
|
24
|
+
*/
|
|
25
|
+
const DTCG_COLOR_SPACES = new Set([
|
|
26
|
+
'srgb',
|
|
27
|
+
'srgb-linear',
|
|
28
|
+
'display-p3',
|
|
29
|
+
'a98-rgb',
|
|
30
|
+
'prophoto-rgb',
|
|
31
|
+
'rec2020',
|
|
32
|
+
'xyz-d50',
|
|
33
|
+
'xyz-d65',
|
|
34
|
+
'lab',
|
|
35
|
+
'lch',
|
|
36
|
+
'oklab',
|
|
37
|
+
'oklch',
|
|
38
|
+
'hsl',
|
|
39
|
+
'hwb',
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Map DTCG color space names to colorjs.io color space IDs
|
|
44
|
+
* @type {Record<string, string>}
|
|
45
|
+
*/
|
|
46
|
+
const DTCG_TO_COLORJS_SPACE = {
|
|
47
|
+
srgb: 'srgb',
|
|
48
|
+
'srgb-linear': 'srgb-linear',
|
|
49
|
+
'display-p3': 'p3',
|
|
50
|
+
'a98-rgb': 'a98rgb',
|
|
51
|
+
'prophoto-rgb': 'prophoto',
|
|
52
|
+
rec2020: 'rec2020',
|
|
53
|
+
'xyz-d50': 'xyz-d50',
|
|
54
|
+
'xyz-d65': 'xyz-d65',
|
|
55
|
+
lab: 'lab',
|
|
56
|
+
lch: 'lch',
|
|
57
|
+
oklab: 'oklab',
|
|
58
|
+
oklch: 'oklch',
|
|
59
|
+
hsl: 'hsl',
|
|
60
|
+
hwb: 'hwb',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if a value is a DTCG color object
|
|
65
|
+
* @param {unknown} val
|
|
66
|
+
* @returns {val is DTCGColorValue}
|
|
67
|
+
*/
|
|
68
|
+
export function isDTCGColorObject(val) {
|
|
69
|
+
return (
|
|
70
|
+
typeof val === 'object' &&
|
|
71
|
+
val !== null &&
|
|
72
|
+
'colorSpace' in val &&
|
|
73
|
+
typeof val.colorSpace === 'string' &&
|
|
74
|
+
DTCG_COLOR_SPACES.has(val.colorSpace) &&
|
|
75
|
+
'components' in val &&
|
|
76
|
+
Array.isArray(val.components)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Convert a DTCG color object to a ColorJS Color instance
|
|
82
|
+
* Handles 'none' values in components and alpha
|
|
83
|
+
* @param {DTCGColorValue} dtcgColor
|
|
84
|
+
* @returns {ColorJS}
|
|
85
|
+
*/
|
|
86
|
+
function dtcgToColorJS(dtcgColor) {
|
|
87
|
+
const { colorSpace, components, alpha } = dtcgColor;
|
|
88
|
+
|
|
89
|
+
// Map DTCG color space to colorjs.io color space ID
|
|
90
|
+
const colorjsSpace = DTCG_TO_COLORJS_SPACE[colorSpace] ?? colorSpace;
|
|
91
|
+
|
|
92
|
+
// Convert 'none' values to NaN (as per CSS Color spec)
|
|
93
|
+
const coords = /** @type {[number, number, number]} */ (
|
|
94
|
+
components.map((c) => (c === 'none' ? NaN : c))
|
|
95
|
+
);
|
|
96
|
+
const alphaValue = alpha === 'none' ? NaN : (alpha ?? 1);
|
|
97
|
+
|
|
98
|
+
return new ColorJS(colorjsSpace, coords, alphaValue);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get the sRGB representation of a color, with gamut mapping if needed
|
|
103
|
+
* Uses the hex property as fallback if provided and color is out of gamut
|
|
104
|
+
* @param {DTCGColorValue} dtcgColor
|
|
105
|
+
* @returns {{ r: number, g: number, b: number, a: number }}
|
|
106
|
+
*/
|
|
107
|
+
function dtcgColorToRgb(dtcgColor) {
|
|
108
|
+
const color = dtcgToColorJS(dtcgColor);
|
|
109
|
+
|
|
110
|
+
// Check if color is in sRGB gamut
|
|
111
|
+
if (!color.inGamut('srgb')) {
|
|
112
|
+
// If hex fallback is provided, use it
|
|
113
|
+
if (dtcgColor.hex) {
|
|
114
|
+
const fallback = Color(dtcgColor.hex).toRgb();
|
|
115
|
+
return fallback;
|
|
116
|
+
}
|
|
117
|
+
// Otherwise, perform gamut mapping
|
|
118
|
+
color.toGamut({ space: 'srgb' });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const srgb = color.to('srgb');
|
|
122
|
+
const alpha = isNaN(srgb.alpha) ? 1 : srgb.alpha;
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
r: Math.round(Math.max(0, Math.min(1, srgb.coords[0])) * 255),
|
|
126
|
+
g: Math.round(Math.max(0, Math.min(1, srgb.coords[1])) * 255),
|
|
127
|
+
b: Math.round(Math.max(0, Math.min(1, srgb.coords[2])) * 255),
|
|
128
|
+
a: alpha,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get color RGB values from either legacy string format or DTCG object format
|
|
134
|
+
* @param {Token} token
|
|
135
|
+
* @param {Config} options
|
|
136
|
+
* @returns {{ r: number, g: number, b: number, a: number }}
|
|
137
|
+
*/
|
|
138
|
+
export function getColorRgb(token, options) {
|
|
139
|
+
const val = options.usesDtcg ? token.$value : token.value;
|
|
140
|
+
|
|
141
|
+
if (isDTCGColorObject(val)) {
|
|
142
|
+
return dtcgColorToRgb(val);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Legacy format - use tinycolor2
|
|
146
|
+
return Color(val).toRgb();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get a tinycolor2 Color instance from either legacy string format or DTCG object format
|
|
151
|
+
* This provides backward compatibility for transformers that use tinycolor2 methods
|
|
152
|
+
* @param {Token} token
|
|
153
|
+
* @param {Config} options
|
|
154
|
+
* @returns {ReturnType<typeof Color>}
|
|
155
|
+
*/
|
|
156
|
+
export function getColor(token, options) {
|
|
157
|
+
const val = options.usesDtcg ? token.$value : token.value;
|
|
158
|
+
|
|
159
|
+
if (isDTCGColorObject(val)) {
|
|
160
|
+
const rgb = dtcgColorToRgb(val);
|
|
161
|
+
return Color(rgb);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return Color(val);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get a ColorJS Color instance from either legacy string format or DTCG object format
|
|
169
|
+
* @param {Token} token
|
|
170
|
+
* @param {Config} options
|
|
171
|
+
* @returns {ColorJS}
|
|
172
|
+
*/
|
|
173
|
+
export function getColorJS(token, options) {
|
|
174
|
+
const val = options.usesDtcg ? token.$value : token.value;
|
|
175
|
+
|
|
176
|
+
if (isDTCGColorObject(val)) {
|
|
177
|
+
return dtcgToColorJS(val);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Parse legacy format with ColorJS
|
|
181
|
+
return new ColorJS(val);
|
|
182
|
+
}
|
|
183
|
+
|
|
18
184
|
/**
|
|
19
185
|
* @param {string} str
|
|
20
186
|
* @returns {string}
|
|
@@ -33,8 +199,14 @@ const camelOpts = {
|
|
|
33
199
|
export function isColor(token, options) {
|
|
34
200
|
const val = options.usesDtcg ? token.$value : token.value;
|
|
35
201
|
const type = options.usesDtcg ? token.$type : token.type;
|
|
202
|
+
|
|
203
|
+
if (type !== 'color') return false;
|
|
204
|
+
|
|
205
|
+
// DTCG object format
|
|
206
|
+
if (isDTCGColorObject(val)) return true;
|
|
207
|
+
|
|
208
|
+
// Legacy string format
|
|
36
209
|
return (
|
|
37
|
-
type === 'color' &&
|
|
38
210
|
Color(val).isValid() &&
|
|
39
211
|
// exclude gradients from being color transformed
|
|
40
212
|
['linear', 'radial', 'conic']
|
|
@@ -257,7 +429,7 @@ export default {
|
|
|
257
429
|
type: attribute,
|
|
258
430
|
filter: isColor,
|
|
259
431
|
transform: function (token, _, options) {
|
|
260
|
-
const color =
|
|
432
|
+
const color = getColor(token, options);
|
|
261
433
|
return {
|
|
262
434
|
hex: color.toHex(),
|
|
263
435
|
rgb: color.toRgb(),
|
|
@@ -399,7 +571,7 @@ export default {
|
|
|
399
571
|
type: value,
|
|
400
572
|
filter: isColor,
|
|
401
573
|
transform: function (token, _, options) {
|
|
402
|
-
return
|
|
574
|
+
return getColor(token, options).toRgbString();
|
|
403
575
|
},
|
|
404
576
|
},
|
|
405
577
|
|
|
@@ -419,7 +591,7 @@ export default {
|
|
|
419
591
|
type: value,
|
|
420
592
|
filter: isColor,
|
|
421
593
|
transform: function (token, _, options) {
|
|
422
|
-
return
|
|
594
|
+
return getColor(token, options).toHslString();
|
|
423
595
|
},
|
|
424
596
|
},
|
|
425
597
|
|
|
@@ -439,7 +611,7 @@ export default {
|
|
|
439
611
|
type: value,
|
|
440
612
|
filter: isColor,
|
|
441
613
|
transform: function (token, _, options) {
|
|
442
|
-
const color =
|
|
614
|
+
const color = getColor(token, options);
|
|
443
615
|
const o = color.toHsl();
|
|
444
616
|
const vals = `${Math.round(o.h)} ${Math.round(o.s * 100)}% ${Math.round(o.l * 100)}%`;
|
|
445
617
|
if (color.getAlpha() === 1) {
|
|
@@ -465,7 +637,7 @@ export default {
|
|
|
465
637
|
type: value,
|
|
466
638
|
filter: isColor,
|
|
467
639
|
transform: function (token, _, options) {
|
|
468
|
-
const color =
|
|
640
|
+
const color = getColor(token, options);
|
|
469
641
|
if (color.getAlpha() === 1) {
|
|
470
642
|
return color.toHexString();
|
|
471
643
|
} else {
|
|
@@ -489,7 +661,7 @@ export default {
|
|
|
489
661
|
type: value,
|
|
490
662
|
filter: isColor,
|
|
491
663
|
transform: function (token, _, options) {
|
|
492
|
-
return
|
|
664
|
+
return getColor(token, options).toHex8String();
|
|
493
665
|
},
|
|
494
666
|
},
|
|
495
667
|
|
|
@@ -508,7 +680,7 @@ export default {
|
|
|
508
680
|
type: value,
|
|
509
681
|
filter: isColor,
|
|
510
682
|
transform: function (token, _, options) {
|
|
511
|
-
const str =
|
|
683
|
+
const str = getColor(token, options).toHex8();
|
|
512
684
|
return '#' + str.slice(6) + str.slice(0, 6);
|
|
513
685
|
},
|
|
514
686
|
},
|
|
@@ -528,7 +700,7 @@ export default {
|
|
|
528
700
|
type: value,
|
|
529
701
|
filter: isColor,
|
|
530
702
|
transform: function (token, _, options) {
|
|
531
|
-
const str =
|
|
703
|
+
const str = getColor(token, options).toHex8();
|
|
532
704
|
return 'Color(0x' + str.slice(6) + str.slice(0, 6) + ')';
|
|
533
705
|
},
|
|
534
706
|
},
|
|
@@ -548,7 +720,7 @@ export default {
|
|
|
548
720
|
type: value,
|
|
549
721
|
filter: isColor,
|
|
550
722
|
transform: function (token, _, options) {
|
|
551
|
-
const rgb =
|
|
723
|
+
const rgb = getColor(token, options).toRgb();
|
|
552
724
|
return (
|
|
553
725
|
'[UIColor colorWithRed:' +
|
|
554
726
|
(rgb.r / 255).toFixed(3) +
|
|
@@ -581,7 +753,7 @@ export default {
|
|
|
581
753
|
type: value,
|
|
582
754
|
filter: isColor,
|
|
583
755
|
transform: function (token, _, options) {
|
|
584
|
-
const { r, g, b, a } =
|
|
756
|
+
const { r, g, b, a } = getColor(token, options).toRgb();
|
|
585
757
|
const rFixed = (r / 255.0).toFixed(3);
|
|
586
758
|
const gFixed = (g / 255.0).toFixed(3);
|
|
587
759
|
const bFixed = (b / 255.0).toFixed(3);
|
|
@@ -604,7 +776,7 @@ export default {
|
|
|
604
776
|
type: value,
|
|
605
777
|
filter: isColor,
|
|
606
778
|
transform: function (token, _, options) {
|
|
607
|
-
const { r, g, b, a } =
|
|
779
|
+
const { r, g, b, a } = getColor(token, options).toRgb();
|
|
608
780
|
const rFixed = (r / 255.0).toFixed(3);
|
|
609
781
|
const gFixed = (g / 255.0).toFixed(3);
|
|
610
782
|
const bFixed = (b / 255.0).toFixed(3);
|
|
@@ -628,7 +800,7 @@ export default {
|
|
|
628
800
|
type: value,
|
|
629
801
|
filter: isColor,
|
|
630
802
|
transform: function (token, _, options) {
|
|
631
|
-
const color =
|
|
803
|
+
const color = getColor(token, options);
|
|
632
804
|
if (color.getAlpha() === 1) {
|
|
633
805
|
return color.toHexString();
|
|
634
806
|
} else {
|
|
@@ -659,7 +831,7 @@ export default {
|
|
|
659
831
|
type: value,
|
|
660
832
|
filter: isColor,
|
|
661
833
|
transform: function (token, _, options) {
|
|
662
|
-
let color =
|
|
834
|
+
let color = getColor(token, options).toRgb();
|
|
663
835
|
return {
|
|
664
836
|
red: (color.r / 255).toFixed(5),
|
|
665
837
|
green: (color.g / 255).toFixed(5),
|
|
@@ -669,6 +841,130 @@ export default {
|
|
|
669
841
|
},
|
|
670
842
|
},
|
|
671
843
|
|
|
844
|
+
/**
|
|
845
|
+
* Transforms the value into an OKLCH CSS color function string.
|
|
846
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
847
|
+
*
|
|
848
|
+
* ```css
|
|
849
|
+
* // Matches: token.type === 'color'
|
|
850
|
+
* // Returns:
|
|
851
|
+
* "oklch(0.7 0.15 180)"
|
|
852
|
+
* "oklch(0.7 0.15 180 / 0.5)"
|
|
853
|
+
* ```
|
|
854
|
+
*
|
|
855
|
+
* @memberof Transforms
|
|
856
|
+
*/
|
|
857
|
+
[transforms.colorOklch]: {
|
|
858
|
+
type: value,
|
|
859
|
+
filter: isColor,
|
|
860
|
+
transform: function (token, _, options) {
|
|
861
|
+
const color = getColorJS(token, options).to('oklch');
|
|
862
|
+
const [l, c, h] = color.coords;
|
|
863
|
+
const alpha = isNaN(color.alpha) ? 1 : color.alpha;
|
|
864
|
+
// Format: oklch(L C H) or oklch(L C H / alpha)
|
|
865
|
+
const lStr = (isNaN(l) ? 0 : l).toFixed(4);
|
|
866
|
+
const cStr = (isNaN(c) ? 0 : c).toFixed(4);
|
|
867
|
+
const hStr = (isNaN(h) ? 0 : h).toFixed(2);
|
|
868
|
+
if (alpha === 1) {
|
|
869
|
+
return `oklch(${lStr} ${cStr} ${hStr})`;
|
|
870
|
+
}
|
|
871
|
+
return `oklch(${lStr} ${cStr} ${hStr} / ${alpha})`;
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Transforms the value into an OKLAB CSS color function string.
|
|
877
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
878
|
+
*
|
|
879
|
+
* ```css
|
|
880
|
+
* // Matches: token.type === 'color'
|
|
881
|
+
* // Returns:
|
|
882
|
+
* "oklab(0.7 -0.1 0.1)"
|
|
883
|
+
* "oklab(0.7 -0.1 0.1 / 0.5)"
|
|
884
|
+
* ```
|
|
885
|
+
*
|
|
886
|
+
* @memberof Transforms
|
|
887
|
+
*/
|
|
888
|
+
[transforms.colorOklab]: {
|
|
889
|
+
type: value,
|
|
890
|
+
filter: isColor,
|
|
891
|
+
transform: function (token, _, options) {
|
|
892
|
+
const color = getColorJS(token, options).to('oklab');
|
|
893
|
+
const [l, a, b] = color.coords;
|
|
894
|
+
const alpha = isNaN(color.alpha) ? 1 : color.alpha;
|
|
895
|
+
// Format: oklab(L a b) or oklab(L a b / alpha)
|
|
896
|
+
const lStr = (isNaN(l) ? 0 : l).toFixed(4);
|
|
897
|
+
const aStr = (isNaN(a) ? 0 : a).toFixed(4);
|
|
898
|
+
const bStr = (isNaN(b) ? 0 : b).toFixed(4);
|
|
899
|
+
if (alpha === 1) {
|
|
900
|
+
return `oklab(${lStr} ${aStr} ${bStr})`;
|
|
901
|
+
}
|
|
902
|
+
return `oklab(${lStr} ${aStr} ${bStr} / ${alpha})`;
|
|
903
|
+
},
|
|
904
|
+
},
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Transforms the value into a Display P3 CSS color function string.
|
|
908
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
909
|
+
*
|
|
910
|
+
* ```css
|
|
911
|
+
* // Matches: token.type === 'color'
|
|
912
|
+
* // Returns:
|
|
913
|
+
* "color(display-p3 0.5 0.5 0.5)"
|
|
914
|
+
* "color(display-p3 0.5 0.5 0.5 / 0.5)"
|
|
915
|
+
* ```
|
|
916
|
+
*
|
|
917
|
+
* @memberof Transforms
|
|
918
|
+
*/
|
|
919
|
+
[transforms.colorP3]: {
|
|
920
|
+
type: value,
|
|
921
|
+
filter: isColor,
|
|
922
|
+
transform: function (token, _, options) {
|
|
923
|
+
const color = getColorJS(token, options).to('p3');
|
|
924
|
+
const [r, g, b] = color.coords;
|
|
925
|
+
const alpha = isNaN(color.alpha) ? 1 : color.alpha;
|
|
926
|
+
// Format: color(display-p3 r g b) or color(display-p3 r g b / alpha)
|
|
927
|
+
const rStr = (isNaN(r) ? 0 : r).toFixed(5);
|
|
928
|
+
const gStr = (isNaN(g) ? 0 : g).toFixed(5);
|
|
929
|
+
const bStr = (isNaN(b) ? 0 : b).toFixed(5);
|
|
930
|
+
if (alpha === 1) {
|
|
931
|
+
return `color(display-p3 ${rStr} ${gStr} ${bStr})`;
|
|
932
|
+
}
|
|
933
|
+
return `color(display-p3 ${rStr} ${gStr} ${bStr} / ${alpha})`;
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Transforms the value into an LCH CSS color function string.
|
|
939
|
+
* Preserves wide-gamut colors without lossy gamut mapping.
|
|
940
|
+
*
|
|
941
|
+
* ```css
|
|
942
|
+
* // Matches: token.type === 'color'
|
|
943
|
+
* // Returns:
|
|
944
|
+
* "lch(50% 30 180)"
|
|
945
|
+
* "lch(50% 30 180 / 0.5)"
|
|
946
|
+
* ```
|
|
947
|
+
*
|
|
948
|
+
* @memberof Transforms
|
|
949
|
+
*/
|
|
950
|
+
[transforms.colorLch]: {
|
|
951
|
+
type: value,
|
|
952
|
+
filter: isColor,
|
|
953
|
+
transform: function (token, _, options) {
|
|
954
|
+
const color = getColorJS(token, options).to('lch');
|
|
955
|
+
const [l, c, h] = color.coords;
|
|
956
|
+
const alpha = isNaN(color.alpha) ? 1 : color.alpha;
|
|
957
|
+
// Format: lch(L% C H) or lch(L% C H / alpha)
|
|
958
|
+
const lStr = (isNaN(l) ? 0 : l).toFixed(2);
|
|
959
|
+
const cStr = (isNaN(c) ? 0 : c).toFixed(2);
|
|
960
|
+
const hStr = (isNaN(h) ? 0 : h).toFixed(2);
|
|
961
|
+
if (alpha === 1) {
|
|
962
|
+
return `lch(${lStr}% ${cStr} ${hStr})`;
|
|
963
|
+
}
|
|
964
|
+
return `lch(${lStr}% ${cStr} ${hStr} / ${alpha})`;
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
|
|
672
968
|
/**
|
|
673
969
|
* Transforms the value into a scale-independent pixel (sp) value for font sizes on Android. It will not scale the number.
|
|
674
970
|
*
|
|
@@ -1515,9 +1811,7 @@ export default {
|
|
|
1515
1811
|
type: value,
|
|
1516
1812
|
filter: isColor,
|
|
1517
1813
|
transform: function (token, _, options) {
|
|
1518
|
-
const str =
|
|
1519
|
-
.toHex8()
|
|
1520
|
-
.toUpperCase();
|
|
1814
|
+
const str = getColor(token, options).toHex8().toUpperCase();
|
|
1521
1815
|
return `Color(0x${str.slice(6)}${str.slice(0, 6)})`;
|
|
1522
1816
|
},
|
|
1523
1817
|
},
|
package/lib/enums/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { actions } from "./actions.js";
|
|
2
|
-
export { logWarningLevels } from "./logWarningLevels.js";
|
|
3
|
-
export { logVerbosityLevels } from "./logVerbosityLevels.js";
|
|
4
|
-
export { logBrokenReferenceLevels } from "./logBrokenReferenceLevels.js";
|
|
5
|
-
export { commentStyles } from "./commentStyles.js";
|
|
6
2
|
export { commentPositions } from "./commentPositions.js";
|
|
3
|
+
export { commentStyles } from "./commentStyles.js";
|
|
7
4
|
export { fileHeaderCommentStyles } from "./fileHeaderCommentStyles.js";
|
|
8
5
|
export { formats } from "./formats.js";
|
|
6
|
+
export { logBrokenReferenceLevels } from "./logBrokenReferenceLevels.js";
|
|
7
|
+
export { logVerbosityLevels } from "./logVerbosityLevels.js";
|
|
8
|
+
export { logWarningLevels } from "./logWarningLevels.js";
|
|
9
|
+
export { propertyFormatNames } from "./propertyFormatNames.js";
|
|
10
|
+
export { builtInSorts } from "./sorts.js";
|
|
9
11
|
export { transformGroups } from "./transformGroups.js";
|
|
10
12
|
export { transforms } from "./transforms.js";
|
|
11
13
|
export { transformTypes } from "./transformTypes.js";
|
|
12
|
-
export { propertyFormatNames } from "./propertyFormatNames.js";
|
package/lib/enums/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { actions } from './actions.js';
|
|
2
|
-
export { logWarningLevels } from './logWarningLevels.js';
|
|
3
|
-
export { logVerbosityLevels } from './logVerbosityLevels.js';
|
|
4
|
-
export { logBrokenReferenceLevels } from './logBrokenReferenceLevels.js';
|
|
5
|
-
export { commentStyles } from './commentStyles.js';
|
|
6
2
|
export { commentPositions } from './commentPositions.js';
|
|
3
|
+
export { commentStyles } from './commentStyles.js';
|
|
7
4
|
export { fileHeaderCommentStyles } from './fileHeaderCommentStyles.js';
|
|
8
5
|
export { formats } from './formats.js';
|
|
6
|
+
export { logBrokenReferenceLevels } from './logBrokenReferenceLevels.js';
|
|
7
|
+
export { logVerbosityLevels } from './logVerbosityLevels.js';
|
|
8
|
+
export { logWarningLevels } from './logWarningLevels.js';
|
|
9
|
+
export { propertyFormatNames } from './propertyFormatNames.js';
|
|
10
|
+
export { builtInSorts } from './sorts.js';
|
|
9
11
|
export { transformGroups } from './transformGroups.js';
|
|
10
12
|
export { transforms } from './transforms.js';
|
|
11
13
|
export { transformTypes } from './transformTypes.js';
|
|
12
|
-
export { propertyFormatNames } from './propertyFormatNames.js';
|
|
@@ -52,6 +52,10 @@ export namespace transforms {
|
|
|
52
52
|
let assetObjCLiteral: "asset/objC/literal";
|
|
53
53
|
let assetSwiftLiteral: "asset/swift/literal";
|
|
54
54
|
let colorHex8flutter: "color/hex8flutter";
|
|
55
|
+
let colorOklch: "color/oklch";
|
|
56
|
+
let colorOklab: "color/oklab";
|
|
57
|
+
let colorP3: "color/p3";
|
|
58
|
+
let colorLch: "color/lch";
|
|
55
59
|
let contentFlutterLiteral: "content/flutter/literal";
|
|
56
60
|
let assetFlutterLiteral: "asset/flutter/literal";
|
|
57
61
|
let sizeFlutterRemToDouble: "size/flutter/remToDouble";
|
package/lib/enums/transforms.js
CHANGED
|
@@ -52,6 +52,10 @@ export const transforms = {
|
|
|
52
52
|
assetObjCLiteral: /** @type {'asset/objC/literal'} */ ('asset/objC/literal'),
|
|
53
53
|
assetSwiftLiteral: /** @type {'asset/swift/literal'} */ ('asset/swift/literal'),
|
|
54
54
|
colorHex8flutter: /** @type {'color/hex8flutter'} */ ('color/hex8flutter'),
|
|
55
|
+
colorOklch: /** @type {'color/oklch'} */ ('color/oklch'),
|
|
56
|
+
colorOklab: /** @type {'color/oklab'} */ ('color/oklab'),
|
|
57
|
+
colorP3: /** @type {'color/p3'} */ ('color/p3'),
|
|
58
|
+
colorLch: /** @type {'color/lch'} */ ('color/lch'),
|
|
55
59
|
contentFlutterLiteral: /** @type {'content/flutter/literal'} */ ('content/flutter/literal'),
|
|
56
60
|
assetFlutterLiteral: /** @type {'asset/flutter/literal'} */ ('asset/flutter/literal'),
|
|
57
61
|
sizeFlutterRemToDouble: /** @type {'size/flutter/remToDouble'} */ ('size/flutter/remToDouble'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "style-dictionary",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Style once, use everywhere. A build system for creating cross-platform styles.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"style dictionary",
|
|
@@ -112,15 +112,17 @@
|
|
|
112
112
|
],
|
|
113
113
|
"homepage": "https://styledictionary.com",
|
|
114
114
|
"overrides": {
|
|
115
|
-
"tmp": "0.2.5"
|
|
115
|
+
"tmp": "0.2.5",
|
|
116
|
+
"lodash": "4.17.23"
|
|
116
117
|
},
|
|
117
118
|
"dependencies": {
|
|
118
119
|
"@bundled-es-modules/deepmerge": "^4.3.1",
|
|
119
|
-
"@bundled-es-modules/glob": "^
|
|
120
|
-
"@bundled-es-modules/memfs": "^4.
|
|
120
|
+
"@bundled-es-modules/glob": "^13.0.1",
|
|
121
|
+
"@bundled-es-modules/memfs": "^4.17.0",
|
|
121
122
|
"@zip.js/zip.js": "^2.7.44",
|
|
122
123
|
"chalk": "^5.3.0",
|
|
123
124
|
"change-case": "^5.3.0",
|
|
125
|
+
"colorjs.io": "^0.5.2",
|
|
124
126
|
"commander": "^12.1.0",
|
|
125
127
|
"is-plain-obj": "^4.1.0",
|
|
126
128
|
"json5": "^2.2.2",
|
|
@@ -163,7 +165,7 @@
|
|
|
163
165
|
"lint-staged": "^12.3.1",
|
|
164
166
|
"lit": "^3.1.2",
|
|
165
167
|
"mdast": "^3.0.0",
|
|
166
|
-
"mermaid": "^
|
|
168
|
+
"mermaid": "^10.9.5",
|
|
167
169
|
"mocha": "^10.2.0",
|
|
168
170
|
"monaco-editor": "^0.47.0",
|
|
169
171
|
"npm-run-all": "^4.1.5",
|
package/types/Config.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Parser } from './Parser.js';
|
|
|
5
5
|
import type { Preprocessor } from './Preprocessor.js';
|
|
6
6
|
import type { Transform } from './Transform.js';
|
|
7
7
|
import type { Format, OutputReferences } from './Format.js';
|
|
8
|
+
import type { SortOption } from './Sort.js';
|
|
8
9
|
import type { Action } from './Action.js';
|
|
9
10
|
import { logBrokenReferenceLevels, logWarningLevels, logVerbosityLevels } from '../lib/enums/index.js';
|
|
10
11
|
type logWarningLevels = typeof logWarningLevels;
|
|
@@ -26,6 +27,7 @@ export interface LocalOptions {
|
|
|
26
27
|
outputReferences?: OutputReferences;
|
|
27
28
|
outputReferenceFallbacks?: boolean;
|
|
28
29
|
formatting?: FormattingOverrides;
|
|
30
|
+
sort?: SortOption;
|
|
29
31
|
[key: string]: any;
|
|
30
32
|
}
|
|
31
33
|
export interface GetReferencesOptions {
|
package/types/DesignToken.d.ts
CHANGED
|
@@ -60,3 +60,18 @@ export interface Dictionary {
|
|
|
60
60
|
unfilteredAllTokens?: TransformedToken[];
|
|
61
61
|
unfilteredTokenMap?: Map<string, TransformedToken>;
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* DTCG v2025.10 color spaces
|
|
65
|
+
* @see https://tr.designtokens.org/format/#color
|
|
66
|
+
*/
|
|
67
|
+
export type DTCGColorSpace = 'srgb' | 'srgb-linear' | 'display-p3' | 'a98-rgb' | 'prophoto-rgb' | 'rec2020' | 'xyz-d50' | 'xyz-d65' | 'lab' | 'lch' | 'oklab' | 'oklch' | 'hsl' | 'hwb';
|
|
68
|
+
/**
|
|
69
|
+
* DTCG v2025.10 color value format
|
|
70
|
+
* @see https://tr.designtokens.org/format/#color
|
|
71
|
+
*/
|
|
72
|
+
export interface DTCGColorValue {
|
|
73
|
+
colorSpace: DTCGColorSpace;
|
|
74
|
+
components: (number | 'none')[];
|
|
75
|
+
alpha?: number | 'none';
|
|
76
|
+
hex?: string;
|
|
77
|
+
}
|
package/types/Sort.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TransformedToken } from './DesignToken.js';
|
|
2
|
+
import { builtInSorts } from '../lib/enums/sorts.js';
|
|
3
|
+
export type BuiltInSorts = typeof builtInSorts;
|
|
4
|
+
export interface Sort {
|
|
5
|
+
name: string;
|
|
6
|
+
sort: SortFn;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A single sort function - either a built-in sort referenced by name string or a custom comparator function
|
|
10
|
+
* for inline usage
|
|
11
|
+
*/
|
|
12
|
+
export type SortFn = string | ((a: TransformedToken, b: TransformedToken) => number);
|
|
13
|
+
/**
|
|
14
|
+
* Sort option for formattedVariables - can be a single sort item or an array of sort items
|
|
15
|
+
* (for chaining multiple sorts as tie-breakers)
|
|
16
|
+
*/
|
|
17
|
+
export type SortOption = SortFn | SortFn[];
|
package/types/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type { DesignToken, DesignTokens, PreprocessedTokens, TransformedToken, T
|
|
|
4
4
|
export type { FileHeader, File, FormattingOptions } from './File.js';
|
|
5
5
|
export type { Filter } from './Filter.js';
|
|
6
6
|
export type { Format, FormatFnArguments, FormatFn, OutputReferences } from './Format.js';
|
|
7
|
+
export type { SortOption, SortFn, BuiltInSorts } from './Sort.js';
|
|
7
8
|
export type { Parser, ParserOptions } from './Parser.js';
|
|
8
9
|
export type { Preprocessor } from './Preprocessor.js';
|
|
9
10
|
export type { Transform, NameTransform, AttributeTransform, ValueTransform } from './Transform.js';
|