@grantcodes/style-dictionary 1.1.0 → 1.2.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.
Files changed (65) hide show
  1. package/.turbo/turbo-build.log +45 -0
  2. package/CHANGELOG.md +7 -0
  3. package/README.md +37 -0
  4. package/biome.json +9 -0
  5. package/build.js +10 -0
  6. package/config.js +485 -0
  7. package/demo.html +171 -0
  8. package/lib/color-generator.js +13 -0
  9. package/lib/get-style-dictionary-config.js +223 -0
  10. package/lib/get-themes.js +10 -0
  11. package/lib/index.js +3 -0
  12. package/package.json +60 -35
  13. package/tests/tokens.test.js +87 -0
  14. package/tokens/core/tier-1-definitions/colors.json +175 -0
  15. package/tokens/core/tier-1-definitions/z-index.json +29 -0
  16. package/tokens/grantcodes/tier-1-definitions/animation.json +27 -0
  17. package/tokens/grantcodes/tier-1-definitions/borders.json +36 -0
  18. package/tokens/grantcodes/tier-1-definitions/colors.json +35 -0
  19. package/tokens/grantcodes/tier-1-definitions/shadows.json +39 -0
  20. package/tokens/grantcodes/tier-1-definitions/typography.json +133 -0
  21. package/tokens/grantcodes/tier-2-usage/00-colors-background.json +72 -0
  22. package/tokens/grantcodes/tier-2-usage/00-colors-border.json +42 -0
  23. package/tokens/grantcodes/tier-2-usage/00-colors-content.json +45 -0
  24. package/tokens/grantcodes/tier-2-usage/animation.json +23 -0
  25. package/tokens/grantcodes/tier-2-usage/borders.json +26 -0
  26. package/tokens/grantcodes/tier-2-usage/shadows.json +39 -0
  27. package/tokens/grantcodes/tier-2-usage/typography-usage.json +277 -0
  28. package/tokens/grantcodes/tier-3-components/button.json +94 -0
  29. package/tokens/grantcodes/tier-3-components/focus-ring.json +26 -0
  30. package/tokens/grantcodes/tier-3-components/form.json +69 -0
  31. package/tokens/grantcodes/tier-3-components/link.json +39 -0
  32. package/tokens/todomap/tier-1-definitions/colors.json +92 -0
  33. package/tokens/todomap/tier-1-definitions/typography.json +10 -0
  34. package/tokens/todomap/tier-2-usage/00-colors-background.json +36 -0
  35. package/tokens/todomap/tier-2-usage/00-colors-content.json +33 -0
  36. package/tokens/todomap/tier-2-usage/typography-usage.json +20 -0
  37. package/tokens/todomap/tier-3-components/button.json +141 -0
  38. package/tokens/todomap/tier-3-components/focus-ring.json +26 -0
  39. package/tokens/todomap/tier-3-components/form.json +69 -0
  40. package/tokens/todomap/tier-3-components/link.json +39 -0
  41. package/tokens/wireframe/tier-1-definitions/animation.json +18 -0
  42. package/tokens/wireframe/tier-1-definitions/borders.json +42 -0
  43. package/tokens/wireframe/tier-1-definitions/colors.json +36 -0
  44. package/tokens/wireframe/tier-1-definitions/shadows.json +39 -0
  45. package/tokens/wireframe/tier-1-definitions/typography.json +130 -0
  46. package/tokens/wireframe/tier-2-usage/00-colors-background.json +86 -0
  47. package/tokens/wireframe/tier-2-usage/00-colors-border.json +42 -0
  48. package/tokens/wireframe/tier-2-usage/00-colors-content.json +45 -0
  49. package/tokens/wireframe/tier-2-usage/animation.json +24 -0
  50. package/tokens/wireframe/tier-2-usage/borders.json +33 -0
  51. package/tokens/wireframe/tier-2-usage/shadows.json +39 -0
  52. package/tokens/wireframe/tier-2-usage/typography-usage.json +445 -0
  53. package/tokens/wireframe/tier-3-components/button.json +94 -0
  54. package/tokens/wireframe/tier-3-components/focus-ring.json +26 -0
  55. package/tokens/wireframe/tier-3-components/form.json +69 -0
  56. package/tokens/wireframe/tier-3-components/link.json +39 -0
  57. package/.github/workflows/create-release.yml +0 -45
  58. package/.simple-git-hooks.cjs +0 -1
  59. package/commitlint.config.cjs +0 -1
  60. package/dist/css/default/style-dictionary.css +0 -181
  61. package/dist/css/todomap/style-dictionary.css +0 -181
  62. package/dist/js/default/style-dictionary.js +0 -179
  63. package/dist/js/todomap/style-dictionary.js +0 -179
  64. package/dist/scss/default/_style-dictionary.scss +0 -181
  65. package/dist/scss/todomap/_style-dictionary.scss +0 -181
@@ -0,0 +1,45 @@
1
+
2
+
3
+ > @grantcodes/style-dictionary@1.2.1 build /home/grantcodes/projects/@grantcodes/ui/packages/style-dictionary
4
+ > node ./build.js
5
+
6
+ 🚧 No theme specified, building all themes...
7
+
8
+ 🏗️ Building WIREFRAME theme
9
+
10
+ 🏗️ Building GRANTCODES theme
11
+
12
+ 🏗️ Building TODOMAP theme
13
+
14
+ css
15
+ ✔︎ dist/css/todomap/tokens.css
16
+ ✔︎ dist/css/todomap/todomap.css
17
+
18
+ json
19
+ ✔︎ dist/json/todomap/tokens.json
20
+
21
+ css
22
+ ✔︎ dist/css/grantcodes/tokens.css
23
+ ✔︎ dist/css/grantcodes/grantcodes.css
24
+
25
+ json
26
+ ✔︎ dist/json/grantcodes/tokens.json
27
+
28
+ css
29
+ ✔︎ dist/css/wireframe/tokens.css
30
+ ✔︎ dist/css/wireframe/wireframe.css
31
+
32
+ json
33
+ ✔︎ dist/json/wireframe/tokens.json
34
+
35
+ ts
36
+ ✔︎ dist/js/todomap/style-dictionary.js
37
+ ✔︎ dist/js/todomap/style-dictionary.d.ts
38
+
39
+ ts
40
+ ✔︎ dist/js/grantcodes/style-dictionary.js
41
+ ✔︎ dist/js/grantcodes/style-dictionary.d.ts
42
+
43
+ ts
44
+ ✔︎ dist/js/wireframe/style-dictionary.js
45
+ ✔︎ dist/js/wireframe/style-dictionary.d.ts
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.1](https://github.com/grantcodes/ui/compare/style-dictionary-v1.2.0...style-dictionary-v1.2.1) (2026-03-07)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * update imports & exports ([db68e69](https://github.com/grantcodes/ui/commit/db68e69f5307fd62f26a6e65c9904e86ab156af6))
9
+
3
10
  ## [1.1.0](https://github.com/grantcodes/style-dictionary/compare/v1.0.0...v1.1.0) (2022-09-23)
4
11
 
5
12
 
package/README.md CHANGED
@@ -1,3 +1,40 @@
1
1
  # @grantcodes/style-dictionary
2
2
 
3
3
  This is my personal [style dictionary](https://github.com/amzn/style-dictionary) that I use for various personal projects.
4
+
5
+ ## Naming & structure
6
+
7
+ It follows [Brad Frost's](https://bradfrost.com) recommended token structure.
8
+
9
+ Priciples:
10
+
11
+ - Clarity over cleverness - keep the names simple and understandable
12
+ - Legibility over succinctiness - long but readable variables > short hard to understand variables
13
+ - Consistency is key
14
+ - Use existing conventions
15
+ - Be environment agnostic - don't name things specifically for the web or mobile
16
+
17
+ ### Tier 1 tokens
18
+
19
+ | Namespace | Category | Property | Variant / Scale | CSS Variable |
20
+ ---------------------------------------------------------------
21
+ | g | color | red | 100 | --g-color-red-100 |
22
+ | g | typography | | | --g- |
23
+ | g | spacing | | | --g- |
24
+ | g | border | | | --g- |
25
+ | g | shadow | | | --g- |
26
+ | g | animation | | | --g- |
27
+ | g | breakpoint | | | --g- |
28
+ | g | z-index | | | --g- |
29
+
30
+
31
+
32
+ ## Usage
33
+
34
+ ```css
35
+
36
+ @import "@grantcodes/style-dictionary";
37
+
38
+
39
+
40
+ ```
package/biome.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "root": false,
3
+ "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
4
+ "extends": ["../biome.json"],
5
+ "files": {
6
+ "ignoreUnknown": false,
7
+ "includes": ["tokens/**", "lib/**", "build.js", "config.js"]
8
+ }
9
+ }
package/build.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Build script to create style dictionary outputs for each theme
3
+ * This now uses the config.js file which contains all the build logic
4
+ */
5
+
6
+ // The build logic is now in config.js and runs automatically when executed
7
+ // This file can be kept for backwards compatibility or removed
8
+ // Run: node config.js or node build.js (which runs config.js)
9
+
10
+ import "./config.js";
package/config.js ADDED
@@ -0,0 +1,485 @@
1
+ import StyleDictionary from "style-dictionary";
2
+ import minimist from "minimist";
3
+
4
+ /**
5
+ * List of themes to build
6
+ * 1) Add your new theme here in order to have it show up in the dropdown
7
+ */
8
+ const AVAILABLE_THEMES = ["wireframe", "grantcodes", "todomap"];
9
+
10
+ /**
11
+ * Look for args passed on the command line
12
+ * 1) Used to build a single theme if needed
13
+ * 2) Pass in the theme name as an argument
14
+ * 3) If no argument is passed, build all themes
15
+ */
16
+ const args = minimist(process.argv.slice(2));
17
+ const theme = args.theme;
18
+
19
+ /**
20
+ * Helper function to check if token is from tier-2 or tier-3
21
+ * 1) Used to determine if the token should be included in the theme tokens to apply `theme` prefix
22
+ */
23
+ const isHigherTierToken = (filePath) => {
24
+ const isHigherTier =
25
+ filePath.includes("tier-2-usage") || filePath.includes("tier-3-components");
26
+ return isHigherTier;
27
+ };
28
+
29
+ /**
30
+ * Transform shadow tokens
31
+ * 1) Used to transform the individual shadow tokens into a single box-shadow-sm, box-shadow-md, etc. with x, y, blur, spread, and color concatenated
32
+ */
33
+ const transformShadowTokens = (dictionary, size, themeTokens) => {
34
+ const shadowProps = dictionary.allTokens.filter(
35
+ (p) =>
36
+ isHigherTierToken(p.filePath) &&
37
+ p.path[0] === "box-shadow" &&
38
+ p.path[1] === size,
39
+ );
40
+
41
+ const x = shadowProps.find((p) => p.path[2] === "x")?.value || "0px";
42
+ const y = shadowProps.find((p) => p.path[2] === "y")?.value || "0px";
43
+ const blur = shadowProps.find((p) => p.path[2] === "blur")?.value || "0px";
44
+ const spread =
45
+ shadowProps.find((p) => p.path[2] === "spread")?.value || "0px";
46
+ const color =
47
+ shadowProps.find((p) => p.path[2] === "color")?.value || "transparent";
48
+
49
+ /* 1 */
50
+ themeTokens.push(
51
+ ` --g-theme-box-shadow-${size}: ${x} ${y} ${blur} ${spread} ${color};`,
52
+ );
53
+ };
54
+
55
+ /**
56
+ * Transform line height tokens
57
+ * 1) Used to transform the line height tokens to a unitless value for CSS by dividing the line height by the font size
58
+ */
59
+ const transformLineHeight = (
60
+ dictionary,
61
+ prop,
62
+ outputArray,
63
+ formatTokenName,
64
+ ) => {
65
+ const cleanPath = prop.path
66
+ .map((segment) =>
67
+ segment.startsWith("@") ? segment.substring(1) : segment,
68
+ )
69
+ .filter((segment) => segment !== "")
70
+ .join("-");
71
+
72
+ // For tier-1 tokens (typography.line-height.16), find matching font-size (typography.font-size.16)
73
+ // For tier-2/3 tokens (typography.headline-lg.line-height), find matching font-size (typography.headline-lg.font-size)
74
+ const isTier1 =
75
+ prop.path.length === 3 &&
76
+ prop.path[0] === "typography" &&
77
+ prop.path[1] === "line-height";
78
+ let fontSizePath;
79
+ if (isTier1) {
80
+ // Tier-1: typography.line-height.16 -> typography.font-size.16
81
+ fontSizePath = ["typography", "font-size", prop.path[2]];
82
+ } else {
83
+ // Tier-2/3: typography.headline-lg.line-height -> typography.headline-lg.font-size
84
+ fontSizePath = [...prop.path.slice(0, -1), "font-size"];
85
+ }
86
+
87
+ const fontSizeProp = dictionary.allTokens.find(
88
+ (p) => p.path.join(".") === fontSizePath.join("."),
89
+ );
90
+
91
+ // Parse line-height value to pixels
92
+ let lineHeightPx;
93
+ if (prop.value.endsWith("px")) {
94
+ lineHeightPx = parseFloat(prop.value.replace("px", ""));
95
+ } else if (prop.value.endsWith("rem")) {
96
+ lineHeightPx = parseFloat(prop.value.replace("rem", "")) * 16;
97
+ } else {
98
+ lineHeightPx = parseFloat(prop.value);
99
+ }
100
+
101
+ if (fontSizeProp) {
102
+ // Found matching font-size - use it for calculation
103
+ let fontSizePx;
104
+ if (fontSizeProp.value.endsWith("px")) {
105
+ fontSizePx = parseFloat(fontSizeProp.value.replace("px", ""));
106
+ } else if (fontSizeProp.value.endsWith("rem")) {
107
+ fontSizePx = parseFloat(fontSizeProp.value.replace("rem", "")) * 16;
108
+ } else {
109
+ fontSizePx = parseFloat(fontSizeProp.value);
110
+ }
111
+
112
+ if (fontSizePx > 0) {
113
+ const unitlessValue = (lineHeightPx / fontSizePx).toFixed(2);
114
+ outputArray.push(
115
+ ` ${formatTokenName(cleanPath, prop)}: ${unitlessValue};`,
116
+ );
117
+ return;
118
+ }
119
+ } else if (isTier1 && lineHeightPx > 0) {
120
+ // For tier-1 tokens without matching font-size, use 16px as base font-size
121
+ const BASE_FONT_SIZE = 16;
122
+ const unitlessValue = (lineHeightPx / BASE_FONT_SIZE).toFixed(2);
123
+ outputArray.push(
124
+ ` ${formatTokenName(cleanPath, prop)}: ${unitlessValue};`,
125
+ );
126
+ return;
127
+ }
128
+
129
+ // Fallback: output as-is if we can't calculate
130
+ outputArray.push(` ${formatTokenName(cleanPath, prop)}: ${prop.value};`);
131
+ };
132
+
133
+ /**
134
+ * Format variables
135
+ * 1) Used to format the inner contents of the :root or .[theme-name] ruleset
136
+ */
137
+ const formatVariables = (dictionary) => {
138
+ const processedShadows = new Set();
139
+ const tier1Tokens = [];
140
+ const themeTokens = [];
141
+
142
+ /**
143
+ * Format token global prefix and tier 2/3 identifier
144
+ * 1) If the token is from tier-2 or tier-3, prefix it with `g-theme-`
145
+ * 2) Otherwise, prefix it with `g-`
146
+ */
147
+ const formatTokenName = (cleanPath, prop) => {
148
+ if (isHigherTierToken(prop.filePath)) {
149
+ return `--g-theme-${cleanPath}`;
150
+ }
151
+ return `--g-${cleanPath}`;
152
+ };
153
+
154
+ /**
155
+ * Get all box-shadow values from tier 2/3
156
+ * 1) Used to determine which box-shadow values to transform into a single box-shadow-sm, box-shadow-md, etc.
157
+ */
158
+ const shadowSizes = dictionary.tokens.shadow
159
+ ? Object.keys(dictionary.tokens.shadow)
160
+ .map((key) => key.split("-")[0])
161
+ .filter((value, index, self) => self.indexOf(value) === index) // Get unique sizes
162
+ : [];
163
+
164
+ /**
165
+ * Iterate over all tokens and format them
166
+ *
167
+ */
168
+ dictionary.allTokens.forEach((prop) => {
169
+ /**
170
+ * 1) Always include z-index and size tokens from core
171
+ */
172
+ if (prop.path[0] === "z-index") {
173
+ const cleanPath = prop.path
174
+ .map((segment) =>
175
+ segment.startsWith("@") ? segment.substring(1) : segment,
176
+ )
177
+ .filter((segment) => segment !== "")
178
+ .join("-");
179
+ tier1Tokens.push(` ${formatTokenName(cleanPath, prop)}: ${prop.value};`);
180
+ return;
181
+ }
182
+
183
+ /**
184
+ * Handle box-shadow token transformations
185
+ * If the token is from tier-2 or tier-3 and is a box-shadow, transform it into a single box-shadow-sm, box-shadow-md, etc.
186
+ */
187
+ if (
188
+ isHigherTierToken(prop.filePath) &&
189
+ prop.path[0] === "box-shadow" &&
190
+ shadowSizes.includes(prop.path[1])
191
+ ) {
192
+ const size = prop.path[1];
193
+ if (processedShadows.has(size)) return;
194
+ processedShadows.add(size);
195
+ transformShadowTokens(dictionary, size, themeTokens);
196
+ } else if (
197
+ prop.path[0] === "typography" &&
198
+ prop.path.includes("line-height")
199
+ ) {
200
+ /**
201
+ * Handle line heights in typography (both tier-1 and tier-2/3)
202
+ * 1) Transform line-height tokens to unitless values by dividing by font-size
203
+ */
204
+ const outputArray = isHigherTierToken(prop.filePath)
205
+ ? themeTokens
206
+ : tier1Tokens;
207
+ transformLineHeight(dictionary, prop, outputArray, formatTokenName);
208
+ } else if (!prop.path.includes("box-shadow") || prop.path.length > 3) {
209
+ /**
210
+ * Handle all other properties
211
+ * 1) If the token is not a box-shadow or typography token, format it as a normal token
212
+ */
213
+ const cleanPath = prop.path
214
+ .map((segment) =>
215
+ segment.startsWith("@") ? segment.substring(1) : segment,
216
+ )
217
+ .filter((segment) => segment !== "")
218
+ .join("-");
219
+
220
+ const token = ` ${formatTokenName(cleanPath, prop)}: ${prop.value};`;
221
+ if (isHigherTierToken(prop.filePath)) {
222
+ themeTokens.push(token);
223
+ } else {
224
+ tier1Tokens.push(token);
225
+ }
226
+ }
227
+ });
228
+
229
+ return [...new Set([...tier1Tokens, ...themeTokens])].join("\n");
230
+ };
231
+
232
+ /**
233
+ * Transform shadow tokens for JSON format
234
+ * Combines individual shadow properties into a single value
235
+ */
236
+ const transformShadowTokensJSON = (dictionary, size) => {
237
+ const shadowProps = dictionary.allTokens.filter(
238
+ (p) =>
239
+ isHigherTierToken(p.filePath) &&
240
+ p.path[0] === "box-shadow" &&
241
+ p.path[1] === size,
242
+ );
243
+
244
+ const x = shadowProps.find((p) => p.path[2] === "x")?.value || "0px";
245
+ const y = shadowProps.find((p) => p.path[2] === "y")?.value || "0px";
246
+ const blur = shadowProps.find((p) => p.path[2] === "blur")?.value || "0px";
247
+ const spread =
248
+ shadowProps.find((p) => p.path[2] === "spread")?.value || "0px";
249
+ const color =
250
+ shadowProps.find((p) => p.path[2] === "color")?.value || "transparent";
251
+
252
+ return `${x} ${y} ${blur} ${spread} ${color}`;
253
+ };
254
+
255
+ /**
256
+ * Generate a Theme-Specific Config
257
+ * This accepts a theme parameter, which is used to control which set of
258
+ * tokens to compile, and to define theme-specific compiled output.
259
+ * @param {string} theme
260
+ */
261
+ const getStyleDictionaryConfig = (theme) => {
262
+ // Register the JSON formatter
263
+ StyleDictionary.registerFormat({
264
+ name: "json/flat/custom",
265
+ format: function (dictionary) {
266
+ const transformedTokens = {};
267
+
268
+ /**
269
+ * Get all box-shadow values from tier 2/3
270
+ * 1) Used to determine which box-shadow values to transform into a single box-shadow-sm, box-shadow-md, etc.
271
+ */
272
+ const shadowSizes = dictionary.tokens.shadow
273
+ ? Object.keys(dictionary.tokens.shadow)
274
+ .map((key) => key.split("-")[0])
275
+ .filter((value, index, self) => self.indexOf(value) === index) // Get unique sizes
276
+ : [];
277
+
278
+ // Process regular tokens
279
+ dictionary.allTokens.forEach((token) => {
280
+ // Remove the isHigherTierToken check to include all tokens
281
+ if (token.path[0] === "box-shadow" && token.path.length > 2) return;
282
+ const prefix = isHigherTierToken(token.filePath) ? "g-theme-" : "g-";
283
+ transformedTokens[`${prefix}${token.path.join("-")}`] = token.value;
284
+ });
285
+
286
+ // Process shadow tokens
287
+ shadowSizes.forEach((size) => {
288
+ transformedTokens[`g-theme-box-shadow-${size}`] =
289
+ transformShadowTokensJSON(dictionary, size);
290
+ });
291
+
292
+ return JSON.stringify(transformedTokens, null, 2);
293
+ },
294
+ });
295
+
296
+ /**
297
+ * Register the CSS formatter
298
+ * 1) Used to format the inner contents of the .[theme-name] ruleset for Storybook only or if you want to define tokens using a theme name
299
+ */
300
+ StyleDictionary.registerFormat({
301
+ name: "css/variables-themed",
302
+ format: function (dictionary) {
303
+ return `.${theme} {\n${formatVariables(dictionary)}\n}\n`;
304
+ },
305
+ });
306
+
307
+ /**
308
+ * Define the base pixel value for rem conversion
309
+ */
310
+ const BASE_FONT_SIZE = 16; // Typically 16px = 1rem
311
+
312
+ /**
313
+ * Register the size/px-to-rem transform
314
+ * 1) Used to convert px values to rem values
315
+ * 2) Match only properties with px values
316
+ * 3) Only transform if it's a valid px value
317
+ */
318
+ StyleDictionary.registerTransform({
319
+ name: "size/px-to-rem",
320
+ type: "value",
321
+ matcher: function (prop) {
322
+ /* 2 */
323
+ return (
324
+ prop &&
325
+ prop.value &&
326
+ typeof prop.value === "string" &&
327
+ prop.value.endsWith("px")
328
+ );
329
+ },
330
+ transform: function (prop) {
331
+ if (!prop || !prop.value) return prop.value;
332
+ /* 3 */
333
+ const pxValue = prop.value.trim();
334
+ if (!pxValue.endsWith("px")) return prop.value;
335
+
336
+ const pixels = parseFloat(pxValue);
337
+ if (isNaN(pixels)) return prop.value;
338
+
339
+ const remValue = Number((pixels / BASE_FONT_SIZE).toFixed(4)).toString();
340
+ return `${remValue}rem`;
341
+ },
342
+ });
343
+
344
+ /**
345
+ * Register the CSS formatter
346
+ * 1) Used to format the inner contents of the :root ruleset for Storybook only or if you want to define tokens with theme prefix
347
+ */
348
+ StyleDictionary.registerFormat({
349
+ name: "css/custom-variables",
350
+ format: function (dictionary) {
351
+ return `:root {\n${formatVariables(dictionary)}\n}`;
352
+ },
353
+ });
354
+
355
+ /**
356
+ * Register the name/theme-prefix transform
357
+ * 1) Used to prefix the token name with the theme name
358
+ * 2) If the token is from tier-2 or tier-3, prefix it with `GTheme` for JS
359
+ * 3) Otherwise, prefix it with `G` for JS
360
+ */
361
+ StyleDictionary.registerTransform({
362
+ name: "name/theme-prefix",
363
+ type: "name",
364
+ transform: function (token) {
365
+ const cleanPath = token.path
366
+ .map((segment) =>
367
+ segment.startsWith("@") ? segment.substring(1) : segment,
368
+ )
369
+ .filter((segment) => segment !== "")
370
+ .join("-");
371
+
372
+ /* 2 */
373
+ if (isHigherTierToken(token.filePath)) {
374
+ return `GTheme${cleanPath
375
+ .split("-")
376
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
377
+ .join("")}`;
378
+ }
379
+
380
+ /* 3 */
381
+ return `G${cleanPath
382
+ .split("-")
383
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
384
+ .join("")}`;
385
+ },
386
+ });
387
+
388
+ /**
389
+ * Register the custom/css transform group
390
+ */
391
+ StyleDictionary.registerTransformGroup({
392
+ name: "custom/css",
393
+ transforms: ["attribute/cti", "name/kebab", "size/px-to-rem"],
394
+ });
395
+
396
+ /**
397
+ * Register the custom/js transform group
398
+ */
399
+ StyleDictionary.registerTransformGroup({
400
+ name: "custom/js",
401
+ transforms: ["attribute/cti", "name/theme-prefix", "size/px-to-rem"],
402
+ });
403
+
404
+ /**
405
+ * Define the config
406
+ * 1) Used to define the platforms, prefixes, etc. to build the tokens with/for
407
+ */
408
+ const config = {
409
+ source: [`./tokens/core/**/*.json`, `./tokens/${theme}/**/*.json`],
410
+ log: {
411
+ // Set the log level to show errors, warnings, and info messages
412
+ verbosity: "verbose",
413
+ },
414
+ platforms: {
415
+ ts: {
416
+ transformGroup: "custom/js",
417
+ prefix: "G",
418
+ buildPath: `./dist/js/${theme}/`,
419
+ filter: {
420
+ attributes: {
421
+ category: "theme",
422
+ },
423
+ },
424
+ files: [
425
+ {
426
+ destination: "style-dictionary.js",
427
+ format: "javascript/es6",
428
+ },
429
+ {
430
+ destination: "style-dictionary.d.ts",
431
+ format: "typescript/es6-declarations",
432
+ },
433
+ ],
434
+ },
435
+ css: {
436
+ transformGroup: "custom/css",
437
+ prefix: "g",
438
+ buildPath: `./dist/css/${theme}/`,
439
+ files: [
440
+ {
441
+ destination: "tokens.css",
442
+ format: "css/custom-variables",
443
+ },
444
+ {
445
+ destination: `${theme}.css`,
446
+ format: "css/variables-themed",
447
+ },
448
+ ],
449
+ },
450
+ json: {
451
+ transformGroup: "custom/css",
452
+ prefix: "g",
453
+ buildPath: `./dist/json/${theme}/`,
454
+ files: [
455
+ {
456
+ destination: "tokens.json",
457
+ format: "json/flat/custom",
458
+ },
459
+ ],
460
+ },
461
+ },
462
+ };
463
+
464
+ return config;
465
+ };
466
+
467
+ /**
468
+ * Build the tokens
469
+ * 1) If no theme is specified, build all themes
470
+ * 2) Otherwise, build the specified theme
471
+ */
472
+ if (!theme) {
473
+ console.log("🚧 No theme specified, building all themes...");
474
+ AVAILABLE_THEMES.forEach((themeName) => {
475
+ console.log(`\n🏗️ Building ${themeName.toUpperCase()} theme`);
476
+ const themeConfig = getStyleDictionaryConfig(themeName);
477
+ const StyleDictionaryExtended = new StyleDictionary(themeConfig);
478
+ StyleDictionaryExtended.buildAllPlatforms();
479
+ });
480
+ } else {
481
+ console.log(`🚧 Building ${theme.toUpperCase()} theme`);
482
+ const config = getStyleDictionaryConfig(theme);
483
+ const StyleDictionaryExtended = new StyleDictionary(config);
484
+ StyleDictionaryExtended.buildAllPlatforms();
485
+ }