eva-css-fluid 1.0.3 → 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
package/src/_eva.scss
CHANGED
|
@@ -40,6 +40,8 @@ $px-rem-suffix: true !default; // Valeurs statiques de debug uniquement
|
|
|
40
40
|
$build-class: false !default; // DEV: false | PROD: true
|
|
41
41
|
$custom-class: false !default; // true pour filtrage personnalisé des classes
|
|
42
42
|
$name-by-size: true !default; // true = utilise les valeurs, false = utilise l'index
|
|
43
|
+
$class-config: () !default; // Configuration pour custom-class mode
|
|
44
|
+
$debug: false !default; // Show class generation summary
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
|
|
@@ -81,7 +83,89 @@ $font-properties: (
|
|
|
81
83
|
fs: font-size,
|
|
82
84
|
);
|
|
83
85
|
|
|
86
|
+
// ===========================================
|
|
87
|
+
// CONFIGURATION VALIDATION
|
|
88
|
+
// ===========================================
|
|
84
89
|
|
|
90
|
+
// Validate that required base sizes exist
|
|
91
|
+
@if not list.index($sizes, 16) {
|
|
92
|
+
@error "EVA CSS: Size 16 is required as a base size. Please add 16 to your $sizes configuration.";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Validate that sizes list is not empty
|
|
96
|
+
@if list.length($sizes) == 0 {
|
|
97
|
+
@error "EVA CSS: $sizes cannot be empty. Please provide at least one size value.";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Validate that font-sizes list is not empty
|
|
101
|
+
@if list.length($font-sizes) == 0 {
|
|
102
|
+
@error "EVA CSS: $font-sizes cannot be empty. Please provide at least one font size value.";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Validate custom-class configuration
|
|
106
|
+
@if $custom-class and meta.type-of($class-config) != "map" {
|
|
107
|
+
@error "EVA CSS: When $custom-class is true, $class-config must be a map. Example: (w: (50, 100), px: (24))";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Validate that class-config properties exist in $properties
|
|
111
|
+
@if $custom-class and list.length(map.keys($class-config)) > 0 {
|
|
112
|
+
@each $key in map.keys($class-config) {
|
|
113
|
+
@if not map.has-key($properties, $key) {
|
|
114
|
+
@error "EVA CSS: Unknown property '#{$key}' in $class-config. Available properties: #{map.keys($properties)}";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Validate that sizes in class-config exist in $sizes
|
|
118
|
+
$allowed-sizes: map.get($class-config, $key);
|
|
119
|
+
@if meta.type-of($allowed-sizes) != "list" {
|
|
120
|
+
@error "EVA CSS: Value for '#{$key}' in $class-config must be a list of sizes. Example: (24, 50, 100)";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@each $size in $allowed-sizes {
|
|
124
|
+
@if not list.index($sizes, $size) {
|
|
125
|
+
@error "EVA CSS: Size #{$size} in $class-config for property '#{$key}' is not in $sizes list. Available sizes: #{$sizes}";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ===========================================
|
|
132
|
+
// DEBUG MODE OUTPUT
|
|
133
|
+
// ===========================================
|
|
134
|
+
|
|
135
|
+
@if $debug {
|
|
136
|
+
@debug "╔══════════════════════════════════════════════════════════════";
|
|
137
|
+
@debug "║ EVA CSS - Configuration Summary";
|
|
138
|
+
@debug "╠══════════════════════════════════════════════════════════════";
|
|
139
|
+
@debug "║ Sizes: #{$sizes}";
|
|
140
|
+
@debug "║ Total sizes: #{list.length($sizes)}";
|
|
141
|
+
@debug "║ Font sizes: #{$font-sizes}";
|
|
142
|
+
@debug "║ Total font sizes: #{list.length($font-sizes)}";
|
|
143
|
+
@debug "╠══════════════════════════════════════════════════════════════";
|
|
144
|
+
@debug "║ Build Configuration:";
|
|
145
|
+
@debug "║ - Build classes: #{$build-class}";
|
|
146
|
+
@debug "║ - Custom class mode: #{$custom-class}";
|
|
147
|
+
@debug "║ - Px/Rem suffix: #{$px-rem-suffix}";
|
|
148
|
+
@debug "║ - Name by size: #{$name-by-size}";
|
|
149
|
+
|
|
150
|
+
@if $custom-class and list.length(map.keys($class-config)) > 0 {
|
|
151
|
+
@debug "╠══════════════════════════════════════════════════════════════";
|
|
152
|
+
@debug "║ Custom Class Configuration:";
|
|
153
|
+
@each $key in map.keys($class-config) {
|
|
154
|
+
$allowed-sizes: map.get($class-config, $key);
|
|
155
|
+
@debug "║ - #{$key}: #{$allowed-sizes} (#{list.length($allowed-sizes)} sizes)";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@if $build-class {
|
|
160
|
+
$class-count: list.length($sizes) * list.length(map.keys($properties)) * list.length($sizing-class);
|
|
161
|
+
@debug "╠══════════════════════════════════════════════════════════════";
|
|
162
|
+
@debug "║ Estimated Utility Classes:";
|
|
163
|
+
@debug "║ - Size properties: ~#{$class-count} classes";
|
|
164
|
+
@debug "║ - Available properties: #{map.keys($properties)}";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@debug "╚══════════════════════════════════════════════════════════════";
|
|
168
|
+
}
|
|
85
169
|
|
|
86
170
|
@function round2($number) {
|
|
87
171
|
@return math.div(math.round($number * 100), 100);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// ===========================================
|
|
2
|
+
// EVA CSS - Colors Only Entry Point
|
|
3
|
+
// ===========================================
|
|
4
|
+
// Use this when you only want the OKLCH
|
|
5
|
+
// color system without any other features
|
|
6
|
+
// ===========================================
|
|
7
|
+
|
|
8
|
+
// Import only the color system
|
|
9
|
+
@use '_colors';
|
|
10
|
+
|
|
11
|
+
// Import theme for dark/light mode support
|
|
12
|
+
@use '_theme';
|
|
13
|
+
|
|
14
|
+
// Result: Only color variables
|
|
15
|
+
// var(--brand), var(--accent), var(--light), var(--dark), etc.
|
|
16
|
+
// Perfect for integrating EVA colors into existing projects!
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EVA CSS Configuration Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads configuration from eva.config.js or package.json
|
|
5
|
+
* Supports both CommonJS and ES Modules
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const {
|
|
11
|
+
validateConfig,
|
|
12
|
+
mergeWithDefaults,
|
|
13
|
+
normalizeConfig,
|
|
14
|
+
toScssVariables
|
|
15
|
+
} = require('./config-schema.cjs');
|
|
16
|
+
|
|
17
|
+
// Import eva-colors for HEX to OKLCH conversion
|
|
18
|
+
let hexToOklch;
|
|
19
|
+
try {
|
|
20
|
+
const evaColors = require('eva-colors');
|
|
21
|
+
hexToOklch = evaColors.hexToOklch;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
// Fallback if eva-colors is not installed
|
|
24
|
+
hexToOklch = null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Find configuration file in the current working directory
|
|
29
|
+
*
|
|
30
|
+
* Priority:
|
|
31
|
+
* 1. eva.config.js
|
|
32
|
+
* 2. eva.config.cjs
|
|
33
|
+
* 3. eva.config.mjs
|
|
34
|
+
* 4. package.json (under "eva" key)
|
|
35
|
+
*
|
|
36
|
+
* @param {string} cwd - Current working directory
|
|
37
|
+
* @returns {Object|null} { path: string, source: string, type: string } or null
|
|
38
|
+
*/
|
|
39
|
+
function findConfigFile(cwd = process.cwd()) {
|
|
40
|
+
const configFiles = [
|
|
41
|
+
{ name: 'eva.config.js', type: 'module' },
|
|
42
|
+
{ name: 'eva.config.cjs', type: 'commonjs' },
|
|
43
|
+
{ name: 'eva.config.mjs', type: 'module' },
|
|
44
|
+
{ name: 'package.json', type: 'package' }
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
for (const { name, type } of configFiles) {
|
|
48
|
+
const filePath = path.join(cwd, name);
|
|
49
|
+
if (fs.existsSync(filePath)) {
|
|
50
|
+
return {
|
|
51
|
+
path: filePath,
|
|
52
|
+
source: name,
|
|
53
|
+
type
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load configuration from a file (CommonJS)
|
|
63
|
+
*
|
|
64
|
+
* @param {string} filePath - Path to config file
|
|
65
|
+
* @param {string} source - Source file name
|
|
66
|
+
* @returns {Object} Configuration object
|
|
67
|
+
*/
|
|
68
|
+
function loadConfigFromFile(filePath, source) {
|
|
69
|
+
try {
|
|
70
|
+
// Clear require cache to ensure fresh load
|
|
71
|
+
delete require.cache[require.resolve(filePath)];
|
|
72
|
+
|
|
73
|
+
if (source === 'package.json') {
|
|
74
|
+
const packageJson = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
75
|
+
if (!packageJson.eva) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return normalizeConfig(packageJson.eva);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// For .js, .cjs, .mjs files
|
|
82
|
+
const config = require(filePath);
|
|
83
|
+
|
|
84
|
+
// Handle ES module default export
|
|
85
|
+
const rawConfig = config.default || config;
|
|
86
|
+
|
|
87
|
+
return normalizeConfig(rawConfig);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Failed to load config from ${source}: Missing dependencies. ` +
|
|
92
|
+
`Run 'npm install' to install required packages.`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (error instanceof SyntaxError) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Failed to parse config from ${source}: ${error.message}\n` +
|
|
99
|
+
`Check for syntax errors in your configuration file.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw new Error(`Failed to load config from ${source}: ${error.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Normalize theme colors - Convert HEX to OKLCH if needed
|
|
109
|
+
*
|
|
110
|
+
* @param {Object} theme - Theme configuration
|
|
111
|
+
* @returns {Object} Theme with normalized colors
|
|
112
|
+
*/
|
|
113
|
+
function normalizeThemeColors(theme) {
|
|
114
|
+
if (!theme || !theme.colors) return theme;
|
|
115
|
+
|
|
116
|
+
const normalized = { ...theme };
|
|
117
|
+
normalized.colors = {};
|
|
118
|
+
|
|
119
|
+
for (const [colorName, colorValue] of Object.entries(theme.colors)) {
|
|
120
|
+
if (typeof colorValue === 'string') {
|
|
121
|
+
// HEX color - convert to OKLCH
|
|
122
|
+
if (!hexToOklch) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'eva-colors package is required to convert HEX colors to OKLCH. ' +
|
|
125
|
+
'Run: npm install eva-colors'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const oklch = hexToOklch(colorValue);
|
|
130
|
+
if (!oklch) {
|
|
131
|
+
throw new Error(`Invalid HEX color "${colorValue}" for theme.colors.${colorName}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
normalized.colors[colorName] = {
|
|
135
|
+
lightness: oklch.l,
|
|
136
|
+
chroma: oklch.c,
|
|
137
|
+
hue: oklch.h
|
|
138
|
+
};
|
|
139
|
+
} else if (typeof colorValue === 'object') {
|
|
140
|
+
// Already in OKLCH format
|
|
141
|
+
normalized.colors[colorName] = colorValue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return normalized;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Load and validate EVA CSS configuration
|
|
150
|
+
*
|
|
151
|
+
* @param {Object} options - Options
|
|
152
|
+
* @param {string} options.cwd - Current working directory
|
|
153
|
+
* @param {boolean} options.silent - Suppress console output
|
|
154
|
+
* @returns {Object|null} Validated and merged configuration, or null if no config found
|
|
155
|
+
*/
|
|
156
|
+
function loadConfig(options = {}) {
|
|
157
|
+
const { cwd = process.cwd(), silent = false } = options;
|
|
158
|
+
|
|
159
|
+
// Find config file
|
|
160
|
+
const configFile = findConfigFile(cwd);
|
|
161
|
+
|
|
162
|
+
if (!configFile) {
|
|
163
|
+
if (!silent) {
|
|
164
|
+
console.log('ℹ️ No EVA CSS config file found, using SCSS variables');
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!silent) {
|
|
170
|
+
console.log(`📋 Loading EVA CSS config from ${configFile.source}...`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Load config
|
|
174
|
+
const userConfig = loadConfigFromFile(configFile.path, configFile.source);
|
|
175
|
+
|
|
176
|
+
if (!userConfig) {
|
|
177
|
+
if (!silent) {
|
|
178
|
+
console.log('ℹ️ No EVA CSS config found in package.json');
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Normalize theme colors (HEX → OKLCH)
|
|
184
|
+
if (userConfig.theme && userConfig.theme.colors) {
|
|
185
|
+
try {
|
|
186
|
+
userConfig.theme = normalizeThemeColors(userConfig.theme);
|
|
187
|
+
if (!silent) {
|
|
188
|
+
const hexColors = Object.entries(userConfig.theme.colors).filter(([_, v]) =>
|
|
189
|
+
typeof v === 'object' && v.lightness !== undefined
|
|
190
|
+
);
|
|
191
|
+
if (hexColors.length > 0) {
|
|
192
|
+
console.log(`🎨 Converted ${hexColors.length} HEX colors to OKLCH`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new Error(`Theme color conversion failed: ${error.message}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Validate config
|
|
201
|
+
const validatedConfig = validateConfig(userConfig, configFile.source);
|
|
202
|
+
|
|
203
|
+
// Merge with defaults
|
|
204
|
+
const finalConfig = mergeWithDefaults(validatedConfig);
|
|
205
|
+
|
|
206
|
+
if (!silent) {
|
|
207
|
+
console.log('✅ Configuration loaded and validated successfully');
|
|
208
|
+
if (finalConfig.debug) {
|
|
209
|
+
console.log('\n📊 Configuration:');
|
|
210
|
+
console.log(` Sizes: ${finalConfig.sizes.join(', ')}`);
|
|
211
|
+
console.log(` Font sizes: ${finalConfig.fontSizes.join(', ')}`);
|
|
212
|
+
console.log(` Build classes: ${finalConfig.buildClass}`);
|
|
213
|
+
console.log(` Custom class mode: ${finalConfig.customClass}`);
|
|
214
|
+
console.log('');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
config: finalConfig,
|
|
220
|
+
source: configFile.source,
|
|
221
|
+
path: configFile.path
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Generate SCSS file from config
|
|
227
|
+
*
|
|
228
|
+
* @param {Object} config - Configuration object
|
|
229
|
+
* @param {string} outputPath - Path to output SCSS file
|
|
230
|
+
*/
|
|
231
|
+
function generateScssConfig(config, outputPath) {
|
|
232
|
+
const scssContent = [
|
|
233
|
+
'// AUTO-GENERATED FROM eva.config.js or package.json',
|
|
234
|
+
'// Do not edit this file directly - it will be overwritten',
|
|
235
|
+
'// Edit your eva.config.js or package.json instead',
|
|
236
|
+
'',
|
|
237
|
+
toScssVariables(config),
|
|
238
|
+
''
|
|
239
|
+
].join('\n');
|
|
240
|
+
|
|
241
|
+
fs.writeFileSync(outputPath, scssContent, 'utf8');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* CLI command to validate configuration
|
|
246
|
+
*/
|
|
247
|
+
function validateConfigCommand() {
|
|
248
|
+
try {
|
|
249
|
+
console.log('🔍 Validating EVA CSS configuration...\n');
|
|
250
|
+
|
|
251
|
+
const result = loadConfig({ silent: false });
|
|
252
|
+
|
|
253
|
+
if (!result) {
|
|
254
|
+
console.log('\n⚠️ No configuration file found');
|
|
255
|
+
console.log(' Create eva.config.js or add "eva" key to package.json');
|
|
256
|
+
console.log(' See: https://eva-css.xyz/configuration\n');
|
|
257
|
+
process.exit(0);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
console.log('\n✅ Configuration is valid!\n');
|
|
261
|
+
console.log('Configuration summary:');
|
|
262
|
+
console.log(` Source: ${result.source}`);
|
|
263
|
+
console.log(` Sizes: ${result.config.sizes.length} values`);
|
|
264
|
+
console.log(` Font sizes: ${result.config.fontSizes.length} values`);
|
|
265
|
+
console.log(` Build mode: ${result.config.buildClass ? 'utility classes' : 'variables only'}`);
|
|
266
|
+
|
|
267
|
+
if (result.config.customClass && Object.keys(result.config.classConfig).length > 0) {
|
|
268
|
+
console.log(` Custom classes: ${Object.keys(result.config.classConfig).length} properties configured`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (result.config.purge && result.config.purge.enabled) {
|
|
272
|
+
console.log(` Purge: enabled`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log('');
|
|
276
|
+
process.exit(0);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error(`\n❌ ${error.message}\n`);
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* CLI command to generate SCSS from config
|
|
285
|
+
*/
|
|
286
|
+
function generateScssCommand(outputPath = 'src/_config-generated.scss') {
|
|
287
|
+
try {
|
|
288
|
+
console.log('🔧 Generating SCSS configuration...\n');
|
|
289
|
+
|
|
290
|
+
const result = loadConfig({ silent: false });
|
|
291
|
+
|
|
292
|
+
if (!result) {
|
|
293
|
+
console.log('\n⚠️ No configuration file found');
|
|
294
|
+
console.log(' Using default SCSS variables instead\n');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const fullOutputPath = path.resolve(process.cwd(), outputPath);
|
|
299
|
+
generateScssConfig(result.config, fullOutputPath);
|
|
300
|
+
|
|
301
|
+
console.log(`\n✅ SCSS configuration generated: ${fullOutputPath}\n`);
|
|
302
|
+
console.log('You can now import this in your SCSS:');
|
|
303
|
+
console.log(` @use '${outputPath}';`);
|
|
304
|
+
console.log('');
|
|
305
|
+
|
|
306
|
+
process.exit(0);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error(`\n❌ ${error.message}\n`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
module.exports = {
|
|
314
|
+
findConfigFile,
|
|
315
|
+
loadConfig,
|
|
316
|
+
generateScssConfig,
|
|
317
|
+
validateConfigCommand,
|
|
318
|
+
generateScssCommand
|
|
319
|
+
};
|