@howssatoshi/quantumcss 1.7.3 → 1.7.5
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/README.md +31 -27
- package/dist/quantum.css +2374 -0
- package/dist/quantum.min.css +1 -6473
- package/examples/admin-panel.html +12 -9
- package/examples/analytics-dashboard.html +53 -1
- package/examples/blog-template.html +34 -15
- package/examples/chat-messaging.html +7 -6
- package/examples/email-template.html +42 -19
- package/examples/gaming-template.html +7 -7
- package/examples/gradient-test.html +106 -30
- package/examples/index.html +17 -17
- package/examples/kitchen-sink.html +164 -38
- package/examples/news-template.html +13 -13
- package/examples/portfolio-resume.html +23 -8
- package/examples/shopping/index.html +15 -15
- package/examples/travel/index.html +61 -39
- package/examples/verify_fixes.html +2 -2
- package/examples/verify_presets.html +1 -1
- package/package.json +3 -1
- package/src/cli.js +160 -27
- package/src/defaults.js +40 -23
- package/src/generator.js +191 -45
- package/src/starlight.js +6 -0
- package/src/styles/quantum-base.css +12 -1
- package/src/styles/quantum-components.css +2 -70
- package/src/styles/starlight.css +64 -43
- package/src/styles/quantum-responsive.css +0 -363
package/src/generator.js
CHANGED
|
@@ -11,6 +11,8 @@ function generateCSS(configPath) {
|
|
|
11
11
|
const config = fs.existsSync(resolvedPath) ? require(resolvedPath) : { content: [], theme: {} };
|
|
12
12
|
|
|
13
13
|
const theme = JSON.parse(JSON.stringify(defaultTheme || {}));
|
|
14
|
+
const currentUtilityMaps = { ...utilityMaps };
|
|
15
|
+
|
|
14
16
|
theme.colors = theme.colors || {};
|
|
15
17
|
theme.spacing = theme.spacing || {};
|
|
16
18
|
theme.borderRadius = theme.borderRadius || {};
|
|
@@ -40,17 +42,59 @@ function generateCSS(configPath) {
|
|
|
40
42
|
function resolveColor(key) {
|
|
41
43
|
if (!key) return null;
|
|
42
44
|
if (flattenedColors[key]) return flattenedColors[key];
|
|
43
|
-
if (key.includes('
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
const
|
|
48
|
-
|
|
45
|
+
if (key.includes('_')) {
|
|
46
|
+
const parts = key.split('_');
|
|
47
|
+
// If it's a color with opacity (e.g. primary_50)
|
|
48
|
+
if (parts.length === 2 && !isNaN(parseInt(parts[1]))) {
|
|
49
|
+
const [base, opacity] = parts;
|
|
50
|
+
const color = flattenedColors[base] || flattenedColors[`${base}-500`];
|
|
51
|
+
if (color && color.startsWith('#')) {
|
|
52
|
+
const r = parseInt(color.slice(1, 3), 16), g = parseInt(color.slice(3, 5), 16), b = parseInt(color.slice(5, 7), 16);
|
|
53
|
+
return `rgba(${r}, ${g}, ${b}, ${parseInt(opacity) / 100})`;
|
|
54
|
+
}
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
57
|
return null;
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
const postProcessors = [];
|
|
61
|
+
|
|
62
|
+
// Load Plugins
|
|
63
|
+
if (config.plugins && Array.isArray(config.plugins)) {
|
|
64
|
+
config.plugins.forEach(pluginPath => {
|
|
65
|
+
try {
|
|
66
|
+
// Support both relative paths and node_modules
|
|
67
|
+
let resolvedPluginPath;
|
|
68
|
+
try {
|
|
69
|
+
resolvedPluginPath = require.resolve(pluginPath, { paths: [process.cwd(), __dirname] });
|
|
70
|
+
} catch {
|
|
71
|
+
resolvedPluginPath = path.resolve(process.cwd(), pluginPath);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (fs.existsSync(resolvedPluginPath)) {
|
|
75
|
+
delete require.cache[resolvedPluginPath];
|
|
76
|
+
const plugin = require(resolvedPluginPath);
|
|
77
|
+
if (typeof plugin === 'function') {
|
|
78
|
+
const result = plugin({
|
|
79
|
+
theme,
|
|
80
|
+
utilityMaps: currentUtilityMaps,
|
|
81
|
+
componentPresets: config.componentPresets || {},
|
|
82
|
+
flattenedColors,
|
|
83
|
+
addUtilities: (newUtils) => Object.assign(currentUtilityMaps, newUtils)
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// If plugin returns an object with transformCSS, add it to postProcessors
|
|
87
|
+
if (result && typeof result.transformCSS === 'function') {
|
|
88
|
+
postProcessors.push(result.transformCSS);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error(`❌ Error loading plugin ${pluginPath}:`, err);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
54
98
|
const getRGBA = (color) => {
|
|
55
99
|
if (!color || color === 'transparent') return 'rgba(0,0,0,0)';
|
|
56
100
|
if (color.startsWith('rgba')) return color;
|
|
@@ -69,15 +113,38 @@ function generateCSS(configPath) {
|
|
|
69
113
|
|
|
70
114
|
const files = (config.content || []).flatMap(p => glob.sync(p));
|
|
71
115
|
const rawClasses = new Set();
|
|
116
|
+
|
|
117
|
+
// Scanners
|
|
72
118
|
const classAttrRegex = /class="([^"]+)"/g;
|
|
119
|
+
const variants = ['sm', 'md', 'lg', 'xl', '2xl', 'dark', 'light', 'hover', 'focus', 'active', 'group-hover'];
|
|
120
|
+
const variantRegexes = variants.map(v => ({
|
|
121
|
+
variant: v,
|
|
122
|
+
regex: new RegExp(`\\s${v}="([^"]+)"`, 'g')
|
|
123
|
+
}));
|
|
73
124
|
|
|
74
125
|
files.forEach(file => {
|
|
75
126
|
try {
|
|
76
127
|
const content = fs.readFileSync(file, 'utf8');
|
|
128
|
+
|
|
129
|
+
// 1. Match standard class="..."
|
|
77
130
|
let match;
|
|
78
131
|
while ((match = classAttrRegex.exec(content)) !== null) {
|
|
79
132
|
match[1].split(/\s+/).forEach(cls => { if (cls) rawClasses.add(cls); });
|
|
80
133
|
}
|
|
134
|
+
|
|
135
|
+
// 2. Match attribute lanes (e.g., md="flex gap-4")
|
|
136
|
+
variantRegexes.forEach(({ variant, regex }) => {
|
|
137
|
+
regex.lastIndex = 0;
|
|
138
|
+
while ((match = regex.exec(content)) !== null) {
|
|
139
|
+
match[1].split(/\s+/).forEach(cls => {
|
|
140
|
+
if (cls) {
|
|
141
|
+
// We convert attribute-based utilities to a canonical internal format
|
|
142
|
+
// using the __ separator, which getRulesForClass already understands.
|
|
143
|
+
rawClasses.add(`${variant}__${cls}`);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
81
148
|
} catch {
|
|
82
149
|
// Ignore errors reading files
|
|
83
150
|
}
|
|
@@ -95,7 +162,7 @@ function generateCSS(configPath) {
|
|
|
95
162
|
* @returns {string} The escaped selector
|
|
96
163
|
*/
|
|
97
164
|
const escapeSelector = (cls) => {
|
|
98
|
-
return cls.replace(/([
|
|
165
|
+
return cls.replace(/([:.[\]\\])/g, '\\$1');
|
|
99
166
|
};
|
|
100
167
|
|
|
101
168
|
const sideMap = {
|
|
@@ -111,20 +178,30 @@ function generateCSS(configPath) {
|
|
|
111
178
|
function getRulesForClass(fullCls, processedPresets = new Set()) {
|
|
112
179
|
let cls = fullCls, variant = null, breakpoint = null, isNeg = false;
|
|
113
180
|
if (cls.startsWith('-')) { isNeg = true; cls = cls.substring(1); }
|
|
114
|
-
|
|
181
|
+
|
|
182
|
+
// Support both : and __ as separators
|
|
183
|
+
const parts = cls.includes('__') ? cls.split('__') : cls.split(':');
|
|
184
|
+
|
|
115
185
|
let currentPart = 0;
|
|
116
186
|
while (currentPart < parts.length) {
|
|
117
187
|
const p = parts[currentPart];
|
|
118
188
|
if (breakpoints[p]) { breakpoint = p; }
|
|
119
189
|
else if (p === 'dark') { breakpoint = 'dark'; }
|
|
120
190
|
else if (p === 'light') { breakpoint = 'light'; }
|
|
121
|
-
else if (['hover', 'focus', 'placeholder', 'group-hover'].includes(p)) { variant = p; }
|
|
122
|
-
else {
|
|
191
|
+
else if (['hover', 'focus', 'active', 'placeholder', 'group-hover'].includes(p)) { variant = p; }
|
|
192
|
+
else {
|
|
193
|
+
// Reconstruct the remaining class part
|
|
194
|
+
cls = parts.slice(currentPart).join(cls.includes('__') ? '__' : ':');
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
123
197
|
currentPart++;
|
|
124
198
|
}
|
|
125
199
|
|
|
126
200
|
// Check Presets (User Config & Defaults)
|
|
127
|
-
|
|
201
|
+
let presetValue = config.componentPresets && config.componentPresets[cls];
|
|
202
|
+
if (!presetValue && currentUtilityMaps[cls] && typeof currentUtilityMaps[cls] === 'string') {
|
|
203
|
+
presetValue = currentUtilityMaps[cls];
|
|
204
|
+
}
|
|
128
205
|
|
|
129
206
|
if (presetValue && !processedPresets.has(cls)) {
|
|
130
207
|
processedPresets.add(cls);
|
|
@@ -146,9 +223,13 @@ function generateCSS(configPath) {
|
|
|
146
223
|
}
|
|
147
224
|
|
|
148
225
|
let property = null, value = null, customSelector = null;
|
|
149
|
-
if (
|
|
150
|
-
const entry =
|
|
151
|
-
if (!Array.isArray(entry)) {
|
|
226
|
+
if (currentUtilityMaps[cls] && typeof currentUtilityMaps[cls] === 'object') {
|
|
227
|
+
const entry = currentUtilityMaps[cls];
|
|
228
|
+
if (!Array.isArray(entry)) {
|
|
229
|
+
property = entry.property;
|
|
230
|
+
value = entry.value;
|
|
231
|
+
if (entry.variant && !variant) variant = entry.variant;
|
|
232
|
+
}
|
|
152
233
|
else { property = entry; }
|
|
153
234
|
}
|
|
154
235
|
|
|
@@ -156,7 +237,10 @@ function generateCSS(configPath) {
|
|
|
156
237
|
const cParts = cls.split('-');
|
|
157
238
|
let prefix = cParts[0], valKey = cParts.slice(1).join('-');
|
|
158
239
|
|
|
159
|
-
if (
|
|
240
|
+
if (cParts[0] === 'focus' && cParts[1] === 'glow') {
|
|
241
|
+
prefix = 'focus-glow';
|
|
242
|
+
valKey = cParts.slice(2).join('-');
|
|
243
|
+
} else if ((prefix === 'max' || prefix === 'min' || prefix === 'gap' || prefix === 'gap-x' || prefix === 'gap-y') && cParts[1]) {
|
|
160
244
|
if (['w', 'h', 'x', 'y'].includes(cParts[1])) {
|
|
161
245
|
prefix = `${cParts[0]}-${cParts[1]}`;
|
|
162
246
|
valKey = cParts.slice(2).join('-');
|
|
@@ -165,7 +249,7 @@ function generateCSS(configPath) {
|
|
|
165
249
|
|
|
166
250
|
if (prefix === 'text') {
|
|
167
251
|
if (theme.fontSize[valKey]) { property = ['font-size', 'line-height']; value = [theme.fontSize[valKey], (valKey.includes('xl') || parseInt(valKey) >= 3) ? '1.2' : '1.5']; }
|
|
168
|
-
else { const color = resolveColor(valKey); if (color) { property = 'color'; value = color
|
|
252
|
+
else { const color = resolveColor(valKey); if (color) { property = 'color'; value = `${color} !important`; } }
|
|
169
253
|
} else if (prefix === 'bg') {
|
|
170
254
|
if (cParts[1] === 'gradient' && cParts[2] === 'to') {
|
|
171
255
|
const dirMap = { r: 'to right', l: 'to left', t: 'to top', b: 'to bottom', tr: 'to top right', tl: 'to top left', br: 'to bottom right', bl: 'to bottom left' };
|
|
@@ -176,14 +260,14 @@ function generateCSS(configPath) {
|
|
|
176
260
|
const rules = [
|
|
177
261
|
' --q-gradient-from-transparent: rgba(0,0,0,0);',
|
|
178
262
|
' --q-gradient-to-transparent: rgba(0,0,0,0);',
|
|
179
|
-
` ${property}: ${value};`,
|
|
263
|
+
` ${property}: ${value} !important;`,
|
|
180
264
|
' --q-gradient-stops: var(--q-gradient-from, var(--q-gradient-from-transparent)), var(--q-gradient-to, var(--q-gradient-to-transparent));'
|
|
181
265
|
];
|
|
182
266
|
return [{ breakpoint: null, variant: null, customSelector: null, rules }];
|
|
183
267
|
}
|
|
184
268
|
}
|
|
185
269
|
const color = resolveColor(valKey);
|
|
186
|
-
if (color) { property = 'background-color'; value = color
|
|
270
|
+
if (color) { property = 'background-color'; value = `${color} !important`; }
|
|
187
271
|
} else if (prefix === 'from') {
|
|
188
272
|
const color = resolveColor(valKey);
|
|
189
273
|
if (color) {
|
|
@@ -214,10 +298,10 @@ function generateCSS(configPath) {
|
|
|
214
298
|
else if (prefix === 'aspect') {
|
|
215
299
|
property = ['aspect-ratio', 'width', 'height'];
|
|
216
300
|
let ratio = 'auto';
|
|
217
|
-
if (valKey.startsWith('[') && valKey.endsWith(']')) ratio = valKey.slice(1, -1).replace(
|
|
301
|
+
if (valKey.startsWith('[') && valKey.endsWith(']')) ratio = valKey.slice(1, -1).replace(/_/g, ' / ');
|
|
218
302
|
else if (valKey === 'video') ratio = '16 / 9';
|
|
219
303
|
else if (valKey === 'square') ratio = '1 / 1';
|
|
220
|
-
else ratio = valKey.replace(
|
|
304
|
+
else ratio = valKey.replace(/_/g, ' / ');
|
|
221
305
|
value = [ratio, '100%', 'auto'];
|
|
222
306
|
} else if (prefix === 'grid' && cParts[1] === 'cols') {
|
|
223
307
|
property = 'grid-template-columns'; value = `repeat(${cParts[2]}, minmax(0, 1fr))`;
|
|
@@ -258,7 +342,7 @@ function generateCSS(configPath) {
|
|
|
258
342
|
property = sideMap[prefix];
|
|
259
343
|
let v = valKey;
|
|
260
344
|
if (v.startsWith('[') && v.endsWith(']')) v = v.slice(1, -1);
|
|
261
|
-
else if (v.includes('
|
|
345
|
+
else if (v.includes('_')) v = `${(parseFloat(v.split('_')[0]) / parseFloat(v.split('_')[1]) * 100).toFixed(2)}%`;
|
|
262
346
|
else {
|
|
263
347
|
// Priority: 1. Specific theme map (e.g. maxWidth for max-w) 2. spacing map 3. Numeric conversion 4. raw value
|
|
264
348
|
const themeMap = prefix === 'max-w' ? theme.maxWidth : (theme[prefix] || theme.spacing);
|
|
@@ -268,15 +352,36 @@ function generateCSS(configPath) {
|
|
|
268
352
|
} else if (prefix === 'shadow') {
|
|
269
353
|
if (theme.shadows[valKey]) { property = 'box-shadow'; value = theme.shadows[valKey]; }
|
|
270
354
|
else if (valKey === '') { property = 'box-shadow'; value = theme.shadows.md || '0 4px 6px -1px rgb(0 0 0 / 0.1)'; }
|
|
271
|
-
}
|
|
272
|
-
else if (prefix === 'border') {
|
|
355
|
+
} else if (prefix === 'border') {
|
|
273
356
|
const color = resolveColor(valKey);
|
|
274
357
|
if (color) { property = 'border-color'; value = color; }
|
|
275
358
|
else if (['l', 'r', 't', 'b'].includes(cParts[1])) {
|
|
276
359
|
const sideMapSide = { l: 'left', r: 'right', t: 'top', b: 'bottom' };
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
360
|
+
const width = cParts[2] ? `${cParts[2]}px` : '1px';
|
|
361
|
+
property = ['border-width', `border-${sideMapSide[cParts[1]]}-width`, 'border-style'];
|
|
362
|
+
value = ['0', width, 'solid'];
|
|
363
|
+
} else if (!isNaN(parseInt(valKey))) {
|
|
364
|
+
property = ['border-width', 'border-style'];
|
|
365
|
+
value = [`${parseInt(valKey)}px`, 'solid'];
|
|
366
|
+
} else if (['solid', 'dashed', 'dotted', 'double', 'none'].includes(valKey)) {
|
|
367
|
+
property = 'border-style'; value = valKey;
|
|
368
|
+
}
|
|
369
|
+
} else if (prefix === 'focus-glow') {
|
|
370
|
+
const color = resolveColor(valKey) || resolveColor('primary') || '#00d4ff';
|
|
371
|
+
const rgba = getRGBA(color);
|
|
372
|
+
const glowColor = withOpacity(rgba, 0.7);
|
|
373
|
+
const ringColor = withOpacity(rgba, 0.4);
|
|
374
|
+
|
|
375
|
+
const rules = [
|
|
376
|
+
' outline: none !important;',
|
|
377
|
+
` box-shadow: 0 0 0 4px ${ringColor}, 0 0 35px ${glowColor} !important;`
|
|
378
|
+
];
|
|
379
|
+
|
|
380
|
+
// Apply to both focus and active states for better interactive feedback
|
|
381
|
+
return [
|
|
382
|
+
{ breakpoint, variant: variant || 'focus', customSelector, rules },
|
|
383
|
+
{ breakpoint, variant: 'active', customSelector, rules }
|
|
384
|
+
];
|
|
280
385
|
}
|
|
281
386
|
}
|
|
282
387
|
|
|
@@ -305,34 +410,70 @@ function generateCSS(configPath) {
|
|
|
305
410
|
merged.forEach(group => {
|
|
306
411
|
const { breakpoint, variant, customSelector, rules } = group;
|
|
307
412
|
const escapedFull = escapeSelector(fullCls);
|
|
308
|
-
let selector = customSelector || `.${escapedFull}`;
|
|
309
|
-
if (variant) { if (variant === 'group-hover') selector = `.group:hover ${selector}`; else selector += `:${variant}`}
|
|
310
413
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
414
|
+
// Generate multiple selectors for maximum compatibility
|
|
415
|
+
const selectors = [];
|
|
416
|
+
|
|
417
|
+
if (customSelector) {
|
|
418
|
+
selectors.push(customSelector);
|
|
419
|
+
} else {
|
|
420
|
+
// 1. Standard class selector (e.g., .md:flex or .md__flex)
|
|
421
|
+
let classSelector = `.${escapedFull}`;
|
|
422
|
+
if (variant) {
|
|
423
|
+
if (variant === 'group-hover') classSelector = `.group:hover ${classSelector}`;
|
|
424
|
+
else classSelector += `:${variant}`;
|
|
425
|
+
}
|
|
426
|
+
selectors.push(classSelector);
|
|
427
|
+
|
|
428
|
+
// 2. Attribute-based selector (e.g., [md~="flex"])
|
|
429
|
+
// If the class has a variant/breakpoint prefix, we can support it as an attribute
|
|
430
|
+
const separator = fullCls.includes('__') ? '__' : ':';
|
|
431
|
+
const parts = fullCls.split(separator);
|
|
432
|
+
if (parts.length > 1) {
|
|
433
|
+
const prefix = parts[0];
|
|
434
|
+
const utility = parts.slice(1).join(separator);
|
|
435
|
+
let attrSelector = `[${prefix}~="${utility}"]`;
|
|
436
|
+
|
|
437
|
+
if (variant && variant !== 'group-hover' && variant === prefix) {
|
|
438
|
+
// Already covered by attribute name (e.g. [hover~="text-primary"])
|
|
439
|
+
// but we still need the :hover pseudo-class if it's a state
|
|
440
|
+
if (['hover', 'focus', 'active'].includes(variant)) {
|
|
441
|
+
attrSelector += `:${variant}`;
|
|
442
|
+
}
|
|
443
|
+
} else if (variant) {
|
|
444
|
+
if (variant === 'group-hover') attrSelector = `.group:hover ${attrSelector}`;
|
|
445
|
+
else attrSelector += `:${variant}`;
|
|
446
|
+
}
|
|
447
|
+
selectors.push(attrSelector);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
selectors.forEach(selector => {
|
|
452
|
+
if (breakpoint === 'light') {
|
|
453
|
+
const block = `html[data-theme="light"] ${selector}, body.light-mode ${selector} {
|
|
454
|
+
${rules.join('\n').trim()}
|
|
314
455
|
}
|
|
315
456
|
`;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
${rules.join('\n')}
|
|
457
|
+
utilities.add(block);
|
|
458
|
+
} else if (breakpoint === 'dark') {
|
|
459
|
+
const block = `html[data-theme="dark"] ${selector}, body.dark-mode ${selector} {
|
|
460
|
+
${rules.join('\n').trim()}
|
|
320
461
|
}
|
|
321
462
|
`;
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
${rules.join('\n')}
|
|
463
|
+
utilities.add(block);
|
|
464
|
+
const mediaBlock = `${selector} {
|
|
465
|
+
${rules.join('\n').trim()}
|
|
326
466
|
}
|
|
327
467
|
`;
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
${rules.join('\n')}
|
|
468
|
+
responsiveUtils['dark'].add(mediaBlock);
|
|
469
|
+
} else {
|
|
470
|
+
const block = `${selector} {
|
|
471
|
+
${rules.join('\n').trim()}
|
|
332
472
|
}
|
|
333
473
|
`;
|
|
334
|
-
|
|
335
|
-
|
|
474
|
+
if (breakpoint) responsiveUtils[breakpoint].add(block); else utilities.add(block);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
336
477
|
});
|
|
337
478
|
}
|
|
338
479
|
|
|
@@ -380,6 +521,11 @@ ${rules.join('\n')}
|
|
|
380
521
|
css += `\n@media (prefers-color-scheme: dark) {\n${Array.from(responsiveUtils.dark).map(u => ' ' + u.replace(/\n/g, '\n ')).join('\n').trimEnd()}\n}\n`;
|
|
381
522
|
}
|
|
382
523
|
|
|
524
|
+
// Apply Post-Processors
|
|
525
|
+
postProcessors.forEach(processor => {
|
|
526
|
+
css = processor(css);
|
|
527
|
+
});
|
|
528
|
+
|
|
383
529
|
return css;
|
|
384
530
|
}
|
|
385
531
|
|
package/src/starlight.js
CHANGED
|
@@ -139,6 +139,12 @@ const Starlight = {
|
|
|
139
139
|
e.stopPropagation();
|
|
140
140
|
const isActive = toggle.classList.toggle('active');
|
|
141
141
|
menu.classList.toggle('active', isActive);
|
|
142
|
+
|
|
143
|
+
// Allow nav to wrap when menu is open so full-width menu stacks below
|
|
144
|
+
const parentNav = menu.closest('.starlight-nav') || menu.closest('.nav-glass');
|
|
145
|
+
if (parentNav) {
|
|
146
|
+
parentNav.style.flexFlow = isActive ? 'row wrap' : 'nowrap';
|
|
147
|
+
}
|
|
142
148
|
});
|
|
143
149
|
|
|
144
150
|
document.addEventListener('click', (e) => {
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
|
|
106
106
|
/* Glassmorphism */
|
|
107
107
|
--q-glass-bg: rgb(255 255 255 / 3%);
|
|
108
|
-
--q-glass-border: rgb(255 255 255 /
|
|
108
|
+
--q-glass-border: rgb(255 255 255 / 5%);
|
|
109
109
|
--q-glass-blur: blur(16px);
|
|
110
110
|
|
|
111
111
|
/* Transitions */
|
|
@@ -190,6 +190,17 @@ input, textarea, select, button {
|
|
|
190
190
|
padding: 0;
|
|
191
191
|
margin: 0;
|
|
192
192
|
background: transparent;
|
|
193
|
+
transition: box-shadow var(--q-transition-base), border-color var(--q-transition-base), transform var(--q-transition-base);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
input:focus,
|
|
197
|
+
textarea:focus,
|
|
198
|
+
select:focus,
|
|
199
|
+
button:focus,
|
|
200
|
+
input:active,
|
|
201
|
+
button:active {
|
|
202
|
+
outline: none;
|
|
203
|
+
box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.15), 0 0 20px rgba(0, 212, 255, 0.3);
|
|
193
204
|
}
|
|
194
205
|
|
|
195
206
|
textarea {
|
|
@@ -1,68 +1,3 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* QuantumCSS Component Utilities & Variants
|
|
3
|
-
* Advanced component patterns, states, and interactive utilities
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/* Hover State Utilities */
|
|
7
|
-
.hover\:text-primary:hover { color: var(--q-color-primary); }
|
|
8
|
-
.hover\:text-secondary:hover { color: var(--q-color-secondary); }
|
|
9
|
-
.hover\:text-white:hover { color: #fff; }
|
|
10
|
-
.hover\:text-black:hover { color: #000; }
|
|
11
|
-
|
|
12
|
-
.hover\:bg-primary:hover { background-color: var(--q-color-primary); }
|
|
13
|
-
.hover\:bg-secondary:hover { background-color: var(--q-color-secondary); }
|
|
14
|
-
.hover\:bg-gray-100:hover { background-color: #f3f4f6; }
|
|
15
|
-
.hover\:bg-gray-200:hover { background-color: #e5e7eb; }
|
|
16
|
-
|
|
17
|
-
.hover\:border-primary:hover { border-color: var(--q-color-primary); }
|
|
18
|
-
.hover\:border-secondary:hover { border-color: var(--q-color-secondary); }
|
|
19
|
-
|
|
20
|
-
.hover\:shadow-lg:hover { box-shadow: var(--q-shadow-lg); }
|
|
21
|
-
.hover\:shadow-xl:hover { box-shadow: var(--q-shadow-xl); }
|
|
22
|
-
|
|
23
|
-
.hover\:scale-105:hover { transform: scale(1.05); }
|
|
24
|
-
.hover\:scale-110:hover { transform: scale(1.1); }
|
|
25
|
-
.hover\:scale-95:hover { transform: scale(0.95); }
|
|
26
|
-
|
|
27
|
-
.hover\:rotate-90:hover { transform: rotate(90deg); }
|
|
28
|
-
.hover\:rotate-180:hover { transform: rotate(180deg); }
|
|
29
|
-
|
|
30
|
-
.hover\:opacity-75:hover { opacity: 0.75; }
|
|
31
|
-
.hover\:opacity-50:hover { opacity: 0.5; }
|
|
32
|
-
.hover\:opacity-100:hover { opacity: 1; }
|
|
33
|
-
|
|
34
|
-
/* Focus State Utilities */
|
|
35
|
-
.focus\:outline-none:focus { outline: none; }
|
|
36
|
-
.focus\:outline:focus { outline: 2px solid; outline-offset: 2px; }
|
|
37
|
-
.focus\:outline-primary:focus { outline-color: var(--q-color-primary); }
|
|
38
|
-
.focus\:outline-secondary:focus { outline-color: var(--q-color-secondary); }
|
|
39
|
-
|
|
40
|
-
.focus\:ring:focus { box-shadow: 0 0 0 3px; }
|
|
41
|
-
.focus\:ring-primary:focus { box-shadow: 0 0 0 3px var(--q-color-primary); }
|
|
42
|
-
.focus\:ring-secondary:focus { box-shadow: 0 0 0 3px var(--q-color-secondary); }
|
|
43
|
-
|
|
44
|
-
.focus\:border-primary:focus { border-color: var(--q-color-primary); }
|
|
45
|
-
.focus\:border-secondary:focus { border-color: var(--q-color-secondary); }
|
|
46
|
-
|
|
47
|
-
/* Active State Utilities */
|
|
48
|
-
.active\:scale-95:active { transform: scale(0.95); }
|
|
49
|
-
.active\:scale-90:active { transform: scale(0.9); }
|
|
50
|
-
|
|
51
|
-
.active\:bg-primary:active { background-color: var(--q-color-primary); }
|
|
52
|
-
.active\:bg-secondary:active { background-color: var(--q-color-secondary); }
|
|
53
|
-
|
|
54
|
-
/* Disabled State Utilities */
|
|
55
|
-
.disabled\:opacity-50:disabled { opacity: 0.5; }
|
|
56
|
-
.disabled\:opacity-75:disabled { opacity: 0.75; }
|
|
57
|
-
.disabled\:cursor-not-allowed:disabled { cursor: not-allowed; }
|
|
58
|
-
.disabled\:pointer-events-none:disabled { pointer-events: none; }
|
|
59
|
-
|
|
60
|
-
/* Group Hover Utilities */
|
|
61
|
-
.group:hover .group-hover\:text-primary { color: var(--q-color-primary); }
|
|
62
|
-
.group:hover .group-hover\:text-white { color: #fff; }
|
|
63
|
-
|
|
64
|
-
.group:hover .group-hover\:bg-primary { background-color: var(--q-color-primary); }
|
|
65
|
-
.group:hover .group-hover\:bg-secondary { background-color: var(--q-color-secondary); }
|
|
66
1
|
|
|
67
2
|
.group:hover .group-hover\:opacity-100 { opacity: 1; }
|
|
68
3
|
.group:hover .group-hover\:scale-110 { transform: scale(1.1); }
|
|
@@ -652,7 +587,6 @@ body.light-mode .tab-button.active {
|
|
|
652
587
|
|
|
653
588
|
.progress-bar {
|
|
654
589
|
height: 100%;
|
|
655
|
-
background-color: var(--q-color-primary);
|
|
656
590
|
transition: width var(--q-duration-300) var(--q-ease-in-out);
|
|
657
591
|
}
|
|
658
592
|
|
|
@@ -857,7 +791,6 @@ body.light-mode .table tbody tr:hover {
|
|
|
857
791
|
|
|
858
792
|
/* Main content area */
|
|
859
793
|
.main-content {
|
|
860
|
-
padding: 1.5rem 2rem;
|
|
861
794
|
overflow-y: auto;
|
|
862
795
|
overflow-x: hidden;
|
|
863
796
|
}
|
|
@@ -921,7 +854,6 @@ body.light-mode .top-nav {
|
|
|
921
854
|
}
|
|
922
855
|
|
|
923
856
|
.main-content {
|
|
924
|
-
padding: 1rem;
|
|
925
857
|
}
|
|
926
858
|
|
|
927
859
|
.page-header {
|
|
@@ -1442,7 +1374,7 @@ body.light-mode .user-email {
|
|
|
1442
1374
|
color: #34d399;
|
|
1443
1375
|
}
|
|
1444
1376
|
|
|
1445
|
-
.status-
|
|
1377
|
+
.status-active__:before {
|
|
1446
1378
|
background: #34d399;
|
|
1447
1379
|
}
|
|
1448
1380
|
|
|
@@ -1460,7 +1392,7 @@ body.light-mode .user-email {
|
|
|
1460
1392
|
color: #f87171;
|
|
1461
1393
|
}
|
|
1462
1394
|
|
|
1463
|
-
.status-
|
|
1395
|
+
.status-inactive__:before {
|
|
1464
1396
|
background: #f87171;
|
|
1465
1397
|
}
|
|
1466
1398
|
|