@howssatoshi/quantumcss 1.0.1 → 1.1.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/defaults.js CHANGED
@@ -2,164 +2,76 @@ const defaultTheme = {
2
2
  colors: {
3
3
  white: '#ffffff',
4
4
  black: '#000000',
5
- gray: {
6
- 50: '#f9fafb',
7
- 100: '#f3f4f6',
8
- 200: '#e5e7eb',
9
- 300: '#d1d5db',
10
- 400: '#9ca3af',
11
- 500: '#6b7280',
12
- 600: '#4b5563',
13
- 700: '#374151',
14
- 800: '#1f2937',
15
- 900: '#111827',
5
+ slate: {
6
+ 50: '#f8fafc', 100: '#f1f5f9', 200: '#e2e8f0', 300: '#cbd5e1', 400: '#94a3b8',
7
+ 500: '#64748b', 600: '#475569', 700: '#334155', 800: '#1e293b', 900: '#0f172a', 950: '#020617'
16
8
  },
17
9
  blue: {
18
- 50: '#eff6ff',
19
- 500: '#3b82f6',
20
- 600: '#2563eb',
10
+ 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa', 500: '#3b82f6', 600: '#2563eb'
21
11
  },
22
- red: {
23
- 500: '#ef4444',
12
+ orange: {
13
+ 50: '#fff7ed', 100: '#ffedd5', 200: '#fed7aa', 300: '#fdba74', 400: '#fb923c', 500: '#f97316', 600: '#ea580c'
24
14
  },
15
+ red: { 500: '#ef4444' },
25
16
  green: {
26
- 500: '#10b981',
17
+ 100: '#d1fae5',
18
+ 500: '#10b981'
27
19
  },
28
20
  starlight: {
29
- blue: '#00d4ff',
30
- peach: '#ffb38a',
31
- orange: '#ff7e5f',
32
- deep: '#08081a',
33
- }
21
+ blue: '#00d4ff', peach: '#ffb38a', orange: '#ff7e5f', deep: '#08081a',
22
+ },
23
+ transparent: 'transparent',
34
24
  },
35
25
  spacing: {
36
- 0: '0px',
37
- 1: '0.25rem',
38
- 2: '0.5rem',
39
- 3: '0.75rem',
40
- 4: '1rem',
41
- 5: '1.25rem',
42
- 6: '1.5rem',
43
- 8: '2rem',
44
- 10: '2.5rem',
45
- 12: '3rem',
46
- 16: '4rem',
47
- 20: '5rem',
48
- 24: '6rem',
49
- 32: '8rem',
50
- 64: '16rem',
51
- 128: '32rem',
52
- 144: '36rem',
26
+ 0: '0px', px: '1px', 1: '0.25rem', 2: '0.5rem', 3: '0.75rem', 4: '1rem', 5: '1.25rem',
27
+ 6: '1.5rem', 8: '2rem', 10: '2.5rem', 12: '3rem', 14: '3.5rem', 16: '4rem', 20: '5rem',
28
+ 24: '6rem', 32: '8rem', 40: '10rem', 48: '12rem', 64: '16rem', 128: '32rem',
53
29
  },
54
30
  fontSize: {
55
- xs: '0.75rem',
56
- sm: '0.875rem',
57
- base: '1rem',
58
- lg: '1.125rem',
59
- xl: '1.25rem',
60
- '2xl': '1.5rem',
61
- '3xl': '2rem',
62
- '4xl': '2.5rem',
63
- '5xl': '3.5rem',
64
- '6xl': '4.5rem',
31
+ xs: '0.75rem', sm: '0.875rem', base: '1rem', lg: '1.125rem', xl: '1.25rem',
32
+ '2xl': '1.5rem', '3xl': '2rem', '4xl': '2.5rem', '5xl': '3rem', '6xl': '4rem',
65
33
  },
66
34
  shadows: {
67
35
  sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
68
36
  md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
69
37
  lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
70
38
  xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
39
+ '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
71
40
  }
72
41
  };
73
42
 
74
43
  const utilityMaps = {
75
- // Layout
76
44
  flex: { property: 'display', value: 'flex' },
77
45
  grid: { property: 'display', value: 'grid' },
78
46
  hidden: { property: 'display', value: 'none' },
79
47
  block: { property: 'display', value: 'block' },
80
48
  'inline-block': { property: 'display', value: 'inline-block' },
81
-
82
- // Alignment
83
49
  'items-center': { property: 'align-items', value: 'center' },
84
50
  'items-start': { property: 'align-items', value: 'flex-start' },
85
51
  'items-end': { property: 'align-items', value: 'flex-end' },
86
52
  'justify-center': { property: 'justify-content', value: 'center' },
87
53
  'justify-between': { property: 'justify-content', value: 'space-between' },
88
- 'justify-start': { property: 'justify-content', value: 'flex-start' },
89
- 'justify-end': { property: 'justify-content', value: 'flex-end' },
90
-
91
- // Flex/Grid specific
92
54
  'flex-col': { property: 'flex-direction', value: 'column' },
93
- 'flex-grow': { property: 'flex-grow', value: '1' },
94
- 'flex-wrap': { property: 'flex-wrap', value: 'wrap' },
95
-
96
- // Sizing
97
- w: 'width',
98
- h: 'height',
55
+ 'flex-row': { property: 'flex-direction', value: 'row' },
56
+ 'flex-1': { property: 'flex', value: '1 1 0%' },
57
+ 'flex-shrink-0': { property: 'flex-shrink', value: '0' },
99
58
  'w-full': { property: 'width', value: '100%' },
100
59
  'h-full': { property: 'height', value: '100%' },
101
- 'max-w-prose': { property: 'max-width', value: '65ch' },
102
- 'min-h-screen': { property: 'min-height', value: '100vh' },
103
-
104
- // Spacing
105
- m: 'margin',
106
- mt: 'margin-top',
107
- mr: 'margin-right',
108
- mb: 'margin-bottom',
109
- ml: 'margin-left',
110
- mx: ['margin-left', 'margin-right'],
111
- my: ['margin-top', 'margin-bottom'],
112
- p: 'padding',
113
- pt: 'padding-top',
114
- pr: 'padding-right',
115
- pb: 'padding-bottom',
116
- pl: 'padding-left',
117
- px: ['padding-left', 'padding-right'],
118
- py: ['padding-top', 'padding-bottom'],
119
- gap: 'gap',
120
-
121
- // Typography
122
- text: 'font-size',
123
- 'font-bold': { property: 'font-weight', value: '700' },
124
- 'font-medium': { property: 'font-weight', value: '500' },
125
- 'font-light': { property: 'font-weight', value: '300' },
126
- 'tracking-tighter': { property: 'letter-spacing', value: '-0.05em' },
127
- 'tracking-tight': { property: 'letter-spacing', value: '-0.025em' },
128
- 'text-center': { property: 'text-align', value: 'center' },
129
- 'text-left': { property: 'text-align', value: 'left' },
130
- 'text-right': { property: 'text-align', value: 'right' },
131
-
132
- // Visuals
133
- bg: 'background-color',
134
- rounded: 'border-radius',
135
- 'rounded-full': { property: 'border-radius', value: '9999px' },
136
- 'rounded-xl': { property: 'border-radius', value: '0.75rem' },
137
- border: { property: 'border-width', value: '1px' },
138
- 'border-t': { property: 'border-top-width', value: '1px' },
139
- 'border-b': { property: 'border-bottom-width', value: '1px' },
140
- shadow: 'box-shadow',
141
-
142
- // Interactivity & States
143
- transition: { property: 'transition', value: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)' },
144
- 'scale-105': { property: 'transform', value: 'scale(1.05)' },
145
- 'active-scale': { property: 'transform', value: 'scale(0.96)' },
146
- 'cursor-pointer': { property: 'cursor', value: 'pointer' },
60
+ 'min-w-0': { property: 'min-width', value: '0' },
147
61
  'overflow-hidden': { property: 'overflow', value: 'hidden' },
148
-
149
- // Starlight Primitives
62
+ 'overflow-visible': { property: 'overflow', value: 'visible' },
63
+ 'border-none': { property: 'border-width', value: '0' },
64
+ 'bg-transparent': { property: 'background-color', value: 'transparent' },
150
65
  'glass': {
151
- property: ['background-color', 'backdrop-filter', '-webkit-backdrop-filter', 'border', 'box-shadow'],
152
- value: ['rgba(255, 255, 255, 0.03)', 'blur(16px)', 'blur(16px)', '1px solid rgba(255, 255, 255, 0.1)', '0 8px 32px 0 rgba(0, 0, 0, 0.37)']
66
+ property: ['background-color', 'backdrop-filter', '-webkit-backdrop-filter', 'border-width', 'border-color'],
67
+ value: ['rgba(255, 255, 255, 0.03)', 'blur(16px)', 'blur(16px)', '1px', 'rgba(255, 255, 255, 0.1)']
153
68
  },
154
69
  'glow-blue': { property: 'box-shadow', value: '0 0 30px rgba(0, 212, 255, 0.25)' },
155
- 'glow-peach': { property: 'box-shadow', value: '0 0 30px rgba(255, 179, 138, 0.2)' },
156
70
  'bg-starlight': { property: 'background', value: 'linear-gradient(135deg, #ffb38a 0%, #00d4ff 100%)' },
157
71
  'text-gradient-starlight': {
158
72
  property: ['background', '-webkit-background-clip', '-webkit-text-fill-color', 'display'],
159
73
  value: ['linear-gradient(to right, #ffb38a, #00d4ff)', 'text', 'transparent', 'inline-block']
160
74
  },
161
-
162
- // Components
163
75
  'btn-starlight': {
164
76
  property: ['background', 'color', 'border', 'box-shadow', 'font-weight', 'transition', 'height', 'padding', 'display', 'align-items', 'justify-content', 'border-radius', 'cursor'],
165
77
  value: ['linear-gradient(135deg, #ffb38a 0%, #00d4ff 100%)', '#000', 'none', '0 0 20px rgba(0, 212, 255, 0.3)', '700', 'all 0.2s ease', '3rem', '0 1.5rem', 'inline-flex', 'center', 'center', '0.75rem', 'pointer']
@@ -169,59 +81,16 @@ const utilityMaps = {
169
81
  value: ['rgba(255, 255, 255, 0.05)', 'inherit', '1px solid rgba(255, 255, 255, 0.15)', '700', 'all 0.2s ease', '3rem', '0 1.5rem', 'inline-flex', 'center', 'center', '0.75rem', 'pointer']
170
82
  },
171
83
  'input-starlight': {
172
- property: ['background-color', 'border', 'color', 'border-radius', 'padding', 'appearance', 'background-image', 'background-repeat', 'background-position', 'background-size', 'transition', 'height'],
173
- value: [
174
- 'rgba(255, 255, 255, 0.04)',
175
- '1px solid rgba(255, 255, 255, 0.15)',
176
- 'inherit',
177
- '0.75rem',
178
- '0 2.5rem 0 1rem',
179
- 'none',
180
- 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'none\' viewBox=\'0 0 24 24\' stroke=\'rgba(255,255,255,0.4)\'%3E%3Cpath stroke-linecap=\'round\' stroke-linejoin=\'round\' stroke-width=\'2\' d=\'M8 9l4 4 4-4\' /%3E%3C/svg%3E")',
181
- 'no-repeat',
182
- 'right 1rem center',
183
- '1rem',
184
- 'all 0.2s ease',
185
- '3rem'
186
- ]
84
+ property: ['background-color', 'border', 'color', 'border-radius', 'padding', 'appearance', 'transition', 'height'],
85
+ value: ['rgba(255, 255, 255, 0.04)', '1px solid rgba(255, 255, 255, 0.15)', 'inherit', '0.75rem', '0 1rem', 'none', 'all 0.2s ease', '3rem']
86
+ },
87
+ 'textarea-starlight': {
88
+ property: ['background-color', 'border', 'color', 'border-radius', 'padding', 'appearance', 'transition', 'min-height', 'width', 'display'],
89
+ value: ['rgba(255, 255, 255, 0.04)', '1px solid rgba(255, 255, 255, 0.15)', 'inherit', '0.75rem', '1rem', 'none', 'all 0.2s ease', '8rem', '100%', 'block']
187
90
  },
188
91
  'checkbox-starlight': {
189
92
  property: ['appearance', 'width', 'height', 'background', 'border', 'border-radius', 'cursor', 'transition', 'position', 'display', 'align-items', 'justify-content'],
190
93
  value: ['none', '1.25rem', '1.25rem', 'rgba(255, 255, 255, 0.05)', '1px solid rgba(255, 255, 255, 0.2)', '0.375rem', 'pointer', 'all 0.2s ease', 'relative', 'inline-flex', 'center', 'center']
191
- },
192
- 'radio-starlight': {
193
- property: ['appearance', 'width', 'height', 'background', 'border', 'border-radius', 'cursor', 'transition', 'position', 'display', 'align-items', 'justify-content'],
194
- value: ['none', '1.25rem', '1.25rem', 'rgba(255, 255, 255, 0.05)', '1px solid rgba(255, 255, 255, 0.2)', '50%', 'pointer', 'all 0.2s ease', 'relative', 'inline-flex', 'center', 'center']
195
- },
196
-
197
- // Dialog & Menu
198
- 'dialog-overlay': {
199
- property: ['position', 'top', 'left', 'width', 'height', 'background', 'backdrop-filter', 'display', 'align-items', 'justify-content', 'z-index'],
200
- value: ['fixed', '0', '0', '100vw', '100vh', 'rgba(0, 0, 0, 0.6)', 'blur(12px)', 'flex', 'center', 'center', '400']
201
- },
202
- 'dialog-content': {
203
- property: ['background-color', 'backdrop-filter', 'border', 'border-radius', 'width', 'max-width', 'box-shadow', 'overflow', 'position'],
204
- value: ['rgba(10, 10, 20, 0.98)', 'blur(20px)', '1px solid rgba(255, 255, 255, 0.1)', '1.5rem', '90%', '600px', '0 25px 50px -12px rgba(0, 0, 0, 0.5)', 'hidden', 'relative']
205
- },
206
- 'dropdown-menu': {
207
- property: ['background-color', 'backdrop-filter', 'border', 'border-radius', 'padding', 'box-shadow', 'min-width', 'z-index', 'margin-top'],
208
- value: ['rgba(15, 15, 30, 0.98)', 'blur(20px)', '1px solid rgba(255, 255, 255, 0.1)', '1rem', '0.5rem', '0 20px 40px rgba(0,0,0,0.4)', '200px', '600', '0.5rem']
209
- },
210
- 'dropdown-item': {
211
- property: ['padding', 'color', 'border-radius', 'display', 'width', 'text-align', 'transition', 'cursor', 'font-size'],
212
- value: ['0.625rem 1rem', 'rgba(255,255,255,0.7)', '0.625rem', 'block', '100%', 'left', 'all 0.2s', 'pointer', '0.875rem']
213
- },
214
-
215
- // Tooltip
216
- 'tooltip': {
217
- property: ['position', 'bottom', 'left', 'transform', 'padding', 'background-color', 'backdrop-filter', 'border', 'border-radius', 'color', 'font-size', 'white-space', 'pointer-events', 'opacity', 'transition', 'z-index', 'box-shadow'],
218
- value: ['absolute', '125%', '50%', 'translateX(-50%)', '0.5rem 0.75rem', 'rgba(10, 10, 30, 0.98)', 'blur(12px)', '1px solid rgba(0, 212, 255, 0.3)', '0.5rem', '#f1f5f9', '0.75rem', 'nowrap', 'none', '0', 'all 0.2s ease', '800', '0 4px 15px rgba(0, 0, 0, 0.4)']
219
- },
220
-
221
- // Skeleton
222
- 'skeleton': {
223
- property: ['background-color', 'background-image', 'background-size', 'background-repeat', 'border-radius', 'width', 'height'],
224
- value: ['rgba(255, 255, 255, 0.1)', 'linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent)', '200% 100%', 'no-repeat', '0.5rem', '100%', '1rem']
225
94
  }
226
95
  };
227
96
 
package/src/generator.js CHANGED
@@ -3,181 +3,190 @@ const path = require('path');
3
3
  const { glob } = require('glob');
4
4
  const { defaultTheme, utilityMaps } = require('./defaults');
5
5
 
6
- const breakpoints = {
7
- sm: '640px',
8
- md: '768px',
9
- lg: '1024px',
10
- xl: '1280px',
11
- };
6
+ const breakpoints = { sm: '640px', md: '768px', lg: '1024px', xl: '1280px', '2xl': '1536px' };
12
7
 
13
8
  function generateCSS(configPath) {
14
- delete require.cache[path.resolve(configPath)];
15
- const config = require(path.resolve(configPath));
9
+ const resolvedPath = path.resolve(configPath);
10
+ if (fs.existsSync(resolvedPath)) delete require.cache[resolvedPath];
11
+ const config = fs.existsSync(resolvedPath) ? require(resolvedPath) : { content: [], theme: {} };
16
12
 
17
- const contentPaths = config.content || [];
18
- const theme = { ...defaultTheme };
19
- if (config.theme) {
20
- if (config.theme.extend) {
21
- theme.colors = { ...theme.colors, ...config.theme.extend.colors };
22
- theme.spacing = { ...theme.spacing, ...config.theme.extend.spacing };
23
- theme.fontSize = { ...theme.fontSize, ...config.theme.extend.fontSize };
24
- theme.shadows = { ...theme.shadows, ...config.theme.extend.shadows };
25
- } else {
26
- Object.assign(theme, config.theme);
27
- }
13
+ const theme = JSON.parse(JSON.stringify(defaultTheme || {}));
14
+ theme.colors = theme.colors || {};
15
+ theme.spacing = theme.spacing || {};
16
+ theme.borderRadius = theme.borderRadius || {};
17
+ theme.fontSize = theme.fontSize || {};
18
+
19
+ if (config.theme && config.theme.extend) {
20
+ const ext = config.theme.extend;
21
+ if (ext.colors) Object.assign(theme.colors, ext.colors);
22
+ if (ext.spacing) Object.assign(theme.spacing, ext.spacing);
23
+ if (ext.borderRadius) Object.assign(theme.borderRadius, ext.borderRadius);
24
+ if (ext.fontSize) Object.assign(theme.fontSize, ext.fontSize);
28
25
  }
29
26
 
30
27
  const flattenedColors = {};
31
28
  Object.entries(theme.colors).forEach(([name, value]) => {
32
- if (typeof value === 'string') {
33
- flattenedColors[name] = value;
34
- } else {
35
- Object.entries(value).forEach(([shade, hex]) => {
36
- flattenedColors[`${name}-${shade}`] = hex;
37
- });
29
+ if (typeof value === 'string') flattenedColors[name] = value;
30
+ else {
31
+ Object.entries(value || {}).forEach(([shade, hex]) => { flattenedColors[`${name}-${shade}`] = hex; });
32
+ if (value && value['500']) flattenedColors[name] = value['500'];
38
33
  }
39
34
  });
40
35
 
41
- const files = contentPaths.flatMap(p => glob.sync(p));
36
+ function resolveColor(key) {
37
+ if (!key) return null;
38
+ if (flattenedColors[key]) return flattenedColors[key];
39
+ if (key.includes('/')) {
40
+ const [base, opacity] = key.split('/');
41
+ const color = flattenedColors[base] || flattenedColors[`${base}-500`];
42
+ if (color && color.startsWith('#')) {
43
+ const r = parseInt(color.slice(1, 3), 16), g = parseInt(color.slice(3, 5), 16), b = parseInt(color.slice(5, 7), 16);
44
+ return `rgba(${r}, ${g}, ${b}, ${parseInt(opacity) / 100})`;
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+
50
+ const files = (config.content || []).flatMap(p => glob.sync(p));
42
51
  const rawClasses = new Set();
52
+ const classAttrRegex = /class="([^"]+)"/g;
43
53
 
44
54
  files.forEach(file => {
45
- const content = fs.readFileSync(file, 'utf8');
46
- const matches = content.match(/[a-z0-9\-\/:]+/g);
47
- if (matches) {
48
- matches.forEach(cls => rawClasses.add(cls));
49
- }
55
+ try {
56
+ const content = fs.readFileSync(file, 'utf8');
57
+ let match;
58
+ while ((match = classAttrRegex.exec(content)) !== null) {
59
+ match[1].split(/\s+/).forEach(cls => { if (cls) rawClasses.add(cls); });
60
+ }
61
+ } catch (e) {}
50
62
  });
51
63
 
52
- let css = '/* Quantum CSS - High Performance Output */\n';
53
- css += '*, ::before, ::after { box-sizing: border-box; border-width: 0; border-style: solid; border-color: #e5e7eb; }\n';
54
- css += 'html { line-height: 1.5; -webkit-text-size-adjust: 100%; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }\n';
55
- css += 'body { margin: 0; line-height: inherit; }\n';
56
- css += 'img { display: block; max-width: 100%; height: auto; }\n';
57
- css += 'button { cursor: pointer; background: transparent; padding: 0; color: inherit; font: inherit; }\n\n';
58
-
59
64
  const utilities = new Set();
60
- const responsiveUtils = { sm: new Set(), md: new Set(), lg: new Set(), xl: new Set() };
65
+ const responsiveUtils = { sm: new Set(), md: new Set(), lg: new Set(), xl: new Set(), '2xl': new Set() };
66
+
67
+ const sideMap = {
68
+ p: 'padding', pt: 'padding-top', pr: 'padding-right', pb: 'padding-bottom', pl: 'padding-left',
69
+ px: ['padding-left', 'padding-right'], py: ['padding-top', 'padding-bottom'],
70
+ m: 'margin', mt: 'margin-top', mr: 'margin-right', mb: 'margin-bottom', ml: 'margin-left',
71
+ mx: ['margin-left', 'margin-right'], my: ['margin-top', 'margin-bottom'],
72
+ w: 'width', h: 'height', top: 'top', right: 'right', bottom: 'bottom', left: 'left',
73
+ 'max-w': 'max-width', 'max-h': 'max-height', 'min-w': 'min-width', 'min-h': 'min-height',
74
+ gap: 'gap', 'gap-x': 'column-gap', 'gap-y': 'row-gap'
75
+ };
61
76
 
62
77
  function processClass(fullCls) {
63
- let cls = fullCls;
64
- let variant = null;
65
- let breakpoint = null;
66
-
78
+ let cls = fullCls, variant = null, breakpoint = null, isNeg = false;
79
+ if (cls.startsWith('-')) { isNeg = true; cls = cls.substring(1); }
67
80
  const parts = cls.split(':');
68
- if (parts.length > 1) {
69
- if (breakpoints[parts[0]]) {
70
- breakpoint = parts[0];
71
- cls = parts.slice(1).join(':');
72
- }
73
- const subParts = cls.split(':');
74
- if (subParts.length > 1) {
75
- variant = subParts[0];
76
- cls = subParts[1];
77
- } else if (!breakpoint) {
78
- variant = parts[0];
79
- cls = parts[1];
80
- }
81
+ let currentPart = 0;
82
+ while (currentPart < parts.length) {
83
+ const p = parts[currentPart];
84
+ if (breakpoints[p]) { breakpoint = p; }
85
+ else if (['hover', 'focus', 'placeholder', 'group-hover'].includes(p)) { variant = p; }
86
+ else { cls = parts.slice(currentPart).join(':'); break; }
87
+ currentPart++;
81
88
  }
82
89
 
83
- let property = null;
84
- let value = null;
85
-
90
+ let property = null, value = null, customSelector = null;
86
91
  if (utilityMaps[cls]) {
87
- if (typeof utilityMaps[cls] === 'object' && !Array.isArray(utilityMaps[cls])) {
88
- property = utilityMaps[cls].property;
89
- value = utilityMaps[cls].value;
90
- } else {
91
- property = utilityMaps[cls];
92
- }
92
+ const entry = utilityMaps[cls];
93
+ if (typeof entry === 'object' && !Array.isArray(entry)) { property = entry.property; value = entry.value; }
94
+ else { property = entry; }
93
95
  }
94
96
 
95
97
  if (!property || !value) {
96
98
  const cParts = cls.split('-');
97
- const prefix = cParts[0];
98
- const valKey = cParts.slice(1).join('-');
99
+ let prefix = cParts[0], valKey = cParts.slice(1).join('-');
100
+
101
+ if ((prefix === 'max' || prefix === 'min' || prefix === 'gap' || prefix === 'gap-x' || prefix === 'gap-y') && cParts[1]) {
102
+ if (['w', 'h', 'x', 'y'].includes(cParts[1])) {
103
+ prefix = `${cParts[0]}-${cParts[1]}`;
104
+ valKey = cParts.slice(2).join('-');
105
+ }
106
+ }
99
107
 
100
108
  if (prefix === 'text') {
101
- if (theme.fontSize[valKey]) { property = 'font-size'; value = theme.fontSize[valKey]; }
102
- else if (flattenedColors[valKey]) { property = 'color'; value = flattenedColors[valKey]; }
103
- } else if (prefix === 'bg') {
104
- property = 'background-color'; value = flattenedColors[valKey];
105
- } else if (prefix === 'shadow') {
106
- property = 'box-shadow'; value = theme.shadows[valKey] || theme.shadows.md;
109
+ if (theme.fontSize[valKey]) { property = ['font-size', 'line-height']; value = [theme.fontSize[valKey], (valKey.includes('xl') || parseInt(valKey) >= 3) ? '1.2' : '1.5']; }
110
+ else { const color = resolveColor(valKey); if (color) { property = 'color'; value = color; } }
111
+ } else if (prefix === 'bg') { const color = resolveColor(valKey); if (color) { property = 'background-color'; value = color; } }
112
+ else if (prefix === 'z') { property = 'z-index'; value = isNeg ? `-${valKey}` : valKey; }
113
+ else if (prefix === 'aspect') {
114
+ property = ['aspect-ratio', 'width', 'height'];
115
+ let ratio = 'auto';
116
+ if (valKey.startsWith('[') && valKey.endsWith(']')) ratio = valKey.slice(1, -1).replace(/\//g, ' / ');
117
+ else if (valKey === 'video') ratio = '16 / 9';
118
+ else if (valKey === 'square') ratio = '1 / 1';
119
+ else ratio = valKey.replace(/\//g, ' / ');
120
+ value = [ratio, '100%', 'auto'];
121
+ } else if (prefix === 'grid' && cParts[1] === 'cols') {
122
+ property = 'grid-template-columns'; value = `repeat(${cParts[2]}, minmax(0, 1fr))`;
123
+ } else if (prefix === 'col' && cParts[1] === 'span') {
124
+ property = 'grid-column'; value = `span ${cParts[2]} / span ${cParts[2]}`;
125
+ } else if (prefix === 'space') {
126
+ const amount = theme.spacing[cParts[2]] || `${parseInt(cParts[2]) * 0.25}rem`;
127
+ const escaped = fullCls.replace(/([:[\/])/g, '\\$1');
128
+ customSelector = `.${escaped} > * + *`;
129
+ property = cParts[1] === 'y' ? 'margin-top' : 'margin-left';
130
+ value = isNeg ? `-${amount}` : amount;
107
131
  } else if (prefix === 'rounded') {
108
132
  property = 'border-radius';
109
- if (valKey === 'lg') value = '0.5rem';
110
- else if (valKey === 'full') value = '9999px';
111
- else value = theme.spacing[valKey] || (valKey ? `${parseInt(valKey) * 0.125}rem` : '0.25rem');
112
- } else if (prefix === 'border') {
113
- if (flattenedColors[valKey]) {
114
- property = 'border-color'; value = flattenedColors[valKey];
115
- } else if (cParts[1] === 'l' || cParts[1] === 'r' || cParts[1] === 't' || cParts[1] === 'b') {
116
- const sideMap = { l: 'left', r: 'right', t: 'top', b: 'bottom' };
117
- property = `border-${sideMap[cParts[1]]}-width`;
118
- value = `${cParts[2]}px`;
119
- }
120
- } else if (prefix === 'gap') {
121
- property = 'gap'; value = theme.spacing[valKey] || (valKey ? (isNaN(valKey) ? valKey : `${valKey}rem`) : '0px');
122
- } else if (prefix === 'grid' && cParts[1] === 'cols') {
123
- property = 'grid-template-columns';
124
- value = `repeat(${cParts[2]}, minmax(0, 1fr))`;
125
- } else if (prefix === 'flex' && cParts[1] === 'row') {
126
- property = 'flex-direction';
127
- value = 'row';
128
- } else if (prefix === 'w' || prefix === 'h') {
129
- property = utilityMaps[prefix];
130
- if (valKey.includes('/')) {
131
- const [n, d] = valKey.split('/');
132
- value = `${(parseInt(n)/parseInt(d)*100).toFixed(2)}%`;
133
- } else {
134
- value = theme.spacing[valKey] || valKey;
135
- }
136
- } else if (utilityMaps[prefix]) {
137
- const mapEntry = utilityMaps[prefix];
138
- if (typeof mapEntry === 'object' && !Array.isArray(mapEntry)) {
139
- property = mapEntry.property;
140
- value = mapEntry.value;
141
- } else {
142
- property = mapEntry;
143
- value = theme.spacing[valKey] || valKey;
133
+ if (valKey.startsWith('[') && valKey.endsWith(']')) value = valKey.slice(1, -1);
134
+ else if (theme.borderRadius[valKey]) value = theme.borderRadius[valKey];
135
+ else {
136
+ const num = parseInt(valKey);
137
+ value = isNaN(num) ? '0.375rem' : `${num * 0.125}rem`;
144
138
  }
139
+ } else if (prefix === 'scale') {
140
+ property = 'transform';
141
+ value = `scale(${parseInt(valKey) / 100})`;
142
+ } else if (prefix === 'transition') {
143
+ property = 'transition-property';
144
+ if (valKey === 'all') value = 'all';
145
+ else if (valKey === 'colors') value = 'color, background-color, border-color, text-decoration-color, fill, stroke';
146
+ else if (valKey === 'transform') value = 'transform';
147
+ else value = valKey;
148
+ } else if (prefix === 'duration') {
149
+ property = 'transition-duration';
150
+ value = `${valKey}ms`;
151
+ } else if (sideMap[prefix]) {
152
+ property = sideMap[prefix];
153
+ let v = valKey;
154
+ if (v.startsWith('[') && v.endsWith(']')) v = v.slice(1, -1);
155
+ else if (v.includes('/')) v = `${(parseInt(v.split('/')[0])/parseInt(v.split('/')[1])*100).toFixed(2)}%`;
156
+ else v = theme.spacing[v] || v;
157
+ value = isNeg ? (Array.isArray(v) ? v.map(x => `-${x}`) : `-${v}`) : v;
158
+ } else if (prefix === 'border') {
159
+ const color = resolveColor(valKey);
160
+ if (color) { property = 'border-color'; value = color; }
161
+ else if (['l', 'r', 't', 'b'].includes(cParts[1])) {
162
+ const sideMapSide = { l: 'left', r: 'right', t: 'top', b: 'bottom' };
163
+ property = `border-${sideMapSide[cParts[1]]}-width`;
164
+ value = `${cParts[2]}px`;
165
+ } else if (!isNaN(parseInt(valKey))) { property = 'border-width'; value = `${parseInt(valKey)}px`; }
145
166
  }
146
167
  }
147
168
 
148
169
  if (property && value) {
149
- let selector = `.${fullCls.replace(/:/g, '\\:').replace(/\//g, '\\/')}`;
150
- if (variant === 'hover' || variant === 'focus') selector += `:${variant}`;
151
-
152
- let rules = '';
153
- if (Array.isArray(property)) {
154
- if (Array.isArray(value)) {
155
- rules = property.map((p, i) => ` ${p}: ${value[i]};`).join('\n');
156
- } else {
157
- rules = property.map(p => ` ${p}: ${value};`).join('\n');
158
- }
159
- } else {
160
- rules = ` ${property}: ${value};`;
161
- }
162
-
163
- const cssBlock = `${selector} {\n${rules}\n}\n`;
164
- if (breakpoint) { responsiveUtils[breakpoint].add(cssBlock); }
165
- else { utilities.add(cssBlock); }
170
+ const escapedFull = fullCls.replace(/([:[\/])/g, '\\$1');
171
+ let selector = customSelector || `.${escapedFull}`;
172
+ if (variant) { if (variant === 'group-hover') selector = `.group:hover ${selector}`; else selector += `:${variant}`}
173
+ let rules = Array.isArray(property) ? property.map((p, i) => ` ${p}: ${Array.isArray(value) ? value[i] : value};`).join('\n') : ` ${property}: ${value};`;
174
+ const block = `${selector} {
175
+ ${rules}
176
+ }
177
+ `;
178
+ if (breakpoint) responsiveUtils[breakpoint].add(block); else utilities.add(block);
166
179
  }
167
180
  }
168
181
 
169
182
  rawClasses.forEach(processClass);
170
-
171
- css += Array.from(utilities).join('\n');
183
+ let css = '/* Quantum CSS JIT Output */\n' + Array.from(utilities).join('\n');
172
184
  Object.entries(breakpoints).forEach(([name, width]) => {
173
185
  if (responsiveUtils[name].size > 0) {
174
- css += `\n@media (min-width: ${width}) {\n`;
175
- css += Array.from(responsiveUtils[name]).map(u => ' ' + u.replace(/\n/g, '\n ')).join('\n').trimEnd();
176
- css += '\n}\n';
186
+ css += `\n@media (min-width: ${width}) {\n${Array.from(responsiveUtils[name]).map(u => ' ' + u.replace(/\n/g, '\n ')).join('\n').trimEnd()}\n}\n`;
177
187
  }
178
188
  });
179
-
180
189
  return css;
181
190
  }
182
191
 
183
- module.exports = { generateCSS };
192
+ module.exports = { generateCSS };