@servicetitan/hammer-token 0.0.0-rc-1.48.0-20251104214834 → 0.0.0-rc-3.0.0-20260127161418
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -1
- package/README.md +222 -0
- package/build/web/core/component-variables.scss +176 -130
- package/build/web/core/component.d.ts +96 -0
- package/build/web/core/component.js +574 -252
- package/build/web/core/component.scss +94 -69
- package/build/web/core/css-utils/a2-border.css +39 -41
- package/build/web/core/css-utils/a2-color.css +367 -227
- package/build/web/core/css-utils/a2-font.css +0 -2
- package/build/web/core/css-utils/a2-spacing.css +0 -2
- package/build/web/core/css-utils/a2-utils.css +434 -292
- package/build/web/core/css-utils/border.css +39 -41
- package/build/web/core/css-utils/color.css +367 -227
- package/build/web/core/css-utils/font.css +0 -2
- package/build/web/core/css-utils/spacing.css +0 -2
- package/build/web/core/css-utils/utils.css +434 -292
- package/build/web/core/index.d.ts +6 -0
- package/build/web/core/index.js +1 -1
- package/build/web/core/primitive-variables.scss +130 -71
- package/build/web/core/primitive.d.ts +185 -0
- package/build/web/core/primitive.js +328 -72
- package/build/web/core/primitive.scss +183 -124
- package/build/web/core/semantic-variables.scss +295 -220
- package/build/web/core/semantic.d.ts +198 -0
- package/build/web/core/semantic.js +913 -341
- package/build/web/core/semantic.scss +196 -140
- package/build/web/index.d.ts +3 -4
- package/build/web/index.js +0 -1
- package/build/web/types.d.ts +17 -0
- package/config.js +121 -496
- package/package.json +5 -4
- package/src/global/primitive/breakpoint.tokens.json +39 -0
- package/src/global/primitive/color.tokens.json +536 -0
- package/src/global/primitive/duration.tokens.json +32 -0
- package/src/global/primitive/font.tokens.json +103 -0
- package/src/global/primitive/radius.tokens.json +67 -0
- package/src/global/primitive/size.tokens.json +123 -0
- package/src/global/primitive/transition.tokens.json +20 -0
- package/src/theme/core/background.tokens.json +1058 -0
- package/src/theme/core/border.tokens.json +148 -0
- package/src/theme/core/charts.tokens.json +802 -0
- package/src/theme/core/component/alert.tokens.json +180 -0
- package/src/theme/core/component/announcement.tokens.json +186 -0
- package/src/theme/core/component/avatar.tokens.json +132 -0
- package/src/theme/core/component/badge.tokens.json +40 -0
- package/src/theme/core/component/button.tokens.json +752 -0
- package/src/theme/core/component/checkbox.tokens.json +292 -0
- package/src/theme/core/focus.tokens.json +48 -0
- package/src/theme/core/foreground.tokens.json +306 -0
- package/src/theme/core/shadow.tokens.json +43 -0
- package/src/theme/core/status.tokens.json +70 -0
- package/src/theme/core/typography.tokens.json +100 -0
- package/src/utils/copy-css-utils-cli.js +13 -0
- package/src/utils/css-utils-format-utils.js +98 -1
- package/src/utils/sd-build-configs.js +372 -0
- package/src/utils/sd-formats.js +752 -0
- package/src/utils/sd-transforms.js +126 -0
- package/src/utils/token-helpers.js +555 -0
- package/tsconfig.json +18 -0
- package/.turbo/turbo-build.log +0 -37
- package/build/web/core/raw.js +0 -234
- package/src/global/primitive/breakpoint.js +0 -19
- package/src/global/primitive/color.js +0 -231
- package/src/global/primitive/duration.js +0 -16
- package/src/global/primitive/font.js +0 -60
- package/src/global/primitive/radius.js +0 -31
- package/src/global/primitive/size.js +0 -55
- package/src/global/primitive/transition.js +0 -16
- package/src/theme/core/background.js +0 -170
- package/src/theme/core/border.js +0 -103
- package/src/theme/core/charts.js +0 -464
- package/src/theme/core/component/button.js +0 -708
- package/src/theme/core/component/checkbox.js +0 -405
- package/src/theme/core/focus.js +0 -35
- package/src/theme/core/foreground.js +0 -148
- package/src/theme/core/overlay.js +0 -137
- package/src/theme/core/shadow.js +0 -29
- package/src/theme/core/status.js +0 -49
- package/src/theme/core/typography.js +0 -82
- package/type/types.ts +0 -344
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
2
|
+
/**
|
|
3
|
+
* Style Dictionary transform registrations
|
|
4
|
+
* @module sd-transforms
|
|
5
|
+
*/
|
|
6
|
+
const {
|
|
7
|
+
getDtcgValue,
|
|
8
|
+
getTokenType,
|
|
9
|
+
isCompositeColor,
|
|
10
|
+
isDimensionValue,
|
|
11
|
+
} = require("./token-helpers");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Registers all custom Style Dictionary transforms for DTCG token format.
|
|
15
|
+
* This includes transforms for token names, values, cubic-bezier functions, and color opacity.
|
|
16
|
+
* Also registers a "dtcg" transform group that combines all these transforms.
|
|
17
|
+
* @param {import('style-dictionary').default} StyleDictionary - The Style Dictionary class instance
|
|
18
|
+
* @returns {void}
|
|
19
|
+
* @example
|
|
20
|
+
* const StyleDictionary = require('style-dictionary');
|
|
21
|
+
* registerTransforms(StyleDictionary);
|
|
22
|
+
*/
|
|
23
|
+
const registerTransforms = (StyleDictionary) => {
|
|
24
|
+
/**
|
|
25
|
+
* Transform: dtcg/name
|
|
26
|
+
* Generates a clean token name from the token path, filtering out $root, $schema,
|
|
27
|
+
* and other $-prefixed segments.
|
|
28
|
+
*/
|
|
29
|
+
StyleDictionary.registerTransform({
|
|
30
|
+
name: "dtcg/name",
|
|
31
|
+
type: "name",
|
|
32
|
+
transform: (token) => {
|
|
33
|
+
const tokenPath =
|
|
34
|
+
token.path || token.original?.path || token.pathSegments;
|
|
35
|
+
|
|
36
|
+
if (Array.isArray(tokenPath) && tokenPath.length > 0) {
|
|
37
|
+
// Filter out $root, $schema, and other $-prefixed segments from path
|
|
38
|
+
const filteredPath = tokenPath.filter(
|
|
39
|
+
(segment) =>
|
|
40
|
+
segment !== "$root" &&
|
|
41
|
+
segment !== "$schema" &&
|
|
42
|
+
!String(segment).startsWith("$"),
|
|
43
|
+
);
|
|
44
|
+
if (filteredPath.length > 0) {
|
|
45
|
+
return filteredPath.join("-");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (token.name && typeof token.name === "string") {
|
|
49
|
+
// Clean up name: remove $root, $schema, collapse dashes
|
|
50
|
+
return token.name
|
|
51
|
+
.replace(/\$root/g, "")
|
|
52
|
+
.replace(/\$schema/g, "")
|
|
53
|
+
.replace(/\$\$+/g, "-")
|
|
54
|
+
.replace(/-+/g, "-")
|
|
55
|
+
.replace(/^-|-$/g, "");
|
|
56
|
+
}
|
|
57
|
+
return "unknown";
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Transform: dtcg/value
|
|
63
|
+
* Extracts the DTCG $value from a token, handling dimension objects with value/unit.
|
|
64
|
+
*/
|
|
65
|
+
StyleDictionary.registerTransform({
|
|
66
|
+
name: "dtcg/value",
|
|
67
|
+
type: "value",
|
|
68
|
+
transform: (token) => {
|
|
69
|
+
const value = getDtcgValue(token);
|
|
70
|
+
|
|
71
|
+
// Handle new format { value, unit }
|
|
72
|
+
if (isDimensionValue(value)) {
|
|
73
|
+
return `${value.value}${value.unit}`;
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Transform: dtcg/cubic-bezier
|
|
81
|
+
* Converts cubicBezier token arrays to CSS cubic-bezier() function strings.
|
|
82
|
+
*/
|
|
83
|
+
StyleDictionary.registerTransform({
|
|
84
|
+
name: "dtcg/cubic-bezier",
|
|
85
|
+
type: "value",
|
|
86
|
+
matcher: (token) => getTokenType(token) === "cubicBezier",
|
|
87
|
+
transform: (token) => {
|
|
88
|
+
const value = getDtcgValue(token);
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
return `cubic-bezier(${value.join(", ")})`;
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Transform: dtcg/color-opacity
|
|
98
|
+
* Handles composite color tokens that have separate color and alpha values.
|
|
99
|
+
*/
|
|
100
|
+
StyleDictionary.registerTransform({
|
|
101
|
+
name: "dtcg/color-opacity",
|
|
102
|
+
type: "value",
|
|
103
|
+
matcher: (token) =>
|
|
104
|
+
getTokenType(token) === "color" && isCompositeColor(getDtcgValue(token)),
|
|
105
|
+
transform: (token) => getDtcgValue(token),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Transform Group: dtcg
|
|
110
|
+
* Combines all DTCG-related transforms plus the built-in color/css transform.
|
|
111
|
+
*/
|
|
112
|
+
StyleDictionary.registerTransformGroup({
|
|
113
|
+
name: "dtcg",
|
|
114
|
+
transforms: [
|
|
115
|
+
"dtcg/name",
|
|
116
|
+
"dtcg/value",
|
|
117
|
+
"dtcg/cubic-bezier",
|
|
118
|
+
"dtcg/color-opacity",
|
|
119
|
+
"color/css",
|
|
120
|
+
],
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
registerTransforms,
|
|
126
|
+
};
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token helper functions for Style Dictionary DTCG format
|
|
3
|
+
* These functions handle token value extraction, resolution, and CSS variable chain building
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extracts the DTCG value from a token
|
|
8
|
+
* @param {Object} token - The token object
|
|
9
|
+
* @returns {*} The token's $value property
|
|
10
|
+
*/
|
|
11
|
+
const getDtcgValue = (token) => token.$value;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extracts the DTCG token type
|
|
15
|
+
* @param {Object} token - The token object
|
|
16
|
+
* @returns {string|undefined} The token's $type property
|
|
17
|
+
*/
|
|
18
|
+
const getTokenType = (token) => token.$type;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a token has a dark mode value
|
|
22
|
+
* @param {Object} token - The token object
|
|
23
|
+
* @returns {boolean} True if the token has a dark mode value defined
|
|
24
|
+
*/
|
|
25
|
+
const hasDarkValue = (token) =>
|
|
26
|
+
token.$extensions?.appearance?.dark?.$value !== undefined;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Gets the dark mode value from a token
|
|
30
|
+
* @param {Object} token - The token object
|
|
31
|
+
* @returns {*} The dark mode value, or undefined if not present
|
|
32
|
+
*/
|
|
33
|
+
const getDarkValue = (token) => token.$extensions?.appearance?.dark?.$value;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checks if a value is a composite color object (contains color and alpha properties)
|
|
37
|
+
* @param {*} value - The value to check
|
|
38
|
+
* @returns {boolean} True if the value is a composite color object
|
|
39
|
+
* @example
|
|
40
|
+
* isCompositeColor({ color: '#fff', alpha: 0.5 }) // true
|
|
41
|
+
* isCompositeColor('#fff') // false
|
|
42
|
+
*/
|
|
43
|
+
const isCompositeColor = (value) =>
|
|
44
|
+
value &&
|
|
45
|
+
typeof value === "object" &&
|
|
46
|
+
!Array.isArray(value) &&
|
|
47
|
+
value.color !== undefined &&
|
|
48
|
+
value.alpha !== undefined;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks if a value is a dimension object (contains value and unit properties)
|
|
52
|
+
* @param {*} value - The value to check
|
|
53
|
+
* @returns {boolean} True if the value is a dimension object
|
|
54
|
+
* @example
|
|
55
|
+
* isDimensionValue({ value: 16, unit: 'px' }) // true
|
|
56
|
+
* isDimensionValue('16px') // false
|
|
57
|
+
*/
|
|
58
|
+
const isDimensionValue = (value) =>
|
|
59
|
+
value &&
|
|
60
|
+
typeof value === "object" &&
|
|
61
|
+
!Array.isArray(value) &&
|
|
62
|
+
value.value !== undefined &&
|
|
63
|
+
value.unit !== undefined;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Wraps values containing commas in double quotes for SCSS map compatibility.
|
|
67
|
+
* SCSS maps use commas as entry separators, so values with commas need quoting.
|
|
68
|
+
* @param {*} value - The value to potentially wrap
|
|
69
|
+
* @returns {*} The wrapped value if it contains commas, otherwise the original value
|
|
70
|
+
* @example
|
|
71
|
+
* wrapScssValue('Arial, sans-serif') // '"Arial, sans-serif"'
|
|
72
|
+
* wrapScssValue('Arial') // 'Arial'
|
|
73
|
+
*/
|
|
74
|
+
const wrapScssValue = (value) => {
|
|
75
|
+
if (typeof value !== "string") return value;
|
|
76
|
+
if (value.includes(",")) {
|
|
77
|
+
return `"${value}"`;
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Gets the token name from its path, filtering out $root segments
|
|
84
|
+
* @param {Object} token - The token object
|
|
85
|
+
* @returns {string} The token name as a hyphen-separated string
|
|
86
|
+
* @example
|
|
87
|
+
* getTokenName({ path: ['color', 'primary', '500'] }) // 'color-primary-500'
|
|
88
|
+
*/
|
|
89
|
+
const getTokenName = (token) => {
|
|
90
|
+
const tokenPath = token.path;
|
|
91
|
+
if (Array.isArray(tokenPath) && tokenPath.length > 0) {
|
|
92
|
+
// Filter out $root if it's the last segment
|
|
93
|
+
const filteredPath =
|
|
94
|
+
tokenPath[tokenPath.length - 1] === "$root"
|
|
95
|
+
? tokenPath.slice(0, -1)
|
|
96
|
+
: tokenPath;
|
|
97
|
+
return filteredPath.join("-");
|
|
98
|
+
}
|
|
99
|
+
return "unknown";
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Builds a Map of tokens keyed by their names for O(1) lookups
|
|
104
|
+
* @param {Object} dictionary - The Style Dictionary dictionary object
|
|
105
|
+
* @returns {Map<string, Object>} A Map where keys are token names and values are token objects
|
|
106
|
+
*/
|
|
107
|
+
const buildTokenMap = (dictionary) =>
|
|
108
|
+
new Map(
|
|
109
|
+
(dictionary.unfilteredAllTokens || dictionary.allTokens).map((t) => [
|
|
110
|
+
getTokenName(t),
|
|
111
|
+
t,
|
|
112
|
+
]),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Converts a hex color to hex8 format (with alpha channel)
|
|
117
|
+
* @param {string} hexColor - The hex color to convert (e.g., '#ff0000' or '#f00')
|
|
118
|
+
* @param {number} alpha - The alpha value between 0 and 1
|
|
119
|
+
* @returns {string} The hex8 color string (e.g., '#ff000080')
|
|
120
|
+
* @example
|
|
121
|
+
* convertToHex8('#ff0000', 0.5) // '#ff000080'
|
|
122
|
+
* convertToHex8('#f00', 1) // '#ff0000FF'
|
|
123
|
+
*/
|
|
124
|
+
const convertToHex8 = (hexColor, alpha) => {
|
|
125
|
+
if (!hexColor || typeof hexColor !== "string" || !hexColor.startsWith("#")) {
|
|
126
|
+
return hexColor;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Remove # and normalize to 6-digit hex
|
|
130
|
+
let hex = hexColor.replace("#", "");
|
|
131
|
+
if (hex.length === 3) {
|
|
132
|
+
// Expand shorthand: #RGB -> #RRGGBB
|
|
133
|
+
hex = hex
|
|
134
|
+
.split("")
|
|
135
|
+
.map((char) => char + char)
|
|
136
|
+
.join("");
|
|
137
|
+
} else if (hex.length !== 6) {
|
|
138
|
+
return hexColor; // Invalid hex format
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Convert alpha (0-1) to hex (00-FF)
|
|
142
|
+
const alphaHex = Math.round(alpha * 255)
|
|
143
|
+
.toString(16)
|
|
144
|
+
.padStart(2, "0")
|
|
145
|
+
.toUpperCase();
|
|
146
|
+
|
|
147
|
+
return `#${hex}${alphaHex}`;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Resolves a reference string using Style Dictionary utilities.
|
|
152
|
+
* If reference points to a group, tries appending .$root to find the token.
|
|
153
|
+
* @param {string} refString - The reference string to resolve (e.g., '{color.primary.500}')
|
|
154
|
+
* @param {Object} dictionary - The Style Dictionary dictionary object
|
|
155
|
+
* @param {Function} resolveReferences - The SD5 resolveReferences function
|
|
156
|
+
* @returns {*} The resolved value, or the original string if resolution fails
|
|
157
|
+
*/
|
|
158
|
+
const resolveReference = (refString, dictionary, resolveReferences) => {
|
|
159
|
+
if (typeof refString !== "string") {
|
|
160
|
+
return refString;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
// 1. Try resolving exactly as requested (your suggestion step 1 & 2)
|
|
164
|
+
const result = resolveReferences(refString, dictionary.tokens, {
|
|
165
|
+
usesDtcg: true,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// 2. If it couldn't be resolved (it still contains braces), try with .$root (your suggestion step 3)
|
|
169
|
+
if (
|
|
170
|
+
typeof result === "string" &&
|
|
171
|
+
result.includes("{") &&
|
|
172
|
+
result.includes("}")
|
|
173
|
+
) {
|
|
174
|
+
const withRoot = refString.replace(/\{([^{}]+)\}/g, (match, path) => {
|
|
175
|
+
// Only append .$root if it's not already there
|
|
176
|
+
return path.endsWith(".$root") ? match : `{${path}.$root}`;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const rootResult = resolveReferences(withRoot, dictionary.tokens, {
|
|
181
|
+
usesDtcg: true,
|
|
182
|
+
});
|
|
183
|
+
// If the .$root version resolved successfully, use it
|
|
184
|
+
if (!rootResult.includes("{")) {
|
|
185
|
+
return rootResult;
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// Fallback to original result if .$root also fails
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
} catch {
|
|
193
|
+
return refString;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolves a composite color object (with color and alpha properties) to a hex8 string
|
|
199
|
+
* @param {Object} colorObj - The composite color object with color and alpha properties
|
|
200
|
+
* @param {Object} dictionary - The Style Dictionary dictionary object
|
|
201
|
+
* @param {Function} resolveReferences - The SD5 resolveReferences function
|
|
202
|
+
* @returns {string|null} The resolved hex8 color string, or null if not a composite color
|
|
203
|
+
*/
|
|
204
|
+
const resolveCompositeColor = (colorObj, dictionary, resolveReferences) => {
|
|
205
|
+
if (!isCompositeColor(colorObj)) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const colorValue = resolveReference(
|
|
209
|
+
colorObj.color,
|
|
210
|
+
dictionary,
|
|
211
|
+
resolveReferences,
|
|
212
|
+
);
|
|
213
|
+
if (typeof colorValue === "string" && colorValue.startsWith("#")) {
|
|
214
|
+
return convertToHex8(colorValue, colorObj.alpha);
|
|
215
|
+
}
|
|
216
|
+
return colorValue;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Gets the resolved token value, handling dark mode, composite colors, and references
|
|
221
|
+
* @param {Object} token - The token object
|
|
222
|
+
* @param {Object} dictionary - The Style Dictionary dictionary object
|
|
223
|
+
* @param {Object} [options] - Options for value resolution
|
|
224
|
+
* @param {boolean} [options.isDark=false] - Whether to get the dark mode value
|
|
225
|
+
* @param {boolean} [options.forScssMap=false] - Whether to format for SCSS map (quotes commas)
|
|
226
|
+
* @param {Function} resolveReferences - The SD5 resolveReferences function
|
|
227
|
+
* @returns {string} The resolved token value as a string
|
|
228
|
+
*/
|
|
229
|
+
const getTokenValue = (
|
|
230
|
+
token,
|
|
231
|
+
dictionary,
|
|
232
|
+
{ isDark = false, forScssMap = false } = {},
|
|
233
|
+
resolveReferences,
|
|
234
|
+
) => {
|
|
235
|
+
let value = getDtcgValue(token);
|
|
236
|
+
|
|
237
|
+
// Handle dark mode
|
|
238
|
+
if (isDark && hasDarkValue(token)) {
|
|
239
|
+
value = getDarkValue(token);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Handle composite colors
|
|
243
|
+
if (isCompositeColor(value)) {
|
|
244
|
+
const resolved = resolveCompositeColor(
|
|
245
|
+
value,
|
|
246
|
+
dictionary,
|
|
247
|
+
resolveReferences,
|
|
248
|
+
);
|
|
249
|
+
if (resolved) {
|
|
250
|
+
value = resolved;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Handle dimension values { value, unit }
|
|
255
|
+
if (isDimensionValue(value)) {
|
|
256
|
+
value = `${value.value}${value.unit}`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Resolve references
|
|
260
|
+
if (typeof value === "string" && value.includes("{") && value.includes("}")) {
|
|
261
|
+
value = resolveReference(value, dictionary, resolveReferences);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Convert to string and clean up
|
|
265
|
+
if (value === undefined || value === null) {
|
|
266
|
+
return "";
|
|
267
|
+
}
|
|
268
|
+
const valueStr = typeof value === "string" ? value : JSON.stringify(value);
|
|
269
|
+
|
|
270
|
+
// Normalize transparent to rgba for consistency
|
|
271
|
+
const cleaned = valueStr.replaceAll('"', "");
|
|
272
|
+
if (cleaned === "transparent") {
|
|
273
|
+
return "rgba(0, 0, 0, 0)";
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Wrap values with commas in quotes for SCSS map compatibility
|
|
277
|
+
if (forScssMap) {
|
|
278
|
+
return wrapScssValue(cleaned);
|
|
279
|
+
}
|
|
280
|
+
return cleaned;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Checks if a value is a reference string using the usesReferences function
|
|
285
|
+
* @param {*} value - The value to check
|
|
286
|
+
* @param {Function} usesReferences - The SD5 usesReferences function
|
|
287
|
+
* @returns {boolean} True if the value is a reference string
|
|
288
|
+
*/
|
|
289
|
+
const isReferenceString = (value, usesReferences) =>
|
|
290
|
+
typeof value === "string" && usesReferences(value);
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Builds a recursive CSS var chain with light-dark() at the level where light/dark values differ.
|
|
294
|
+
* Creates nested var() fallbacks that ultimately resolve to light-dark() at the primitive level.
|
|
295
|
+
* @param {string} originalValue - The original reference string (e.g., '{status.color.danger}')
|
|
296
|
+
* @param {Map<string, Object>} tokenMap - Map of token names to token objects
|
|
297
|
+
* @param {Object} dictionary - The Style Dictionary dictionary object
|
|
298
|
+
* @param {boolean} [useLightDark=false] - Whether to include light-dark() for color variants
|
|
299
|
+
* @param {Function} usesReferences - The SD5 usesReferences function
|
|
300
|
+
* @param {Function} resolveReferences - The SD5 resolveReferences function
|
|
301
|
+
* @param {string} [cssVarPrefix=''] - Prefix for CSS variable names (e.g., 'a2-')
|
|
302
|
+
* @param {boolean} [forScssMap=false] - Whether to format for SCSS map
|
|
303
|
+
* @returns {string} The CSS var chain string
|
|
304
|
+
* @example
|
|
305
|
+
* // Returns: 'var(--a2-status-color-danger, light-dark(var(--a2-color-red-500, #e13212), var(--a2-color-red-100, #ff745f)))'
|
|
306
|
+
* buildRecursiveVarChainWithLightDark('{status.color.danger}', tokenMap, dictionary, true, usesReferences, resolveReferences, 'a2-')
|
|
307
|
+
*/
|
|
308
|
+
const buildRecursiveVarChainWithLightDark = (
|
|
309
|
+
originalValue,
|
|
310
|
+
tokenMap,
|
|
311
|
+
dictionary,
|
|
312
|
+
useLightDark = false,
|
|
313
|
+
usesReferences,
|
|
314
|
+
resolveReferences,
|
|
315
|
+
cssVarPrefix = "",
|
|
316
|
+
forScssMap = false,
|
|
317
|
+
) => {
|
|
318
|
+
// If not a reference string, return the value as-is
|
|
319
|
+
if (
|
|
320
|
+
!originalValue ||
|
|
321
|
+
typeof originalValue !== "string" ||
|
|
322
|
+
!usesReferences(originalValue)
|
|
323
|
+
) {
|
|
324
|
+
return originalValue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Replace each {reference} with var(--name, recursiveResult)
|
|
328
|
+
return originalValue.replace(/\{([^{}]+)\}/g, (_, refPath) => {
|
|
329
|
+
const refTokenName = refPath.replace(/\./g, "-");
|
|
330
|
+
const refToken = tokenMap.get(refTokenName);
|
|
331
|
+
if (!refToken) return `var(--${cssVarPrefix}${refTokenName})`;
|
|
332
|
+
|
|
333
|
+
const refOriginalLight = refToken.original?.$value;
|
|
334
|
+
const refOriginalDark =
|
|
335
|
+
refToken.original?.$extensions?.appearance?.dark?.$value;
|
|
336
|
+
const refHasDarkVariant = refOriginalDark !== undefined;
|
|
337
|
+
|
|
338
|
+
// Check if light and dark values are DIFFERENT - this is where light-dark() should go
|
|
339
|
+
const lightDarkDiffer =
|
|
340
|
+
refHasDarkVariant && refOriginalLight !== refOriginalDark;
|
|
341
|
+
|
|
342
|
+
if (useLightDark && lightDarkDiffer) {
|
|
343
|
+
// Light and dark values differ - insert light-dark() here
|
|
344
|
+
const lightResult = buildRecursiveVarChainWithLightDark(
|
|
345
|
+
refOriginalLight,
|
|
346
|
+
tokenMap,
|
|
347
|
+
dictionary,
|
|
348
|
+
false,
|
|
349
|
+
usesReferences,
|
|
350
|
+
resolveReferences,
|
|
351
|
+
cssVarPrefix,
|
|
352
|
+
forScssMap,
|
|
353
|
+
);
|
|
354
|
+
// Same fix as darkFinal: ensure lightResult is a string before using it
|
|
355
|
+
const lightFinal =
|
|
356
|
+
typeof lightResult === "string" && usesReferences(refOriginalLight)
|
|
357
|
+
? lightResult
|
|
358
|
+
: getTokenValue(
|
|
359
|
+
refToken,
|
|
360
|
+
dictionary,
|
|
361
|
+
{ isDark: false, forScssMap },
|
|
362
|
+
resolveReferences,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const darkResult = buildRecursiveVarChainWithLightDark(
|
|
366
|
+
refOriginalDark,
|
|
367
|
+
tokenMap,
|
|
368
|
+
dictionary,
|
|
369
|
+
false,
|
|
370
|
+
usesReferences,
|
|
371
|
+
resolveReferences,
|
|
372
|
+
cssVarPrefix,
|
|
373
|
+
forScssMap,
|
|
374
|
+
);
|
|
375
|
+
// Ensure darkResult is a string (resolved var chain), not just truthy.
|
|
376
|
+
// Composite color objects pass through buildRecursiveVarChainWithLightDark unchanged,
|
|
377
|
+
// but usesReferences() returns true for them (checks nested properties).
|
|
378
|
+
const darkFinal =
|
|
379
|
+
typeof darkResult === "string" && usesReferences(refOriginalDark)
|
|
380
|
+
? darkResult
|
|
381
|
+
: getTokenValue(
|
|
382
|
+
refToken,
|
|
383
|
+
dictionary,
|
|
384
|
+
{ isDark: true, forScssMap },
|
|
385
|
+
resolveReferences,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
return `var(--${cssVarPrefix}${refTokenName}, light-dark(${lightFinal}, ${darkFinal}))`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Light and dark are same (or no dark) - continue recursing, propagate useLightDark
|
|
392
|
+
if (
|
|
393
|
+
refOriginalLight &&
|
|
394
|
+
typeof refOriginalLight === "string" &&
|
|
395
|
+
usesReferences(refOriginalLight)
|
|
396
|
+
) {
|
|
397
|
+
const nestedResult = buildRecursiveVarChainWithLightDark(
|
|
398
|
+
refOriginalLight,
|
|
399
|
+
tokenMap,
|
|
400
|
+
dictionary,
|
|
401
|
+
useLightDark,
|
|
402
|
+
usesReferences,
|
|
403
|
+
resolveReferences,
|
|
404
|
+
cssVarPrefix,
|
|
405
|
+
forScssMap,
|
|
406
|
+
);
|
|
407
|
+
return `var(--${cssVarPrefix}${refTokenName}, ${nestedResult})`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Final value (primitive) - get resolved value
|
|
411
|
+
const finalValue = getTokenValue(
|
|
412
|
+
refToken,
|
|
413
|
+
dictionary,
|
|
414
|
+
{ isDark: false, forScssMap },
|
|
415
|
+
resolveReferences,
|
|
416
|
+
);
|
|
417
|
+
return `var(--${cssVarPrefix}${refTokenName}, ${finalValue})`;
|
|
418
|
+
});
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Builds a fallback value with recursive refs and light-dark() at the appropriate level.
|
|
423
|
+
* This is the main entry point for generating CSS fallback values with dark mode support.
|
|
424
|
+
* @param {Object} token - The token object
|
|
425
|
+
* @param {Object} dictionary - The Style Dictionary dictionary object
|
|
426
|
+
* @param {Map<string, Object>} tokenMap - Map of token names to token objects
|
|
427
|
+
* @param {Object} [options] - Options for fallback building
|
|
428
|
+
* @param {boolean} [options.isDark=false] - Whether to get the dark mode value
|
|
429
|
+
* @param {boolean} [options.useLightDark=false] - Whether to include light-dark() for color variants
|
|
430
|
+
* @param {string} [options.cssVarPrefix=''] - Prefix for CSS variable names
|
|
431
|
+
* @param {boolean} [options.forScssMap=false] - Whether to format for SCSS map
|
|
432
|
+
* @param {Function} usesReferences - The SD5 usesReferences function
|
|
433
|
+
* @param {Function} resolveReferences - The SD5 resolveReferences function
|
|
434
|
+
* @returns {string} The fallback value string with CSS var chains and/or light-dark()
|
|
435
|
+
*/
|
|
436
|
+
const buildFallbackWithRefs = (
|
|
437
|
+
token,
|
|
438
|
+
dictionary,
|
|
439
|
+
tokenMap,
|
|
440
|
+
{
|
|
441
|
+
isDark = false,
|
|
442
|
+
useLightDark = false,
|
|
443
|
+
cssVarPrefix = "",
|
|
444
|
+
forScssMap = false,
|
|
445
|
+
} = {},
|
|
446
|
+
usesReferences,
|
|
447
|
+
resolveReferences,
|
|
448
|
+
) => {
|
|
449
|
+
const originalValue = token.original?.$value;
|
|
450
|
+
const originalDark = token.original?.$extensions?.appearance?.dark?.$value;
|
|
451
|
+
const hasDark = originalDark !== undefined;
|
|
452
|
+
|
|
453
|
+
// Check if this token's light/dark values are different at top level
|
|
454
|
+
// Use JSON.stringify for object comparison (composite colors)
|
|
455
|
+
const lightDarkDifferAtTop =
|
|
456
|
+
hasDark && JSON.stringify(originalValue) !== JSON.stringify(originalDark);
|
|
457
|
+
|
|
458
|
+
// If useLightDark and light/dark differ at this level, insert light-dark() here
|
|
459
|
+
if (useLightDark && lightDarkDifferAtTop) {
|
|
460
|
+
// Handle light value - could be a string reference or composite object
|
|
461
|
+
let lightFinal;
|
|
462
|
+
if (isReferenceString(originalValue, usesReferences)) {
|
|
463
|
+
lightFinal = buildRecursiveVarChainWithLightDark(
|
|
464
|
+
originalValue,
|
|
465
|
+
tokenMap,
|
|
466
|
+
dictionary,
|
|
467
|
+
false,
|
|
468
|
+
usesReferences,
|
|
469
|
+
resolveReferences,
|
|
470
|
+
cssVarPrefix,
|
|
471
|
+
forScssMap,
|
|
472
|
+
);
|
|
473
|
+
} else {
|
|
474
|
+
// Composite color or plain value - resolve directly
|
|
475
|
+
lightFinal = getTokenValue(
|
|
476
|
+
token,
|
|
477
|
+
dictionary,
|
|
478
|
+
{ isDark: false, forScssMap },
|
|
479
|
+
resolveReferences,
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Handle dark value - could be a string reference or composite object
|
|
484
|
+
let darkFinal;
|
|
485
|
+
if (isReferenceString(originalDark, usesReferences)) {
|
|
486
|
+
darkFinal = buildRecursiveVarChainWithLightDark(
|
|
487
|
+
originalDark,
|
|
488
|
+
tokenMap,
|
|
489
|
+
dictionary,
|
|
490
|
+
false,
|
|
491
|
+
usesReferences,
|
|
492
|
+
resolveReferences,
|
|
493
|
+
cssVarPrefix,
|
|
494
|
+
forScssMap,
|
|
495
|
+
);
|
|
496
|
+
} else {
|
|
497
|
+
// Composite color or plain value - resolve directly
|
|
498
|
+
darkFinal = getTokenValue(
|
|
499
|
+
token,
|
|
500
|
+
dictionary,
|
|
501
|
+
{ isDark: true, forScssMap },
|
|
502
|
+
resolveReferences,
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return `light-dark(${lightFinal}, ${darkFinal})`;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Light/dark same or no dark - recurse and propagate useLightDark to find where they differ
|
|
510
|
+
// When isDark is true and we have a dark value, use originalDark instead of originalValue
|
|
511
|
+
const valueToProcess = isDark && hasDark ? originalDark : originalValue;
|
|
512
|
+
|
|
513
|
+
if (isReferenceString(valueToProcess, usesReferences)) {
|
|
514
|
+
const result = buildRecursiveVarChainWithLightDark(
|
|
515
|
+
valueToProcess,
|
|
516
|
+
tokenMap,
|
|
517
|
+
dictionary,
|
|
518
|
+
useLightDark,
|
|
519
|
+
usesReferences,
|
|
520
|
+
resolveReferences,
|
|
521
|
+
cssVarPrefix,
|
|
522
|
+
forScssMap,
|
|
523
|
+
);
|
|
524
|
+
if (result && result !== valueToProcess) {
|
|
525
|
+
return result;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// No references or composite value - return the resolved final value
|
|
530
|
+
return getTokenValue(
|
|
531
|
+
token,
|
|
532
|
+
dictionary,
|
|
533
|
+
{ isDark, forScssMap },
|
|
534
|
+
resolveReferences,
|
|
535
|
+
);
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
module.exports = {
|
|
539
|
+
getDtcgValue,
|
|
540
|
+
getTokenType,
|
|
541
|
+
hasDarkValue,
|
|
542
|
+
getDarkValue,
|
|
543
|
+
isCompositeColor,
|
|
544
|
+
isDimensionValue,
|
|
545
|
+
wrapScssValue,
|
|
546
|
+
getTokenName,
|
|
547
|
+
buildTokenMap,
|
|
548
|
+
convertToHex8,
|
|
549
|
+
resolveReference,
|
|
550
|
+
resolveCompositeColor,
|
|
551
|
+
getTokenValue,
|
|
552
|
+
isReferenceString,
|
|
553
|
+
buildRecursiveVarChainWithLightDark,
|
|
554
|
+
buildFallbackWithRefs,
|
|
555
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"moduleResolution": "node",
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"allowJs": true,
|
|
13
|
+
"checkJs": false,
|
|
14
|
+
"noEmit": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.js", "config.js"],
|
|
17
|
+
"exclude": ["node_modules", "build", "build-reference"]
|
|
18
|
+
}
|