@carbon/layout 11.59.0-rc.0 → 11.60.0-rc.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 +62 -60
- package/js/generated/layout-tokens.d.ts +51 -0
- package/js/generated/layout-tokens.js +51 -0
- package/lib/index.d.ts +1 -38
- package/lib/index.js +67 -59
- package/package.json +7 -5
- package/src/dtcg/README.md +266 -0
- package/src/dtcg/layout.json +432 -0
- package/src/index.ts +74 -60
- package/style-dictionary/formats/js-layout.js +104 -0
- package/style-dictionary/formats/scss-layout.js +188 -0
- package/style-dictionary/sd.config.js +104 -0
- package/umd/index.js +67 -59
package/src/index.ts
CHANGED
|
@@ -5,7 +5,58 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// Re-export individual token constants generated from src/dtcg/layout.json
|
|
9
|
+
// via the Style Dictionary pipeline (tasks/build.mjs → style-dictionary/sd.config.js).
|
|
10
|
+
// The generated file is kept in sync with the DTCG source of truth.
|
|
11
|
+
export * from '../js/generated/layout-tokens';
|
|
12
|
+
|
|
8
13
|
import { unstable_tokens } from './tokens';
|
|
14
|
+
import {
|
|
15
|
+
spacing01,
|
|
16
|
+
spacing02,
|
|
17
|
+
spacing03,
|
|
18
|
+
spacing04,
|
|
19
|
+
spacing05,
|
|
20
|
+
spacing06,
|
|
21
|
+
spacing07,
|
|
22
|
+
spacing08,
|
|
23
|
+
spacing09,
|
|
24
|
+
spacing10,
|
|
25
|
+
spacing11,
|
|
26
|
+
spacing12,
|
|
27
|
+
spacing13,
|
|
28
|
+
fluidSpacing01,
|
|
29
|
+
fluidSpacing02,
|
|
30
|
+
fluidSpacing03,
|
|
31
|
+
fluidSpacing04,
|
|
32
|
+
container01,
|
|
33
|
+
container02,
|
|
34
|
+
container03,
|
|
35
|
+
container04,
|
|
36
|
+
container05,
|
|
37
|
+
sizeXs,
|
|
38
|
+
sizeSm,
|
|
39
|
+
sizeMd,
|
|
40
|
+
sizeLg,
|
|
41
|
+
sizeXl,
|
|
42
|
+
size2xl,
|
|
43
|
+
iconSize01,
|
|
44
|
+
iconSize02,
|
|
45
|
+
layout01,
|
|
46
|
+
layout02,
|
|
47
|
+
layout03,
|
|
48
|
+
layout04,
|
|
49
|
+
layout05,
|
|
50
|
+
layout06,
|
|
51
|
+
layout07,
|
|
52
|
+
borderRadius00,
|
|
53
|
+
borderRadius02,
|
|
54
|
+
borderRadius04,
|
|
55
|
+
borderRadius08,
|
|
56
|
+
borderRadius16,
|
|
57
|
+
borderRadius24,
|
|
58
|
+
borderRadiusMax,
|
|
59
|
+
} from '../js/generated/layout-tokens';
|
|
9
60
|
|
|
10
61
|
export { unstable_tokens };
|
|
11
62
|
|
|
@@ -51,29 +102,29 @@ export const px = (value: number) => {
|
|
|
51
102
|
// Initial map of our breakpoints and their values
|
|
52
103
|
export const breakpoints: Record<BreakpointName, Breakpoint> = {
|
|
53
104
|
sm: {
|
|
54
|
-
width:
|
|
105
|
+
width: rem(320),
|
|
55
106
|
columns: 4,
|
|
56
107
|
margin: '0',
|
|
57
108
|
},
|
|
58
109
|
md: {
|
|
59
|
-
width:
|
|
110
|
+
width: rem(672),
|
|
60
111
|
columns: 8,
|
|
61
|
-
margin:
|
|
112
|
+
margin: rem(16),
|
|
62
113
|
},
|
|
63
114
|
lg: {
|
|
64
|
-
width:
|
|
115
|
+
width: rem(1056),
|
|
65
116
|
columns: 16,
|
|
66
|
-
margin:
|
|
117
|
+
margin: rem(16),
|
|
67
118
|
},
|
|
68
119
|
xlg: {
|
|
69
|
-
width:
|
|
120
|
+
width: rem(1312),
|
|
70
121
|
columns: 16,
|
|
71
|
-
margin:
|
|
122
|
+
margin: rem(16),
|
|
72
123
|
},
|
|
73
124
|
max: {
|
|
74
|
-
width:
|
|
125
|
+
width: rem(1584),
|
|
75
126
|
columns: 16,
|
|
76
|
-
margin:
|
|
127
|
+
margin: rem(24),
|
|
77
128
|
},
|
|
78
129
|
};
|
|
79
130
|
|
|
@@ -94,20 +145,7 @@ export const miniUnits = (count: number) => {
|
|
|
94
145
|
return rem(miniUnit * count);
|
|
95
146
|
};
|
|
96
147
|
|
|
97
|
-
// Spacing
|
|
98
|
-
export const spacing01 = /*#__PURE__*/ miniUnits(0.25);
|
|
99
|
-
export const spacing02 = /*#__PURE__*/ miniUnits(0.5);
|
|
100
|
-
export const spacing03 = /*#__PURE__*/ miniUnits(1);
|
|
101
|
-
export const spacing04 = /*#__PURE__*/ miniUnits(1.5);
|
|
102
|
-
export const spacing05 = /*#__PURE__*/ miniUnits(2);
|
|
103
|
-
export const spacing06 = /*#__PURE__*/ miniUnits(3);
|
|
104
|
-
export const spacing07 = /*#__PURE__*/ miniUnits(4);
|
|
105
|
-
export const spacing08 = /*#__PURE__*/ miniUnits(5);
|
|
106
|
-
export const spacing09 = /*#__PURE__*/ miniUnits(6);
|
|
107
|
-
export const spacing10 = /*#__PURE__*/ miniUnits(8);
|
|
108
|
-
export const spacing11 = /*#__PURE__*/ miniUnits(10);
|
|
109
|
-
export const spacing12 = /*#__PURE__*/ miniUnits(12);
|
|
110
|
-
export const spacing13 = /*#__PURE__*/ miniUnits(20);
|
|
148
|
+
// Spacing — aggregate array (individual tokens come from generated re-export above)
|
|
111
149
|
export const spacing = [
|
|
112
150
|
spacing01,
|
|
113
151
|
spacing02,
|
|
@@ -124,11 +162,7 @@ export const spacing = [
|
|
|
124
162
|
spacing13,
|
|
125
163
|
];
|
|
126
164
|
|
|
127
|
-
// Fluid spacing
|
|
128
|
-
export const fluidSpacing01 = 0;
|
|
129
|
-
export const fluidSpacing02 = '2vw';
|
|
130
|
-
export const fluidSpacing03 = '5vw';
|
|
131
|
-
export const fluidSpacing04 = '10vw';
|
|
165
|
+
// Fluid spacing — aggregate array
|
|
132
166
|
export const fluidSpacing = [
|
|
133
167
|
fluidSpacing01,
|
|
134
168
|
fluidSpacing02,
|
|
@@ -136,15 +170,7 @@ export const fluidSpacing = [
|
|
|
136
170
|
fluidSpacing04,
|
|
137
171
|
];
|
|
138
172
|
|
|
139
|
-
// Layout
|
|
140
|
-
// Deprecated
|
|
141
|
-
export const layout01 = /*#__PURE__*/ miniUnits(2);
|
|
142
|
-
export const layout02 = /*#__PURE__*/ miniUnits(3);
|
|
143
|
-
export const layout03 = /*#__PURE__*/ miniUnits(4);
|
|
144
|
-
export const layout04 = /*#__PURE__*/ miniUnits(6);
|
|
145
|
-
export const layout05 = /*#__PURE__*/ miniUnits(8);
|
|
146
|
-
export const layout06 = /*#__PURE__*/ miniUnits(12);
|
|
147
|
-
export const layout07 = /*#__PURE__*/ miniUnits(20);
|
|
173
|
+
// Layout (deprecated) — aggregate array
|
|
148
174
|
export const layout = [
|
|
149
175
|
layout01,
|
|
150
176
|
layout02,
|
|
@@ -155,12 +181,7 @@ export const layout = [
|
|
|
155
181
|
layout07,
|
|
156
182
|
];
|
|
157
183
|
|
|
158
|
-
// Container
|
|
159
|
-
export const container01 = /*#__PURE__*/ miniUnits(3);
|
|
160
|
-
export const container02 = /*#__PURE__*/ miniUnits(4);
|
|
161
|
-
export const container03 = /*#__PURE__*/ miniUnits(5);
|
|
162
|
-
export const container04 = /*#__PURE__*/ miniUnits(6);
|
|
163
|
-
export const container05 = /*#__PURE__*/ miniUnits(8);
|
|
184
|
+
// Container — aggregate array
|
|
164
185
|
export const container = [
|
|
165
186
|
container01,
|
|
166
187
|
container02,
|
|
@@ -168,12 +189,14 @@ export const container = [
|
|
|
168
189
|
container04,
|
|
169
190
|
container05,
|
|
170
191
|
];
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
export const
|
|
174
|
-
export const
|
|
175
|
-
export const
|
|
176
|
-
export const
|
|
192
|
+
|
|
193
|
+
// Size — camelCase aliases matching the existing SizeName API
|
|
194
|
+
export const sizeXSmall = sizeXs;
|
|
195
|
+
export const sizeSmall = sizeSm;
|
|
196
|
+
export const sizeMedium = sizeMd;
|
|
197
|
+
export const sizeLarge = sizeLg;
|
|
198
|
+
export const sizeXLarge = sizeXl;
|
|
199
|
+
export const size2XLarge = size2xl;
|
|
177
200
|
export const sizes: Record<SizeName, string> = {
|
|
178
201
|
XSmall: sizeXSmall,
|
|
179
202
|
Small: sizeSmall,
|
|
@@ -183,19 +206,10 @@ export const sizes: Record<SizeName, string> = {
|
|
|
183
206
|
'2XLarge': size2XLarge,
|
|
184
207
|
};
|
|
185
208
|
|
|
186
|
-
// Icon
|
|
187
|
-
export const iconSize01 = '1rem';
|
|
188
|
-
export const iconSize02 = '1.25rem';
|
|
209
|
+
// Icon — aggregate array
|
|
189
210
|
export const iconSize = [iconSize01, iconSize02];
|
|
190
211
|
|
|
191
|
-
// Border radius
|
|
192
|
-
export const borderRadius00 = '0px';
|
|
193
|
-
export const borderRadius02 = rem(2);
|
|
194
|
-
export const borderRadius04 = rem(4);
|
|
195
|
-
export const borderRadius08 = rem(8);
|
|
196
|
-
export const borderRadius16 = rem(16);
|
|
197
|
-
export const borderRadius24 = rem(24);
|
|
198
|
-
export const borderRadiusMax = '999999px';
|
|
212
|
+
// Border radius — aggregate record keyed by CSS custom property name
|
|
199
213
|
export const borderRadius: Record<BorderRadiusToken, string> = {
|
|
200
214
|
'border-radius-00': borderRadius00,
|
|
201
215
|
'border-radius-02': borderRadius02,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2018, 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
|
+
* Style Dictionary format — carbon/js-layout
|
|
12
|
+
*
|
|
13
|
+
* Emits either:
|
|
14
|
+
* - An ES module (.js) with `export const camelName = value;`
|
|
15
|
+
* - A TypeScript declaration file (.d.ts) with `export declare const camelName: string;`
|
|
16
|
+
*
|
|
17
|
+
* Token names arrive as kebab-case from SD (e.g. "spacing-01") and are
|
|
18
|
+
* converted to camelCase ("spacing01") so the output matches the existing
|
|
19
|
+
* hand-authored exports in src/index.ts.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const FILE_BANNER = `// Code generated by @carbon/layout. DO NOT EDIT.
|
|
23
|
+
//
|
|
24
|
+
// Copyright IBM Corp. 2018, 2026
|
|
25
|
+
//
|
|
26
|
+
// This source code is licensed under the Apache-2.0 license found in the
|
|
27
|
+
// LICENSE file in the root directory of this source tree.
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Convert kebab-case to camelCase.
|
|
32
|
+
* "spacing-01" → "spacing01"
|
|
33
|
+
* "fluid-spacing-02" → "fluidSpacing02"
|
|
34
|
+
* "border-radius-08" → "borderRadius08"
|
|
35
|
+
* "size-xs" → "sizeXs" (kept for completeness; index re-exports under SizeName aliases)
|
|
36
|
+
* "icon-size-01" → "iconSize01"
|
|
37
|
+
* "layout-03" → "layout03"
|
|
38
|
+
*
|
|
39
|
+
* @param {string} str
|
|
40
|
+
* @returns {string}
|
|
41
|
+
*/
|
|
42
|
+
function kebabToCamel(str) {
|
|
43
|
+
return str.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const BASE_FONT_SIZE = 16;
|
|
47
|
+
const MINI_UNIT = 8;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a token's raw $value using the carbon.layout.converter extension.
|
|
51
|
+
* - "miniUnits": steps × 8px ÷ 16 → rem string
|
|
52
|
+
* - "rem": px ÷ 16 → rem string
|
|
53
|
+
* - (none): value is already a resolved CSS string, returned as-is
|
|
54
|
+
*
|
|
55
|
+
* @param {string|number} value
|
|
56
|
+
* @param {object|undefined} carbonLayout — token.$extensions?.['carbon.layout']
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
function resolveValue(value, carbonLayout) {
|
|
60
|
+
const converter = carbonLayout?.converter;
|
|
61
|
+
if (converter === 'miniUnits') {
|
|
62
|
+
return `${(Number(value) * MINI_UNIT) / BASE_FONT_SIZE}rem`;
|
|
63
|
+
}
|
|
64
|
+
if (converter === 'rem') {
|
|
65
|
+
return `${Number(value) / BASE_FONT_SIZE}rem`;
|
|
66
|
+
}
|
|
67
|
+
// Unitless zero — preserve as number 0, not the string "0", to match the
|
|
68
|
+
// original hand-authored export: `export const fluidSpacing01 = 0`
|
|
69
|
+
if (value === '0' || value === 0) return 0;
|
|
70
|
+
return String(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {{ dictionary: import('style-dictionary').Dictionary, options: object }} args
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
function carbonJsLayoutFormat({ dictionary, options }) {
|
|
78
|
+
const outputMode = options?.output ?? 'js'; // 'js' | 'dts'
|
|
79
|
+
const lines = [FILE_BANNER];
|
|
80
|
+
|
|
81
|
+
for (const token of dictionary.allTokens) {
|
|
82
|
+
const camelName = kebabToCamel(token.name);
|
|
83
|
+
const carbonLayout =
|
|
84
|
+
token.$extensions?.['carbon.layout'] ??
|
|
85
|
+
token.extensions?.['carbon.layout'];
|
|
86
|
+
const resolved = resolveValue(token.value ?? token.$value, carbonLayout);
|
|
87
|
+
|
|
88
|
+
const tsType = resolved === 0 ? 'number' : 'string';
|
|
89
|
+
if (outputMode === 'js') {
|
|
90
|
+
// Numeric 0 is emitted bare (no quotes) to preserve the original type.
|
|
91
|
+
const src = resolved === 0 ? '0' : `'${resolved}'`;
|
|
92
|
+
lines.push(`export const ${camelName} = ${src};`);
|
|
93
|
+
} else {
|
|
94
|
+
lines.push(`export declare const ${camelName}: ${tsType};`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return lines.join('\n') + '\n';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
name: 'carbon/js-layout',
|
|
103
|
+
format: carbonJsLayoutFormat,
|
|
104
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2018, 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
|
+
* Style Dictionary format — carbon/scss-layout
|
|
12
|
+
*
|
|
13
|
+
* Generates per-category Sass files from layout.json whose content is
|
|
14
|
+
* byte-for-byte identical to what build.mjs / @carbon/scss-generator produced,
|
|
15
|
+
* so that nothing downstream breaks.
|
|
16
|
+
*
|
|
17
|
+
* Files emitted:
|
|
18
|
+
* _spacing.scss variables + map, !default
|
|
19
|
+
* _fluid-spacing.scss variables + map, !default
|
|
20
|
+
* _container.scss variables + map, !default
|
|
21
|
+
* _icon-size.scss variables + map, !default
|
|
22
|
+
* _border-radius.scss variables + map, !default
|
|
23
|
+
* _layout.scss variables + map, !default (deprecated tokens)
|
|
24
|
+
* _size.scss bare assignments, no !default, no map
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
// ── Banners ───────────────────────────────────────────────────────────────────
|
|
28
|
+
// Most files use 2023; _size.scss uses 2026 — match exactly.
|
|
29
|
+
const BANNER_2023 = `// Code generated by @carbon/layout. 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
|
+
const BANNER_2026 = `// Code generated by @carbon/layout. DO NOT EDIT.
|
|
38
|
+
//
|
|
39
|
+
// Copyright IBM Corp. 2018, 2026
|
|
40
|
+
//
|
|
41
|
+
// This source code is licensed under the Apache-2.0 license found in the
|
|
42
|
+
// LICENSE file in the root directory of this source tree.
|
|
43
|
+
//`;
|
|
44
|
+
|
|
45
|
+
const VAR_COMMENT = `/// @type Number
|
|
46
|
+
/// @access public
|
|
47
|
+
/// @group @carbon/layout`;
|
|
48
|
+
|
|
49
|
+
const MAP_COMMENT = `/// @type Map
|
|
50
|
+
/// @access public
|
|
51
|
+
/// @group @carbon/layout`;
|
|
52
|
+
|
|
53
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
54
|
+
const BASE_FONT_SIZE = 16;
|
|
55
|
+
const MINI_UNIT = 8;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a token's $value using carbon.layout.converter if present.
|
|
59
|
+
* @param {string|number} value
|
|
60
|
+
* @param {object|undefined} carbonLayout
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
function resolveValue(value, carbonLayout) {
|
|
64
|
+
const converter = carbonLayout?.converter;
|
|
65
|
+
if (converter === 'miniUnits') {
|
|
66
|
+
return `${(Number(value) * MINI_UNIT) / BASE_FONT_SIZE}rem`;
|
|
67
|
+
}
|
|
68
|
+
if (converter === 'rem') {
|
|
69
|
+
return `${Number(value) / BASE_FONT_SIZE}rem`;
|
|
70
|
+
}
|
|
71
|
+
return String(value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build a standard file: banner + (comment + $var: value !default;) * n + map.
|
|
76
|
+
* @param {Array<{name: string, resolved: string}>} tokens
|
|
77
|
+
* @param {string} mapName — Sass map variable name
|
|
78
|
+
* @param {string} banner
|
|
79
|
+
* @returns {string}
|
|
80
|
+
*/
|
|
81
|
+
function buildStandardFile(tokens, mapName, banner) {
|
|
82
|
+
const lines = [banner, ''];
|
|
83
|
+
|
|
84
|
+
for (const { name, resolved } of tokens) {
|
|
85
|
+
lines.push(VAR_COMMENT);
|
|
86
|
+
lines.push(`$${name}: ${resolved} !default;`);
|
|
87
|
+
lines.push('');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
lines.push(MAP_COMMENT);
|
|
91
|
+
lines.push(`$${mapName}: (`);
|
|
92
|
+
for (const { name } of tokens) {
|
|
93
|
+
lines.push(` ${name}: $${name},`);
|
|
94
|
+
}
|
|
95
|
+
lines.push(');');
|
|
96
|
+
|
|
97
|
+
return lines.join('\n') + '\n';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Build _size.scss — no !default, no map, no per-variable comments.
|
|
102
|
+
* @param {Array<{name: string, resolved: string}>} tokens
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
function buildSizeFile(tokens) {
|
|
106
|
+
const lines = [BANNER_2026, ''];
|
|
107
|
+
lines.push(VAR_COMMENT);
|
|
108
|
+
for (const { name, resolved } of tokens) {
|
|
109
|
+
lines.push(`$${name}: ${resolved};`);
|
|
110
|
+
}
|
|
111
|
+
return lines.join('\n') + '\n';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Extract tokens belonging to a DTCG group from the SD dictionary.
|
|
116
|
+
* Routing is done by token.path[0] — the group name in the JSON — which is
|
|
117
|
+
* the native DTCG mechanism. No prefix inference needed.
|
|
118
|
+
*
|
|
119
|
+
* @param {import('style-dictionary').Dictionary} dictionary
|
|
120
|
+
* @param {string} groupName — top-level DTCG group key (e.g. "spacing")
|
|
121
|
+
* @returns {Array<{name: string, resolved: string}>}
|
|
122
|
+
*/
|
|
123
|
+
function tokensForGroup(dictionary, groupName) {
|
|
124
|
+
return dictionary.allTokens
|
|
125
|
+
.filter((t) => t.path[0] === groupName)
|
|
126
|
+
.map((t) => {
|
|
127
|
+
const carbonLayout =
|
|
128
|
+
t.$extensions?.['carbon.layout'] ?? t.extensions?.['carbon.layout'];
|
|
129
|
+
return {
|
|
130
|
+
name: t.name,
|
|
131
|
+
resolved: resolveValue(t.value ?? t.$value, carbonLayout),
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── Format functions — one per DTCG group ─────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
function formatSpacing({ dictionary }) {
|
|
139
|
+
const tokens = tokensForGroup(dictionary, 'spacing');
|
|
140
|
+
return buildStandardFile(tokens, 'spacing', BANNER_2023);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function formatFluidSpacing({ dictionary }) {
|
|
144
|
+
const tokens = tokensForGroup(dictionary, 'fluid-spacing');
|
|
145
|
+
// Sass expects unitless 0, not the string "0"
|
|
146
|
+
const fixed = tokens.map((t) => ({
|
|
147
|
+
...t,
|
|
148
|
+
resolved: t.resolved === '0' ? '0' : t.resolved,
|
|
149
|
+
}));
|
|
150
|
+
return buildStandardFile(fixed, 'fluid-spacing', BANNER_2023);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function formatContainer({ dictionary }) {
|
|
154
|
+
const tokens = tokensForGroup(dictionary, 'container');
|
|
155
|
+
return buildStandardFile(tokens, 'container', BANNER_2023);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function formatIconSize({ dictionary }) {
|
|
159
|
+
const tokens = tokensForGroup(dictionary, 'icon-size');
|
|
160
|
+
return buildStandardFile(tokens, 'icon-size', BANNER_2023);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function formatBorderRadius({ dictionary }) {
|
|
164
|
+
const tokens = tokensForGroup(dictionary, 'border-radius');
|
|
165
|
+
return buildStandardFile(tokens, 'border-radius', BANNER_2023);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function formatLayout({ dictionary }) {
|
|
169
|
+
const tokens = tokensForGroup(dictionary, 'layout');
|
|
170
|
+
return buildStandardFile(tokens, 'layout', BANNER_2023);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function formatSize({ dictionary }) {
|
|
174
|
+
const tokens = tokensForGroup(dictionary, 'size');
|
|
175
|
+
return buildSizeFile(tokens);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ── Exports ───────────────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
module.exports = [
|
|
181
|
+
{ name: 'carbon/scss-spacing', format: formatSpacing },
|
|
182
|
+
{ name: 'carbon/scss-fluid-spacing', format: formatFluidSpacing },
|
|
183
|
+
{ name: 'carbon/scss-container', format: formatContainer },
|
|
184
|
+
{ name: 'carbon/scss-icon-size', format: formatIconSize },
|
|
185
|
+
{ name: 'carbon/scss-border-radius', format: formatBorderRadius },
|
|
186
|
+
{ name: 'carbon/scss-layout', format: formatLayout },
|
|
187
|
+
{ name: 'carbon/scss-size', format: formatSize },
|
|
188
|
+
];
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2018, 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
|
+
* Style Dictionary configuration for @carbon/layout.
|
|
12
|
+
*
|
|
13
|
+
* Reads src/dtcg/layout.json and generates:
|
|
14
|
+
* scss/generated/*.scss — Sass variables + maps (one file per token group)
|
|
15
|
+
* js/generated/layout-tokens.js — ES module named exports
|
|
16
|
+
* js/generated/layout-tokens.d.ts — TypeScript declarations
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { default: StyleDictionary } = require('style-dictionary');
|
|
21
|
+
|
|
22
|
+
const scssFormats = require('./formats/scss-layout');
|
|
23
|
+
const carbonJsLayout = require('./formats/js-layout');
|
|
24
|
+
|
|
25
|
+
// ── Paths ─────────────────────────────────────────────────────────────────────
|
|
26
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
27
|
+
const DTCG_SOURCE = path.join(ROOT, 'src', 'dtcg', 'layout.json');
|
|
28
|
+
const SCSS_GENERATED = path.join(ROOT, 'scss', 'generated');
|
|
29
|
+
const JS_GENERATED = path.join(ROOT, 'js', 'generated');
|
|
30
|
+
|
|
31
|
+
// ── Custom name transform ─────────────────────────────────────────────────────
|
|
32
|
+
// Uses only the token's own key (last path segment) as the name, discarding
|
|
33
|
+
// the group prefix. With DTCG groups, path is ["spacing", "spacing-01"] so
|
|
34
|
+
// joining would produce "spacing-spacing-01" — we want just "spacing-01".
|
|
35
|
+
const carbonNameKebab = {
|
|
36
|
+
name: 'carbon/name-kebab',
|
|
37
|
+
type: 'name',
|
|
38
|
+
transform(token) {
|
|
39
|
+
return token.path[token.path.length - 1];
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ── Transform group ───────────────────────────────────────────────────────────
|
|
44
|
+
const CARBON_TRANSFORMS = ['attribute/cti', 'carbon/name-kebab'];
|
|
45
|
+
|
|
46
|
+
// ── SD config ─────────────────────────────────────────────────────────────────
|
|
47
|
+
const config = {
|
|
48
|
+
source: [DTCG_SOURCE],
|
|
49
|
+
platforms: {
|
|
50
|
+
scss: {
|
|
51
|
+
transformGroup: 'carbon',
|
|
52
|
+
buildPath: SCSS_GENERATED + '/',
|
|
53
|
+
files: [
|
|
54
|
+
{ destination: '_spacing.scss', format: 'carbon/scss-spacing' },
|
|
55
|
+
{
|
|
56
|
+
destination: '_fluid-spacing.scss',
|
|
57
|
+
format: 'carbon/scss-fluid-spacing',
|
|
58
|
+
},
|
|
59
|
+
{ destination: '_container.scss', format: 'carbon/scss-container' },
|
|
60
|
+
{ destination: '_icon-size.scss', format: 'carbon/scss-icon-size' },
|
|
61
|
+
{
|
|
62
|
+
destination: '_border-radius.scss',
|
|
63
|
+
format: 'carbon/scss-border-radius',
|
|
64
|
+
},
|
|
65
|
+
{ destination: '_layout.scss', format: 'carbon/scss-layout' },
|
|
66
|
+
{ destination: '_size.scss', format: 'carbon/scss-size' },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
js: {
|
|
70
|
+
transformGroup: 'carbon',
|
|
71
|
+
buildPath: JS_GENERATED + '/',
|
|
72
|
+
files: [
|
|
73
|
+
{
|
|
74
|
+
destination: 'layout-tokens.js',
|
|
75
|
+
format: 'carbon/js-layout',
|
|
76
|
+
options: { output: 'js' },
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
destination: 'layout-tokens.d.ts',
|
|
80
|
+
format: 'carbon/js-layout',
|
|
81
|
+
options: { output: 'dts' },
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// ── Build a registered SD instance ────────────────────────────────────────────
|
|
89
|
+
function createInstance() {
|
|
90
|
+
const sd = new StyleDictionary({});
|
|
91
|
+
sd.registerTransform(carbonNameKebab);
|
|
92
|
+
sd.registerTransformGroup({ name: 'carbon', transforms: CARBON_TRANSFORMS });
|
|
93
|
+
for (const fmt of scssFormats) sd.registerFormat(fmt);
|
|
94
|
+
sd.registerFormat(carbonJsLayout);
|
|
95
|
+
return sd;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── run ───────────────────────────────────────────────────────────────────────
|
|
99
|
+
async function run() {
|
|
100
|
+
const sd = await createInstance().extend(config);
|
|
101
|
+
await sd.buildAllPlatforms();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { run };
|