@massimo-cassandro/minimo 0.1.12 → 0.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 (95) hide show
  1. package/package.json +8 -4
  2. package/src/{js → components}/dialog-content.js +1 -1
  3. package/src/{css → components/inner-nav}/inner-nav.css +2 -2
  4. package/src/{js → components/inner-nav}/inner-nav.js +8 -0
  5. package/src/components/modal-content/modal-content.js +1 -1
  6. package/src/components/modal-content/modal-content.module.css +4 -4
  7. package/src/components/recaptcha/recaptcha.css +4 -5
  8. package/src/components/recaptcha/recaptcha.minimo.tokens.jsonc +24 -0
  9. package/src/{css/spinners → components/spinner}/TODO spinner-circle.css +1 -1
  10. package/src/components/spinner/readme.md +1 -0
  11. package/src/{css/spinners → components/spinner}/spinner-circle-basic.css +3 -2
  12. package/src/{css/spinners → components/spinner}/spinner-wrapper.css +1 -1
  13. package/src/components/spinner/spinner.minimo.tokens.jsonc +22 -0
  14. package/src/components/toast/toast.js +77 -0
  15. package/src/components/toast/toast.minimo.tokens.jsonc +29 -0
  16. package/src/components/toast/toast.module.css +59 -0
  17. package/src/css/alerts.css +23 -23
  18. package/src/css/anchors.css +17 -16
  19. package/src/css/buttons/btn-close.css +8 -7
  20. package/src/css/buttons/buttons.css +101 -94
  21. package/src/css/buttons/status-buttons.css +43 -43
  22. package/src/css/container.css +3 -12
  23. package/src/css/details.css +1 -1
  24. package/src/css/dialog-content.css +2 -2
  25. package/src/css/flex.css +9 -0
  26. package/src/css/forms/fieldset.css +75 -0
  27. package/src/css/forms/form-group.css +18 -0
  28. package/src/css/forms/form-utilities.css +32 -0
  29. package/src/css/forms/forms.css +37 -202
  30. package/src/css/forms/input-group.css +49 -0
  31. package/src/css/forms/validation.css +25 -0
  32. package/src/css/grid.css +25 -18
  33. package/src/css/headings.css +10 -9
  34. package/src/css/icons.css +1 -40
  35. package/src/css/layout-base.css +3 -3
  36. package/src/css/overlay.css +1 -2
  37. package/src/css/table.css +26 -19
  38. package/src/css/text.css +8 -7
  39. package/src/css/utilities.css +90 -29
  40. package/src/custom-media-default.css +20 -8
  41. package/src/custom-properties-default.css +212 -317
  42. package/src/design-tokens/TODO.md +2 -0
  43. package/src/design-tokens/alerts.minimo.tokens.jsonc +23 -0
  44. package/src/design-tokens/basic.minimo.tokens.jsonc +52 -0
  45. package/src/design-tokens/btn-close.minimo.tokens.jsonc +95 -0
  46. package/src/design-tokens/btn-primary.minimo.tokens.jsonc +100 -0
  47. package/src/design-tokens/btn-secondary.minimo.tokens.jsonc +33 -0
  48. package/src/design-tokens/btn.minimo.tokens.jsonc +200 -0
  49. package/src/design-tokens/color-accent.minimo.tokens.jsonc +13 -0
  50. package/src/design-tokens/color-neutral.minimo.tokens.jsonc +12 -0
  51. package/src/design-tokens/color-primary.minimo.tokens.jsonc +12 -0
  52. package/src/design-tokens/color-secondary.minimo.tokens.jsonc +12 -0
  53. package/src/design-tokens/flex-grid.minimo.tokens.jsonc +18 -0
  54. package/src/design-tokens/form.minimo.tokens.jsonc +295 -0
  55. package/src/design-tokens/gradients-shadowes.minimo.tokens.jsonc +4 -0
  56. package/src/design-tokens/headings.minimo.tokens.jsonc +57 -0
  57. package/src/design-tokens/link.minimo.tokens.jsonc +41 -0
  58. package/src/design-tokens/radius.minimo.tokens.jsonc +45 -0
  59. package/src/design-tokens/size.minimo.tokens.jsonc +101 -0
  60. package/src/design-tokens/status.minimo.tokens.jsonc +69 -0
  61. package/src/design-tokens/table.minimo.tokens.jsonc +66 -0
  62. package/src/design-tokens/typography.minimo.tokens.jsonc +120 -0
  63. package/src/design-tokens/utility.minimo.tokens.jsonc +8 -0
  64. package/src/design-tokens/z-index.minimo.tokens.jsonc +8 -0
  65. package/src/design-tokens-utilities/build-tokens.mjs +501 -0
  66. package/src/design-tokens-utilities/check-unresolved-custom-props.mjs +131 -0
  67. package/src/design-tokens-utilities/config-templates/tokens-config.mjs +34 -0
  68. package/src/design-tokens-utilities/readme.md +16 -0
  69. package/src/fonts-template.css +7 -0
  70. package/src/index-template.css +5 -10
  71. package/src/index.js +1 -0
  72. package/src/utilities/classnames.js +3 -0
  73. package/src/web-components/simple-datatable/icons/arrow-down.svg +1 -0
  74. package/src/web-components/simple-datatable/icons/arrow-up.svg +2 -0
  75. package/src/web-components/simple-datatable/icons/arrows-down-up.svg +1 -0
  76. package/src/web-components/simple-datatable/icons/caret-left.svg +1 -0
  77. package/src/web-components/simple-datatable/icons/caret-right.svg +1 -0
  78. package/src/web-components/simple-datatable/icons/check-bold.svg +1 -0
  79. package/src/web-components/simple-datatable/icons/x-bold.svg +1 -0
  80. package/src/web-components/simple-datatable/simple-datatable.js +219 -0
  81. package/src/web-components/simple-datatable/simple-datatable.minimo.tokens.jsonc +50 -0
  82. package/src/web-components/simple-datatable/simple-datatable.module.css +214 -0
  83. package/src/web-components/simple-datatable/src/parse-cols.js +117 -0
  84. package/src/components/init.js +0 -7
  85. package/src/css/forms/form-edit-info.css +0 -17
  86. /package/src/{js → components}/alert-autoclose.js +0 -0
  87. /package/src/{js → components}/create-blurhash-canvas.js +0 -0
  88. /package/src/{js → components}/dismiss-alerts.js +0 -0
  89. /package/src/{js → components}/forms.js +0 -0
  90. /package/src/{js → components}/img-viewer-dom-builder.js +0 -0
  91. /package/src/{js → components}/overlay.js +0 -0
  92. /package/src/{js → components}/print-icon.js +0 -0
  93. /package/src/{css/spinners → components/spinner}/TODO spinner-three-dots.css +0 -0
  94. /package/src/{css/spinners → components/spinner}/spinner-trailing-dots.css +0 -0
  95. /package/src/{js → components/spinner}/spinner.js +0 -0
@@ -0,0 +1,120 @@
1
+ {
2
+ "font": {
3
+
4
+ "family": {
5
+ "$value": "sans-serif",
6
+ "$type": "fontFamily"
7
+ },
8
+ "family-mono": {
9
+ "$value": "'Monaspace Neon Var', 'Monaspace Neon', 'Aptos Mono', 'Roboto Mono', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Andale Mono', 'Courier New', monospace",
10
+ "$type": "fontFamily"
11
+ },
12
+
13
+ "size": {
14
+ "base": {
15
+ "$value": "clamp(.9rem, .6538rem + .3846vw, 1rem)",
16
+ "$type": "string"
17
+ },
18
+ "xxs": {
19
+ "$value": "clamp(.6rem, .3538rem + .3846vw, .7rem)",
20
+ "$type": "string"
21
+ },
22
+ "xs": {
23
+ "$value": "clamp(.7rem, .4538rem + .3846vw, .8rem)",
24
+ "$type": "string"
25
+ },
26
+ "sm": {
27
+ "$value": "clamp(.8rem, .5538rem + .3846vw, .9rem)",
28
+ "$type": "string"
29
+ },
30
+ // "lead": {
31
+ // "$value": "clamp(1rem, 0.6923rem + 0.4808vw, 1.125rem)",
32
+ // "$type": "string"
33
+ // },
34
+ "md": {
35
+ "$value": "clamp(1.05rem, .6808rem + .5769vw, 1.2rem)",
36
+ "$type": "string"
37
+ },
38
+ "lg": {
39
+ "$value": "clamp(1.2rem, .7077rem + .7692vw, 1.4rem)",
40
+ "$type": "string"
41
+ },
42
+ "xl": {
43
+ "$value": "clamp(1.6rem, 1.1077rem + .7692vw, 1.8rem)",
44
+ "$type": "string"
45
+ },
46
+ "2xl": {
47
+ "$value": "clamp(2rem, .7692rem + 1.9231vw, 2.5rem)",
48
+ "$type": "string"
49
+ },
50
+ "3xl": {
51
+ "$value": "clamp(2.2rem, .2308rem + 3.0769vw, 3rem)",
52
+ "$type": "string"
53
+ }
54
+
55
+ },
56
+
57
+
58
+ // Rhythm in Web Typography | Better Web Type <https://betterwebtype.com/articles/2018/10/15/rhythm-in-web-typography/>
59
+ // The good line-height <https://www.thegoodlineheight.com/>
60
+
61
+ "line-height": {
62
+ "base": {
63
+ "$value": "1.5",
64
+ "$type": "number"
65
+ }
66
+ },
67
+
68
+ "style": {
69
+ "base": {
70
+ "$value": "normal",
71
+ "$type": "string"
72
+ }
73
+
74
+ },
75
+
76
+
77
+ "weight": {
78
+ "xxlight": {
79
+ "$value": "100",
80
+ "$type": "string",
81
+ "$description": ""
82
+ },
83
+ "xlight": {
84
+ "$value": "200",
85
+ "$type": "string",
86
+ "$description": ""
87
+ },
88
+ "light": {
89
+ "$value": "300",
90
+ "$type": "string",
91
+ "$description": ""
92
+ },
93
+ "regular": {
94
+ "$value": "400",
95
+ "$type": "string",
96
+ "$description": ""
97
+ },
98
+ "medium": {
99
+ "$value": "500",
100
+ "$type": "string",
101
+ "$description": ""
102
+ },
103
+ "semibold": {
104
+ "$value": "600",
105
+ "$type": "string",
106
+ "$description": ""
107
+ },
108
+ "bold": {
109
+ "$value": "700",
110
+ "$type": "string",
111
+ "$description": ""
112
+ },
113
+ "xbold": {
114
+ "$value": "900",
115
+ "$type": "string",
116
+ "$description": ""
117
+ }
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "overlay": {
3
+ "color": {
4
+ "$value": "rgb(0 0 0 / .6)",
5
+ "$type": "color"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "z-index": {
3
+ "overlay": {
4
+ "$value": 99,
5
+ "$type": "number"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,501 @@
1
+ // StyleDictionary v.5
2
+ import StyleDictionary from 'style-dictionary';
3
+ import path from 'path';
4
+ import { pathToFileURL } from 'url';
5
+ import { styleText } from 'node:util';
6
+
7
+ import { homedir } from 'os';
8
+
9
+ import stylelint from 'stylelint';
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Risoluzione del config file tramite flag --config
13
+ // ---------------------------------------------------------------------------
14
+ const args = process.argv.slice(2);
15
+ const configFlagIndex = args.indexOf('--config');
16
+
17
+ if (configFlagIndex === -1 || !args[configFlagIndex + 1]) {
18
+ console.error('Errore: devi specificare il file di configurazione con --config ./path/to/config/file');
19
+ process.exit(1);
20
+ }
21
+
22
+ let customPropsCount;
23
+
24
+ // Il percorso passato è relativo alla CWD (root del progetto)
25
+ const configArgPath = args[configFlagIndex + 1];
26
+ const configAbsPath = path.resolve(process.cwd(), configArgPath);
27
+
28
+ // La directory del config file: usata come base per risolvere i percorsi interni
29
+ const configDir = path.dirname(configAbsPath);
30
+
31
+ // Importa il config dinamicamente (usa fileURL per compatibilità ESM su Windows)
32
+ const buildConfig = (await import(pathToFileURL(configAbsPath))).default;
33
+
34
+ // Helper: risolve un percorso relativo rispetto alla dir del config file.
35
+ // Se il percorso è già assoluto lo lascia intatto.
36
+ const resolveFromConfig = (p) => path.resolve(configDir, p);
37
+
38
+ // Risolve tutti i percorsi dichiarati nel config
39
+ const stylelintConfigPath = resolveFromConfig(buildConfig.stylelintConfigPath);
40
+ const buildPath = resolveFromConfig(buildConfig.buildPath);
41
+ const destFile = buildConfig.destFile; // nome file, non un percorso da risolvere
42
+
43
+ // source può contenere glob/percorsi: li risolviamo tutti rispetto alla configDir.
44
+ // path.resolve non gestisce i glob, quindi usiamo path.posix per mantenere le slash
45
+ // forward anche su Windows (fast-glob le richiede).
46
+ const source = buildConfig.source.map((s) => {
47
+ if (path.isAbsolute(s)) return s; // già assoluto, niente da fare
48
+
49
+ // Unisce configDir + s e normalizza, poi ri-converte in forward-slash per fast-glob
50
+ const joined = path.join(configDir, s);
51
+ // Su Windows path.join usa '\', fast-glob vuole '/': normalizziamo sempre
52
+ return joined.split(path.sep).join('/');
53
+ });
54
+
55
+ const stylelintConfig = await import(pathToFileURL(stylelintConfigPath)).then(m => m.default);
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // 1. Shadow Transform
59
+ // ---------------------------------------------------------------------------
60
+ StyleDictionary.registerTransform({
61
+ name: 'shadow/css',
62
+ type: 'value',
63
+ transitive: true,
64
+ filter: (token) => (token.$type === 'shadow' || token.type === 'shadow'),
65
+ transform: (token) => {
66
+ const original = token.original?.$value ?? token.original?.value;
67
+
68
+ const value = (typeof original === 'string' && original.startsWith('{'))
69
+ ? (token.$value ?? token.value)
70
+ : (original ?? token.$value ?? token.value);
71
+
72
+ const shadows = Array.isArray(value) ? value : [value];
73
+
74
+ if (shadows.length === 1 && typeof shadows[0] === 'string') {
75
+ return shadows[0];
76
+ }
77
+
78
+ const BASE_FONT_SIZE = 16;
79
+ const toRem = (val) => {
80
+ if (val === undefined || val === null) return '0';
81
+ const str = String(val).trim();
82
+ const num = parseFloat(str);
83
+ if (isNaN(num)) return str;
84
+ if (num === 0) return '0';
85
+ if (str.endsWith('px')) return `${num / BASE_FONT_SIZE}rem`;
86
+ return str;
87
+ };
88
+
89
+ return shadows
90
+ .map((s) => {
91
+ const offsetX = toRem(s.offsetX ?? s.x);
92
+ const offsetY = toRem(s.offsetY ?? s.y);
93
+ const blur = toRem(s.blur);
94
+ const spread = toRem(s.spread);
95
+ const color = s.color ?? 'transparent';
96
+ const inset = s.inset ? 'inset ' : '';
97
+ return `${inset}${offsetX} ${offsetY} ${blur} ${spread} ${color}`;
98
+ })
99
+ .join(', ');
100
+ }
101
+ });
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // 2. PX to REM Transform
105
+ // ---------------------------------------------------------------------------
106
+ StyleDictionary.registerTransform({
107
+ name: 'size/pxToRem-smart',
108
+ type: 'value',
109
+ filter: (token) => {
110
+ const value = token.$value || token.value;
111
+ return (token.$type === 'dimension' || token.type === 'dimension')
112
+ && typeof value === 'string'
113
+ && !value.endsWith('rem');
114
+ },
115
+ transform: (token) => {
116
+ const baseFontSize = 16;
117
+ const value = token.$value || token.value;
118
+ const floatValue = parseFloat(value);
119
+
120
+ if (isNaN(floatValue)) return value;
121
+
122
+ return `${floatValue / baseFontSize}rem`;
123
+ }
124
+ });
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // 3. Color passthrough
128
+ // ---------------------------------------------------------------------------
129
+ StyleDictionary.registerTransform({
130
+ name: 'color/css-modern',
131
+ type: 'value',
132
+ transitive: true,
133
+ filter: (token) => token.$type === 'color' || token.type === 'color',
134
+ transform: (token) => {
135
+ const value = token.$value ?? token.value;
136
+ if (typeof value === 'string') return value;
137
+ return value;
138
+ }
139
+ });
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // 4. Gradient Transform
143
+ // ---------------------------------------------------------------------------
144
+ StyleDictionary.registerTransform({
145
+ name: 'gradient/css',
146
+ type: 'value',
147
+ transitive: true,
148
+ filter: (token) => token.$type === 'gradient' || token.type === 'gradient',
149
+ transform: (token) => {
150
+ const original = token.original?.$value ?? token.original?.value;
151
+
152
+ const value = (typeof original === 'string' && original.startsWith('{'))
153
+ ? (token.$value ?? token.value)
154
+ : (original ?? token.$value ?? token.value);
155
+
156
+ if (typeof value === 'string') return value;
157
+
158
+ const { type = 'linear', angle = 90, stops = [] } = value;
159
+
160
+ const stopsList = stops
161
+ .map((s) => {
162
+ let color = s.color ?? 'transparent';
163
+ if (s.alpha !== undefined) {
164
+ color = `color-mix(in srgb, ${color} ${s.alpha * 100}%, transparent)`;
165
+ }
166
+ const position = s.position !== undefined ? ` ${s.position * 100}%` : '';
167
+ return `${color}${position}`;
168
+ })
169
+ .join(', ');
170
+
171
+ if (type === 'linear') return `linear-gradient(${angle}deg, ${stopsList})`;
172
+ if (type === 'radial') return `radial-gradient(circle, ${stopsList})`;
173
+ if (type === 'conic') return `conic-gradient(from ${angle}deg, ${stopsList})`;
174
+
175
+ return `linear-gradient(${angle}${typeof angle === 'number'? 'deg' : ''}, ${stopsList})`;
176
+ }
177
+ });
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // 5. Composite Transform (border, outline, transition, animation)
181
+ // Tutti i tipi che hanno un shorthand CSS diretto.
182
+ // ---------------------------------------------------------------------------
183
+ StyleDictionary.registerTransform({
184
+ name: 'composite/css',
185
+ type: 'value',
186
+ transitive: true,
187
+ filter: (token) => {
188
+ const type = token.$type ?? token.type;
189
+ const value = token.$value ?? token.value;
190
+ return (
191
+ typeof value === 'object' &&
192
+ value !== null &&
193
+ !Array.isArray(value) &&
194
+ ['border', 'outline', 'transition', 'animation'].includes(type)
195
+ );
196
+ },
197
+ transform: (token) => {
198
+ const type = token.$type ?? token.type;
199
+ const v = token.$value ?? token.value;
200
+
201
+ if (type === 'border') {
202
+ const parts = [v.width ?? '1px'];
203
+ if (v.style !== undefined && v.style !== null && v.style !== '') parts.push(v.style);
204
+ parts.push(v.color ?? 'transparent');
205
+ return parts.join(' ');
206
+ }
207
+
208
+ if (type === 'outline') {
209
+ return `${v.width ?? '1px'} ${v.style ?? 'solid'} ${v.color ?? 'transparent'}`;
210
+ }
211
+
212
+ if (type === 'transition') {
213
+ // Ordine: duration | timing-function | delay | property
214
+ return [
215
+ v.duration ?? '0s',
216
+ v.timingFunction ?? 'ease',
217
+ v.delay ?? '0s',
218
+ v.property ?? 'all',
219
+ ].join(' ');
220
+ }
221
+
222
+ if (type === 'animation') {
223
+ // Ordine: duration | timing-function | delay | iteration-count |
224
+ // direction | fill-mode | play-state | name
225
+ return [
226
+ v.duration ?? '0s',
227
+ v.timingFunction ?? 'ease',
228
+ v.delay ?? '0s',
229
+ v.iterationCount ?? '1',
230
+ v.direction ?? 'normal',
231
+ v.fillMode ?? 'none',
232
+ v.playState ?? 'running',
233
+ v.name ?? 'none',
234
+ ].join(' ');
235
+ }
236
+ }
237
+ });
238
+
239
+ // ---------------------------------------------------------------------------
240
+ // 6. Typography Transform
241
+ // Restituisce l'oggetto così com'è: il format lo esploderà in più props.
242
+ // ---------------------------------------------------------------------------
243
+ StyleDictionary.registerTransform({
244
+ name: 'typography/css',
245
+ type: 'value',
246
+ transitive: true,
247
+ filter: (token) => token.$type === 'typography' || token.type === 'typography',
248
+ transform: (token) => token.$value ?? token.value,
249
+ });
250
+
251
+ // ---------------------------------------------------------------------------
252
+ // Format: css/variables-sorted
253
+ // ---------------------------------------------------------------------------
254
+ StyleDictionary.registerFormat({
255
+ name: 'css/variables-sorted',
256
+ format: ({ dictionary, options }) => {
257
+ const selector = options.selector ?? ':root';
258
+
259
+ const tokenByPath = {};
260
+ for (const token of dictionary.allTokens) {
261
+ tokenByPath[token.path.join('.')] = token;
262
+ }
263
+
264
+ const resolveRefs = (str) =>
265
+ String(str).replace(/\{([^}]+)\}/g, (_, p) => {
266
+ const ref = tokenByPath[p];
267
+ return ref ? `var(--${ref.name})` : `{${p}}`;
268
+ });
269
+
270
+ const BASE_FONT_SIZE = 16;
271
+ const toRem = (val) => {
272
+ if (val === undefined || val === null) return '0';
273
+ const str = String(val).trim();
274
+ const num = parseFloat(str);
275
+ if (isNaN(num) || num === 0) return num === 0 ? '0' : str;
276
+ if (str.endsWith('px')) return `${num / BASE_FONT_SIZE}rem`;
277
+ return str;
278
+ };
279
+
280
+ const buildShadow = (original) => {
281
+ const shadows = Array.isArray(original) ? original : [original];
282
+ return shadows.map((s) => {
283
+ const offsetX = toRem(s.offsetX ?? s.x);
284
+ const offsetY = toRem(s.offsetY ?? s.y);
285
+ const blur = toRem(s.blur);
286
+ const spread = toRem(s.spread);
287
+ const color = resolveRefs(s.color ?? 'transparent');
288
+ const inset = s.inset ? 'inset ' : '';
289
+ return `${inset}${offsetX} ${offsetY} ${blur} ${spread} ${color}`;
290
+ }).join(', ');
291
+ };
292
+
293
+ const buildGradient = (original) => {
294
+ const { type = 'linear', angle = 90, stops = [] } = original;
295
+ const stopsList = stops.map((s) => {
296
+ let color = resolveRefs(s.color ?? 'transparent');
297
+ if (s.alpha !== undefined) {
298
+ color = `color-mix(in srgb, ${color} ${s.alpha * 100}%, transparent)`;
299
+ }
300
+ const position = s.position !== undefined ? ` ${s.position * 100}%` : '';
301
+ return `${color}${position}`;
302
+ }).join(', ');
303
+
304
+ if (type === 'radial') return `radial-gradient(circle, ${stopsList})`;
305
+ if (type === 'conic') return `conic-gradient(from ${angle}deg, ${stopsList})`;
306
+ return `linear-gradient(${angle}deg, ${stopsList})`;
307
+ };
308
+
309
+ // border e outline: shorthand con resolveRefs su tutti i campi; style è opzionale
310
+ const buildBorderLike = (original) => {
311
+ const parts = [resolveRefs(String(original.width ?? '1px'))];
312
+ if (original.style !== undefined && original.style !== null && original.style !== '') {
313
+ parts.push(resolveRefs(String(original.style)));
314
+ }
315
+ parts.push(resolveRefs(String(original.color ?? 'transparent')));
316
+ return parts.join(' ');
317
+ };
318
+
319
+ // transition: resolveRefs su tutti i campi
320
+ const buildTransition = (original) => [
321
+ resolveRefs(String(original.duration ?? '0s')),
322
+ resolveRefs(String(original.timingFunction ?? 'ease')),
323
+ resolveRefs(String(original.delay ?? '0s')),
324
+ resolveRefs(String(original.property ?? 'all')),
325
+ ].join(' ');
326
+
327
+ // animation: resolveRefs su tutti i campi
328
+ const buildAnimation = (original) => [
329
+ resolveRefs(String(original.duration ?? '0s')),
330
+ resolveRefs(String(original.timingFunction ?? 'ease')),
331
+ resolveRefs(String(original.delay ?? '0s')),
332
+ resolveRefs(String(original.iterationCount ?? '1')),
333
+ resolveRefs(String(original.direction ?? 'normal')),
334
+ resolveRefs(String(original.fillMode ?? 'none')),
335
+ resolveRefs(String(original.playState ?? 'running')),
336
+ resolveRefs(String(original.name ?? 'none')),
337
+ ].join(' ');
338
+
339
+ const buildTypography = (tokenName, original) => {
340
+ const {
341
+ fontStyle,
342
+ fontVariant,
343
+ fontWeight,
344
+ fontStretch,
345
+ fontSize,
346
+ lineHeight,
347
+ fontFamily,
348
+ // queste non entrano nel shorthand font
349
+ letterSpacing,
350
+ textTransform,
351
+ textDecoration,
352
+ } = original;
353
+
354
+ const lines = [];
355
+
356
+ // shorthand font se ci sono size e family (requisiti minimi CSS)
357
+ if (fontSize && fontFamily) {
358
+ const parts = [];
359
+ if (fontStyle) parts.push(resolveRefs(String(fontStyle)));
360
+ if (fontVariant) parts.push(resolveRefs(String(fontVariant)));
361
+ if (fontWeight) parts.push(resolveRefs(String(fontWeight)));
362
+ if (fontStretch) parts.push(resolveRefs(String(fontStretch)));
363
+
364
+ const sizeSlash = lineHeight
365
+ ? `${resolveRefs(String(fontSize))}/${resolveRefs(String(lineHeight))}`
366
+ : resolveRefs(String(fontSize));
367
+
368
+ parts.push(sizeSlash);
369
+ parts.push(resolveRefs(String(fontFamily)));
370
+
371
+ lines.push(` --${tokenName}-font: ${parts.join(' ')};`);
372
+ }
373
+
374
+ // proprietà che non entrano nel shorthand font
375
+ const EXTRA_PROPS = {
376
+ letterSpacing: ['letter-spacing', letterSpacing],
377
+ textTransform: ['text-transform', textTransform],
378
+ textDecoration: ['text-decoration', textDecoration],
379
+ // fallback: se mancano size o family, le esponiamo comunque separatamente
380
+ ...(!fontSize ? { fontSize: ['font-size', original.fontSize] } : {}),
381
+ ...(!fontFamily ? { fontFamily: ['font-family', original.fontFamily] } : {}),
382
+ };
383
+
384
+ for (const [, [cssProp, val]] of Object.entries(EXTRA_PROPS)) {
385
+ if (val !== undefined) {
386
+ lines.push(` --${tokenName}-${cssProp}: ${resolveRefs(String(val))};`);
387
+ }
388
+ }
389
+
390
+ return lines.join('\n');
391
+ };
392
+
393
+ const lines = dictionary.allTokens
394
+ .slice()
395
+ .sort((a, b) => a.name.localeCompare(b.name))
396
+ .flatMap((token) => {
397
+ const type = token.$type ?? token.type;
398
+ const orig = token.original?.$value ?? token.original?.value;
399
+
400
+ // typography: genera più righe, una per proprietà
401
+ if (type === 'typography') {
402
+ return [buildTypography(token.name, orig ?? {})];
403
+ }
404
+
405
+ let value;
406
+
407
+ if (type === 'shadow' && options.outputReferences) {
408
+ value = (typeof orig === 'string' && orig.startsWith('{'))
409
+ ? resolveRefs(orig)
410
+ : buildShadow(orig);
411
+
412
+ } else if (type === 'gradient' && options.outputReferences) {
413
+ value = (typeof orig === 'string' && orig.startsWith('{'))
414
+ ? resolveRefs(orig)
415
+ : buildGradient(orig);
416
+
417
+ } else if ((type === 'border' || type === 'outline') && options.outputReferences) {
418
+ value = (typeof orig === 'string' && orig.startsWith('{'))
419
+ ? resolveRefs(orig)
420
+ : buildBorderLike(orig);
421
+
422
+ } else if (type === 'transition' && options.outputReferences) {
423
+ value = (typeof orig === 'string' && orig.startsWith('{'))
424
+ ? resolveRefs(orig)
425
+ : buildTransition(orig);
426
+
427
+ } else if (type === 'animation' && options.outputReferences) {
428
+ value = (typeof orig === 'string' && orig.startsWith('{'))
429
+ ? resolveRefs(orig)
430
+ : buildAnimation(orig);
431
+
432
+ } else if (options.outputReferences && typeof orig === 'string' && orig.includes('{')) {
433
+ value = resolveRefs(orig);
434
+
435
+ } else {
436
+ value = String(token.$value ?? token.value);
437
+ }
438
+
439
+ return [` --${token.name}: ${value};`];
440
+ });
441
+
442
+ customPropsCount = lines.length;
443
+ return `${selector} {\n${lines.join('\n')}\n}\n`;
444
+ }
445
+ });
446
+
447
+ // ---------------------------------------------------------------------------
448
+ // Style Dictionary config
449
+ // ---------------------------------------------------------------------------
450
+ const config = {
451
+ source,
452
+
453
+ log: {
454
+ verbosity: 'verbose'
455
+ },
456
+
457
+ platforms: {
458
+ css: {
459
+ buildPath: buildPath + '/',
460
+ transforms: [
461
+ 'attribute/cti',
462
+ 'name/kebab',
463
+ 'time/seconds',
464
+ 'asset/url',
465
+ 'size/pxToRem-smart',
466
+ 'color/css-modern',
467
+ 'shadow/css',
468
+ 'gradient/css',
469
+ 'composite/css', // border, outline, transition, animation
470
+ 'typography/css', // typography (esploso nel format)
471
+ ],
472
+ files: [
473
+ {
474
+ destination: destFile,
475
+ format: 'css/variables-sorted',
476
+ options: {
477
+ outputReferences: true,
478
+ showFileHeader: true,
479
+ }
480
+ }
481
+ ]
482
+ }
483
+ }
484
+ };
485
+
486
+ const sd = new StyleDictionary(config);
487
+ await sd.buildAllPlatforms();
488
+
489
+ await stylelint.lint({
490
+ config: stylelintConfig,
491
+ files: [ path.join(buildPath, destFile) ],
492
+ fix: true
493
+ });
494
+
495
+ console.log(styleText(['yellow'], `[build-tokens] config file : ${configAbsPath.replace(homedir(), '~')}` ));
496
+ console.log(styleText(['yellow'], `[build-tokens] source : ${source.map(i => i.replace(homedir(), '~'))}` ));
497
+ console.log(styleText(['yellow'], `[build-tokens] dest file : ${path.join(buildPath, destFile).replace(homedir(), '~')}` ));
498
+
499
+ console.log(styleText(['green'], `[build-tokens] ${customPropsCount} custom props elaborate`));
500
+
501
+ console.log(styleText(['green'], '**** FINE ****'));