@webmate-studio/builder 0.2.130 → 0.2.132
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/package.json +1 -1
- package/src/design-tokens-v2-css.js +710 -0
- package/src/design-tokens-v2-migrate.js +510 -0
- package/src/design-tokens-v2.js +739 -0
- package/src/index.js +10 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design Token V2 — Migration + Validation
|
|
3
|
+
*
|
|
4
|
+
* Konvertiert V1-Token-Objekte in V2-Format.
|
|
5
|
+
* Validiert V2-Token-Objekte und füllt fehlende Felder mit Defaults auf.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
defaultDesignTokensV2,
|
|
10
|
+
generateColorScale,
|
|
11
|
+
isV1Format,
|
|
12
|
+
isV2Format,
|
|
13
|
+
COLOR_WORLDS,
|
|
14
|
+
SEMANTIC_COLOR_WORLDS,
|
|
15
|
+
TEXT_VOICES,
|
|
16
|
+
TEXT_LEVELS,
|
|
17
|
+
BUTTON_VARIANTS,
|
|
18
|
+
BUTTON_SIZES,
|
|
19
|
+
DEFAULT_SEMANTIC_MAPPINGS,
|
|
20
|
+
DEFAULT_TEXT_ALIASES
|
|
21
|
+
} from './design-tokens-v2.js';
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// ─── Hauptfunktionen ────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Migriert ein V1-Token-Objekt zu V2.
|
|
28
|
+
* Gibt ein vollständiges V2-Token-Objekt zurück.
|
|
29
|
+
*/
|
|
30
|
+
export function migrateDesignTokensV1toV2(v1) {
|
|
31
|
+
if (!v1) return structuredClone(defaultDesignTokensV2);
|
|
32
|
+
if (isV2Format(v1)) return validateDesignTokensV2(v1);
|
|
33
|
+
|
|
34
|
+
const v2 = structuredClone(defaultDesignTokensV2);
|
|
35
|
+
|
|
36
|
+
// ── Farben migrieren ──
|
|
37
|
+
migrateColors(v1, v2);
|
|
38
|
+
|
|
39
|
+
// ── Fonts migrieren ──
|
|
40
|
+
migrateFonts(v1, v2);
|
|
41
|
+
|
|
42
|
+
// ── Text-Stile migrieren ──
|
|
43
|
+
migrateTextStyles(v1, v2);
|
|
44
|
+
|
|
45
|
+
// ── Buttons migrieren ──
|
|
46
|
+
migrateButtons(v1, v2);
|
|
47
|
+
|
|
48
|
+
// ── Layout migrieren ──
|
|
49
|
+
if (v1.borderRadius) v2.borderRadius = v1.borderRadius;
|
|
50
|
+
if (v1.borderWidth) v2.borderWidth = v1.borderWidth;
|
|
51
|
+
if (v1.spacing) v2.spacing = structuredClone(v1.spacing);
|
|
52
|
+
if (v1.container) v2.container = structuredClone(v1.container);
|
|
53
|
+
if (v1.breakpoints) v2.breakpoints = structuredClone(v1.breakpoints);
|
|
54
|
+
|
|
55
|
+
return v2;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validiert ein V2-Token-Objekt und füllt fehlende Felder auf.
|
|
60
|
+
* Gibt ein vollständiges V2-Token-Objekt zurück.
|
|
61
|
+
*/
|
|
62
|
+
export function validateDesignTokensV2(tokens) {
|
|
63
|
+
if (!tokens) return structuredClone(defaultDesignTokensV2);
|
|
64
|
+
|
|
65
|
+
const v2 = structuredClone(tokens);
|
|
66
|
+
v2.version = 2;
|
|
67
|
+
|
|
68
|
+
// ── Farben validieren ──
|
|
69
|
+
if (!v2.colors) v2.colors = structuredClone(defaultDesignTokensV2.colors);
|
|
70
|
+
|
|
71
|
+
for (const world of COLOR_WORLDS) {
|
|
72
|
+
if (!v2.colors[world]) {
|
|
73
|
+
v2.colors[world] = structuredClone(defaultDesignTokensV2.colors[world]);
|
|
74
|
+
} else if (v2.colors[world].base && !v2.colors[world].scale) {
|
|
75
|
+
// Basisfarbe vorhanden aber keine Skala → generieren
|
|
76
|
+
v2.colors[world].scale = generateColorScale(v2.colors[world].base);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Semantic Mappings
|
|
81
|
+
if (!v2.semanticMappings) v2.semanticMappings = {};
|
|
82
|
+
for (const world of SEMANTIC_COLOR_WORLDS) {
|
|
83
|
+
if (!v2.semanticMappings[world]) {
|
|
84
|
+
v2.semanticMappings[world] = { ...DEFAULT_SEMANTIC_MAPPINGS };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Dark Mode
|
|
89
|
+
if (!v2.darkMode) v2.darkMode = { enabled: false, colors: null, semanticMappings: null };
|
|
90
|
+
|
|
91
|
+
// ── Typografie validieren ──
|
|
92
|
+
if (!v2.typography) v2.typography = structuredClone(defaultDesignTokensV2.typography);
|
|
93
|
+
if (!v2.typography.fonts || v2.typography.fonts.length === 0) {
|
|
94
|
+
v2.typography.fonts = structuredClone(defaultDesignTokensV2.typography.fonts);
|
|
95
|
+
}
|
|
96
|
+
if (!v2.typography.monoFont) {
|
|
97
|
+
v2.typography.monoFont = structuredClone(defaultDesignTokensV2.typography.monoFont);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Text Styles
|
|
101
|
+
if (!v2.typography.textStyles) v2.typography.textStyles = structuredClone(defaultDesignTokensV2.typography.textStyles);
|
|
102
|
+
for (const voice of TEXT_VOICES) {
|
|
103
|
+
if (!v2.typography.textStyles[voice]) {
|
|
104
|
+
v2.typography.textStyles[voice] = structuredClone(defaultDesignTokensV2.typography.textStyles[voice]);
|
|
105
|
+
}
|
|
106
|
+
for (const level of TEXT_LEVELS) {
|
|
107
|
+
if (!v2.typography.textStyles[voice][level]) {
|
|
108
|
+
v2.typography.textStyles[voice][level] = structuredClone(defaultDesignTokensV2.typography.textStyles[voice][level]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Aliases
|
|
114
|
+
if (!v2.typography.aliases) v2.typography.aliases = { ...DEFAULT_TEXT_ALIASES };
|
|
115
|
+
|
|
116
|
+
// ── Buttons validieren ──
|
|
117
|
+
if (!v2.buttons) v2.buttons = structuredClone(defaultDesignTokensV2.buttons);
|
|
118
|
+
if (!v2.buttons.sizes) v2.buttons.sizes = structuredClone(defaultDesignTokensV2.buttons.sizes);
|
|
119
|
+
if (!v2.buttons.variants) v2.buttons.variants = structuredClone(defaultDesignTokensV2.buttons.variants);
|
|
120
|
+
if (!v2.buttons.global) v2.buttons.global = structuredClone(defaultDesignTokensV2.buttons.global);
|
|
121
|
+
|
|
122
|
+
for (const size of BUTTON_SIZES) {
|
|
123
|
+
if (!v2.buttons.sizes[size]) {
|
|
124
|
+
v2.buttons.sizes[size] = structuredClone(defaultDesignTokensV2.buttons.sizes[size]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const variant of BUTTON_VARIANTS) {
|
|
128
|
+
if (!v2.buttons.variants[variant]) {
|
|
129
|
+
v2.buttons.variants[variant] = structuredClone(defaultDesignTokensV2.buttons.variants[variant]);
|
|
130
|
+
}
|
|
131
|
+
if (!v2.buttons.variants[variant].normal) {
|
|
132
|
+
v2.buttons.variants[variant].normal = structuredClone(defaultDesignTokensV2.buttons.variants[variant].normal);
|
|
133
|
+
}
|
|
134
|
+
if (!v2.buttons.variants[variant].onSurface) {
|
|
135
|
+
v2.buttons.variants[variant].onSurface = structuredClone(defaultDesignTokensV2.buttons.variants[variant].onSurface);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── Layout validieren ──
|
|
140
|
+
if (!v2.spacing) v2.spacing = structuredClone(defaultDesignTokensV2.spacing);
|
|
141
|
+
if (!v2.borderRadius) v2.borderRadius = defaultDesignTokensV2.borderRadius;
|
|
142
|
+
if (!v2.borderWidth) v2.borderWidth = defaultDesignTokensV2.borderWidth;
|
|
143
|
+
if (!v2.container) v2.container = structuredClone(defaultDesignTokensV2.container);
|
|
144
|
+
if (!v2.breakpoints) v2.breakpoints = structuredClone(defaultDesignTokensV2.breakpoints);
|
|
145
|
+
|
|
146
|
+
return v2;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
// ─── Farb-Migration ─────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
function migrateColors(v1, v2) {
|
|
153
|
+
if (!v1.colors) return;
|
|
154
|
+
|
|
155
|
+
// V1 hat flache Farben → V2 braucht 12-Stufen-Skalen
|
|
156
|
+
|
|
157
|
+
// Primary: v1.colors.primary → v2.colors.primary.base + Skala
|
|
158
|
+
migrateColorWorld(v1, v2, 'primary');
|
|
159
|
+
migrateColorWorld(v1, v2, 'secondary');
|
|
160
|
+
migrateColorWorld(v1, v2, 'success');
|
|
161
|
+
migrateColorWorld(v1, v2, 'warning');
|
|
162
|
+
migrateColorWorld(v1, v2, 'error');
|
|
163
|
+
migrateColorWorld(v1, v2, 'info');
|
|
164
|
+
|
|
165
|
+
// Accent: V1 hat bgAccentBase als Hauptfarbe
|
|
166
|
+
if (v1.colors.bgAccentBase) {
|
|
167
|
+
v2.colors.accent = {
|
|
168
|
+
base: v1.colors.bgAccentBase,
|
|
169
|
+
scale: generateColorScale(v1.colors.bgAccentBase)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Neutral: V1 hat keine Neutral-Skala → Defaults behalten
|
|
174
|
+
// Aber wir können die V1 Hintergrund-/Text-Werte als Hinweis nehmen
|
|
175
|
+
if (v1.colors.bgBody && v1.colors.textBase) {
|
|
176
|
+
// Prüfe ob das ein helles oder dunkles Theme ist
|
|
177
|
+
const bgIsLight = isLightColor(v1.colors.bgBody);
|
|
178
|
+
if (!bgIsLight) {
|
|
179
|
+
// Dunkles Theme → invertierte Neutral-Skala
|
|
180
|
+
v2.colors.neutral.scale = {
|
|
181
|
+
1: v1.colors.bgBody || '#0F172A',
|
|
182
|
+
2: v1.colors.bgBase || '#1E293B',
|
|
183
|
+
3: v1.colors.bgElevated || '#334155',
|
|
184
|
+
4: '#475569',
|
|
185
|
+
5: '#64748B',
|
|
186
|
+
6: '#78859B',
|
|
187
|
+
7: '#94A3B8',
|
|
188
|
+
8: v1.colors.textMuted || '#A8B5C7',
|
|
189
|
+
9: '#CBD5E1',
|
|
190
|
+
10: '#D5DDE7',
|
|
191
|
+
11: v1.colors.textSubtle || '#E2E8F0',
|
|
192
|
+
12: v1.colors.textBase || '#F8FAFC'
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function migrateColorWorld(v1, v2, world) {
|
|
199
|
+
if (v1.colors[world]) {
|
|
200
|
+
v2.colors[world] = {
|
|
201
|
+
base: v1.colors[world],
|
|
202
|
+
scale: generateColorScale(v1.colors[world])
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function isLightColor(hex) {
|
|
208
|
+
if (!hex) return true;
|
|
209
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
210
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
211
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
212
|
+
// Relative luminance
|
|
213
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) > 128;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
// ─── Font-Migration ─────────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
function migrateFonts(v1, v2) {
|
|
220
|
+
if (!v1.typography) return;
|
|
221
|
+
|
|
222
|
+
const fonts = [];
|
|
223
|
+
const seen = new Set();
|
|
224
|
+
|
|
225
|
+
// Sammle einzigartige Fonts aus V1-Slots
|
|
226
|
+
const slots = ['heading', 'body', 'accent'];
|
|
227
|
+
const fontIds = {
|
|
228
|
+
heading: v1.typography.fontHeadingId,
|
|
229
|
+
body: v1.typography.fontBodyId,
|
|
230
|
+
accent: v1.typography.fontAccentId
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
for (const slot of slots) {
|
|
234
|
+
const familyString = v1.typography.fontFamily?.[slot];
|
|
235
|
+
if (!familyString) continue;
|
|
236
|
+
|
|
237
|
+
// Extrahiere den ersten Font-Namen aus dem CSS font-family string
|
|
238
|
+
const name = familyString.split(',')[0].trim().replace(/['"]/g, '');
|
|
239
|
+
if (seen.has(name)) continue;
|
|
240
|
+
seen.add(name);
|
|
241
|
+
|
|
242
|
+
fonts.push({
|
|
243
|
+
name,
|
|
244
|
+
source: fontIds[slot] ? 'fontsource' : 'system',
|
|
245
|
+
id: fontIds[slot] || null,
|
|
246
|
+
fallback: familyString.split(',').slice(1).map(s => s.trim()).join(', ') || 'sans-serif'
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (fonts.length > 0) v2.typography.fonts = fonts;
|
|
251
|
+
|
|
252
|
+
// Mono
|
|
253
|
+
if (v1.typography.fontFamily?.mono) {
|
|
254
|
+
const monoName = v1.typography.fontFamily.mono.split(',')[0].trim().replace(/['"]/g, '');
|
|
255
|
+
const monoFallback = v1.typography.fontFamily.mono.split(',').slice(1).map(s => s.trim()).join(', ') || 'monospace';
|
|
256
|
+
v2.typography.monoFont = {
|
|
257
|
+
name: monoName,
|
|
258
|
+
source: v1.typography.fontMonoId ? 'fontsource' : 'system',
|
|
259
|
+
id: v1.typography.fontMonoId || null,
|
|
260
|
+
fallback: monoFallback
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
// ─── Text-Stil-Migration ────────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
function migrateTextStyles(v1, v2) {
|
|
269
|
+
if (!v1.textStyles) return;
|
|
270
|
+
|
|
271
|
+
// Finde Font-Namen für V1-Slots
|
|
272
|
+
const headingFont = v2.typography.fonts.find(f =>
|
|
273
|
+
v1.typography?.fontFamily?.heading?.includes(f.name))?.name || v2.typography.fonts[0]?.name || 'Inter';
|
|
274
|
+
const bodyFont = v2.typography.fonts.find(f =>
|
|
275
|
+
v1.typography?.fontFamily?.body?.includes(f.name))?.name || v2.typography.fonts[0]?.name || 'Inter';
|
|
276
|
+
const accentFont = v2.typography.fonts.find(f =>
|
|
277
|
+
v1.typography?.fontFamily?.accent?.includes(f.name))?.name || bodyFont;
|
|
278
|
+
|
|
279
|
+
// Display: V1 display/heading1-5 → V2 display.1-5
|
|
280
|
+
const displayMap = {
|
|
281
|
+
display: 1,
|
|
282
|
+
heading1: 2,
|
|
283
|
+
heading2: 3,
|
|
284
|
+
heading3: 4,
|
|
285
|
+
heading4: 4, // heading4 und heading5 werden zu display.4 und display.5
|
|
286
|
+
heading5: 5,
|
|
287
|
+
heading6: 5
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
for (const [v1Name, v2Level] of Object.entries(displayMap)) {
|
|
291
|
+
if (v1.textStyles[v1Name]) {
|
|
292
|
+
const s = v1.textStyles[v1Name];
|
|
293
|
+
v2.typography.textStyles.display[v2Level] = {
|
|
294
|
+
font: headingFont,
|
|
295
|
+
fontSize: s.fontSize || v2.typography.textStyles.display[v2Level].fontSize,
|
|
296
|
+
fontWeight: s.fontWeight || 600,
|
|
297
|
+
lineHeight: s.lineHeight || '1.2',
|
|
298
|
+
letterSpacing: s.letterSpacing || '0em',
|
|
299
|
+
textTransform: s.textTransform || 'none'
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Body: V1 lead/body/bodySmall/caption → V2 body.1-5
|
|
305
|
+
const bodyMap = {
|
|
306
|
+
lead: 1,
|
|
307
|
+
body: 3,
|
|
308
|
+
bodySmall: 4,
|
|
309
|
+
caption: 5
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
for (const [v1Name, v2Level] of Object.entries(bodyMap)) {
|
|
313
|
+
if (v1.textStyles[v1Name]) {
|
|
314
|
+
const s = v1.textStyles[v1Name];
|
|
315
|
+
v2.typography.textStyles.body[v2Level] = {
|
|
316
|
+
font: bodyFont,
|
|
317
|
+
fontSize: s.fontSize || v2.typography.textStyles.body[v2Level].fontSize,
|
|
318
|
+
fontWeight: s.fontWeight || 400,
|
|
319
|
+
lineHeight: s.lineHeight || '1.5',
|
|
320
|
+
letterSpacing: s.letterSpacing || '0em',
|
|
321
|
+
textTransform: s.textTransform || 'none'
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// UI: V1 label → V2 ui.3
|
|
327
|
+
if (v1.textStyles.label) {
|
|
328
|
+
const s = v1.textStyles.label;
|
|
329
|
+
v2.typography.textStyles.ui[3] = {
|
|
330
|
+
font: bodyFont,
|
|
331
|
+
fontSize: s.fontSize || '0.875rem',
|
|
332
|
+
fontWeight: s.fontWeight || 500,
|
|
333
|
+
lineHeight: s.lineHeight || '1.4',
|
|
334
|
+
letterSpacing: s.letterSpacing || '0em',
|
|
335
|
+
textTransform: s.textTransform || 'none'
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Accent: V1 overlines → V2 accent.4-5
|
|
340
|
+
const overlineMap = {
|
|
341
|
+
overlineDisplay: 3,
|
|
342
|
+
overlineHeading1: 4,
|
|
343
|
+
overlineHeading2: 4,
|
|
344
|
+
overlineHeading3: 5,
|
|
345
|
+
overlineHeading4: 5,
|
|
346
|
+
overlineHeading5: 5,
|
|
347
|
+
overlineHeading6: 5
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
for (const [v1Name, v2Level] of Object.entries(overlineMap)) {
|
|
351
|
+
if (v1.textStyles[v1Name]) {
|
|
352
|
+
const s = v1.textStyles[v1Name];
|
|
353
|
+
v2.typography.textStyles.accent[v2Level] = {
|
|
354
|
+
font: accentFont,
|
|
355
|
+
fontSize: s.fontSize || v2.typography.textStyles.accent[v2Level].fontSize,
|
|
356
|
+
fontWeight: s.fontWeight || 600,
|
|
357
|
+
lineHeight: s.lineHeight || '1.4',
|
|
358
|
+
letterSpacing: s.letterSpacing || '0.08em',
|
|
359
|
+
textTransform: s.textTransform || 'uppercase'
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
// ─── Button-Migration ───────────────────────────────────────────────────────
|
|
367
|
+
|
|
368
|
+
function migrateButtons(v1, v2) {
|
|
369
|
+
if (!v1.buttons) return;
|
|
370
|
+
|
|
371
|
+
// Sizes: V1 small/medium/large → V2 sm/md/lg
|
|
372
|
+
const sizeMap = { small: 'sm', medium: 'md', large: 'lg' };
|
|
373
|
+
if (v1.buttons.sizes) {
|
|
374
|
+
for (const [v1Size, v2Size] of Object.entries(sizeMap)) {
|
|
375
|
+
const s = v1.buttons.sizes[v1Size];
|
|
376
|
+
if (!s) continue;
|
|
377
|
+
|
|
378
|
+
v2.buttons.sizes[v2Size] = {
|
|
379
|
+
paddingX: typeof s.paddingX === 'number' ? `${s.paddingX / 16}rem` : s.paddingX,
|
|
380
|
+
paddingY: typeof s.paddingY === 'number' ? `${s.paddingY / 16}rem` : s.paddingY,
|
|
381
|
+
fontSize: typeof s.fontSize === 'number' ? `${s.fontSize / 16}rem` : s.fontSize,
|
|
382
|
+
fontWeight: 500,
|
|
383
|
+
lineHeight: '1.4',
|
|
384
|
+
letterSpacing: '0em',
|
|
385
|
+
borderRadius: typeof s.borderRadius === 'number' ? `${s.borderRadius / 16}rem` : s.borderRadius,
|
|
386
|
+
minHeight: typeof s.minHeight === 'number' ? `${s.minHeight / 16}rem` : s.minHeight,
|
|
387
|
+
gap: typeof s.gap === 'number' ? `${s.gap / 16}rem` : s.gap,
|
|
388
|
+
iconSize: typeof s.iconSize === 'number' ? `${s.iconSize / 16}rem` : s.iconSize
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Variants: V1 primary → V2 filled, V1 secondary → V2 tonal, etc.
|
|
394
|
+
const variantMap = {
|
|
395
|
+
primary: 'filled',
|
|
396
|
+
secondary: 'tonal',
|
|
397
|
+
ghost: 'ghost',
|
|
398
|
+
accent: 'tonal', // Fallback: accent → tonal
|
|
399
|
+
inverted: null // Wird als filled.onSurface migriert
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
for (const [v1Name, v2Name] of Object.entries(variantMap)) {
|
|
403
|
+
const variant = v1.buttons[v1Name];
|
|
404
|
+
if (!variant || !v2Name) continue;
|
|
405
|
+
|
|
406
|
+
// V1 Farbreferenzen zu V2 Stufen konvertieren
|
|
407
|
+
v2.buttons.variants[v2Name].normal = {
|
|
408
|
+
bg: mapV1ColorToV2Ref(variant.bgColor, v1),
|
|
409
|
+
bgHover: mapV1ColorToV2Ref(variant.bgColorHover, v1),
|
|
410
|
+
text: mapV1ColorToV2Ref(variant.textColor, v1),
|
|
411
|
+
textHover: mapV1ColorToV2Ref(variant.textColorHover || variant.textColor, v1),
|
|
412
|
+
border: variant.borderColor === 'transparent' ? 'none' : mapV1ColorToV2Ref(variant.borderColor, v1),
|
|
413
|
+
borderHover: variant.borderColorHover === 'transparent' ? 'none' : mapV1ColorToV2Ref(variant.borderColorHover, v1),
|
|
414
|
+
shadow: 'none',
|
|
415
|
+
shadowHover: 'none',
|
|
416
|
+
focusRingColor: 'primary-8',
|
|
417
|
+
customCSS: null
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// inverted → filled.onSurface
|
|
422
|
+
if (v1.buttons.inverted) {
|
|
423
|
+
const inv = v1.buttons.inverted;
|
|
424
|
+
v2.buttons.variants.filled.onSurface = {
|
|
425
|
+
bg: mapV1ColorToV2Ref(inv.bgColor, v1),
|
|
426
|
+
bgHover: mapV1ColorToV2Ref(inv.bgColorHover, v1),
|
|
427
|
+
text: mapV1ColorToV2Ref(inv.textColor, v1),
|
|
428
|
+
textHover: mapV1ColorToV2Ref(inv.textColorHover || inv.textColor, v1),
|
|
429
|
+
border: 'none',
|
|
430
|
+
borderHover: 'none',
|
|
431
|
+
customCSS: null
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Konvertiert V1-Farbreferenzen (Token-Name oder Hex) zu V2-Stufenreferenzen.
|
|
438
|
+
* V1: "primary" → V2: "primary-9"
|
|
439
|
+
* V1: "primaryDark" → V2: "primary-10"
|
|
440
|
+
* V1: "white" → V2: "neutral-1"
|
|
441
|
+
* V1: "textBase" → V2: "neutral-12"
|
|
442
|
+
* V1: "transparent" → V2: "transparent"
|
|
443
|
+
*/
|
|
444
|
+
function mapV1ColorToV2Ref(v1Ref, v1) {
|
|
445
|
+
if (!v1Ref) return 'none';
|
|
446
|
+
if (v1Ref === 'transparent') return 'transparent';
|
|
447
|
+
|
|
448
|
+
// Direkte Mappings V1 → V2
|
|
449
|
+
const mapping = {
|
|
450
|
+
// Brand
|
|
451
|
+
'primary': 'primary-9',
|
|
452
|
+
'primaryDark': 'primary-10',
|
|
453
|
+
'primaryLight': 'primary-7',
|
|
454
|
+
'secondary': 'secondary-9',
|
|
455
|
+
'secondaryDark': 'secondary-10',
|
|
456
|
+
'secondaryLight': 'secondary-7',
|
|
457
|
+
|
|
458
|
+
// Status
|
|
459
|
+
'success': 'success-9',
|
|
460
|
+
'successDark': 'success-10',
|
|
461
|
+
'successLight': 'success-3',
|
|
462
|
+
'warning': 'warning-9',
|
|
463
|
+
'warningDark': 'warning-10',
|
|
464
|
+
'warningLight': 'warning-3',
|
|
465
|
+
'error': 'error-9',
|
|
466
|
+
'errorDark': 'error-10',
|
|
467
|
+
'errorLight': 'error-3',
|
|
468
|
+
'info': 'info-9',
|
|
469
|
+
'infoDark': 'info-10',
|
|
470
|
+
'infoLight': 'info-3',
|
|
471
|
+
|
|
472
|
+
// Neutral
|
|
473
|
+
'white': 'neutral-1',
|
|
474
|
+
'black': 'neutral-12',
|
|
475
|
+
|
|
476
|
+
// Background
|
|
477
|
+
'bgBody': 'neutral-1',
|
|
478
|
+
'bgBase': 'neutral-1',
|
|
479
|
+
'bgElevated': 'neutral-2',
|
|
480
|
+
'bgLifted': 'neutral-3',
|
|
481
|
+
|
|
482
|
+
// Text
|
|
483
|
+
'textBase': 'neutral-12',
|
|
484
|
+
'textSubtle': 'neutral-11',
|
|
485
|
+
'textMuted': 'neutral-8',
|
|
486
|
+
|
|
487
|
+
// Border
|
|
488
|
+
'borderBase': 'neutral-7',
|
|
489
|
+
'borderSubtle': 'neutral-6',
|
|
490
|
+
'borderMuted': 'neutral-5',
|
|
491
|
+
|
|
492
|
+
// On-colors
|
|
493
|
+
'textOnPrimary': 'on-primary',
|
|
494
|
+
'textOnSecondary': 'on-secondary',
|
|
495
|
+
'textOnAccent': 'on-accent',
|
|
496
|
+
|
|
497
|
+
// Accent backgrounds
|
|
498
|
+
'bgAccentBase': 'accent-9',
|
|
499
|
+
'bgAccentElevated': 'accent-10',
|
|
500
|
+
'bgAccentLifted': 'accent-3'
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
if (mapping[v1Ref]) return mapping[v1Ref];
|
|
504
|
+
|
|
505
|
+
// Wenn es ein Hex-Wert ist, gib ihn als Stufe 9 der nächsten Welt zurück
|
|
506
|
+
// (Fallback — sollte selten vorkommen)
|
|
507
|
+
if (v1Ref.startsWith('#')) return v1Ref;
|
|
508
|
+
|
|
509
|
+
return 'primary-9'; // Letzter Fallback
|
|
510
|
+
}
|