eva-css-fluid 1.0.4 → 2.0.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/MIGRATION.md +337 -0
- package/README.md +310 -3
- package/cli.cjs +57 -0
- package/dist/eva.css +2133 -2093
- package/dist/eva.css.map +1 -1
- package/dist/eva.min.css +1 -1
- package/dist/eva.min.css.map +1 -1
- package/dist/test.css +5689 -0
- package/dist/test.css.map +1 -0
- package/eva.config.template.js +216 -0
- package/index.scss +9 -0
- package/package.json +40 -7
- package/scripts/build-with-config.cjs +169 -0
- package/src/_colors.scss +41 -6
- package/src/_config.scss +55 -0
- package/src/_eva.scss +84 -0
- package/src/colors-only.scss +16 -0
- package/src/config-loader.cjs +319 -0
- package/src/config-schema.cjs +455 -0
- package/src/core.scss +43 -0
- package/src/index.scss +5 -1
- package/src/variables.scss +33 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EVA CSS Configuration Schema
|
|
3
|
+
*
|
|
4
|
+
* Defines and validates the structure of eva.config.js and package.json "eva" configuration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default configuration values
|
|
9
|
+
*/
|
|
10
|
+
const defaults = {
|
|
11
|
+
sizes: [4, 8, 12, 16, 24, 32, 48, 64, 96, 128],
|
|
12
|
+
fontSizes: [12, 14, 16, 18, 20, 24, 32, 48],
|
|
13
|
+
buildClass: true,
|
|
14
|
+
pxRemSuffix: false,
|
|
15
|
+
nameBySize: true,
|
|
16
|
+
customClass: false,
|
|
17
|
+
classConfig: {},
|
|
18
|
+
debug: false,
|
|
19
|
+
theme: {
|
|
20
|
+
name: 'eva',
|
|
21
|
+
colors: {
|
|
22
|
+
brand: { lightness: 80, chroma: 0.1924, hue: 169 },
|
|
23
|
+
accent: { lightness: 80, chroma: 0.1924, hue: 10 },
|
|
24
|
+
extra: { lightness: 80, chroma: 0.1924, hue: 200 },
|
|
25
|
+
dark: { lightness: 20, chroma: 0.05, hue: 200 },
|
|
26
|
+
light: { lightness: 95, chroma: 0.01, hue: 200 }
|
|
27
|
+
},
|
|
28
|
+
lightMode: {
|
|
29
|
+
lightness: 96.4,
|
|
30
|
+
darkness: 6.4
|
|
31
|
+
},
|
|
32
|
+
darkMode: {
|
|
33
|
+
lightness: 5,
|
|
34
|
+
darkness: 95
|
|
35
|
+
},
|
|
36
|
+
autoSwitch: false
|
|
37
|
+
},
|
|
38
|
+
purge: {
|
|
39
|
+
enabled: false,
|
|
40
|
+
content: ['**/*.{html,js,jsx,tsx,vue,svelte}'],
|
|
41
|
+
css: 'dist/eva.css',
|
|
42
|
+
output: 'dist/eva-purged.css',
|
|
43
|
+
safelist: ['theme-', 'current-', 'toggle-theme', 'all-grads']
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Available property keys for classConfig
|
|
49
|
+
*/
|
|
50
|
+
const availableProperties = [
|
|
51
|
+
'w', 'mw', 'h', 'mh',
|
|
52
|
+
'p', 'px', 'py', 'pt', 'pb', 'pr', 'pl',
|
|
53
|
+
'm', 'mx', 'my', 'mt', 'mb', 'mr', 'ml',
|
|
54
|
+
'g', 'gap', 'br',
|
|
55
|
+
'fs', 'lh'
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validation errors
|
|
60
|
+
*/
|
|
61
|
+
class ValidationError extends Error {
|
|
62
|
+
constructor(message, field) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = 'ValidationError';
|
|
65
|
+
this.field = field;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validate configuration object
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} config - Configuration object to validate
|
|
73
|
+
* @param {string} source - Source of config ('eva.config.js' or 'package.json')
|
|
74
|
+
* @returns {Object} Validated configuration
|
|
75
|
+
* @throws {ValidationError} If configuration is invalid
|
|
76
|
+
*/
|
|
77
|
+
function validateConfig(config, source = 'config file') {
|
|
78
|
+
if (!config || typeof config !== 'object') {
|
|
79
|
+
throw new ValidationError(
|
|
80
|
+
`Configuration must be an object, received ${typeof config}`,
|
|
81
|
+
'root'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const errors = [];
|
|
86
|
+
const warnings = [];
|
|
87
|
+
|
|
88
|
+
// Validate sizes
|
|
89
|
+
if (config.sizes !== undefined) {
|
|
90
|
+
if (!Array.isArray(config.sizes)) {
|
|
91
|
+
errors.push('sizes: Must be an array of numbers');
|
|
92
|
+
} else if (config.sizes.length === 0) {
|
|
93
|
+
errors.push('sizes: Array cannot be empty');
|
|
94
|
+
} else if (!config.sizes.every(s => typeof s === 'number' && s > 0)) {
|
|
95
|
+
errors.push('sizes: All values must be positive numbers');
|
|
96
|
+
} else if (!config.sizes.includes(16)) {
|
|
97
|
+
errors.push('sizes: Must include 16 as a base size (required by EVA CSS)');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Validate fontSizes
|
|
102
|
+
if (config.fontSizes !== undefined) {
|
|
103
|
+
if (!Array.isArray(config.fontSizes)) {
|
|
104
|
+
errors.push('fontSizes: Must be an array of numbers');
|
|
105
|
+
} else if (config.fontSizes.length === 0) {
|
|
106
|
+
errors.push('fontSizes: Array cannot be empty');
|
|
107
|
+
} else if (!config.fontSizes.every(s => typeof s === 'number' && s > 0)) {
|
|
108
|
+
errors.push('fontSizes: All values must be positive numbers');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Validate boolean flags
|
|
113
|
+
const booleanFlags = ['buildClass', 'pxRemSuffix', 'nameBySize', 'customClass', 'debug'];
|
|
114
|
+
booleanFlags.forEach(flag => {
|
|
115
|
+
if (config[flag] !== undefined && typeof config[flag] !== 'boolean') {
|
|
116
|
+
errors.push(`${flag}: Must be a boolean (true or false)`);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Validate classConfig
|
|
121
|
+
if (config.classConfig !== undefined) {
|
|
122
|
+
if (typeof config.classConfig !== 'object' || Array.isArray(config.classConfig)) {
|
|
123
|
+
errors.push('classConfig: Must be an object mapping properties to size arrays');
|
|
124
|
+
} else {
|
|
125
|
+
// Check if customClass is enabled when classConfig is provided
|
|
126
|
+
if (config.customClass === false && Object.keys(config.classConfig).length > 0) {
|
|
127
|
+
warnings.push('classConfig: Setting classConfig has no effect when customClass is false');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Validate each property in classConfig
|
|
131
|
+
Object.entries(config.classConfig).forEach(([prop, sizes]) => {
|
|
132
|
+
if (!availableProperties.includes(prop)) {
|
|
133
|
+
errors.push(
|
|
134
|
+
`classConfig.${prop}: Unknown property. Available properties: ${availableProperties.join(', ')}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!Array.isArray(sizes)) {
|
|
139
|
+
errors.push(`classConfig.${prop}: Must be an array of size values`);
|
|
140
|
+
} else if (sizes.length === 0) {
|
|
141
|
+
errors.push(`classConfig.${prop}: Array cannot be empty`);
|
|
142
|
+
} else {
|
|
143
|
+
// Validate that all sizes in classConfig exist in the main sizes array
|
|
144
|
+
const mainSizes = config.sizes || defaults.sizes;
|
|
145
|
+
sizes.forEach(size => {
|
|
146
|
+
if (!mainSizes.includes(size)) {
|
|
147
|
+
errors.push(
|
|
148
|
+
`classConfig.${prop}: Size ${size} is not in the main sizes array [${mainSizes.join(', ')}]`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Validate theme configuration
|
|
158
|
+
if (config.theme !== undefined) {
|
|
159
|
+
if (typeof config.theme !== 'object' || Array.isArray(config.theme)) {
|
|
160
|
+
errors.push('theme: Must be an object');
|
|
161
|
+
} else {
|
|
162
|
+
// Validate theme name
|
|
163
|
+
if (config.theme.name !== undefined) {
|
|
164
|
+
if (typeof config.theme.name !== 'string' || config.theme.name.trim() === '') {
|
|
165
|
+
errors.push('theme.name: Must be a non-empty string');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Validate colors
|
|
170
|
+
if (config.theme.colors !== undefined) {
|
|
171
|
+
if (typeof config.theme.colors !== 'object' || Array.isArray(config.theme.colors)) {
|
|
172
|
+
errors.push('theme.colors: Must be an object');
|
|
173
|
+
} else {
|
|
174
|
+
const validColorNames = ['brand', 'accent', 'extra', 'dark', 'light'];
|
|
175
|
+
|
|
176
|
+
Object.entries(config.theme.colors).forEach(([colorName, colorValue]) => {
|
|
177
|
+
if (!validColorNames.includes(colorName)) {
|
|
178
|
+
errors.push(
|
|
179
|
+
`theme.colors.${colorName}: Unknown color. Valid colors: ${validColorNames.join(', ')}`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Validate color value (HEX string or OKLCH object)
|
|
184
|
+
if (typeof colorValue === 'string') {
|
|
185
|
+
// HEX format
|
|
186
|
+
if (!/^#[0-9A-Fa-f]{6}$/.test(colorValue)) {
|
|
187
|
+
errors.push(
|
|
188
|
+
`theme.colors.${colorName}: Invalid HEX color "${colorValue}". Must be format #RRGGBB`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
} else if (typeof colorValue === 'object' && !Array.isArray(colorValue)) {
|
|
192
|
+
// OKLCH format
|
|
193
|
+
if (typeof colorValue.lightness !== 'number' || colorValue.lightness < 0 || colorValue.lightness > 100) {
|
|
194
|
+
errors.push(
|
|
195
|
+
`theme.colors.${colorName}.lightness: Must be a number between 0 and 100`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (typeof colorValue.chroma !== 'number' || colorValue.chroma < 0 || colorValue.chroma > 0.4) {
|
|
199
|
+
errors.push(
|
|
200
|
+
`theme.colors.${colorName}.chroma: Must be a number between 0 and 0.4`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (typeof colorValue.hue !== 'number' || colorValue.hue < 0 || colorValue.hue > 360) {
|
|
204
|
+
errors.push(
|
|
205
|
+
`theme.colors.${colorName}.hue: Must be a number between 0 and 360`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
errors.push(
|
|
210
|
+
`theme.colors.${colorName}: Must be a HEX string (#RRGGBB) or OKLCH object {lightness, chroma, hue}`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Validate lightMode
|
|
218
|
+
if (config.theme.lightMode !== undefined) {
|
|
219
|
+
if (typeof config.theme.lightMode !== 'object' || Array.isArray(config.theme.lightMode)) {
|
|
220
|
+
errors.push('theme.lightMode: Must be an object with lightness and darkness properties');
|
|
221
|
+
} else {
|
|
222
|
+
if (typeof config.theme.lightMode.lightness !== 'number' || config.theme.lightMode.lightness < 0 || config.theme.lightMode.lightness > 100) {
|
|
223
|
+
errors.push('theme.lightMode.lightness: Must be a number between 0 and 100');
|
|
224
|
+
}
|
|
225
|
+
if (typeof config.theme.lightMode.darkness !== 'number' || config.theme.lightMode.darkness < 0 || config.theme.lightMode.darkness > 100) {
|
|
226
|
+
errors.push('theme.lightMode.darkness: Must be a number between 0 and 100');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Validate darkMode
|
|
232
|
+
if (config.theme.darkMode !== undefined) {
|
|
233
|
+
if (typeof config.theme.darkMode !== 'object' || Array.isArray(config.theme.darkMode)) {
|
|
234
|
+
errors.push('theme.darkMode: Must be an object with lightness and darkness properties');
|
|
235
|
+
} else {
|
|
236
|
+
if (typeof config.theme.darkMode.lightness !== 'number' || config.theme.darkMode.lightness < 0 || config.theme.darkMode.lightness > 100) {
|
|
237
|
+
errors.push('theme.darkMode.lightness: Must be a number between 0 and 100');
|
|
238
|
+
}
|
|
239
|
+
if (typeof config.theme.darkMode.darkness !== 'number' || config.theme.darkMode.darkness < 0 || config.theme.darkMode.darkness > 100) {
|
|
240
|
+
errors.push('theme.darkMode.darkness: Must be a number between 0 and 100');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Validate autoSwitch
|
|
246
|
+
if (config.theme.autoSwitch !== undefined && typeof config.theme.autoSwitch !== 'boolean') {
|
|
247
|
+
errors.push('theme.autoSwitch: Must be a boolean');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Validate purge configuration
|
|
253
|
+
if (config.purge !== undefined) {
|
|
254
|
+
if (typeof config.purge !== 'object' || Array.isArray(config.purge)) {
|
|
255
|
+
errors.push('purge: Must be an object');
|
|
256
|
+
} else {
|
|
257
|
+
if (config.purge.enabled !== undefined && typeof config.purge.enabled !== 'boolean') {
|
|
258
|
+
errors.push('purge.enabled: Must be a boolean');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (config.purge.content !== undefined) {
|
|
262
|
+
if (!Array.isArray(config.purge.content)) {
|
|
263
|
+
errors.push('purge.content: Must be an array of glob patterns');
|
|
264
|
+
} else if (config.purge.content.length === 0) {
|
|
265
|
+
errors.push('purge.content: Array cannot be empty');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (config.purge.css !== undefined && typeof config.purge.css !== 'string') {
|
|
270
|
+
errors.push('purge.css: Must be a string (path to CSS file)');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (config.purge.output !== undefined && typeof config.purge.output !== 'string') {
|
|
274
|
+
errors.push('purge.output: Must be a string (output path)');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (config.purge.safelist !== undefined && !Array.isArray(config.purge.safelist)) {
|
|
278
|
+
errors.push('purge.safelist: Must be an array of strings or patterns');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Throw if there are errors
|
|
284
|
+
if (errors.length > 0) {
|
|
285
|
+
const errorMessage = [
|
|
286
|
+
`Invalid EVA CSS configuration in ${source}:`,
|
|
287
|
+
'',
|
|
288
|
+
...errors.map(e => ` ❌ ${e}`),
|
|
289
|
+
'',
|
|
290
|
+
'See documentation: https://eva-css.xyz/configuration'
|
|
291
|
+
].join('\n');
|
|
292
|
+
|
|
293
|
+
throw new ValidationError(errorMessage, 'validation');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Display warnings
|
|
297
|
+
if (warnings.length > 0) {
|
|
298
|
+
console.warn(`\n⚠️ EVA CSS configuration warnings in ${source}:`);
|
|
299
|
+
warnings.forEach(w => console.warn(` ${w}`));
|
|
300
|
+
console.warn('');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return config;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Merge user configuration with defaults
|
|
308
|
+
*
|
|
309
|
+
* @param {Object} userConfig - User provided configuration
|
|
310
|
+
* @returns {Object} Merged configuration
|
|
311
|
+
*/
|
|
312
|
+
function mergeWithDefaults(userConfig) {
|
|
313
|
+
const config = { ...defaults };
|
|
314
|
+
|
|
315
|
+
// Merge top-level properties
|
|
316
|
+
Object.keys(userConfig).forEach(key => {
|
|
317
|
+
if (key === 'purge' && typeof userConfig.purge === 'object') {
|
|
318
|
+
// Deep merge purge configuration
|
|
319
|
+
config.purge = {
|
|
320
|
+
...defaults.purge,
|
|
321
|
+
...userConfig.purge
|
|
322
|
+
};
|
|
323
|
+
} else if (key === 'theme' && typeof userConfig.theme === 'object') {
|
|
324
|
+
// Deep merge theme configuration
|
|
325
|
+
config.theme = {
|
|
326
|
+
...defaults.theme,
|
|
327
|
+
...userConfig.theme,
|
|
328
|
+
colors: {
|
|
329
|
+
...defaults.theme.colors,
|
|
330
|
+
...(userConfig.theme.colors || {})
|
|
331
|
+
},
|
|
332
|
+
lightMode: {
|
|
333
|
+
...defaults.theme.lightMode,
|
|
334
|
+
...(userConfig.theme.lightMode || {})
|
|
335
|
+
},
|
|
336
|
+
darkMode: {
|
|
337
|
+
...defaults.theme.darkMode,
|
|
338
|
+
...(userConfig.theme.darkMode || {})
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
} else if (key === 'classConfig' && typeof userConfig.classConfig === 'object') {
|
|
342
|
+
// Replace classConfig entirely (no merge)
|
|
343
|
+
config.classConfig = userConfig.classConfig;
|
|
344
|
+
} else {
|
|
345
|
+
config[key] = userConfig[key];
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
return config;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Convert JavaScript config to SCSS variable format
|
|
354
|
+
*
|
|
355
|
+
* @param {Object} config - Configuration object
|
|
356
|
+
* @returns {string} SCSS variable declarations
|
|
357
|
+
*/
|
|
358
|
+
function toScssVariables(config) {
|
|
359
|
+
const lines = [];
|
|
360
|
+
|
|
361
|
+
// Sizes
|
|
362
|
+
if (config.sizes) {
|
|
363
|
+
lines.push(`$sizes: ${config.sizes.join(', ')};`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Font sizes
|
|
367
|
+
if (config.fontSizes) {
|
|
368
|
+
lines.push(`$font-sizes: ${config.fontSizes.join(', ')};`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Boolean flags
|
|
372
|
+
lines.push(`$build-class: ${config.buildClass};`);
|
|
373
|
+
lines.push(`$px-rem-suffix: ${config.pxRemSuffix};`);
|
|
374
|
+
lines.push(`$name-by-size: ${config.nameBySize};`);
|
|
375
|
+
lines.push(`$custom-class: ${config.customClass};`);
|
|
376
|
+
lines.push(`$debug: ${config.debug};`);
|
|
377
|
+
|
|
378
|
+
// Class config (convert to SCSS map)
|
|
379
|
+
if (config.customClass && Object.keys(config.classConfig).length > 0) {
|
|
380
|
+
lines.push('$class-config: (');
|
|
381
|
+
Object.entries(config.classConfig).forEach(([prop, sizes], index, arr) => {
|
|
382
|
+
const isLast = index === arr.length - 1;
|
|
383
|
+
// Handle single-item arrays with trailing comma for SCSS
|
|
384
|
+
const sizesList = sizes.length === 1 ? `${sizes[0]},` : sizes.join(', ');
|
|
385
|
+
lines.push(` ${prop}: (${sizesList})${isLast ? '' : ','}`);
|
|
386
|
+
});
|
|
387
|
+
lines.push(');');
|
|
388
|
+
} else {
|
|
389
|
+
lines.push('$class-config: ();');
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Theme configuration
|
|
393
|
+
if (config.theme) {
|
|
394
|
+
lines.push('');
|
|
395
|
+
lines.push('// Theme configuration');
|
|
396
|
+
lines.push(`$theme-name: '${config.theme.name}';`);
|
|
397
|
+
lines.push(`$auto-theme-switch: ${config.theme.autoSwitch};`);
|
|
398
|
+
|
|
399
|
+
// Light and dark mode settings
|
|
400
|
+
lines.push(`$light-mode-lightness: ${config.theme.lightMode.lightness}%;`);
|
|
401
|
+
lines.push(`$light-mode-darkness: ${config.theme.lightMode.darkness}%;`);
|
|
402
|
+
lines.push(`$dark-mode-lightness: ${config.theme.darkMode.lightness}%;`);
|
|
403
|
+
lines.push(`$dark-mode-darkness: ${config.theme.darkMode.darkness}%;`);
|
|
404
|
+
|
|
405
|
+
// Colors map
|
|
406
|
+
lines.push('$theme-colors: (');
|
|
407
|
+
const colorEntries = Object.entries(config.theme.colors);
|
|
408
|
+
colorEntries.forEach(([colorName, colorValue], index) => {
|
|
409
|
+
const isLast = index === colorEntries.length - 1;
|
|
410
|
+
lines.push(` '${colorName}': (${colorValue.lightness}% ${colorValue.chroma} ${colorValue.hue})${isLast ? '' : ','}`);
|
|
411
|
+
});
|
|
412
|
+
lines.push(');');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return lines.join('\n');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Normalize config from package.json format to standard format
|
|
420
|
+
* Handles camelCase vs kebab-case conversions
|
|
421
|
+
*
|
|
422
|
+
* @param {Object} config - Configuration from package.json or eva.config.js
|
|
423
|
+
* @returns {Object} Normalized configuration
|
|
424
|
+
*/
|
|
425
|
+
function normalizeConfig(config) {
|
|
426
|
+
const normalized = {};
|
|
427
|
+
|
|
428
|
+
// Map camelCase to internal format
|
|
429
|
+
const keyMap = {
|
|
430
|
+
fontSizes: 'fontSizes',
|
|
431
|
+
buildClass: 'buildClass',
|
|
432
|
+
pxRemSuffix: 'pxRemSuffix',
|
|
433
|
+
nameBySize: 'nameBySize',
|
|
434
|
+
customClass: 'customClass',
|
|
435
|
+
classConfig: 'classConfig'
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
439
|
+
// Use mapped key if it exists, otherwise use original key
|
|
440
|
+
const normalizedKey = keyMap[key] || key;
|
|
441
|
+
normalized[normalizedKey] = value;
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
return normalized;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
module.exports = {
|
|
448
|
+
defaults,
|
|
449
|
+
availableProperties,
|
|
450
|
+
ValidationError,
|
|
451
|
+
validateConfig,
|
|
452
|
+
mergeWithDefaults,
|
|
453
|
+
toScssVariables,
|
|
454
|
+
normalizeConfig
|
|
455
|
+
};
|
package/src/core.scss
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ===========================================
|
|
2
|
+
// EVA CSS - Core Framework Entry Point
|
|
3
|
+
// ===========================================
|
|
4
|
+
// Use this when you want the framework
|
|
5
|
+
// without pre-built utility classes
|
|
6
|
+
// Perfect for building your own components
|
|
7
|
+
// ===========================================
|
|
8
|
+
|
|
9
|
+
// Configuration variables with defaults
|
|
10
|
+
$sizes: 4, 8, 12, 16, 24, 32, 48, 64, 96, 128 !default;
|
|
11
|
+
$font-sizes: 12, 14, 16, 18, 20, 24, 32, 48 !default;
|
|
12
|
+
$px-rem-suffix: false !default;
|
|
13
|
+
$name-by-size: true !default;
|
|
14
|
+
$class-config: () !default;
|
|
15
|
+
|
|
16
|
+
// Core variable generation system
|
|
17
|
+
@use '_eva' with (
|
|
18
|
+
$sizes: $sizes,
|
|
19
|
+
$font-sizes: $font-sizes,
|
|
20
|
+
$build-class: false, // ← No utility classes
|
|
21
|
+
$px-rem-suffix: $px-rem-suffix,
|
|
22
|
+
$name-by-size: $name-by-size,
|
|
23
|
+
$custom-class: false,
|
|
24
|
+
$class-config: $class-config
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Color system
|
|
28
|
+
@use '_colors';
|
|
29
|
+
|
|
30
|
+
// Gradients
|
|
31
|
+
@use '_gradients';
|
|
32
|
+
|
|
33
|
+
// Theme system
|
|
34
|
+
@use '_theme';
|
|
35
|
+
|
|
36
|
+
// CSS Reset
|
|
37
|
+
@use '_reset';
|
|
38
|
+
|
|
39
|
+
// Typography
|
|
40
|
+
@use '_font';
|
|
41
|
+
|
|
42
|
+
// Result: Variables + theme + reset + typography
|
|
43
|
+
// No utility classes - build your own!
|
package/src/index.scss
CHANGED
|
@@ -13,6 +13,8 @@ $build-class: true !default;
|
|
|
13
13
|
$px-rem-suffix: false !default;
|
|
14
14
|
$name-by-size: true !default;
|
|
15
15
|
$custom-class: false !default;
|
|
16
|
+
$class-config: () !default;
|
|
17
|
+
$debug: false !default;
|
|
16
18
|
|
|
17
19
|
// ===========================================
|
|
18
20
|
// CORE FRAMEWORK MODULES
|
|
@@ -27,7 +29,9 @@ $custom-class: false !default;
|
|
|
27
29
|
$build-class: $build-class,
|
|
28
30
|
$px-rem-suffix: $px-rem-suffix,
|
|
29
31
|
$name-by-size: $name-by-size,
|
|
30
|
-
$custom-class: $custom-class
|
|
32
|
+
$custom-class: $custom-class,
|
|
33
|
+
$class-config: $class-config,
|
|
34
|
+
$debug: $debug
|
|
31
35
|
);
|
|
32
36
|
|
|
33
37
|
// OKLCH color system with opacity/brightness modifiers
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// ===========================================
|
|
2
|
+
// EVA CSS - Variables Only Entry Point
|
|
3
|
+
// ===========================================
|
|
4
|
+
// Use this when you only want CSS variables
|
|
5
|
+
// and no utility classes
|
|
6
|
+
// ===========================================
|
|
7
|
+
|
|
8
|
+
// Configuration variables with defaults
|
|
9
|
+
$sizes: 4, 8, 12, 16, 24, 32, 48, 64, 96, 128 !default;
|
|
10
|
+
$font-sizes: 12, 14, 16, 18, 20, 24, 32, 48 !default;
|
|
11
|
+
$px-rem-suffix: false !default;
|
|
12
|
+
$name-by-size: true !default;
|
|
13
|
+
$class-config: () !default;
|
|
14
|
+
|
|
15
|
+
// Import only the core variable generation system
|
|
16
|
+
@use '_eva' with (
|
|
17
|
+
$sizes: $sizes,
|
|
18
|
+
$font-sizes: $font-sizes,
|
|
19
|
+
$build-class: false, // ← No utility classes
|
|
20
|
+
$px-rem-suffix: $px-rem-suffix,
|
|
21
|
+
$name-by-size: $name-by-size,
|
|
22
|
+
$custom-class: false,
|
|
23
|
+
$class-config: $class-config
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Import colors system
|
|
27
|
+
@use '_colors';
|
|
28
|
+
|
|
29
|
+
// Import theme system
|
|
30
|
+
@use '_theme';
|
|
31
|
+
|
|
32
|
+
// Result: Only CSS variables like var(--16), var(--brand), etc.
|
|
33
|
+
// Perfect for custom component development!
|