@carbon/themes 11.81.0 → 11.82.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/es/index.js +195 -195
- package/js/generated/themes/white.d.ts +195 -195
- package/js/generated/themes/white.js +195 -195
- package/lib/index.js +196 -196
- package/lib/v10/g10.d.ts +120 -120
- package/lib/v10/g100.d.ts +122 -122
- package/lib/v10/g90.d.ts +122 -122
- package/lib/v10/white.d.ts +120 -120
- package/package.json +6 -6
- package/src/dtcg/README.md +188 -62
- package/src/dtcg/themes.json +3356 -0
- package/style-dictionary/preprocessors/component-tokens.js +58 -5
- package/style-dictionary/preprocessors/dual-role.js +6 -6
- package/style-dictionary/preprocessors/theme-metadata.js +1 -1
- package/style-dictionary/sd.config.js +117 -10
- package/style-dictionary/transforms/alpha-modifier.js +1 -1
- package/umd/index.js +196 -196
- package/src/dtcg/g10.json +0 -1453
- package/src/dtcg/g100.json +0 -1478
- package/src/dtcg/g90.json +0 -1478
- package/src/dtcg/white.json +0 -1345
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
* token files (button.json, tag.json, …) use into flat per-theme tokens that
|
|
15
15
|
* Style Dictionary can process natively.
|
|
16
16
|
*
|
|
17
|
-
* Input — one token node with no $value but a per-theme map in extensions
|
|
17
|
+
* Input — one token node with no $value but a per-theme map in extensions.
|
|
18
|
+
* Two equivalent forms are accepted:
|
|
18
19
|
*
|
|
20
|
+
* Component-token form (legacy, bare strings + separate alphaModifiers):
|
|
19
21
|
* "button": {
|
|
20
22
|
* "danger-active": {
|
|
21
23
|
* "$type": "color",
|
|
@@ -26,6 +28,21 @@
|
|
|
26
28
|
* }
|
|
27
29
|
* }
|
|
28
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
|
+
*
|
|
29
46
|
* Output — one flat sibling token per theme, keyed as
|
|
30
47
|
* `<tokenPath>-_by_theme-<themeName>` so the format functions can reconstruct
|
|
31
48
|
* the original token name and theme:
|
|
@@ -80,9 +97,33 @@ function carbonComponentTokensPreprocessor(dictionary) {
|
|
|
80
97
|
if (carbonThemes && value.$value === undefined) {
|
|
81
98
|
const byTheme = {};
|
|
82
99
|
|
|
83
|
-
// Preserve original JSON key order (important for 'fallback' position)
|
|
84
|
-
|
|
85
|
-
|
|
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];
|
|
86
127
|
const syntheticNode = {
|
|
87
128
|
$type: value.$type ?? 'color',
|
|
88
129
|
$value: themeValue,
|
|
@@ -95,12 +136,24 @@ function carbonComponentTokensPreprocessor(dictionary) {
|
|
|
95
136
|
byTheme[theme] = syntheticNode;
|
|
96
137
|
}
|
|
97
138
|
|
|
98
|
-
|
|
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 = {
|
|
99
143
|
...(value.$type ? { $type: value.$type } : {}),
|
|
100
144
|
...(value.$description ? { $description: value.$description } : {}),
|
|
101
145
|
// _by_theme is a DTCG group — each child is a leaf token with $value
|
|
102
146
|
_by_theme: byTheme,
|
|
103
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;
|
|
104
157
|
} else {
|
|
105
158
|
// Recurse into nested groups
|
|
106
159
|
out[key] = walk(value);
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* simultaneously token leaves (they have a `$value`) AND
|
|
14
|
-
* have non-`$`-prefixed children). Style Dictionary v5 does
|
|
15
|
-
* pattern — when a node has `$value`, SD treats it as a leaf
|
|
16
|
-
* drops any non-`$` children.
|
|
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
17
|
*
|
|
18
|
-
* Example
|
|
18
|
+
* Example:
|
|
19
19
|
*
|
|
20
20
|
* background: {
|
|
21
21
|
* $type: "color",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
/**
|
|
11
11
|
*
|
|
12
12
|
* Extracts the `$extensions["org.carbon"]["color-scheme"]` value that lives
|
|
13
|
-
* at the top level of
|
|
13
|
+
* at the top level of the unified themes.json and
|
|
14
14
|
* injects it as a synthetic `color-scheme` token, so the SCSS format can
|
|
15
15
|
* include it in the theme Sass map alongside the regular color tokens.
|
|
16
16
|
*
|
|
@@ -21,7 +21,9 @@ const { default: StyleDictionary } = require('style-dictionary');
|
|
|
21
21
|
// ── Custom plugins ────────────────────────────────────────────────────────────
|
|
22
22
|
const carbonColorFlatten = require('./transforms/color-flatten');
|
|
23
23
|
const carbonAlphaModifier = require('./transforms/alpha-modifier');
|
|
24
|
-
const
|
|
24
|
+
const carbonComponentTokensPreprocessorModule = require('./preprocessors/component-tokens');
|
|
25
|
+
const { preprocessor: carbonComponentTokensPreprocessor } =
|
|
26
|
+
carbonComponentTokensPreprocessorModule;
|
|
25
27
|
const carbonThemeMetadataPreprocessor = require('./preprocessors/theme-metadata');
|
|
26
28
|
const { preprocessor: flattenDualRole } = require('./preprocessors/dual-role');
|
|
27
29
|
const carbonScssThemes = require('./formats/scss-themes');
|
|
@@ -43,6 +45,15 @@ const JS_GENERATED_COMPONENTS = path.join(
|
|
|
43
45
|
);
|
|
44
46
|
|
|
45
47
|
const THEME_NAMES = ['white', 'g10', 'g90', 'g100'];
|
|
48
|
+
|
|
49
|
+
// Light/dark scheme per theme — used to re-inject color-scheme metadata after
|
|
50
|
+
// extractThemeSlice strips the root $extensions from the unified file.
|
|
51
|
+
const THEME_COLOR_SCHEME = {
|
|
52
|
+
white: 'light',
|
|
53
|
+
g10: 'light',
|
|
54
|
+
g90: 'dark',
|
|
55
|
+
g100: 'dark',
|
|
56
|
+
};
|
|
46
57
|
const COMPONENT_NAMES = [
|
|
47
58
|
'button',
|
|
48
59
|
'tag',
|
|
@@ -51,6 +62,72 @@ const COMPONENT_NAMES = [
|
|
|
51
62
|
'content-switcher',
|
|
52
63
|
];
|
|
53
64
|
|
|
65
|
+
// ── extractThemeSlice ─────────────────────────────────────────────────────────
|
|
66
|
+
//
|
|
67
|
+
// After the component-tokens preprocessor runs on themes.json, every leaf token
|
|
68
|
+
// node becomes a group with a `_by_theme` child containing one synthetic leaf
|
|
69
|
+
// per theme. This function walks the expanded tree and replaces each
|
|
70
|
+
// `_by_theme` group with the single synthetic leaf for `themeName`, collapsing
|
|
71
|
+
// the tree back into a normal per-theme token structure that the rest of the
|
|
72
|
+
// pipeline (flattenDualRole, SD transforms, formatters) understands.
|
|
73
|
+
//
|
|
74
|
+
// Tokens that do NOT have a _by_theme group (palette reference tokens injected
|
|
75
|
+
// via `source:`) are passed through unchanged.
|
|
76
|
+
function extractThemeSlice(node, themeName) {
|
|
77
|
+
if (!node || typeof node !== 'object') return node;
|
|
78
|
+
|
|
79
|
+
const out = {};
|
|
80
|
+
for (const [key, value] of Object.entries(node)) {
|
|
81
|
+
if (key.startsWith('$')) {
|
|
82
|
+
out[key] = value;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (!value || typeof value !== 'object') {
|
|
86
|
+
out[key] = value;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (key === '_by_theme') {
|
|
90
|
+
// Skip — handled by the parent via the `value._by_theme` branch below.
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if this child node has a _by_theme group (i.e. it was a leaf or
|
|
95
|
+
// dual-role node in themes.json that the preprocessor expanded).
|
|
96
|
+
if (value._by_theme) {
|
|
97
|
+
const leaf = value._by_theme[themeName];
|
|
98
|
+
if (leaf) {
|
|
99
|
+
// Start with the leaf's DTCG fields ($type, $value, $extensions).
|
|
100
|
+
const merged = { ...leaf };
|
|
101
|
+
// Preserve $description from the group wrapper (leaf synthetic nodes
|
|
102
|
+
// don't carry it, but the original themes.json node does).
|
|
103
|
+
if (!merged.$description && value.$description)
|
|
104
|
+
merged.$description = value.$description;
|
|
105
|
+
if (!merged.$type && value.$type) merged.$type = value.$type;
|
|
106
|
+
|
|
107
|
+
// Recurse into any group children of this node (dual-role case):
|
|
108
|
+
// e.g. `background` is both a token and the parent of `background-active`.
|
|
109
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
110
|
+
if (childKey.startsWith('$') || childKey === '_by_theme') continue;
|
|
111
|
+
if (!childValue || typeof childValue !== 'object') continue;
|
|
112
|
+
const sliced = extractThemeSlice(
|
|
113
|
+
{ [childKey]: childValue },
|
|
114
|
+
themeName
|
|
115
|
+
);
|
|
116
|
+
if (sliced[childKey] !== undefined)
|
|
117
|
+
merged[childKey] = sliced[childKey];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
out[key] = merged;
|
|
121
|
+
}
|
|
122
|
+
// If the theme had no entry, skip the token.
|
|
123
|
+
} else {
|
|
124
|
+
const sliced = extractThemeSlice(value, themeName);
|
|
125
|
+
if (sliced !== null) out[key] = sliced;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
54
131
|
// ── Custom name transform ─────────────────────────────────────────────────────
|
|
55
132
|
//
|
|
56
133
|
// Builds a kebab-case name from the SD token path, with one special rule:
|
|
@@ -89,16 +166,46 @@ const CARBON_TRANSFORMS = [
|
|
|
89
166
|
];
|
|
90
167
|
|
|
91
168
|
// ── Helper: build config for one theme ───────────────────────────────────────
|
|
169
|
+
//
|
|
170
|
+
// themes.json is the unified source — all four themes in one file, with each
|
|
171
|
+
// token carrying per-theme values under $extensions["carbon.themes"].
|
|
172
|
+
// The component-tokens preprocessor expands those into per-theme leaf tokens
|
|
173
|
+
// (identical to what it does for component files), and dual-role splitting
|
|
174
|
+
// then runs on the resulting tree so SD sees clean leaf+group pairs.
|
|
92
175
|
function themeConfig(themeName) {
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
fs.readFileSync(path.join(DTCG_DIR, `${themeName}.json`), 'utf8')
|
|
176
|
+
// Read the unified themes.json. The component-tokens preprocessor first
|
|
177
|
+
// expands carbon.themes entries into _by_theme.<theme> leaf nodes, then
|
|
178
|
+
// flattenDualRole splits any dual-role nodes (value + children) so SD can
|
|
179
|
+
// process them without losing children.
|
|
180
|
+
const rawThemes = JSON.parse(
|
|
181
|
+
fs.readFileSync(path.join(DTCG_DIR, 'themes.json'), 'utf8')
|
|
100
182
|
);
|
|
101
|
-
|
|
183
|
+
// The component-tokens preprocessor understands the { value, alpha } entry
|
|
184
|
+
// shape from the unified file as well as the legacy bare-string shape from
|
|
185
|
+
// component files. Run it first so carbon.themes entries become $value leaves.
|
|
186
|
+
const expanded = carbonComponentTokensPreprocessor(rawThemes);
|
|
187
|
+
// Now extract only this theme's _by_theme.<themeName> leaf nodes, rewriting
|
|
188
|
+
// them back to plain $value tokens so the rest of the pipeline sees a normal
|
|
189
|
+
// single-theme token tree.
|
|
190
|
+
const rawSlice = extractThemeSlice(expanded, themeName);
|
|
191
|
+
|
|
192
|
+
// Re-attach the per-theme color-scheme to the root $extensions so that
|
|
193
|
+
// carbonThemeMetadataPreprocessor (which reads
|
|
194
|
+
// dictionary.$extensions["org.carbon"]["color-scheme"]) can inject the
|
|
195
|
+
// color-scheme synthetic token. extractThemeSlice does not carry the root
|
|
196
|
+
// $extensions through because they are global, not per-token.
|
|
197
|
+
const themeSlice = {
|
|
198
|
+
...rawSlice,
|
|
199
|
+
$extensions: {
|
|
200
|
+
...(rawSlice.$extensions ?? {}),
|
|
201
|
+
'org.carbon': {
|
|
202
|
+
...(rawSlice.$extensions?.['org.carbon'] ?? {}),
|
|
203
|
+
'color-scheme': THEME_COLOR_SCHEME[themeName],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const flatTokens = flattenDualRole(themeSlice);
|
|
102
209
|
|
|
103
210
|
return {
|
|
104
211
|
source: [path.join(DTCG_DIR, 'color-palette.json')],
|
|
@@ -246,7 +353,7 @@ function createBase() {
|
|
|
246
353
|
name: 'carbon',
|
|
247
354
|
transforms: CARBON_TRANSFORMS,
|
|
248
355
|
});
|
|
249
|
-
base.registerPreprocessor(
|
|
356
|
+
base.registerPreprocessor(carbonComponentTokensPreprocessorModule);
|
|
250
357
|
base.registerPreprocessor(carbonThemeMetadataPreprocessor);
|
|
251
358
|
base.registerFormat(carbonScssThemes);
|
|
252
359
|
base.registerFormat(carbonScssTokens);
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* If no alphaModifier extension is present the token is passed through
|
|
20
20
|
* unchanged (carbon/color-flatten will handle it next).
|
|
21
21
|
*
|
|
22
|
-
* Example in
|
|
22
|
+
* Example in themes.json:
|
|
23
23
|
* "background-active": {
|
|
24
24
|
* "$type": "color",
|
|
25
25
|
* "$value": "{gray.50}", ← resolved by SD to '#8d8d8d'
|