@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/README.md +42 -432
- package/dist/quantum.css +1086 -394
- package/dist/quantum.min.css +4162 -1
- package/package.json +3 -2
- package/src/cli.js +53 -0
- package/src/defaults.js +35 -166
- package/src/generator.js +199 -131
- package/src/styles/quantum-animations.css +73 -0
- package/src/styles/quantum-components.css +102 -32
- package/src/styles/quantum.css +76 -10
- package/src/styles/starlight-ui.css +295 -60
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
|
-
|
|
15
|
-
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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 = {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
|
103
|
-
} else if (prefix === 'bg') {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
property = '
|
|
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
|
|
110
|
-
else if (valKey
|
|
111
|
-
else
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|
218
|
+
merged.get(key).rules.push(...group.rules);
|
|
161
219
|
}
|
|
220
|
+
});
|
|
162
221
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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:
|
|
105
|
+
background-color: rgba(255, 255, 255, 0.05);
|
|
106
106
|
color: white;
|
|
107
|
-
border
|
|
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:
|
|
112
|
-
|
|
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:
|
|
218
|
-
border-radius:
|
|
253
|
+
padding: 0.25rem 0.75rem;
|
|
254
|
+
border-radius: 0.375rem;
|
|
219
255
|
font-size: 0.75rem;
|
|
220
|
-
font-weight:
|
|
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:
|
|
227
|
-
color:
|
|
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:
|
|
232
|
-
color:
|
|
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:
|
|
237
|
-
color: #
|
|
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:
|
|
242
|
-
color: #
|
|
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:
|
|
247
|
-
color: #
|
|
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
|
|
307
|
-
border-top-color:
|
|
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,
|
|
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:
|
|
395
|
+
animation: shimmer 2s infinite;
|
|
331
396
|
border-radius: var(--radius-md);
|
|
332
397
|
}
|
|
333
398
|
|
|
334
|
-
@keyframes
|
|
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;
|