@uniweb/build 0.8.13 → 0.8.14
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 +5 -3
- package/src/content/index.js +13 -0
- package/src/i18n/extract.js +12 -7
- package/src/i18n/merge.js +28 -19
- package/src/theme/index.js +4 -59
- package/src/theme/css-generator.js +0 -400
- package/src/theme/processor.js +0 -584
- package/src/theme/shade-generator.js +0 -712
package/src/theme/processor.js
DELETED
|
@@ -1,584 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Theme Processor
|
|
3
|
-
*
|
|
4
|
-
* Reads, validates, and processes theme configuration from theme.yml,
|
|
5
|
-
* merges with foundation defaults, and produces a complete theme config
|
|
6
|
-
* ready for CSS generation.
|
|
7
|
-
*
|
|
8
|
-
* @module @uniweb/build/theme/processor
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { isValidColor, generatePalettes } from './shade-generator.js'
|
|
12
|
-
import { getDefaultColors, getDefaultContextTokens } from './css-generator.js'
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Named neutral presets mapping to Tailwind gray families
|
|
16
|
-
*/
|
|
17
|
-
const NEUTRAL_PRESETS = {
|
|
18
|
-
stone: '#78716c',
|
|
19
|
-
zinc: '#71717a',
|
|
20
|
-
gray: '#6b7280',
|
|
21
|
-
slate: '#64748b',
|
|
22
|
-
neutral: '#737373',
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Default inline text styles (content-author markdown: [text]{accent})
|
|
27
|
-
* These reference semantic tokens so they adapt to context automatically
|
|
28
|
-
*/
|
|
29
|
-
const DEFAULT_INLINE = {
|
|
30
|
-
accent: {
|
|
31
|
-
color: 'var(--link)',
|
|
32
|
-
'font-weight': '600',
|
|
33
|
-
},
|
|
34
|
-
muted: {
|
|
35
|
-
color: 'var(--subtle)',
|
|
36
|
-
},
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Default appearance configuration
|
|
41
|
-
*/
|
|
42
|
-
const DEFAULT_APPEARANCE = {
|
|
43
|
-
default: 'light', // Default color scheme
|
|
44
|
-
allowToggle: false, // Whether to show scheme toggle
|
|
45
|
-
respectSystemPreference: true, // Honor prefers-color-scheme
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Default font configuration
|
|
50
|
-
*/
|
|
51
|
-
const DEFAULT_FONTS = {
|
|
52
|
-
body: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
53
|
-
heading: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
54
|
-
mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace',
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Default code block theme configuration
|
|
59
|
-
* Uses Shiki CSS variable names for compatibility
|
|
60
|
-
* These values are NOT converted to CSS here - the kit's Code component
|
|
61
|
-
* injects them at runtime only when code blocks are used (tree-shaking)
|
|
62
|
-
*/
|
|
63
|
-
const DEFAULT_CODE_THEME = {
|
|
64
|
-
// Background and foreground
|
|
65
|
-
background: '#1e1e2e', // Dark editor background
|
|
66
|
-
foreground: '#cdd6f4', // Default text color
|
|
67
|
-
|
|
68
|
-
// Syntax highlighting colors (Shiki token variables)
|
|
69
|
-
keyword: '#cba6f7', // Purple - keywords (if, else, function)
|
|
70
|
-
string: '#a6e3a1', // Green - strings
|
|
71
|
-
number: '#fab387', // Orange - numbers
|
|
72
|
-
comment: '#6c7086', // Gray - comments
|
|
73
|
-
function: '#89b4fa', // Blue - function names
|
|
74
|
-
variable: '#f5e0dc', // Light pink - variables
|
|
75
|
-
operator: '#89dceb', // Cyan - operators
|
|
76
|
-
punctuation: '#9399b2', // Gray - punctuation
|
|
77
|
-
type: '#f9e2af', // Yellow - types
|
|
78
|
-
constant: '#f38ba8', // Red - constants
|
|
79
|
-
property: '#94e2d5', // Teal - properties
|
|
80
|
-
tag: '#89b4fa', // Blue - HTML/JSX tags
|
|
81
|
-
attribute: '#f9e2af', // Yellow - attributes
|
|
82
|
-
|
|
83
|
-
// UI elements
|
|
84
|
-
lineNumber: '#6c7086', // Line number color
|
|
85
|
-
selection: '#45475a', // Selection background
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Valid shade levels for palette references
|
|
90
|
-
*/
|
|
91
|
-
const SHADE_LEVELS = new Set([50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950])
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Resolve context token values to valid CSS.
|
|
95
|
-
*
|
|
96
|
-
* Content authors write palette references as bare names: `primary-500`,
|
|
97
|
-
* `neutral-200`. This is the natural syntax in theme.yml. The processor
|
|
98
|
-
* resolves these to `var(--primary-500)` etc. Plain CSS values (hex, var(),
|
|
99
|
-
* named colors) pass through as-is — that's the escape hatch.
|
|
100
|
-
*
|
|
101
|
-
* @param {string} value - The token value from theme.yml
|
|
102
|
-
* @returns {string} Valid CSS value
|
|
103
|
-
*/
|
|
104
|
-
function normalizePaletteRef(value) {
|
|
105
|
-
if (typeof value !== 'string') return value
|
|
106
|
-
|
|
107
|
-
// Already a CSS function (var(), rgb(), etc.) — pass through
|
|
108
|
-
if (value.includes('(')) return value
|
|
109
|
-
|
|
110
|
-
// Hex color — pass through
|
|
111
|
-
if (value.startsWith('#')) return value
|
|
112
|
-
|
|
113
|
-
// Bare palette reference: "primary-500", "--primary-500"
|
|
114
|
-
const bare = value.replace(/^-{0,2}/, '')
|
|
115
|
-
const match = bare.match(/^([a-z][a-z0-9]*)-(\d+)$/)
|
|
116
|
-
|
|
117
|
-
if (match) {
|
|
118
|
-
const shade = parseInt(match[2], 10)
|
|
119
|
-
if (SHADE_LEVELS.has(shade)) {
|
|
120
|
-
return `var(--${bare})`
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return value
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Validate color configuration
|
|
129
|
-
*
|
|
130
|
-
* @param {Object} colors - Color configuration object
|
|
131
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
132
|
-
*/
|
|
133
|
-
function validateColors(colors) {
|
|
134
|
-
const errors = []
|
|
135
|
-
|
|
136
|
-
if (!colors || typeof colors !== 'object') {
|
|
137
|
-
return { valid: true, errors } // No colors is valid (use defaults)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
for (const [name, value] of Object.entries(colors)) {
|
|
141
|
-
// Skip pre-defined palette objects
|
|
142
|
-
if (typeof value === 'object' && value !== null) {
|
|
143
|
-
continue
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (typeof value !== 'string') {
|
|
147
|
-
errors.push(`Color "${name}" must be a string or shade object, got ${typeof value}`)
|
|
148
|
-
continue
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Accept neutral preset names (stone, zinc, gray, slate, neutral)
|
|
152
|
-
if (name === 'neutral' && NEUTRAL_PRESETS[value]) {
|
|
153
|
-
continue
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (!isValidColor(value)) {
|
|
157
|
-
errors.push(`Color "${name}" has invalid value: ${value}`)
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return { valid: errors.length === 0, errors }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Validate context configuration
|
|
166
|
-
*
|
|
167
|
-
* @param {Object} contexts - Context configuration object
|
|
168
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
169
|
-
*/
|
|
170
|
-
function validateContexts(contexts) {
|
|
171
|
-
const errors = []
|
|
172
|
-
|
|
173
|
-
if (!contexts || typeof contexts !== 'object') {
|
|
174
|
-
return { valid: true, errors }
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const validContexts = ['light', 'medium', 'dark']
|
|
178
|
-
|
|
179
|
-
for (const [context, tokens] of Object.entries(contexts)) {
|
|
180
|
-
if (!validContexts.includes(context)) {
|
|
181
|
-
errors.push(`Unknown context "${context}". Valid contexts: ${validContexts.join(', ')}`)
|
|
182
|
-
continue
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (typeof tokens !== 'object' || tokens === null) {
|
|
186
|
-
errors.push(`Context "${context}" must be an object`)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return { valid: errors.length === 0, errors }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Validate font configuration
|
|
195
|
-
*
|
|
196
|
-
* @param {Object} fonts - Font configuration object
|
|
197
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
198
|
-
*/
|
|
199
|
-
function validateFonts(fonts) {
|
|
200
|
-
const errors = []
|
|
201
|
-
|
|
202
|
-
if (!fonts || typeof fonts !== 'object') {
|
|
203
|
-
return { valid: true, errors }
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Validate imports
|
|
207
|
-
if (fonts.import !== undefined) {
|
|
208
|
-
if (!Array.isArray(fonts.import)) {
|
|
209
|
-
errors.push('fonts.import must be an array')
|
|
210
|
-
} else {
|
|
211
|
-
for (const [index, item] of fonts.import.entries()) {
|
|
212
|
-
if (typeof item !== 'object' || !item.url) {
|
|
213
|
-
errors.push(`fonts.import[${index}] must have a "url" property`)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return { valid: errors.length === 0, errors }
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Validate appearance configuration
|
|
224
|
-
*
|
|
225
|
-
* @param {Object} appearance - Appearance configuration
|
|
226
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
227
|
-
*/
|
|
228
|
-
function validateAppearance(appearance) {
|
|
229
|
-
const errors = []
|
|
230
|
-
|
|
231
|
-
if (!appearance || typeof appearance !== 'object') {
|
|
232
|
-
// Simple string value (e.g., appearance: light)
|
|
233
|
-
if (typeof appearance === 'string') {
|
|
234
|
-
if (!['light', 'dark', 'system'].includes(appearance)) {
|
|
235
|
-
errors.push(`Invalid appearance value: ${appearance}. Must be "light", "dark", or "system"`)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return { valid: errors.length === 0, errors }
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (appearance.default && !['light', 'dark', 'system'].includes(appearance.default)) {
|
|
242
|
-
errors.push(`Invalid appearance.default: ${appearance.default}`)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (appearance.schemes !== undefined) {
|
|
246
|
-
if (!Array.isArray(appearance.schemes)) {
|
|
247
|
-
errors.push('appearance.schemes must be an array')
|
|
248
|
-
} else {
|
|
249
|
-
const validSchemes = ['light', 'dark']
|
|
250
|
-
for (const scheme of appearance.schemes) {
|
|
251
|
-
if (!validSchemes.includes(scheme)) {
|
|
252
|
-
errors.push(`Invalid scheme: ${scheme}. Valid schemes: ${validSchemes.join(', ')}`)
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return { valid: errors.length === 0, errors }
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Validate code block theme configuration
|
|
263
|
-
*
|
|
264
|
-
* @param {Object} code - Code theme configuration
|
|
265
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
266
|
-
*/
|
|
267
|
-
function validateCodeTheme(code) {
|
|
268
|
-
const errors = []
|
|
269
|
-
|
|
270
|
-
if (!code || typeof code !== 'object') {
|
|
271
|
-
return { valid: true, errors } // No code config is valid (use defaults)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Validate color values
|
|
275
|
-
for (const [name, value] of Object.entries(code)) {
|
|
276
|
-
if (typeof value !== 'string') {
|
|
277
|
-
errors.push(`code.${name} must be a string, got ${typeof value}`)
|
|
278
|
-
continue
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Basic color format check (hex, rgb, hsl, or color name)
|
|
282
|
-
if (!isValidColor(value)) {
|
|
283
|
-
errors.push(`code.${name} has invalid color value: ${value}`)
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return { valid: errors.length === 0, errors }
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Validate foundation variables configuration
|
|
292
|
-
*
|
|
293
|
-
* @param {Object} vars - Foundation variables
|
|
294
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
295
|
-
*/
|
|
296
|
-
function validateFoundationVars(vars) {
|
|
297
|
-
const errors = []
|
|
298
|
-
|
|
299
|
-
if (!vars || typeof vars !== 'object') {
|
|
300
|
-
return { valid: true, errors }
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
for (const [name, config] of Object.entries(vars)) {
|
|
304
|
-
// Variable name validation
|
|
305
|
-
if (!/^[a-z][a-z0-9-]*$/i.test(name)) {
|
|
306
|
-
errors.push(`Invalid variable name "${name}". Use lowercase letters, numbers, and hyphens.`)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Config validation
|
|
310
|
-
if (typeof config !== 'object' && typeof config !== 'string' && typeof config !== 'number') {
|
|
311
|
-
errors.push(`Variable "${name}" must have a string, number, or config object value`)
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return { valid: errors.length === 0, errors }
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Validate complete theme configuration
|
|
320
|
-
*
|
|
321
|
-
* @param {Object} config - Raw theme configuration
|
|
322
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
323
|
-
*/
|
|
324
|
-
export function validateThemeConfig(config) {
|
|
325
|
-
const allErrors = []
|
|
326
|
-
|
|
327
|
-
if (!config || typeof config !== 'object') {
|
|
328
|
-
return { valid: true, errors: [] } // Empty config is valid (use all defaults)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const colorValidation = validateColors(config.colors)
|
|
332
|
-
const contextValidation = validateContexts(config.contexts)
|
|
333
|
-
const fontValidation = validateFonts(config.fonts)
|
|
334
|
-
const appearanceValidation = validateAppearance(config.appearance)
|
|
335
|
-
const codeValidation = validateCodeTheme(config.code)
|
|
336
|
-
|
|
337
|
-
allErrors.push(...colorValidation.errors)
|
|
338
|
-
allErrors.push(...contextValidation.errors)
|
|
339
|
-
allErrors.push(...fontValidation.errors)
|
|
340
|
-
allErrors.push(...appearanceValidation.errors)
|
|
341
|
-
allErrors.push(...codeValidation.errors)
|
|
342
|
-
|
|
343
|
-
return {
|
|
344
|
-
valid: allErrors.length === 0,
|
|
345
|
-
errors: allErrors,
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Normalize appearance configuration
|
|
351
|
-
*
|
|
352
|
-
* @param {string|Object} appearance - Raw appearance config
|
|
353
|
-
* @returns {Object} Normalized appearance config
|
|
354
|
-
*/
|
|
355
|
-
function normalizeAppearance(appearance) {
|
|
356
|
-
if (!appearance) {
|
|
357
|
-
return { ...DEFAULT_APPEARANCE }
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Simple string value: "light", "dark", or "system"
|
|
361
|
-
if (typeof appearance === 'string') {
|
|
362
|
-
return {
|
|
363
|
-
default: appearance,
|
|
364
|
-
allowToggle: false,
|
|
365
|
-
respectSystemPreference: appearance === 'system',
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return {
|
|
370
|
-
...DEFAULT_APPEARANCE,
|
|
371
|
-
...appearance,
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Merge foundation variables with site overrides
|
|
377
|
-
*
|
|
378
|
-
* @param {Object} foundationVars - Variables from foundation vars.js
|
|
379
|
-
* @param {Object} siteVars - Site-level variable overrides
|
|
380
|
-
* @returns {Object} Merged variables
|
|
381
|
-
*/
|
|
382
|
-
function mergeFoundationVars(foundationVars = {}, siteVars = {}) {
|
|
383
|
-
const merged = {}
|
|
384
|
-
|
|
385
|
-
// Start with foundation defaults
|
|
386
|
-
for (const [name, config] of Object.entries(foundationVars)) {
|
|
387
|
-
merged[name] = typeof config === 'object' ? { ...config } : { default: config }
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Apply site overrides
|
|
391
|
-
for (const [name, value] of Object.entries(siteVars)) {
|
|
392
|
-
if (merged[name]) {
|
|
393
|
-
// Override the default value
|
|
394
|
-
merged[name].default = value
|
|
395
|
-
} else {
|
|
396
|
-
// New variable from site
|
|
397
|
-
merged[name] = { default: value }
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return merged
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Process raw theme configuration into a complete, validated config
|
|
406
|
-
*
|
|
407
|
-
* @param {Object} rawConfig - Raw theme.yml content
|
|
408
|
-
* @param {Object} options - Processing options
|
|
409
|
-
* @param {Object} options.foundationVars - Foundation variables from vars.js
|
|
410
|
-
* @param {boolean} options.strict - Throw on validation errors (default: false)
|
|
411
|
-
* @returns {{ config: Object, errors: string[], warnings: string[] }}
|
|
412
|
-
*/
|
|
413
|
-
export function processTheme(rawConfig = {}, options = {}) {
|
|
414
|
-
const { foundationVars = {}, strict = false } = options
|
|
415
|
-
const errors = []
|
|
416
|
-
const warnings = []
|
|
417
|
-
|
|
418
|
-
// Validate raw config
|
|
419
|
-
const validation = validateThemeConfig(rawConfig)
|
|
420
|
-
if (!validation.valid) {
|
|
421
|
-
errors.push(...validation.errors)
|
|
422
|
-
if (strict) {
|
|
423
|
-
throw new Error(`Theme configuration errors:\n${errors.join('\n')}`)
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Process colors
|
|
428
|
-
const defaultColors = getDefaultColors()
|
|
429
|
-
const rawColors = { ...(rawConfig.colors || {}) }
|
|
430
|
-
|
|
431
|
-
// Resolve named neutral presets to hex values
|
|
432
|
-
if (typeof rawColors.neutral === 'string' && NEUTRAL_PRESETS[rawColors.neutral]) {
|
|
433
|
-
rawColors.neutral = NEUTRAL_PRESETS[rawColors.neutral]
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Filter to only valid colors (skip invalid ones in non-strict mode)
|
|
437
|
-
const validColors = {}
|
|
438
|
-
for (const [name, value] of Object.entries({ ...defaultColors, ...rawColors })) {
|
|
439
|
-
// Skip objects (pre-defined palettes) or invalid color strings
|
|
440
|
-
if (typeof value === 'object' && value !== null) {
|
|
441
|
-
validColors[name] = value
|
|
442
|
-
} else if (isValidColor(value)) {
|
|
443
|
-
validColors[name] = value
|
|
444
|
-
}
|
|
445
|
-
// Invalid colors are skipped (error already recorded during validation)
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const colors = validColors
|
|
449
|
-
|
|
450
|
-
// Generate color palettes (shades 50-950 for each color)
|
|
451
|
-
// This is used by the Theme class for runtime color access
|
|
452
|
-
const palettes = generatePalettes(colors)
|
|
453
|
-
|
|
454
|
-
// Warn if required colors are missing
|
|
455
|
-
if (!rawConfig.colors?.primary) {
|
|
456
|
-
warnings.push('No primary color specified, using default blue (#3b82f6)')
|
|
457
|
-
}
|
|
458
|
-
if (!rawConfig.colors?.neutral) {
|
|
459
|
-
warnings.push('No neutral color specified, using default stone (#78716c)')
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Process contexts (resolve bare palette refs like "primary-500" to var())
|
|
463
|
-
const defaultContexts = getDefaultContextTokens()
|
|
464
|
-
const rawContexts = rawConfig.contexts || {}
|
|
465
|
-
const contexts = {}
|
|
466
|
-
|
|
467
|
-
for (const name of ['light', 'medium', 'dark']) {
|
|
468
|
-
const overrides = rawContexts[name] || {}
|
|
469
|
-
const normalized = {}
|
|
470
|
-
|
|
471
|
-
for (const [token, value] of Object.entries(overrides)) {
|
|
472
|
-
normalized[token] = normalizePaletteRef(value)
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
contexts[name] = { ...defaultContexts[name], ...normalized }
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// Process fonts
|
|
479
|
-
const fonts = {
|
|
480
|
-
...DEFAULT_FONTS,
|
|
481
|
-
...(rawConfig.fonts || {}),
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Normalize and process appearance
|
|
485
|
-
const appearance = normalizeAppearance(rawConfig.appearance)
|
|
486
|
-
|
|
487
|
-
// Merge foundation variables with site overrides
|
|
488
|
-
const mergedFoundationVars = mergeFoundationVars(
|
|
489
|
-
foundationVars,
|
|
490
|
-
rawConfig.vars || rawConfig.foundationVars || {}
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
// Validate merged foundation vars
|
|
494
|
-
const foundationValidation = validateFoundationVars(mergedFoundationVars)
|
|
495
|
-
if (!foundationValidation.valid) {
|
|
496
|
-
warnings.push(...foundationValidation.errors)
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Process code block theme
|
|
500
|
-
// These values are stored for runtime injection by kit's Code component
|
|
501
|
-
// (not converted to CSS here - enables tree-shaking when code blocks aren't used)
|
|
502
|
-
const code = {
|
|
503
|
-
...DEFAULT_CODE_THEME,
|
|
504
|
-
...(rawConfig.code || {}),
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Site background (pass through as CSS value)
|
|
508
|
-
const background = rawConfig.background || null
|
|
509
|
-
|
|
510
|
-
// Inline text styles (semantic names → CSS declarations)
|
|
511
|
-
// Merge framework defaults with user overrides (user values win)
|
|
512
|
-
const inline = { ...DEFAULT_INLINE, ...(rawConfig.inline || {}) }
|
|
513
|
-
|
|
514
|
-
const config = {
|
|
515
|
-
colors, // Raw colors for CSS generator
|
|
516
|
-
palettes, // Generated palettes for Theme class
|
|
517
|
-
contexts,
|
|
518
|
-
fonts,
|
|
519
|
-
appearance,
|
|
520
|
-
foundationVars: mergedFoundationVars,
|
|
521
|
-
code, // Code block theme for runtime injection
|
|
522
|
-
background, // Site-level background CSS value
|
|
523
|
-
inline, // Inline text style definitions
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return { config, errors, warnings }
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Load foundation variables from vars.js export
|
|
531
|
-
*
|
|
532
|
-
* @param {Object} varsModule - Imported vars.js module
|
|
533
|
-
* @returns {Object} Foundation variables
|
|
534
|
-
*/
|
|
535
|
-
export function extractFoundationVars(varsModule) {
|
|
536
|
-
if (!varsModule) {
|
|
537
|
-
return {}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Handle default export
|
|
541
|
-
const module = varsModule.default || varsModule
|
|
542
|
-
|
|
543
|
-
// Extract vars property or use whole object
|
|
544
|
-
return module.vars || module
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Check if a foundation has theme variables
|
|
549
|
-
*
|
|
550
|
-
* @param {Object} foundationSchema - Foundation schema.json content
|
|
551
|
-
* @returns {boolean}
|
|
552
|
-
*/
|
|
553
|
-
export function foundationHasVars(foundationSchema) {
|
|
554
|
-
// Check _self.vars (new), _self.themeVars (legacy), root themeVars (backwards compat)
|
|
555
|
-
return (
|
|
556
|
-
foundationSchema?._self?.vars != null ||
|
|
557
|
-
foundationSchema?._self?.themeVars != null ||
|
|
558
|
-
foundationSchema?.themeVars != null
|
|
559
|
-
)
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Get foundation variables from schema
|
|
564
|
-
* Supports both new 'vars' and legacy 'themeVars' naming
|
|
565
|
-
*
|
|
566
|
-
* @param {Object} foundationSchema - Foundation schema.json content
|
|
567
|
-
* @returns {Object} Foundation variables
|
|
568
|
-
*/
|
|
569
|
-
export function getFoundationVars(foundationSchema) {
|
|
570
|
-
return (
|
|
571
|
-
foundationSchema?._self?.vars ||
|
|
572
|
-
foundationSchema?._self?.themeVars ||
|
|
573
|
-
foundationSchema?.themeVars ||
|
|
574
|
-
{}
|
|
575
|
-
)
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
export default {
|
|
579
|
-
validateThemeConfig,
|
|
580
|
-
processTheme,
|
|
581
|
-
extractFoundationVars,
|
|
582
|
-
foundationHasVars,
|
|
583
|
-
getFoundationVars,
|
|
584
|
-
}
|