@carbon/themes 12.0.0-alpha.1 → 12.0.0-alpha.3

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 (57) hide show
  1. package/es/index.js +5572 -0
  2. package/lib/__tests__/themes-test.d.ts +1 -0
  3. package/lib/__tests__/tools-test.d.ts +1 -0
  4. package/lib/tokens/__tests__/Token-test.d.ts +1 -0
  5. package/lib/tokens/__tests__/TokenFormat-test.d.ts +1 -0
  6. package/lib/tokens/__tests__/TokenGroup-test.d.ts +1 -0
  7. package/lib/tokens/__tests__/TokenSet-test.d.ts +1 -0
  8. package/lib/tokens/__tests__/metadata-test.d.ts +1 -0
  9. package/lib/tokens/__tests__/v11-test.d.ts +1 -0
  10. package/lib/v10/__tests__/themes-test.d.ts +1 -0
  11. package/lib/v10/__tests__/tokens-test.d.ts +1 -0
  12. package/package.json +13 -4
  13. package/umd/index.js +6322 -0
  14. package/docs/sass.md +0 -189
  15. package/src/dtcg/README.md +0 -336
  16. package/src/dtcg/color-palette.json +0 -4424
  17. package/src/dtcg/components/button.json +0 -193
  18. package/src/dtcg/components/content-switcher.json +0 -60
  19. package/src/dtcg/components/notification.json +0 -130
  20. package/src/dtcg/components/status.json +0 -122
  21. package/src/dtcg/components/tag.json +0 -486
  22. package/src/dtcg/themes.json +0 -3356
  23. package/src/index.ts +0 -129
  24. package/src/tokens/Token.ts +0 -41
  25. package/src/tokens/TokenFormat.ts +0 -93
  26. package/src/tokens/TokenGroup.ts +0 -185
  27. package/src/tokens/TokenSet.ts +0 -91
  28. package/src/tokens/components.ts +0 -120
  29. package/src/tokens/index.ts +0 -59
  30. package/src/tokens/layout.ts +0 -39
  31. package/src/tokens/type.ts +0 -49
  32. package/src/tokens/types.ts +0 -28
  33. package/src/tokens/v10.ts +0 -188
  34. package/src/tokens/v11TokenGroup.ts +0 -546
  35. package/src/tokens/v11TokenSet.ts +0 -93
  36. package/src/tools.ts +0 -69
  37. package/src/v10/g10.ts +0 -349
  38. package/src/v10/g100.ts +0 -348
  39. package/src/v10/g90.ts +0 -350
  40. package/src/v10/index.ts +0 -22
  41. package/src/v10/metadata.yml +0 -217
  42. package/src/v10/tokens.ts +0 -397
  43. package/src/v10/white.ts +0 -352
  44. package/style-dictionary/formats/js-component-tokens.js +0 -128
  45. package/style-dictionary/formats/js-themes.js +0 -84
  46. package/style-dictionary/formats/scss-component-tokens.js +0 -149
  47. package/style-dictionary/formats/scss-themes.js +0 -127
  48. package/style-dictionary/formats/scss-tokens.js +0 -91
  49. package/style-dictionary/preprocessors/component-tokens.js +0 -172
  50. package/style-dictionary/preprocessors/dual-role.js +0 -157
  51. package/style-dictionary/preprocessors/theme-metadata.js +0 -46
  52. package/style-dictionary/sd.config.js +0 -449
  53. package/style-dictionary/transforms/alpha-modifier.js +0 -68
  54. package/style-dictionary/transforms/color-flatten.js +0 -79
  55. package/test-dtcg.scss +0 -28
  56. package/tsconfig.json +0 -8
  57. package/tsconfig.types.json +0 -10
@@ -1,127 +0,0 @@
1
- /**
2
- * Copyright IBM Corp. 2026
3
- *
4
- * This source code is licensed under the Apache-2.0 license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- 'use strict';
9
-
10
- /**
11
- *
12
- *
13
- * Each theme (white, g10, g90, g100) is expected to be a separate SD
14
- * *platform* build run. This format receives all tokens for ONE theme and
15
- * emits the Sass map variable for that theme plus the utilities.merge call
16
- * that folds in layout spacing and type tokens.
17
- *
18
- * ─────────────────────────────────────────────────────────────────────────────
19
- * NOTE ON MULTI-THEME FILES
20
- * ─────────────────────────────────────────────────────────────────────────────
21
- * The recommended approach: run all four platforms, collect their individual
22
- * strings, and concatenate them in sd.config.js using a custom action.
23
- * A simpler alternative (and the one wired up in sd.config.js) is to use SD's
24
- * `buildAllPlatforms()` with four platforms that each write to a *separate*
25
- * file, then a post-build step (`buildAllPlatforms` custom action) concatenates
26
- * them. Both patterns are documented in style-dictionary/README.md.
27
- */
28
-
29
- const FILE_BANNER = `// Code generated by @carbon/themes. DO NOT EDIT.
30
- //
31
- // Copyright IBM Corp. 2018, 2023
32
- //
33
- // This source code is licensed under the Apache-2.0 license found in the
34
- // LICENSE file in the root directory of this source tree.
35
- //
36
- `;
37
-
38
- const SCSS_IMPORTS = `@use 'sass:map';
39
- @use 'sass:string';
40
- @use '@carbon/layout';
41
- @use '@carbon/type';
42
- @use '../utilities';
43
- `;
44
-
45
- /**
46
- * Serialize a CSS color value to a Sass literal.
47
- *
48
- * - Hex colors → bare hex (e.g. #ffffff)
49
- * - rgba() → bare function (e.g. rgba(141, 141, 141, 0.5))
50
- * - Strings → string.unquote("…")
51
- * - Numbers → bare number
52
- *
53
- * @param {string|number} value
54
- * @returns {string} Sass source fragment
55
- */
56
- function toSassLiteral(value) {
57
- if (typeof value === 'number') {
58
- return String(value);
59
- }
60
- if (typeof value !== 'string') {
61
- return JSON.stringify(value);
62
- }
63
- if (value.startsWith('#')) {
64
- return value;
65
- }
66
- if (
67
- value.startsWith('rgb') ||
68
- value.endsWith('px') ||
69
- value.endsWith('em') ||
70
- value.endsWith('%') ||
71
- value.endsWith('vw') ||
72
- value.endsWith('vh') ||
73
- value === '0'
74
- ) {
75
- return value;
76
- }
77
- // Plain string (e.g. 'light', 'dark') → Sass unquote with single quotes,
78
- // matching t.SassValue("string.unquote('...')") in the old builder.
79
- return `string.unquote('${value}')`;
80
- }
81
-
82
- /**
83
- * @param {{ dictionary: import('style-dictionary').Dictionary, options: object, file: object }} args
84
- * @returns {string}
85
- */
86
- function carbonScssThemesFormat({ dictionary, options, file }) {
87
- const themeName = options?.themeName ?? file?.options?.themeName ?? 'unknown';
88
-
89
- // Sort alphabetically — matches dtcg-themes.js which calls .sort(([a],[b])=>a.localeCompare(b))
90
- // color-scheme lands at its natural alpha position ('c' section).
91
- const sorted = [...dictionary.allTokens].sort((a, b) =>
92
- a.name.localeCompare(b.name)
93
- );
94
-
95
- const entries = sorted
96
- .map((token) => {
97
- const name = token.name;
98
- // SD v5 may store the post-alias value as a full token node object in
99
- // token.value when an alias points to a sibling token. Unwrap it.
100
- let value = token.value ?? token.$value;
101
- if (value !== null && typeof value === 'object' && '$value' in value) {
102
- value = value.$value;
103
- }
104
- // Unquoted key — matches t.SassMapProperty(t.Identifier(token), ...) in old builder
105
- return ` ${name}: ${toSassLiteral(value)},`;
106
- })
107
- .join('\n');
108
-
109
- return (
110
- FILE_BANNER +
111
- '\n' +
112
- SCSS_IMPORTS +
113
- '\n' +
114
- `$${themeName}: (\n${entries}\n) !default;\n` +
115
- `$${themeName}: utilities.merge(\n` +
116
- ` $${themeName},\n` +
117
- ` layout.$spacing,\n` +
118
- ` layout.$fluid-spacing,\n` +
119
- ` type.$tokens\n` +
120
- `);\n`
121
- );
122
- }
123
-
124
- module.exports = {
125
- name: 'carbon/scss-themes',
126
- format: carbonScssThemesFormat,
127
- };
@@ -1,91 +0,0 @@
1
- /**
2
- * Copyright IBM Corp. 2026
3
- *
4
- * This source code is licensed under the Apache-2.0 license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- 'use strict';
9
-
10
- /**
11
- * The format does not actually use the SD `dictionary` — it reads the token
12
- * list from the Carbon JS metadata directly, matching the original builder.
13
- * This is intentional: _tokens.scss is a mapping layer, not derived from the
14
- * token values.
15
- */
16
-
17
- const { types: t } = require('@carbon/scss-generator');
18
- const { generate } = require('@carbon/scss-generator');
19
- const { group } = require('../../src/tokens');
20
-
21
- const FILE_BANNER = `// Code generated by @carbon/themes. DO NOT EDIT.
22
- //
23
- // Copyright IBM Corp. 2018, 2023
24
- //
25
- // This source code is licensed under the Apache-2.0 license found in the
26
- // LICENSE file in the root directory of this source tree.
27
- //
28
- `;
29
-
30
- /**
31
- * @returns {Promise<string>}
32
- */
33
- async function carbonScssTokensFormat() {
34
- const tokens = group.getTokens();
35
-
36
- const variables = tokens.flatMap((token) => {
37
- const id = token.name;
38
- return [
39
- t.Newline(),
40
- t.Comment(`/ The CSS Custom Property for the \`${id}\` token`),
41
- t.Assignment({
42
- id: t.Identifier(id),
43
- init: t.SassFunctionCall({
44
- id: t.Identifier('_get'),
45
- params: [t.SassString(id)],
46
- }),
47
- default: true,
48
- }),
49
- ];
50
- });
51
-
52
- const stylesheet = t.StyleSheet([
53
- t.SassModule('sass:map'),
54
- t.SassModule('../config'),
55
- t.SassModule('../theme'),
56
- t.Newline(),
57
- t.Comment('/ Internal helper for generating CSS Custom Properties'),
58
- t.SassFunction({
59
- id: t.Identifier('_get'),
60
- params: [t.Identifier('token')],
61
- body: t.BlockStatement([
62
- t.IfStatement({
63
- test: t.LogicalExpression({
64
- left: t.SassValue('config.$use-fallback-value'),
65
- operator: '==',
66
- right: t.SassBoolean(false),
67
- }),
68
- consequent: t.BlockStatement([
69
- t.AtReturn(t.SassValue('var(--#{config.$prefix}-#{$token})')),
70
- ]),
71
- alternate: t.BlockStatement([
72
- t.AtReturn(
73
- t.SassValue(
74
- 'var(--#{config.$prefix}-#{$token}, #{theme.get($token)})'
75
- )
76
- ),
77
- ]),
78
- }),
79
- ]),
80
- }),
81
- ...variables,
82
- ]);
83
-
84
- const { code } = generate(stylesheet);
85
- return FILE_BANNER + '\n' + (await code);
86
- }
87
-
88
- module.exports = {
89
- name: 'carbon/scss-tokens',
90
- format: carbonScssTokensFormat,
91
- };
@@ -1,172 +0,0 @@
1
- /**
2
- * Copyright IBM Corp. 2026
3
- *
4
- * This source code is licensed under the Apache-2.0 license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- 'use strict';
9
-
10
- /**
11
- * Preprocessor: carbon/component-tokens
12
- *
13
- * Expands the `$extensions["carbon.themes"]` multi-theme shape that component
14
- * token files (button.json, tag.json, …) use into flat per-theme tokens that
15
- * Style Dictionary can process natively.
16
- *
17
- * Input — one token node with no $value but a per-theme map in extensions.
18
- * Two equivalent forms are accepted:
19
- *
20
- * Component-token form (legacy, bare strings + separate alphaModifiers):
21
- * "button": {
22
- * "danger-active": {
23
- * "$type": "color",
24
- * "$extensions": {
25
- * "carbon.themes": { "white": "{red.80}", "g10": "{red.80}", ... },
26
- * "org.carbon": { "alphaModifiers": { "g90": 0.3, "g100": 0.3 } }
27
- * }
28
- * }
29
- * }
30
- *
31
- * Unified theme-token form (value + alpha co-located per theme):
32
- * "background": {
33
- * "hover": {
34
- * "$type": "color",
35
- * "$extensions": {
36
- * "carbon.themes": {
37
- * "white": { "value": "{gray.50}", "alpha": 0.12 },
38
- * "g10": { "value": "{gray.50}", "alpha": 0.12 },
39
- * "g90": "{gray.50}",
40
- * "g100": "{gray.50}"
41
- * }
42
- * }
43
- * }
44
- * }
45
- *
46
- * Output — one flat sibling token per theme, keyed as
47
- * `<tokenPath>-_by_theme-<themeName>` so the format functions can reconstruct
48
- * the original token name and theme:
49
- *
50
- * "button": {
51
- * "danger-active": {
52
- * "_by_theme": {
53
- * "white": { "$type": "color", "$value": "{red.80}" },
54
- * "g10": { "$type": "color", "$value": "{red.80}" },
55
- * "g90": { "$type": "color", "$value": "{red.80}",
56
- * "$extensions": { "org.carbon": { "alphaModifier": 0.3 } } },
57
- * "g100": { "$type": "color", "$value": "{red.80}",
58
- * "$extensions": { "org.carbon": { "alphaModifier": 0.3 } } }
59
- * }
60
- * }
61
- * }
62
- *
63
- * SD v5 walks DTCG token trees and treats any node with a `$value` as a leaf
64
- * token. The `_by_theme.<themeName>` nodes each have `$value`, so SD resolves
65
- * aliases and applies transforms on them individually.
66
- *
67
- * The format functions identify these tokens by the `-_by_theme-` infix in
68
- * their flattened name (using the `name/kebab` transform).
69
- *
70
- * Tokens that already carry a top-level $value are left untouched.
71
- *
72
- * @param {object} dictionary Raw DTCG token tree passed by SD
73
- * @returns {object} Expanded token tree
74
- */
75
- function carbonComponentTokensPreprocessor(dictionary) {
76
- function walk(node) {
77
- if (!node || typeof node !== 'object') return node;
78
-
79
- const out = {};
80
-
81
- for (const [key, value] of Object.entries(node)) {
82
- if (key.startsWith('$')) {
83
- out[key] = value;
84
- continue;
85
- }
86
-
87
- if (!value || typeof value !== 'object') {
88
- out[key] = value;
89
- continue;
90
- }
91
-
92
- const carbonThemes = value.$extensions?.['carbon.themes'];
93
- const alphaModifiers =
94
- value.$extensions?.['org.carbon']?.alphaModifiers ?? {};
95
-
96
- // Only expand nodes that have carbon.themes and NO existing $value.
97
- if (carbonThemes && value.$value === undefined) {
98
- const byTheme = {};
99
-
100
- // Preserve original JSON key order (important for 'fallback' position).
101
- // Each entry in carbon.themes is either:
102
- // - a bare string/alias → { "white": "{red.80}" } (legacy component form)
103
- // - an object { value, alpha? } → unified theme form where value and
104
- // alpha are co-located under the same theme key.
105
- // The legacy separate "org.carbon.alphaModifiers" map is still supported
106
- // for backwards-compat with existing component token files.
107
- for (const [theme, themeEntry] of Object.entries(carbonThemes)) {
108
- // An entry can be:
109
- // 1. A bare string alias/hex → treat as the value directly
110
- // 2. A composite color object → { colorSpace, components, hex }
111
- // Used for hardcoded non-palette colors. Treat as the value directly.
112
- // 3. A { value, alpha? } wrapper object → unified theme format
113
- const isComposite =
114
- themeEntry !== null &&
115
- typeof themeEntry === 'object' &&
116
- !Array.isArray(themeEntry) &&
117
- 'hex' in themeEntry;
118
- const isWrapper =
119
- themeEntry !== null &&
120
- typeof themeEntry === 'object' &&
121
- !Array.isArray(themeEntry) &&
122
- !isComposite;
123
- const themeValue = isWrapper ? themeEntry.value : themeEntry;
124
- // Per-theme alpha: only { value, alpha } wrappers carry alpha.
125
- // Legacy org.carbon.alphaModifiers map is the fallback for component files.
126
- const alpha = isWrapper ? themeEntry.alpha : alphaModifiers[theme];
127
- const syntheticNode = {
128
- $type: value.$type ?? 'color',
129
- $value: themeValue,
130
- };
131
- if (alpha !== undefined) {
132
- syntheticNode.$extensions = {
133
- 'org.carbon': { alphaModifier: alpha },
134
- };
135
- }
136
- byTheme[theme] = syntheticNode;
137
- }
138
-
139
- // Build the expanded node: preserve DTCG metadata, add _by_theme, and
140
- // recurse into any non-$ children (dual-role nodes in themes.json have
141
- // both a carbon.themes value AND group children like `active`, `hover`).
142
- const expandedNode = {
143
- ...(value.$type ? { $type: value.$type } : {}),
144
- ...(value.$description ? { $description: value.$description } : {}),
145
- // _by_theme is a DTCG group — each child is a leaf token with $value
146
- _by_theme: byTheme,
147
- };
148
- for (const [childKey, childValue] of Object.entries(value)) {
149
- if (childKey.startsWith('$') || childKey === '_by_theme') continue;
150
- if (!childValue || typeof childValue !== 'object') continue;
151
- // Wrap in a single-key object so walk() sees the key and can apply
152
- // the carbon.themes expansion logic to it (walk iterates the entries
153
- // of the object passed in, so childValue alone would skip expansion).
154
- expandedNode[childKey] = walk({ [childKey]: childValue })[childKey];
155
- }
156
- out[key] = expandedNode;
157
- } else {
158
- // Recurse into nested groups
159
- out[key] = walk(value);
160
- }
161
- }
162
-
163
- return out;
164
- }
165
-
166
- return walk(dictionary);
167
- }
168
-
169
- module.exports = {
170
- name: 'carbon/component-tokens',
171
- preprocessor: carbonComponentTokensPreprocessor,
172
- };
@@ -1,157 +0,0 @@
1
- /**
2
- * Copyright IBM Corp. 2026
3
- *
4
- * This source code is licensed under the Apache-2.0 license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- 'use strict';
9
-
10
- /**
11
- *
12
- * The unified themes.json (and previously the individual per-theme files) can
13
- * contain nodes that are simultaneously token leaves (they have a `$value`) AND
14
- * token groups (they have non-`$`-prefixed children). Style Dictionary v5 does
15
- * not support this pattern — when a node has `$value`, SD treats it as a leaf
16
- * token and silently drops any non-`$` children.
17
- *
18
- * Example:
19
- *
20
- * background: {
21
- * $type: "color",
22
- * $value: "{gray.10}", ← token leaf value
23
- * active: { $type: "color", $value: "{gray.50}", ... }, ← child token
24
- * brand: { ... }
25
- * }
26
- *
27
- * Without this preprocessor, SD emits `background` but loses `background-active`
28
- * and `background-brand`. There are 27 such dual-role nodes in each of
29
- * g10 / g90 / g100, causing 48 tokens to go missing from the generated output.
30
- *
31
- * ── Solution ────────────────────────────────────────────────────────────────
32
- *
33
- * For any dual-role node (has `$value` AND non-`$` children), this preprocessor
34
- * splits the node:
35
- *
36
- * 1. The parent's token value (`$value`, `$type`, `$extensions`) is moved into
37
- * a synthetic `_self` child at the same level.
38
- * 2. The parent node becomes a pure group (its `$value` is removed).
39
- *
40
- * After this transform, the tree becomes:
41
- *
42
- * background: {
43
- * _self: { $type: "color", $value: "{gray.10}" }, ← parent value
44
- * active: { $type: "color", $value: "{gray.50}", ... },
45
- * brand: { ... }
46
- * }
47
- *
48
- * SD sees `background._self` as path `['background', '_self']` and
49
- * `background.active` as path `['background', 'active']`.
50
- *
51
- * A companion custom name transform (`carbon/name-kebab`, registered in
52
- * sd.config.js) strips `_self` from the path when building the token name:
53
- *
54
- * ['background', '_self'] → 'background' (parent token)
55
- * ['background', 'active'] → 'background-active'
56
- * ['layer', '_self'] → 'layer'
57
- * ['layer', '01'] → 'layer-01'
58
- *
59
- * This reproduces the flat token names that the old dtcg-converter produced,
60
- * while preserving correct hierarchy for SD's alias resolution and path-based
61
- * name computation.
62
- *
63
- * ── Alias resolution ────────────────────────────────────────────────────────
64
- *
65
- * The color-palette.json file is loaded as `source:` (not pre-processed) by
66
- * sd.config.js, so its tokens stay in their original nested form and SD alias
67
- * resolution ({blue.60} etc.) continues to work.
68
- *
69
- * This preprocessor is called with only the theme JSON as input (passed as
70
- * `tokens:` in themeConfig()), never seeing the merged palette+theme tree.
71
- * Therefore palette tokens are unaffected.
72
- *
73
- * @param {object} dictionary Raw DTCG token tree (theme file only)
74
- * @returns {object} Normalised tree with dual-role nodes split
75
- */
76
-
77
- /**
78
- * Process a single DTCG token node.
79
- *
80
- * The node may be:
81
- * - A pure leaf token (has `$value`, no non-`$` children) → returned as-is.
82
- * - A pure group (no `$value`, has non-`$` children) → children are recursed.
83
- * - A dual-role node (has BOTH `$value` AND non-`$` children) → split: the
84
- * `$value`/`$type`/`$extensions` are moved to a `_self` synthetic leaf, and
85
- * the node becomes a pure group whose children include `_self` plus the
86
- * original children (each recursively processed).
87
- *
88
- * @param {object} node A single DTCG node object.
89
- * @returns {object} The normalised node.
90
- */
91
- function processNode(node) {
92
- if (!node || typeof node !== 'object') return node;
93
-
94
- const hasValue = '$value' in node;
95
- const nonDollarChildren = Object.keys(node).filter((k) => !k.startsWith('$'));
96
- const hasChildren = nonDollarChildren.length > 0;
97
-
98
- if (hasValue && hasChildren) {
99
- // ── Dual-role node ────────────────────────────────────────────────────────
100
- // Collect the DTCG metadata into a `_self` leaf.
101
- const selfLeaf = {};
102
- for (const [k, v] of Object.entries(node)) {
103
- if (k.startsWith('$')) selfLeaf[k] = v;
104
- }
105
- // Build the pure-group: _self first, then recursively-processed children.
106
- const group = { _self: selfLeaf };
107
- for (const childKey of nonDollarChildren) {
108
- group[childKey] = processNode(node[childKey]);
109
- }
110
- return group;
111
- } else if (hasChildren) {
112
- // ── Pure group ────────────────────────────────────────────────────────────
113
- const out = {};
114
- for (const [k, v] of Object.entries(node)) {
115
- if (k.startsWith('$')) {
116
- out[k] = v;
117
- } else {
118
- out[k] = processNode(v);
119
- }
120
- }
121
- return out;
122
- } else {
123
- // ── Pure leaf token ───────────────────────────────────────────────────────
124
- return node;
125
- }
126
- }
127
-
128
- /**
129
- * Top-level preprocessor function.
130
- *
131
- * The top-level dictionary itself is always a group (never a token), so we
132
- * iterate its entries and call processNode() on each non-`$` child.
133
- *
134
- * @param {object} dictionary Raw DTCG token tree from the theme JSON file
135
- * @returns {object} Normalised tree with all dual-role nodes split
136
- */
137
- function carbonDualRolePreprocessor(dictionary) {
138
- if (!dictionary || typeof dictionary !== 'object') return dictionary;
139
-
140
- const out = {};
141
- for (const [key, value] of Object.entries(dictionary)) {
142
- if (key.startsWith('$')) {
143
- // Top-level DTCG metadata — preserve as-is.
144
- out[key] = value;
145
- } else if (value && typeof value === 'object') {
146
- out[key] = processNode(value);
147
- } else {
148
- out[key] = value;
149
- }
150
- }
151
- return out;
152
- }
153
-
154
- module.exports = {
155
- name: 'carbon/dual-role',
156
- preprocessor: carbonDualRolePreprocessor,
157
- };
@@ -1,46 +0,0 @@
1
- /**
2
- * Copyright IBM Corp. 2026
3
- *
4
- * This source code is licensed under the Apache-2.0 license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
- 'use strict';
9
-
10
- /**
11
- *
12
- * Extracts the `$extensions["org.carbon"]["color-scheme"]` value that lives
13
- * at the top level of the unified themes.json and
14
- * injects it as a synthetic `color-scheme` token, so the SCSS format can
15
- * include it in the theme Sass map alongside the regular color tokens.
16
- *
17
- *
18
- * @param {object} dictionary Style Dictionary raw token tree
19
- * @returns {object}
20
- */
21
- function carbonThemeMetadataPreprocessor(dictionary) {
22
- const colorScheme = dictionary.$extensions?.['org.carbon']?.['color-scheme'];
23
-
24
- if (!colorScheme) {
25
- return dictionary;
26
- }
27
-
28
- // Inject as color.scheme so the flattened token name becomes 'color-scheme',
29
- // matching what dtcg-converter.js produces today.
30
- return {
31
- ...dictionary,
32
- color: {
33
- ...(dictionary.color ?? {}),
34
- scheme: {
35
- $type: 'other',
36
- $value: colorScheme,
37
- $description: 'Color scheme of this theme (light or dark)',
38
- },
39
- },
40
- };
41
- }
42
-
43
- module.exports = {
44
- name: 'carbon/theme-metadata',
45
- preprocessor: carbonThemeMetadataPreprocessor,
46
- };