@howssatoshi/quantumcss 1.0.2 → 1.2.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/generator.js CHANGED
@@ -3,181 +3,249 @@ 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() };
61
-
62
- function processClass(fullCls) {
63
- let cls = fullCls;
64
- let variant = null;
65
- let breakpoint = null;
66
-
65
+ const responsiveUtils = {
66
+ sm: new Set(), md: new Set(), lg: new Set(), xl: new Set(), '2xl': new Set(),
67
+ dark: new Set()
68
+ };
69
+
70
+ const sideMap = {
71
+ p: 'padding', pt: 'padding-top', pr: 'padding-right', pb: 'padding-bottom', pl: 'padding-left',
72
+ px: ['padding-left', 'padding-right'], py: ['padding-top', 'padding-bottom'],
73
+ m: 'margin', mt: 'margin-top', mr: 'margin-right', mb: 'margin-bottom', ml: 'margin-left',
74
+ mx: ['margin-left', 'margin-right'], my: ['margin-top', 'margin-bottom'],
75
+ w: 'width', h: 'height', top: 'top', right: 'right', bottom: 'bottom', left: 'left',
76
+ 'max-w': 'max-width', 'max-h': 'max-height', 'min-w': 'min-width', 'min-h': 'min-height',
77
+ gap: 'gap', 'gap-x': 'column-gap', 'gap-y': 'row-gap'
78
+ };
79
+
80
+ function getRulesForClass(fullCls) {
81
+ let cls = fullCls, variant = null, breakpoint = null, isNeg = false;
82
+ if (cls.startsWith('-')) { isNeg = true; cls = cls.substring(1); }
67
83
  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
- }
84
+ let currentPart = 0;
85
+ while (currentPart < parts.length) {
86
+ const p = parts[currentPart];
87
+ if (breakpoints[p]) { breakpoint = p; }
88
+ else if (p === 'dark') { breakpoint = 'dark'; }
89
+ else if (['hover', 'focus', 'placeholder', 'group-hover'].includes(p)) { variant = p; }
90
+ else { cls = parts.slice(currentPart).join(':'); break; }
91
+ currentPart++;
81
92
  }
82
93
 
83
- let property = null;
84
- let value = null;
94
+ // Check Presets
95
+ if (config.componentPresets && config.componentPresets[cls]) {
96
+ const presetClasses = config.componentPresets[cls].split(/\s+/);
97
+ let allGroups = [];
98
+ presetClasses.forEach(pCls => {
99
+ const subGroups = getRulesForClass(pCls);
100
+ if (subGroups) {
101
+ subGroups.forEach(group => {
102
+ // Apply the preset's own breakpoint/variant to sub-groups if they don't have one?
103
+ // Actually, usually presets are used as base classes.
104
+ // If someone does md:btn-primary, we want the md: to apply to all rules in the preset.
105
+ allGroups.push({
106
+ breakpoint: breakpoint || group.breakpoint,
107
+ variant: variant || group.variant,
108
+ rules: group.rules
109
+ });
110
+ });
111
+ }
112
+ });
113
+ return allGroups;
114
+ }
85
115
 
116
+ let property = null, value = null, customSelector = null;
86
117
  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
- }
118
+ const entry = utilityMaps[cls];
119
+ if (typeof entry === 'object' && !Array.isArray(entry)) { property = entry.property; value = entry.value; }
120
+ else { property = entry; }
93
121
  }
94
122
 
95
123
  if (!property || !value) {
96
124
  const cParts = cls.split('-');
97
- const prefix = cParts[0];
98
- const valKey = cParts.slice(1).join('-');
125
+ let prefix = cParts[0], valKey = cParts.slice(1).join('-');
126
+
127
+ if ((prefix === 'max' || prefix === 'min' || prefix === 'gap' || prefix === 'gap-x' || prefix === 'gap-y') && cParts[1]) {
128
+ if (['w', 'h', 'x', 'y'].includes(cParts[1])) {
129
+ prefix = `${cParts[0]}-${cParts[1]}`;
130
+ valKey = cParts.slice(2).join('-');
131
+ }
132
+ }
99
133
 
100
134
  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;
135
+ if (theme.fontSize[valKey]) { property = ['font-size', 'line-height']; value = [theme.fontSize[valKey], (valKey.includes('xl') || parseInt(valKey) >= 3) ? '1.2' : '1.5']; }
136
+ else { const color = resolveColor(valKey); if (color) { property = 'color'; value = color; } }
137
+ } else if (prefix === 'bg') { const color = resolveColor(valKey); if (color) { property = 'background-color'; value = color; } }
138
+ else if (prefix === 'z') { property = 'z-index'; value = isNeg ? `-${valKey}` : valKey; }
139
+ else if (prefix === 'aspect') {
140
+ property = ['aspect-ratio', 'width', 'height'];
141
+ let ratio = 'auto';
142
+ if (valKey.startsWith('[') && valKey.endsWith(']')) ratio = valKey.slice(1, -1).replace(/\//g, ' / ');
143
+ else if (valKey === 'video') ratio = '16 / 9';
144
+ else if (valKey === 'square') ratio = '1 / 1';
145
+ else ratio = valKey.replace(/\//g, ' / ');
146
+ value = [ratio, '100%', 'auto'];
147
+ } else if (prefix === 'grid' && cParts[1] === 'cols') {
148
+ property = 'grid-template-columns'; value = `repeat(${cParts[2]}, minmax(0, 1fr))`;
149
+ } else if (prefix === 'col' && cParts[1] === 'span') {
150
+ property = 'grid-column'; value = `span ${cParts[2]} / span ${cParts[2]}`;
151
+ } else if (prefix === 'space') {
152
+ const amount = theme.spacing[cParts[2]] || `${parseInt(cParts[2]) * 0.25}rem`;
153
+ const escaped = fullCls.replace(/([:[\/])/g, '\\$1');
154
+ customSelector = `.${escaped} > * + *`;
155
+ property = cParts[1] === 'y' ? 'margin-top' : 'margin-left';
156
+ value = isNeg ? `-${amount}` : amount;
107
157
  } else if (prefix === 'rounded') {
108
158
  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;
159
+ if (valKey.startsWith('[') && valKey.endsWith(']')) value = valKey.slice(1, -1);
160
+ else if (theme.borderRadius[valKey]) value = theme.borderRadius[valKey];
161
+ else {
162
+ const num = parseInt(valKey);
163
+ value = isNaN(num) ? '0.375rem' : `${num * 0.125}rem`;
144
164
  }
165
+ } else if (prefix === 'scale') {
166
+ property = 'transform';
167
+ value = `scale(${parseInt(valKey) / 100})`;
168
+ } else if (prefix === 'transition') {
169
+ property = 'transition-property';
170
+ if (valKey === 'all') value = 'all';
171
+ else if (valKey === 'colors') value = 'color, background-color, border-color, text-decoration-color, fill, stroke';
172
+ else if (valKey === 'transform') value = 'transform';
173
+ else value = valKey;
174
+ } else if (prefix === 'duration') {
175
+ property = 'transition-duration';
176
+ value = `${valKey}ms`;
177
+ } else if (prefix === 'backdrop' && cParts[1] === 'blur') {
178
+ property = ['backdrop-filter', '-webkit-backdrop-filter'];
179
+ const blurMap = { sm: '4px', md: '12px', lg: '16px', xl: '24px' };
180
+ const blurVal = blurMap[cParts[2]] || (isNaN(parseInt(cParts[2])) ? '8px' : `${cParts[2]}px`);
181
+ value = `blur(${blurVal})`;
182
+ } else if (sideMap[prefix]) {
183
+ property = sideMap[prefix];
184
+ let v = valKey;
185
+ if (v.startsWith('[') && v.endsWith(']')) v = v.slice(1, -1);
186
+ else if (v.includes('/')) v = `${(parseInt(v.split('/')[0])/parseInt(v.split('/')[1])*100).toFixed(2)}%`;
187
+ else v = theme.spacing[v] || v;
188
+ value = isNeg ? (Array.isArray(v) ? v.map(x => `-${x}`) : `-${v}`) : v;
189
+ } else if (prefix === 'border') {
190
+ const color = resolveColor(valKey);
191
+ if (color) { property = 'border-color'; value = color; }
192
+ else if (['l', 'r', 't', 'b'].includes(cParts[1])) {
193
+ const sideMapSide = { l: 'left', r: 'right', t: 'top', b: 'bottom' };
194
+ property = `border-${sideMapSide[cParts[1]]}-width`;
195
+ value = `${cParts[2]}px`;
196
+ } else if (!isNaN(parseInt(valKey))) { property = 'border-width'; value = `${parseInt(valKey)}px`; }
145
197
  }
146
198
  }
147
199
 
148
200
  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
- }
201
+ let rules = Array.isArray(property) ? property.map((p, i) => ` ${p}: ${Array.isArray(value) ? value[i] : value};`) : [` ${property}: ${value};`];
202
+ return [{ breakpoint, variant, customSelector, rules }];
203
+ }
204
+ return null;
205
+ }
206
+
207
+ function processClass(fullCls) {
208
+ const groups = getRulesForClass(fullCls);
209
+ if (!groups) return;
210
+
211
+ // Merge groups with same selector (breakpoint + variant)
212
+ const merged = new Map();
213
+ groups.forEach(group => {
214
+ const key = `${group.breakpoint || ''}|${group.variant || ''}|${group.customSelector || ''}`;
215
+ if (!merged.has(key)) {
216
+ merged.set(key, { ...group, rules: [...group.rules] });
159
217
  } else {
160
- rules = ` ${property}: ${value};`;
218
+ merged.get(key).rules.push(...group.rules);
161
219
  }
220
+ });
162
221
 
163
- const cssBlock = `${selector} {\n${rules}\n}\n`;
164
- if (breakpoint) { responsiveUtils[breakpoint].add(cssBlock); }
165
- else { utilities.add(cssBlock); }
166
- }
222
+ merged.forEach(group => {
223
+ const { breakpoint, variant, customSelector, rules } = group;
224
+ const escapedFull = fullCls.replace(/([:[\/])/g, '\\$1');
225
+ let selector = customSelector || `.${escapedFull}`;
226
+ if (variant) { if (variant === 'group-hover') selector = `.group:hover ${selector}`; else selector += `:${variant}`}
227
+
228
+ const block = `${selector} {
229
+ ${rules.join('\n')}
230
+ }
231
+ `;
232
+ if (breakpoint) responsiveUtils[breakpoint].add(block); else utilities.add(block);
233
+ });
167
234
  }
168
235
 
169
236
  rawClasses.forEach(processClass);
170
-
171
- css += Array.from(utilities).join('\n');
237
+ let css = '/* Quantum CSS JIT Output */\n' + Array.from(utilities).join('\n');
172
238
  Object.entries(breakpoints).forEach(([name, width]) => {
173
- 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';
239
+ if (responsiveUtils[name] && responsiveUtils[name].size > 0) {
240
+ css += `\n@media (min-width: ${width}) {\n${Array.from(responsiveUtils[name]).map(u => ' ' + u.replace(/\n/g, '\n ')).join('\n').trimEnd()}\n}\n`;
177
241
  }
178
242
  });
179
243
 
244
+ if (responsiveUtils.dark && responsiveUtils.dark.size > 0) {
245
+ css += `\n@media (prefers-color-scheme: dark) {\n${Array.from(responsiveUtils.dark).map(u => ' ' + u.replace(/\n/g, '\n ')).join('\n').trimEnd()}\n}\n`;
246
+ }
247
+
180
248
  return css;
181
249
  }
182
250
 
183
- module.exports = { generateCSS };
251
+ module.exports = { generateCSS };
@@ -0,0 +1,73 @@
1
+ /* Cosmic Animation Library */
2
+
3
+ @keyframes nebula-drift {
4
+ 0% { transform: translate(-5%, -5%) scale(1); opacity: 0.4; }
5
+ 50% { transform: translate(5%, 5%) scale(1.2); opacity: 0.7; }
6
+ 100% { transform: translate(-5%, -5%) scale(1); opacity: 0.4; }
7
+ }
8
+
9
+ @keyframes cosmic-pulse {
10
+ 0%, 100% { box-shadow: 0 0 20px rgba(0, 212, 255, 0.2), 0 0 40px rgba(0, 212, 255, 0.1); }
11
+ 50% { box-shadow: 0 0 40px rgba(0, 212, 255, 0.5), 0 0 80px rgba(0, 212, 255, 0.2); }
12
+ }
13
+
14
+ @keyframes star-twinkle {
15
+ 0%, 100% { opacity: 0.3; transform: scale(0.8); }
16
+ 50% { opacity: 1; transform: scale(1.2); }
17
+ }
18
+
19
+ @keyframes orbit {
20
+ from { transform: rotate(0deg) translateX(20px) rotate(0deg); }
21
+ to { transform: rotate(360deg) translateX(20px) rotate(-360deg); }
22
+ }
23
+
24
+ @keyframes svg-draw {
25
+ from { stroke-dashoffset: 1000; }
26
+ to { stroke-dashoffset: 0; }
27
+ }
28
+
29
+ @keyframes float-y {
30
+ 0%, 100% { transform: translateY(0); }
31
+ 50% { transform: translateY(-20px); }
32
+ }
33
+
34
+ /* Animation Classes */
35
+
36
+ .ani-nebula {
37
+ animation: nebula-drift 20s ease-in-out infinite;
38
+ will-change: transform, opacity;
39
+ }
40
+
41
+ .ani-cosmic-pulse {
42
+ animation: cosmic-pulse 4s ease-in-out infinite;
43
+ }
44
+
45
+ .ani-twinkle {
46
+ animation: star-twinkle var(--twinkle-duration, 3s) ease-in-out infinite;
47
+ }
48
+
49
+ .ani-orbit {
50
+ animation: orbit var(--orbit-duration, 10s) linear infinite;
51
+ }
52
+
53
+ .ani-svg-draw {
54
+ stroke-dasharray: 1000;
55
+ stroke-dashoffset: 1000;
56
+ animation: svg-draw 3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
57
+ }
58
+
59
+ .ani-float {
60
+ animation: float-y 6s ease-in-out infinite;
61
+ }
62
+
63
+ /* Staggered Animations */
64
+ .ani-stagger-1 { animation-delay: 0.1s; }
65
+ .ani-stagger-2 { animation-delay: 0.2s; }
66
+ .ani-stagger-3 { animation-delay: 0.3s; }
67
+ .ani-stagger-4 { animation-delay: 0.4s; }
68
+ .ani-stagger-5 { animation-delay: 0.5s; }
69
+
70
+ /* Speed Modifiers */
71
+ .ani-fast { animation-duration: 0.5s !important; }
72
+ .ani-slow { animation-duration: 8s !important; }
73
+ .ani-slower { animation-duration: 15s !important; }
@@ -102,14 +102,21 @@
102
102
  }
103
103
 
104
104
  .btn-secondary {
105
- background-color: var(--color-secondary);
105
+ background-color: rgba(255, 255, 255, 0.05);
106
106
  color: white;
107
- border-color: var(--color-secondary);
107
+ border: 1px solid rgba(255, 255, 255, 0.1);
108
+ backdrop-filter: blur(12px);
109
+ -webkit-backdrop-filter: blur(12px);
108
110
  }
109
111
 
110
112
  .btn-secondary:hover {
111
- background-color: var(--color-secondary-600);
112
- border-color: var(--color-secondary-600);
113
+ background-color: rgba(255, 255, 255, 0.1);
114
+ }
115
+
116
+ body.light-mode .btn-secondary {
117
+ background-color: rgba(0, 0, 0, 0.03);
118
+ color: #1e293b;
119
+ border-color: rgba(0, 0, 0, 0.1);
113
120
  }
114
121
 
115
122
  .btn-outline {
@@ -189,12 +196,41 @@
189
196
  transition: all var(--duration-150) var(--ease-in-out);
190
197
  }
191
198
 
199
+ textarea.input {
200
+ min-height: 100px;
201
+ }
202
+
192
203
  .input:focus {
193
204
  outline: none;
194
205
  border-color: var(--color-primary);
195
206
  box-shadow: 0 0 0 3px var(--color-primary);
196
207
  }
197
208
 
209
+ /* Date & Time Input Specifics */
210
+ input[type="date"].input,
211
+ input[type="datetime-local"].input,
212
+ input[type="time"].input {
213
+ appearance: none;
214
+ -webkit-appearance: none;
215
+ min-height: 2.5rem;
216
+ display: inline-flex;
217
+ align-items: center;
218
+ }
219
+
220
+ /* Ensure dark-mode calendar picker */
221
+ .glass input[type="date"],
222
+ .starlight-card input[type="date"],
223
+ [class*="dark"] input[type="date"] {
224
+ color-scheme: dark;
225
+ }
226
+
227
+ /* Fix for alignment in flex containers */
228
+ input[type="date"]::-webkit-calendar-picker-indicator {
229
+ cursor: pointer;
230
+ filter: invert(0.5);
231
+ margin-left: 0.5rem;
232
+ }
233
+
198
234
  .input:disabled {
199
235
  background-color: #f3f4f6;
200
236
  color: #6b7280;
@@ -214,37 +250,76 @@
214
250
  .badge {
215
251
  display: inline-flex;
216
252
  align-items: center;
217
- padding: var(--space-1) var(--space-2);
218
- border-radius: var(--radius-full);
253
+ padding: 0.25rem 0.75rem;
254
+ border-radius: 0.375rem;
219
255
  font-size: 0.75rem;
220
- font-weight: 500;
256
+ font-weight: 600;
221
257
  text-transform: uppercase;
222
258
  letter-spacing: 0.05em;
259
+ border: 1px solid;
260
+ backdrop-filter: blur(4px);
261
+ -webkit-backdrop-filter: blur(4px);
223
262
  }
224
263
 
225
264
  .badge-primary {
226
- background-color: var(--color-primary-100);
227
- color: var(--color-primary-800);
265
+ background-color: rgba(0, 212, 255, 0.15);
266
+ color: #00d4ff;
267
+ border-color: rgba(0, 212, 255, 0.3);
228
268
  }
229
269
 
230
270
  .badge-secondary {
231
- background-color: #e5e7eb;
232
- color: #374151;
271
+ background-color: rgba(255, 255, 255, 0.05);
272
+ color: rgba(255, 255, 255, 0.8);
273
+ border-color: rgba(255, 255, 255, 0.1);
233
274
  }
234
275
 
235
276
  .badge-success {
236
- background-color: #d1fae5;
237
- color: #065f46;
277
+ background-color: rgba(16, 185, 129, 0.15);
278
+ color: #10b981;
279
+ border-color: rgba(16, 185, 129, 0.3);
238
280
  }
239
281
 
240
282
  .badge-warning {
241
- background-color: #fed7aa;
242
- color: #92400e;
283
+ background-color: rgba(245, 158, 11, 0.15);
284
+ color: #f59e0b;
285
+ border-color: rgba(245, 158, 11, 0.3);
243
286
  }
244
287
 
245
288
  .badge-error {
246
- background-color: #fee2e2;
247
- color: #991b1b;
289
+ background-color: rgba(239, 68, 68, 0.15);
290
+ color: #ef4444;
291
+ border-color: rgba(239, 68, 68, 0.3);
292
+ }
293
+
294
+ /* Light Mode Overrides for Badges */
295
+ body.light-mode .badge-primary {
296
+ background-color: rgba(59, 130, 246, 0.1);
297
+ color: #2563eb;
298
+ border-color: rgba(59, 130, 246, 0.2);
299
+ }
300
+
301
+ body.light-mode .badge-secondary {
302
+ background-color: rgba(0, 0, 0, 0.05);
303
+ color: #475569;
304
+ border-color: rgba(0, 0, 0, 0.1);
305
+ }
306
+
307
+ body.light-mode .badge-success {
308
+ background-color: rgba(16, 185, 129, 0.1);
309
+ color: #059669;
310
+ border-color: rgba(16, 185, 129, 0.2);
311
+ }
312
+
313
+ body.light-mode .badge-warning {
314
+ background-color: rgba(245, 158, 11, 0.1);
315
+ color: #d97706;
316
+ border-color: rgba(245, 158, 11, 0.2);
317
+ }
318
+
319
+ body.light-mode .badge-error {
320
+ background-color: rgba(239, 68, 68, 0.1);
321
+ color: #dc2626;
322
+ border-color: rgba(239, 68, 68, 0.2);
248
323
  }
249
324
 
250
325
  /* Alert Component */
@@ -303,8 +378,8 @@
303
378
  display: inline-block;
304
379
  width: 1.5rem;
305
380
  height: 1.5rem;
306
- border: 2px solid transparent;
307
- border-top-color: currentColor;
381
+ border: 2px solid rgba(255, 255, 255, 0.1);
382
+ border-top-color: var(--color-primary);
308
383
  border-radius: 50%;
309
384
  animation: spin 1s linear infinite;
310
385
  }
@@ -313,29 +388,24 @@
313
388
  to { transform: rotate(360deg); }
314
389
  }
315
390
 
316
- .spinner-sm {
317
- width: 1rem;
318
- height: 1rem;
319
- }
320
-
321
- .spinner-lg {
322
- width: 2rem;
323
- height: 2rem;
324
- }
325
-
326
391
  /* Skeleton Loading */
327
392
  .skeleton {
328
- background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
393
+ background: linear-gradient(90deg, rgba(255,255,255,0.05) 25%, rgba(255,255,255,0.1) 50%, rgba(255,255,255,0.05) 75%);
329
394
  background-size: 200% 100%;
330
- animation: loading 1.5s infinite;
395
+ animation: shimmer 2s infinite;
331
396
  border-radius: var(--radius-md);
332
397
  }
333
398
 
334
- @keyframes loading {
399
+ @keyframes shimmer {
335
400
  0% { background-position: 200% 0; }
336
401
  100% { background-position: -200% 0; }
337
402
  }
338
403
 
404
+ body.light-mode .skeleton {
405
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
406
+ background-size: 200% 100%;
407
+ }
408
+
339
409
  /* Tooltip Component */
340
410
  .tooltip {
341
411
  position: relative;