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/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
+ };