@howssatoshi/quantumcss 1.4.3 → 1.5.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.
@@ -260,22 +260,8 @@
260
260
  </footer>
261
261
 
262
262
  <script>
263
- // Theme Toggle Logic
264
- const themeBtn = document.getElementById('theme-btn');
265
- if (themeBtn) {
266
- themeBtn.addEventListener('click', () => {
267
- document.body.classList.toggle('light-mode');
268
- const isLight = document.body.classList.contains('light-mode');
269
-
270
- // Update all icons
271
- document.querySelectorAll('.sun-icon').forEach(icon => {
272
- icon.classList.toggle('hidden', !isLight);
273
- });
274
- document.querySelectorAll('.moon-icon').forEach(icon => {
275
- icon.classList.toggle('hidden', isLight);
276
- });
277
- });
278
- }
263
+ // Theme initialization is handled automatically by starlight.js
264
+ // Custom template logic can go here
279
265
  </script>
280
266
 
281
267
  </body>
@@ -333,26 +333,6 @@
333
333
 
334
334
  <script src="../src/starlight.js"></script>
335
335
  <script>
336
- const themeBtns = [document.getElementById('theme-btn'), document.getElementById('theme-btn-mobile')];
337
-
338
- // Theme Toggle Logic
339
- themeBtns.forEach(btn => {
340
- if (btn) {
341
- btn.addEventListener('click', () => {
342
- document.body.classList.toggle('light-mode');
343
- const isLight = document.body.classList.contains('light-mode');
344
-
345
- // Update all icons
346
- document.querySelectorAll('.sun-icon').forEach(icon => {
347
- icon.classList.toggle('hidden', !isLight);
348
- });
349
- document.querySelectorAll('.moon-icon').forEach(icon => {
350
- icon.classList.toggle('hidden', isLight);
351
- });
352
- });
353
- }
354
- });
355
-
356
336
  // Accordion Logic
357
337
  document.querySelectorAll('.accordion-header').forEach(header => {
358
338
  header.addEventListener('click', () => {
@@ -19,7 +19,7 @@
19
19
  flex-shrink: 0;
20
20
  }
21
21
  .theme-toggle:hover { background: rgba(255,255,255,0.1); transform: scale(1.05); }
22
- body.light-mode .theme-toggle { background: #fff; border-color: #cbd5e1; box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
22
+ html[data-theme="light"] .theme-toggle { background: #fff; border-color: #cbd5e1; box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
23
23
  .theme-toggle svg { width: 1.5rem; height: 1.5rem; }
24
24
  .hidden { display: none; }
25
25
  </style>
@@ -61,7 +61,7 @@
61
61
 
62
62
  <header class="hero container">
63
63
  <div class="pt-8 pb-12 text-center">
64
- <span class="text-[10px] font-black tracking-[0.3em] text-starlight uppercase mb-6 block">QuantumCSS v1.4.2</span>
64
+ <span class="text-[10px] font-black tracking-[0.3em] text-starlight uppercase mb-6 block">QuantumCSS v1.5.0</span>
65
65
  <h1 class="text-7xl md:text-8xl text-gradient-starlight font-black uppercase italic tracking-tighter mb-8">Starlight Design</h1>
66
66
  <p class="text-xl text-slate-400 max-w-3xl mx-auto mb-12 font-medium leading-relaxed">
67
67
  A standardized, high-performance UI library.
@@ -128,14 +128,11 @@
128
128
  </main>
129
129
 
130
130
  <script>
131
- function toggleTheme() {
132
- document.body.classList.toggle('light-mode');
133
- const isLight = document.body.classList.contains('light-mode');
134
- document.querySelector('.sun-icon').classList.toggle('hidden', !isLight);
135
- document.querySelector('.moon-icon').classList.toggle('hidden', isLight);
136
- }
137
131
  function openDialog() { document.getElementById('dialog').style.display = 'flex'; }
138
132
  function closeDialog() { document.getElementById('dialog').style.display = 'none'; }
133
+
134
+ // Theme initialization and system preference detection are now handled
135
+ // automatically by Starlight.initTheme() in starlight.js
139
136
  </script>
140
137
  </body>
141
138
  </html>
@@ -5,9 +5,10 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Quantum News | Decoding the Future</title>
7
7
  <link rel="stylesheet" href="../dist/quantum.min.css">
8
+ <script src="../src/starlight.js"></script>
8
9
  <style>
9
10
  body { background-color: #020617; color: #f1f5f9; transition: background-color 0.5s ease, color 0.5s ease; }
10
- body.light-mode { background-color: #ffffff; color: #0f172a; }
11
+ html[data-theme="light"] body { background-color: #ffffff; color: #0f172a; }
11
12
  .news-grid { display: grid; grid-template-columns: 1fr; gap: 2rem; }
12
13
  @media (min-width: 1024px) {
13
14
  .news-grid { grid-template-columns: 3fr 1fr; }
@@ -147,7 +148,7 @@
147
148
  .sun-icon { display: none; }
148
149
  </style>
149
150
  </head>
150
- <body class="font-sans antialiased light-mode">
151
+ <body class="font-sans antialiased">
151
152
  <!-- Top Bar -->
152
153
  <div class="border-b border-slate-100 py-2 bg-slate-50">
153
154
  <div class="container mx-auto px-6 flex justify-between items-center text-[10px] font-bold uppercase tracking-widest text-slate-500">
@@ -312,22 +313,8 @@
312
313
  }
313
314
  </style>
314
315
  <script>
315
- // Theme Toggle Logic
316
- const themeBtn = document.getElementById('theme-btn');
317
- if (themeBtn) {
318
- themeBtn.addEventListener('click', () => {
319
- document.body.classList.toggle('light-mode');
320
- const isLight = document.body.classList.contains('light-mode');
321
-
322
- // Update all icons
323
- document.querySelectorAll('.sun-icon').forEach(icon => {
324
- icon.style.display = isLight ? 'block' : 'none';
325
- });
326
- document.querySelectorAll('.moon-icon').forEach(icon => {
327
- icon.style.display = isLight ? 'none' : 'block';
328
- });
329
- });
330
- }
316
+ // Theme initialization and management is now handled
317
+ // automatically by Starlight.initTheme() in starlight.js
331
318
  </script>
332
319
  </body>
333
320
  </html>
@@ -5,6 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Quantum Store | Future of Fashion</title>
7
7
  <link rel="stylesheet" href="../../dist/quantum.min.css">
8
+ <script src="../../src/starlight.js"></script>
8
9
  <style>
9
10
  body { background-color: #f8fafc; color: #0f172a; transition: background-color 0.5s ease, color 0.5s ease; }
10
11
  body.dark-mode { background-color: #020617; color: #f1f5f9; }
@@ -111,7 +112,7 @@
111
112
  .moon-icon { display: none; }
112
113
  </style>
113
114
  </head>
114
- <body class="light-mode">
115
+ <body class="">
115
116
  <!-- Announcement Bar -->
116
117
  <div class="bg-slate-900 text-white py-3 text-center text-[10px] font-black tracking-[0.3em] uppercase">
117
118
  Interstellar Shipping Enabled • New Drops Every Sunday
@@ -313,24 +314,6 @@
313
314
  </footer>
314
315
 
315
316
  <script>
316
- // Theme Toggle Logic
317
- const themeBtn = document.getElementById('theme-btn');
318
- if (themeBtn) {
319
- themeBtn.addEventListener('click', () => {
320
- document.body.classList.toggle('light-mode');
321
- document.body.classList.toggle('dark-mode');
322
- const isDark = document.body.classList.contains('dark-mode');
323
-
324
- // Update all icons
325
- document.querySelectorAll('.sun-icon').forEach(icon => {
326
- icon.style.display = isDark ? 'none' : 'block';
327
- });
328
- document.querySelectorAll('.moon-icon').forEach(icon => {
329
- icon.style.display = isDark ? 'block' : 'none';
330
- });
331
- });
332
- }
333
-
334
317
  // Color Swatch Selection
335
318
  const swatches = document.querySelectorAll('.color-swatch');
336
319
  swatches.forEach(swatch => {
@@ -339,6 +322,9 @@
339
322
  swatch.classList.add('selected');
340
323
  });
341
324
  });
325
+
326
+ // Theme initialization and management is now handled
327
+ // automatically by Starlight.initTheme() in starlight.js
342
328
  </script>
343
329
  </body>
344
330
  </html>
@@ -5,6 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Starlight AI - Design System</title>
7
7
  <link rel="stylesheet" href="../dist/quantum.min.css">
8
+ <script src="../src/starlight.js"></script>
8
9
  <style>
9
10
  body {
10
11
  background: linear-gradient(135deg, #0a0a1a 0%, #1a1a3a 100%);
@@ -153,22 +154,8 @@
153
154
  </div>
154
155
 
155
156
  <script>
156
- // Theme Toggle Logic
157
- const themeBtn = document.getElementById('theme-btn');
158
- if (themeBtn) {
159
- themeBtn.addEventListener('click', () => {
160
- document.body.classList.toggle('light-mode');
161
- const isLight = document.body.classList.contains('light-mode');
162
-
163
- // Update all icons
164
- document.querySelectorAll('.sun-icon').forEach(icon => {
165
- icon.classList.toggle('hidden', !isLight);
166
- });
167
- document.querySelectorAll('.moon-icon').forEach(icon => {
168
- icon.classList.toggle('hidden', isLight);
169
- });
170
- });
171
- }
157
+ // Theme initialization and management is now handled
158
+ // automatically by Starlight.initTheme() in starlight.js
172
159
  </script>
173
160
  </body>
174
161
  </html>
@@ -0,0 +1,174 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Theme Auto-Detection Test</title>
7
+ <link rel="stylesheet" href="../dist/quantum.min.css">
8
+ <script src="../src/starlight.js"></script>
9
+ <style>
10
+ body { min-height: 100vh; }
11
+ .demo-section {
12
+ margin: 2rem auto;
13
+ max-width: 600px;
14
+ padding: 2rem;
15
+ }
16
+ .theme-controls {
17
+ display: flex;
18
+ gap: 1rem;
19
+ margin-bottom: 2rem;
20
+ justify-content: center;
21
+ }
22
+ .status-message {
23
+ background: var(--glass-bg);
24
+ border: 1px solid var(--glass-border);
25
+ border-radius: var(--radius-lg);
26
+ padding: 1rem;
27
+ margin-top: 1rem;
28
+ font-family: monospace;
29
+ font-size: 0.875rem;
30
+ }
31
+ </style>
32
+ </head>
33
+ <body>
34
+ <div class="starlight-stars" id="stars"></div>
35
+
36
+ <div class="demo-section starlight-card">
37
+ <h1>🎨 Theme Auto-Detection Test</h1>
38
+
39
+ <div class="theme-controls">
40
+ <button class="btn-starlight" onclick="setTheme('light')">☀️ Light</button>
41
+ <button class="btn-secondary" onclick="setTheme('dark')">🌙 Dark</button>
42
+ <button class="btn-outline" onclick="setTheme('auto')">🖥️ Follow System</button>
43
+ <button class="btn-ghost" onclick="toggleTheme()">🔄 Cycle</button>
44
+ <button class="btn-success" onclick="resetToAuto()">🔄 Reset to Auto</button>
45
+ <button class="btn-warning" onclick="forceRefresh()">🔃 Force Refresh</button>
46
+ </div>
47
+
48
+ <div class="status-message" id="status">
49
+ Loading theme information...
50
+ </div>
51
+
52
+ <h3>Instructions:</h3>
53
+ <ol>
54
+ <li>Click "Follow System" to enable automatic theme switching</li>
55
+ <li>Change your OS theme (macOS: System Settings → Appearance, Windows: Settings → Personalization → Colors)</li>
56
+ <li>The webpage should automatically switch to match your OS</li>
57
+ <li>Try "Cycle" to rotate through all available themes</li>
58
+ </ol>
59
+
60
+ <h3>Current State:</h3>
61
+ <div id="current-state"></div>
62
+ </div>
63
+
64
+ <script>
65
+ // Monitor theme changes
66
+ function updateStatus() {
67
+ const html = document.documentElement;
68
+ const currentTheme = html.getAttribute('data-theme');
69
+ const savedTheme = localStorage.getItem('theme');
70
+ const effectiveTheme = localStorage.getItem('theme-effective');
71
+ const systemPrefers = window.matchMedia('(prefers-color-scheme: light)').matches;
72
+
73
+ const statusEl = document.getElementById('status');
74
+ const stateEl = document.getElementById('current-state');
75
+
76
+ // Debug information
77
+ console.log('Theme Debug:', {
78
+ currentTheme,
79
+ savedTheme,
80
+ effectiveTheme,
81
+ systemPrefers,
82
+ htmlDataTheme: html.getAttribute('data-theme'),
83
+ htmlAttributes: Object.keys(html.attributes).map(attr => `${attr}: ${html.getAttribute(attr)}`)
84
+ });
85
+
86
+ statusEl.innerHTML = `
87
+ <strong>Current Theme:</strong> ${currentTheme || 'null'}<br>
88
+ <strong>Saved Preference:</strong> ${savedTheme || 'none'}<br>
89
+ <strong>Effective Theme:</strong> ${effectiveTheme || currentTheme || 'null'}<br>
90
+ <strong>System Prefers:</strong> ${systemPrefers ? 'light' : 'dark'}
91
+ `;
92
+
93
+ stateEl.innerHTML = `
94
+ <div class="starlight-card" style="padding: 1rem; margin-top: 1rem;">
95
+ <h4>🎯 Theme Configuration:</h4>
96
+ <p><strong>html[data-theme]:</strong> ${currentTheme}</p>
97
+ <p><strong>localStorage theme:</strong> ${savedTheme || 'null'}</p>
98
+ <p><strong>localStorage theme-effective:</strong> ${effectiveTheme || 'null'}</p>
99
+ <p><strong>System prefers-color-scheme:</strong> ${systemPrefers ? 'light' : 'dark'}</p>
100
+
101
+ <h5>💡 How this works:</h5>
102
+ <ul>
103
+ <li>If theme is "auto", the system preference is followed</li>
104
+ <li>System theme changes are automatically detected</li>
105
+ <li>Your manual choice is saved and restored</li>
106
+ </ul>
107
+ </div>
108
+ `;
109
+ }
110
+
111
+ // Listen for theme changes
112
+ window.addEventListener('themechange', (e) => {
113
+ console.log('Theme changed:', e.detail);
114
+ updateStatus();
115
+ });
116
+
117
+ // Listen for system theme changes
118
+ window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', (e) => {
119
+ console.log('System theme changed to:', e.matches ? 'light' : 'dark');
120
+ updateStatus();
121
+ });
122
+
123
+ // Initial update - wait for first themechange event or timeout
124
+ let themeInitialized = false;
125
+ const firstThemeChange = (e) => {
126
+ if (!themeInitialized) {
127
+ themeInitialized = true;
128
+ window.removeEventListener('themechange', firstThemeChange);
129
+ updateStatus();
130
+ }
131
+ };
132
+
133
+ window.addEventListener('themechange', firstThemeChange);
134
+
135
+ document.addEventListener('DOMContentLoaded', () => {
136
+ // Wait for Starlight to initialize, then check status
137
+ setTimeout(() => {
138
+ if (!themeInitialized) {
139
+ // If no themechange event fired yet, check current state
140
+ themeInitialized = true;
141
+ window.removeEventListener('themechange', firstThemeChange);
142
+ updateStatus();
143
+ }
144
+ }, 500); // Longer delay to ensure Starlight initialization
145
+ });
146
+
147
+ // Reset to auto function
148
+ function resetToAuto() {
149
+ console.log('Resetting to auto theme...');
150
+ if (typeof setTheme === 'function') {
151
+ setTheme('auto');
152
+ updateStatus();
153
+ } else {
154
+ console.error('setTheme function not available');
155
+ }
156
+ }
157
+
158
+ // Also update every 2 seconds to show real-time changes
159
+ setInterval(updateStatus, 2000);
160
+
161
+ // Force refresh function
162
+ function forceRefresh() {
163
+ console.log('Forcing theme refresh...');
164
+ // Manually trigger Starlight theme initialization
165
+ if (window.Starlight && window.Starlight.initTheme) {
166
+ window.Starlight.initTheme();
167
+ setTimeout(updateStatus, 100);
168
+ } else {
169
+ console.error('Starlight not available');
170
+ }
171
+ }
172
+ </script>
173
+ </body>
174
+ </html>
@@ -5,6 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Nebula Voyage | Starlight Travel</title>
7
7
  <link rel="stylesheet" href="../../dist/quantum.min.css">
8
+ <script src="../../src/starlight.js"></script>
8
9
  <style>
9
10
  body {
10
11
  background: radial-gradient(circle at top right, #08081a, #000);
@@ -246,22 +247,8 @@
246
247
  </footer>
247
248
 
248
249
  <script>
249
- // Theme Toggle Logic
250
- const themeBtn = document.getElementById('theme-btn');
251
- if (themeBtn) {
252
- themeBtn.addEventListener('click', () => {
253
- document.body.classList.toggle('light-mode');
254
- const isLight = document.body.classList.contains('light-mode');
255
-
256
- // Update all icons
257
- document.querySelectorAll('.sun-icon').forEach(icon => {
258
- icon.classList.toggle('hidden', !isLight);
259
- });
260
- document.querySelectorAll('.moon-icon').forEach(icon => {
261
- icon.classList.toggle('hidden', isLight);
262
- });
263
- });
264
- }
250
+ // Theme initialization and management is now handled
251
+ // automatically by Starlight.initTheme() in starlight.js
265
252
  </script>
266
253
  </body>
267
254
  </html>
@@ -4,6 +4,7 @@
4
4
  <meta charset="UTF-8">
5
5
  <title>Verification of Fixes</title>
6
6
  <link rel="stylesheet" href="../dist/quantum.min.css">
7
+ <script src="../src/starlight.js"></script>
7
8
  </head>
8
9
  <body class="p-10 space-y-8 bg-black text-white">
9
10
  <nav class="sticky top-0 z-50 glass p-4 mb-8">
@@ -4,7 +4,8 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Quantum CSS - Verify Presets</title>
7
- <link rel="stylesheet" href="../dist/quantum.css">
7
+ <link rel="stylesheet" href="../dist/quantum.min.css">
8
+ <script src="../src/starlight.js"></script>
8
9
  </head>
9
10
  <body class="bg-gray-50 p-12">
10
11
  <div class="max-w-4xl mx-auto space-y-12">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howssatoshi/quantumcss",
3
- "version": "1.4.3",
3
+ "version": "1.5.0",
4
4
  "description": "Advanced utility-first CSS framework with JIT generation and modern components",
5
5
  "main": "dist/quantum.min.css",
6
6
  "bin": {
@@ -46,5 +46,11 @@
46
46
  "dependencies": {
47
47
  "chokidar": "^5.0.0",
48
48
  "glob": "^13.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/js": "^9.39.2",
52
+ "eslint": "^9.39.2",
53
+ "stylelint": "^17.1.1",
54
+ "stylelint-config-standard": "^40.0.0"
49
55
  }
50
56
  }
package/src/generator.js CHANGED
@@ -58,7 +58,9 @@ function generateCSS(configPath) {
58
58
  while ((match = classAttrRegex.exec(content)) !== null) {
59
59
  match[1].split(/\s+/).forEach(cls => { if (cls) rawClasses.add(cls); });
60
60
  }
61
- } catch (e) {}
61
+ } catch {
62
+ // Ignore errors reading files
63
+ }
62
64
  });
63
65
 
64
66
  const utilities = new Set();
@@ -67,6 +69,15 @@ function generateCSS(configPath) {
67
69
  dark: new Set()
68
70
  };
69
71
 
72
+ /**
73
+ * Escapes a class name for use in a CSS selector
74
+ * @param {string} cls - The raw class name
75
+ * @returns {string} The escaped selector
76
+ */
77
+ const escapeSelector = (cls) => {
78
+ return cls.replace(/([:[\]/.\\])/g, '\\$1');
79
+ };
80
+
70
81
  const sideMap = {
71
82
  p: 'padding', pt: 'padding-top', pr: 'padding-right', pb: 'padding-bottom', pl: 'padding-left',
72
83
  px: ['padding-left', 'padding-right'], py: ['padding-top', 'padding-bottom'],
@@ -159,8 +170,8 @@ function generateCSS(configPath) {
159
170
  } else if (prefix === 'col' && cParts[1] === 'span') {
160
171
  property = 'grid-column'; value = `span ${cParts[2]} / span ${cParts[2]}`;
161
172
  } else if (prefix === 'space') {
162
- const amount = theme.spacing[cParts[2]] || `${parseInt(cParts[2]) * 0.25}rem`;
163
- const escaped = fullCls.replace(/([:[\/])/g, '\\$1');
173
+ const amount = theme.spacing[cParts[2]] || `${parseFloat(cParts[2]) * 0.25}rem`;
174
+ const escaped = escapeSelector(fullCls);
164
175
  customSelector = `.${escaped} > * + *`;
165
176
  property = cParts[1] === 'y' ? 'margin-top' : 'margin-left';
166
177
  value = isNeg ? `-${amount}` : amount;
@@ -169,12 +180,12 @@ function generateCSS(configPath) {
169
180
  if (valKey.startsWith('[') && valKey.endsWith(']')) value = valKey.slice(1, -1);
170
181
  else if (theme.borderRadius[valKey]) value = theme.borderRadius[valKey];
171
182
  else {
172
- const num = parseInt(valKey);
183
+ const num = parseFloat(valKey);
173
184
  value = isNaN(num) ? '0.375rem' : `${num * 0.125}rem`;
174
185
  }
175
186
  } else if (prefix === 'scale') {
176
187
  property = 'transform';
177
- value = `scale(${parseInt(valKey) / 100})`;
188
+ value = `scale(${parseFloat(valKey) / 100})`;
178
189
  } else if (prefix === 'transition') {
179
190
  property = 'transition-property';
180
191
  if (valKey === 'all') value = 'all';
@@ -193,8 +204,8 @@ function generateCSS(configPath) {
193
204
  property = sideMap[prefix];
194
205
  let v = valKey;
195
206
  if (v.startsWith('[') && v.endsWith(']')) v = v.slice(1, -1);
196
- else if (v.includes('/')) v = `${(parseInt(v.split('/')[0])/parseInt(v.split('/')[1])*100).toFixed(2)}%`;
197
- else v = theme.spacing[v] || v;
207
+ else if (v.includes('/')) v = `${(parseFloat(v.split('/')[0])/parseFloat(v.split('/')[1])*100).toFixed(2)}%`;
208
+ else v = theme.spacing[v] || (isNaN(parseFloat(v)) ? v : `${parseFloat(v) * 0.25}rem`);
198
209
  value = isNeg ? (Array.isArray(v) ? v.map(x => `-${x}`) : `-${v}`) : v;
199
210
  } else if (prefix === 'shadow') {
200
211
  if (theme.shadows[valKey]) { property = 'box-shadow'; value = theme.shadows[valKey]; }
@@ -235,7 +246,7 @@ function generateCSS(configPath) {
235
246
 
236
247
  merged.forEach(group => {
237
248
  const { breakpoint, variant, customSelector, rules } = group;
238
- const escapedFull = fullCls.replace(/([:[\/])/g, '\\$1');
249
+ const escapedFull = escapeSelector(fullCls);
239
250
  let selector = customSelector || `.${escapedFull}`;
240
251
  if (variant) { if (variant === 'group-hover') selector = `.group:hover ${selector}`; else selector += `:${variant}`}
241
252