actual-css 0.3.1 → 0.4.1

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.
Files changed (223) hide show
  1. package/CHANGELOG.md +109 -3
  2. package/README.md +35 -30
  3. package/dist/actual.css +66 -4958
  4. package/dist/actual.full.css +7548 -0
  5. package/dist/actual.full.js +1 -0
  6. package/dist/actual.full.min.css +1 -0
  7. package/dist/actual.js +1 -1
  8. package/dist/actual.min.css +1 -1
  9. package/docs/design-notes/app-ui.md +33 -0
  10. package/docs/design-notes/cascade-layer.md +34 -13
  11. package/docs/design-notes/column-layout.md +677 -0
  12. package/docs/design-notes/enhancement-contract.md +25 -0
  13. package/docs/design-notes/forced-colors.md +3 -0
  14. package/docs/design-notes/grid.md +785 -0
  15. package/docs/design-notes/naming.md +15 -0
  16. package/docs/design-notes/references.md +1 -1
  17. package/docs/design-notes/widget-primitives.md +1 -0
  18. package/docs/navigation.json +12 -2
  19. package/docs/pages/components/alert.md +21 -14
  20. package/docs/pages/components/app-navigation.md +34 -0
  21. package/docs/pages/components/aura.md +1 -1
  22. package/docs/pages/components/avatar.md +16 -6
  23. package/docs/pages/components/badge.md +9 -7
  24. package/docs/pages/components/button.md +46 -16
  25. package/docs/pages/components/card.md +17 -9
  26. package/docs/pages/components/chat.md +8 -8
  27. package/docs/pages/components/dialog.md +7 -7
  28. package/docs/pages/components/drawer.md +3 -3
  29. package/docs/pages/components/fab.md +9 -7
  30. package/docs/pages/components/indicator.md +52 -0
  31. package/docs/pages/components/list.md +62 -0
  32. package/docs/pages/components/navbar.md +15 -8
  33. package/docs/pages/components/pagination.md +4 -3
  34. package/docs/pages/components/rating.md +68 -0
  35. package/docs/pages/components/skeleton.md +4 -4
  36. package/docs/pages/components/spinner.md +5 -5
  37. package/docs/pages/components/steps.md +57 -0
  38. package/docs/pages/components/table.md +30 -5
  39. package/docs/pages/enhancements/flyout.md +15 -15
  40. package/docs/pages/enhancements/overview.md +1 -1
  41. package/docs/pages/enhancements/scrollspy.md +6 -6
  42. package/docs/pages/enhancements/status-bar.md +5 -1
  43. package/docs/pages/enhancements/tabs.md +7 -8
  44. package/docs/pages/enhancements/tooltip.md +1 -1
  45. package/docs/pages/examples/overview.md +47 -22
  46. package/docs/pages/forms/choice-cards.md +11 -6
  47. package/docs/pages/forms/controls.md +1 -1
  48. package/docs/pages/forms/detached-actions.md +1 -1
  49. package/docs/pages/forms/floating-field.md +10 -5
  50. package/docs/pages/forms/form-actions.md +1 -1
  51. package/docs/pages/forms/input-policy.md +1 -1
  52. package/docs/pages/forms/joined-controls.md +4 -4
  53. package/docs/pages/forms/labels.md +6 -6
  54. package/docs/pages/forms/otp.md +33 -1
  55. package/docs/pages/forms/overview.md +19 -19
  56. package/docs/pages/forms/password.md +1 -1
  57. package/docs/pages/forms/range.md +1 -1
  58. package/docs/pages/forms/states-and-sizes.md +44 -48
  59. package/docs/pages/forms/sticky-actions.md +3 -3
  60. package/docs/pages/forms/switch.md +110 -2
  61. package/docs/pages/forms/validation.md +9 -9
  62. package/docs/pages/foundations/philosophy.md +1 -1
  63. package/docs/pages/foundations/theming.md +17 -2
  64. package/docs/pages/foundations/tokens.md +22 -10
  65. package/docs/pages/foundations/typography.md +50 -20
  66. package/docs/pages/guides/bootstrap.md +11 -11
  67. package/docs/pages/guides/integrating-actual-css.md +192 -0
  68. package/docs/pages/guides/introduction.md +23 -26
  69. package/docs/pages/guides/modular-import.md +173 -0
  70. package/docs/pages/guides/progressive-enhancement.md +81 -78
  71. package/docs/pages/guides/tailwind.md +6 -8
  72. package/docs/pages/layout/app-layout.md +57 -0
  73. package/docs/pages/layout/app-shell.md +28 -7
  74. package/docs/pages/layout/center.md +12 -1
  75. package/docs/pages/layout/choosing.md +54 -0
  76. package/docs/pages/layout/column-layout.md +206 -0
  77. package/docs/pages/layout/frame.md +6 -1
  78. package/docs/pages/layout/grid.md +133 -102
  79. package/docs/pages/layout/media.md +1 -1
  80. package/docs/pages/layout/overview.md +15 -2
  81. package/docs/pages/layout/scroll-snap.md +2 -2
  82. package/docs/pages/layout/scroll-target.md +45 -0
  83. package/docs/pages/layout/sidebar-layout.md +38 -18
  84. package/docs/pages/layout/stack.md +70 -1
  85. package/docs/pages/layout/switcher.md +10 -26
  86. package/docs/pages/layout/topbar.md +2 -2
  87. package/docs/pages/patterns/actions.md +1 -1
  88. package/docs/pages/patterns/code-block.md +1 -1
  89. package/docs/pages/patterns/exclusive-accordion.md +1 -1
  90. package/docs/pages/patterns/hierarchical-nav.md +1 -1
  91. package/docs/pages/patterns/icon-input.md +1 -1
  92. package/docs/pages/patterns/icon-slot.md +61 -0
  93. package/docs/pages/patterns/intent-nesting.md +1 -1
  94. package/docs/pages/patterns/navigation-list.md +1 -1
  95. package/docs/pages/patterns/overview.md +1 -1
  96. package/docs/pages/patterns/structured-lists.md +38 -40
  97. package/docs/pages/utilities/accessibility.md +3 -3
  98. package/docs/pages/utilities/flex.md +15 -15
  99. package/docs/pages/utilities/links.md +4 -4
  100. package/docs/pages/utilities/list.md +8 -8
  101. package/docs/pages/utilities/overflow.md +8 -8
  102. package/docs/pages/utilities/overview.md +1 -1
  103. package/docs/pages/utilities/shape.md +3 -3
  104. package/docs/pages/utilities/sizing-wrapping.md +23 -16
  105. package/docs/pages/utilities/spacing.md +20 -20
  106. package/docs/pages/utilities/text.md +10 -10
  107. package/llms.txt +46 -4
  108. package/package.json +43 -13
  109. package/scripts/reserved-classes.json +278 -0
  110. package/src/cli/actual-css.js +133 -0
  111. package/src/css/actual.css +1 -31
  112. package/src/css/actual.full.css +11 -2
  113. package/src/css/components/alert.css +17 -4
  114. package/src/css/components/app-nav.css +76 -0
  115. package/src/css/components/avatar.css +4 -0
  116. package/src/css/components/badge.css +19 -19
  117. package/src/css/components/breadcrumb.css +1 -1
  118. package/src/css/components/button.css +20 -32
  119. package/src/css/components/card.css +34 -9
  120. package/src/css/{optional → components}/chat.css +0 -6
  121. package/src/css/components/drawer.css +24 -5
  122. package/src/css/{optional → components}/fab.css +1 -7
  123. package/src/css/components/flyout.css +5 -0
  124. package/src/css/components/index.css +37 -27
  125. package/src/css/components/indicator.css +47 -0
  126. package/src/css/components/join.css +8 -1
  127. package/src/css/components/key.css +2 -0
  128. package/src/css/components/list.css +83 -0
  129. package/src/css/components/menu.css +11 -8
  130. package/src/css/components/meter.css +4 -1
  131. package/src/css/components/modal.css +5 -17
  132. package/src/css/components/navbar.css +6 -15
  133. package/src/css/components/pagination.css +11 -0
  134. package/src/css/components/rating.css +101 -0
  135. package/src/css/components/spinner.css +5 -0
  136. package/src/css/components/status-bar.css +5 -0
  137. package/src/css/components/steps.css +122 -0
  138. package/src/css/components/surface.css +5 -0
  139. package/src/css/components/tab.css +20 -22
  140. package/src/css/components/table.css +6 -0
  141. package/src/css/components/tooltip.css +3 -7
  142. package/src/css/{focus.css → core/focus.css} +0 -8
  143. package/src/css/core/index.css +9 -0
  144. package/src/css/core/print.css +18 -0
  145. package/src/css/{theme.css → core/theme.css} +19 -19
  146. package/src/css/{tokens.css → core/tokens.css} +16 -4
  147. package/src/css/{variants.css → core/variants.css} +3 -2
  148. package/src/css/{optional → effects}/aura.css +1 -10
  149. package/src/css/effects/index.css +2 -0
  150. package/src/css/forms/base.css +8 -0
  151. package/src/css/forms/choice-card.css +7 -15
  152. package/src/css/forms/choice.css +49 -37
  153. package/src/css/forms/control.css +4 -1
  154. package/src/css/forms/custom-select.css +2 -0
  155. package/src/css/{optional → forms}/floating-field.css +25 -7
  156. package/src/css/forms/index.css +10 -13
  157. package/src/css/forms/native.css +15 -2
  158. package/src/css/{optional → forms}/otp.css +25 -16
  159. package/src/css/forms/range.css +14 -4
  160. package/src/css/forms/switch.css +31 -29
  161. package/src/css/layout/app-layout.css +102 -0
  162. package/src/css/layout/app-shell.css +6 -0
  163. package/src/css/layout/center.css +14 -0
  164. package/src/css/layout/cluster.css +21 -0
  165. package/src/css/layout/column-layout.css +144 -0
  166. package/src/css/layout/container-query.css +15 -0
  167. package/src/css/layout/frame.css +19 -0
  168. package/src/css/layout/grid.css +155 -0
  169. package/src/css/layout/index.css +44 -0
  170. package/src/css/layout/measure.css +9 -0
  171. package/src/css/layout/media.css +10 -0
  172. package/src/css/layout/scroll-target.css +15 -0
  173. package/src/css/layout/scroller.css +41 -0
  174. package/src/css/layout/sidebar-layout.css +52 -0
  175. package/src/css/layout/stack.css +21 -0
  176. package/src/css/layout/switcher.css +23 -0
  177. package/src/css/{optional/layout-extra.css → layout/topbar.css} +0 -20
  178. package/src/css/typography/index.css +5 -0
  179. package/src/css/typography/lead.css +7 -0
  180. package/src/css/{components → typography}/overline.css +2 -2
  181. package/src/css/{prose.css → typography/prose.css} +57 -38
  182. package/src/css/{utilities.css → utilities/base.css} +2 -0
  183. package/src/css/utilities/index.css +3 -0
  184. package/src/js/escape.js +36 -0
  185. package/src/js/flyout.js +22 -12
  186. package/src/js/full.js +26 -0
  187. package/src/js/index.js +5 -21
  188. package/src/js/surface.js +1 -31
  189. package/src/js/tooltip.js +48 -19
  190. package/src/js/validation.js +1 -1
  191. package/src/tooling/css-bundle.js +308 -0
  192. package/dist/actual-themes.min.css +0 -1
  193. package/dist/optional.css +0 -1048
  194. package/dist/optional.min.css +0 -1
  195. package/docs/pages/guides/optional-components.md +0 -33
  196. package/docs/pages/layout/list.md +0 -41
  197. package/src/css/grid.css +0 -109
  198. package/src/css/layout.css +0 -123
  199. package/src/css/optional/index.css +0 -10
  200. package/src/css/optional/scroller.css +0 -63
  201. package/src/css/print.css +0 -47
  202. package/src/css/themes/corporate.css +0 -60
  203. package/src/css/themes/cyberpunk.css +0 -60
  204. package/src/css/themes/dim.css +0 -33
  205. package/src/css/themes/edge.css +0 -117
  206. package/src/css/themes/forest.css +0 -65
  207. package/src/css/themes/gradient.css +0 -97
  208. package/src/css/themes/index.css +0 -47
  209. package/src/css/themes/indigo.css +0 -33
  210. package/src/css/themes/lavender.css +0 -66
  211. package/src/css/themes/mono.css +0 -60
  212. package/src/css/themes/neon.css +0 -66
  213. package/src/css/themes/ocean.css +0 -60
  214. package/src/css/themes/petrol.css +0 -64
  215. package/src/css/themes/spruce.css +0 -64
  216. package/src/css/themes/square.css +0 -65
  217. package/src/css/themes/sunset.css +0 -66
  218. /package/src/css/{base.css → core/base.css} +0 -0
  219. /package/src/css/{intents.css → core/intents.css} +0 -0
  220. /package/src/css/{reset.css → core/reset.css} +0 -0
  221. /package/src/css/{optional → layout}/scroll-snap.css +0 -0
  222. /package/src/css/{optional/typography-fluid.css → typography/fluid.css} +0 -0
  223. /package/src/css/{optional/utilities-extra.css → utilities/extra.css} +0 -0
package/src/js/tooltip.js CHANGED
@@ -28,6 +28,7 @@
28
28
  */
29
29
 
30
30
  import enhance from "./enhance.js";
31
+ import { registerEscapeDismissal } from "./escape.js";
31
32
  import { autoUpdate, reposition } from "./floating.js";
32
33
  import { CLASSES } from "./selectors.js";
33
34
 
@@ -40,6 +41,8 @@ const HIDE_DELAY_MS = 100;
40
41
  * @property {Element | null} activeRef Trigger that last showed this tip.
41
42
  * @property {AbortController} controller Tip-level event listener cleanup.
42
43
  * @property {(() => void) | null} stopTracking Floating-position cleanup while visible.
44
+ * @property {(() => void) | null} unregisterEscape Escape-stack cleanup while visible and dismissable.
45
+ * @property {Element | null} escapeRef Trigger represented by the current Escape-stack entry.
43
46
  * @property {boolean} generated True when the tip was created from data-tooltip text.
44
47
  * @property {{ parent: Node, next: ChildNode | null } | null} mount Original DOM position for explicit tips moved into a dialog/body root.
45
48
  * @property {ReturnType<typeof setTimeout> | null} timer Pending delayed show.
@@ -140,15 +143,27 @@ function repositionTip(ref, tip) {
140
143
  });
141
144
  }
142
145
 
146
+ function updateTrackedTip(tip, state) {
147
+ const ref = state.activeRef;
148
+ if (!ref) return;
149
+
150
+ const alwaysVisible = isAlwaysVisible(ref);
151
+ // Reveal before measuring: an always-visible tooltip may currently be
152
+ // hidden because its trigger was outside the positioning boundary.
153
+ if (alwaysVisible) tip.hidden = false;
154
+ if (tip.hidden || repositionTip(ref, tip)) {
155
+ syncEscapeDismissal(tip, state);
156
+ return;
157
+ }
158
+
159
+ if (alwaysVisible) tip.hidden = true;
160
+ else hideTip(tip, true);
161
+ }
162
+
143
163
  function startTracking(tip, state) {
144
164
  if (state.stopTracking) return;
145
165
 
146
- state.stopTracking = autoUpdate(tip, () => {
147
- if (isAlwaysVisible(state.activeRef)) tip.hidden = false;
148
- if (state.activeRef && !tip.hidden && !repositionTip(state.activeRef, tip)) {
149
- hideTip(tip, true);
150
- }
151
- });
166
+ state.stopTracking = autoUpdate(tip, () => updateTrackedTip(tip, state));
152
167
  }
153
168
 
154
169
  function stopTracking(state) {
@@ -156,6 +171,22 @@ function stopTracking(state) {
156
171
  if (state) state.stopTracking = null;
157
172
  }
158
173
 
174
+ function syncEscapeDismissal(tip, state) {
175
+ const ref = state.activeRef;
176
+ const dismissable = !tip.hidden && !isAlwaysVisible(ref);
177
+ // Position tracking calls this after every successful update. Preserve the
178
+ // current entry so a scroll/resize tick cannot reorder the LIFO stack.
179
+ if (dismissable && state.unregisterEscape && state.escapeRef === ref) return;
180
+
181
+ state.unregisterEscape?.();
182
+ state.unregisterEscape = null;
183
+ state.escapeRef = null;
184
+ if (!dismissable) return;
185
+
186
+ state.unregisterEscape = registerEscapeDismissal(tip, () => hideTip(tip, true));
187
+ state.escapeRef = ref;
188
+ }
189
+
159
190
  function hideTip(tip, force = false) {
160
191
  const state = tipStates.get(tip);
161
192
  if (!force && isAlwaysVisible(state?.activeRef)) return;
@@ -168,6 +199,11 @@ function hideTip(tip, force = false) {
168
199
  state.hideTimer = null;
169
200
  }
170
201
  tip.hidden = true;
202
+ state?.unregisterEscape?.();
203
+ if (state) {
204
+ state.unregisterEscape = null;
205
+ state.escapeRef = null;
206
+ }
171
207
  stopTracking(state);
172
208
  }
173
209
 
@@ -203,6 +239,8 @@ function wireTip(tip, options = {}) {
203
239
  activeRef: null,
204
240
  controller,
205
241
  stopTracking: null,
242
+ unregisterEscape: null,
243
+ escapeRef: null,
206
244
  generated: options.generated === true,
207
245
  mount: options.mount || null,
208
246
  timer: null,
@@ -229,17 +267,6 @@ function wireTip(tip, options = {}) {
229
267
  { signal: controller.signal },
230
268
  );
231
269
 
232
- tip.ownerDocument.addEventListener(
233
- "keydown",
234
- (event) => {
235
- if (event.key !== "Escape" || event.ctrlKey || event.altKey || event.shiftKey) return;
236
- if (tip.hidden) return;
237
- event.preventDefault();
238
- hideTip(tip);
239
- },
240
- { signal: controller.signal },
241
- );
242
-
243
270
  tipStates.set(tip, state);
244
271
  return state;
245
272
  }
@@ -331,7 +358,8 @@ function ensureTip(trigger) {
331
358
  }
332
359
 
333
360
  function cleanupTrigger(trigger) {
334
- if (trigger.isConnected) return;
361
+ // The enhancement runtime owns the root-membership decision; a trigger may
362
+ // still be connected after moving beyond that lifecycle boundary.
335
363
  triggerStates.get(trigger)?.cleanup?.();
336
364
  }
337
365
 
@@ -340,13 +368,14 @@ function show(tip, ref, immediate = false) {
340
368
  if (!state) return;
341
369
 
342
370
  state.activeRef = ref;
371
+ syncEscapeDismissal(tip, state);
343
372
  clearHide(tip);
344
373
  if (state.timer) clearTimeout(state.timer);
345
374
  const reveal = () => {
346
375
  state.timer = null;
347
376
  tip.hidden = false;
348
- repositionTip(ref, tip);
349
377
  startTracking(tip, state);
378
+ updateTrackedTip(tip, state);
350
379
  };
351
380
 
352
381
  if (immediate) reveal();
@@ -391,7 +391,7 @@ export class FormValidator {
391
391
  static setErrors(form, errors) {
392
392
  if (!isFormElement(form)) return;
393
393
  for (const [name, message] of Object.entries(errors)) {
394
- const found = form.elements[name];
394
+ const found = form.elements.namedItem(name);
395
395
  if (!found) continue;
396
396
  if (found instanceof Element) {
397
397
  FormValidator.setFieldError(found, message);
@@ -0,0 +1,308 @@
1
+ /*
2
+ * Shared CSS bundling helpers: inline @import chains, strip comments, minify.
3
+ * Used by build-dist.js (package artifacts), build-themes.js (demo asset),
4
+ * and the public CLI.
5
+ */
6
+ import { existsSync } from "node:fs";
7
+ import { readFile } from "node:fs/promises";
8
+ import { createRequire } from "node:module";
9
+ import { dirname, resolve } from "node:path";
10
+ import { pathToFileURL } from "node:url";
11
+
12
+ const IMPORT_PREFIX_RE = /^\s*@import\b/;
13
+ // Two or more characters before the colon: a Windows drive letter (D:/app.css)
14
+ // is a local path, not a URL scheme.
15
+ const EXTERNAL_SPECIFIER_RE = /^(?:[a-z][a-z0-9+.-]+:|\/\/|\/)/i;
16
+ const CHARSET_RE = /^\s*@charset\s+["'][^"']*["']\s*;[^\n]*\n?/;
17
+ const LAYER_TRAILER_RE = /^layer(?:\(\s*([A-Za-z_-][\w-]*(?:\.[A-Za-z_-][\w-]*)*)?\s*\))?$/;
18
+ const RESOLVER_STUB = "__actual_css_resolve__.js";
19
+
20
+ function skipWhitespace(source, index) {
21
+ let next = index;
22
+
23
+ while (next < source.length && /\s/.test(source[next])) {
24
+ next += 1;
25
+ }
26
+
27
+ return next;
28
+ }
29
+
30
+ function readQuoted(source, index) {
31
+ const quote = source[index];
32
+
33
+ if (quote !== '"' && quote !== "'") return null;
34
+
35
+ let value = "";
36
+
37
+ for (let cursor = index + 1; cursor < source.length; cursor += 1) {
38
+ const char = source[cursor];
39
+
40
+ if (char === "\\") {
41
+ const next = source[cursor + 1];
42
+ if (next === undefined) return null;
43
+ value += char;
44
+ value += next;
45
+ cursor += 1;
46
+ continue;
47
+ }
48
+
49
+ if (char === quote) {
50
+ return { value, index: cursor + 1 };
51
+ }
52
+
53
+ value += char;
54
+ }
55
+
56
+ return null;
57
+ }
58
+
59
+ function parseImportLine(line) {
60
+ let index = skipWhitespace(line, 0);
61
+
62
+ if (!line.startsWith("@import", index)) return null;
63
+
64
+ index += "@import".length;
65
+ index = skipWhitespace(line, index);
66
+
67
+ let parsed = null;
68
+
69
+ if (line.startsWith("url(", index)) {
70
+ index += 4;
71
+ index = skipWhitespace(line, index);
72
+
73
+ if (line[index] === '"' || line[index] === "'") {
74
+ parsed = readQuoted(line, index);
75
+ if (!parsed) return null;
76
+ index = parsed.index;
77
+ } else {
78
+ const end = line.indexOf(")", index);
79
+ if (end === -1) return null;
80
+ parsed = { value: line.slice(index, end).trim(), index: end };
81
+ index = end;
82
+ }
83
+
84
+ index = skipWhitespace(line, index);
85
+ if (line[index] !== ")") return null;
86
+ index += 1;
87
+ } else {
88
+ parsed = readQuoted(line, index);
89
+ if (!parsed) return null;
90
+ index = parsed.index;
91
+ }
92
+
93
+ const rest = line.slice(index);
94
+
95
+ // A comment opened on the import line and closed further down cannot be
96
+ // reasoned about line by line: treat the whole statement as unsupported.
97
+ if (hasUnterminatedComment(rest)) return null;
98
+
99
+ const trailer = stripComments(rest).replace(/;\s*$/, "").trim();
100
+
101
+ return {
102
+ specifier: parsed.value,
103
+ trailer,
104
+ };
105
+ }
106
+
107
+ function hasUnterminatedComment(text) {
108
+ const open = text.lastIndexOf("/*");
109
+ return open !== -1 && text.indexOf("*/", open + 2) === -1;
110
+ }
111
+
112
+ function isExternalImportSpecifier(specifier) {
113
+ return EXTERNAL_SPECIFIER_RE.test(specifier);
114
+ }
115
+
116
+ function resolveImportTarget(file, specifier) {
117
+ const dir = dirname(file);
118
+
119
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
120
+ return resolve(dir, specifier);
121
+ }
122
+
123
+ // Plain CSS reads "app.css" as a relative URL, so a neighbouring file wins
124
+ // over package resolution; everything else goes through the exports map.
125
+ const local = resolve(dir, specifier);
126
+ if (existsSync(local)) return local;
127
+
128
+ try {
129
+ const require = createRequire(pathToFileURL(resolve(dir, RESOLVER_STUB)));
130
+ return require.resolve(specifier);
131
+ } catch {
132
+ throw new Error(`Cannot resolve CSS @import "${specifier}" from ${file}`);
133
+ }
134
+ }
135
+
136
+ function preservedImportStatement(line) {
137
+ const statement = line.trim();
138
+ return statement.endsWith(";") ? statement : `${statement};`;
139
+ }
140
+
141
+ // `layer`, `layer()` and `layer(name)` are the only modifiers this bundler
142
+ // understands: dropping the @import means re-stating the layer, which an
143
+ // @layer block does exactly. Conditional modifiers stay out of scope.
144
+ function parseLayerTrailer(trailer) {
145
+ const match = trailer.match(LAYER_TRAILER_RE);
146
+ return match ? { name: match[1] ?? "" } : null;
147
+ }
148
+
149
+ function wrapInLayer(css, name) {
150
+ const body = css
151
+ .split("\n")
152
+ .map((line) => (line.trim() === "" ? "" : ` ${line}`))
153
+ .join("\n")
154
+ .replace(/\n+$/, "");
155
+ const head = name ? `@layer ${name} {` : "@layer {";
156
+
157
+ return `${head}\n${body}\n}\n`;
158
+ }
159
+
160
+ async function inlineFile(path, stack, preserved, layered) {
161
+ if (stack.includes(path)) {
162
+ const chain = [...stack, path].map((item) => item.replace(/.*[/\\]/, ""));
163
+ throw new Error(`Circular CSS import: ${chain.join(" -> ")}`);
164
+ }
165
+
166
+ const css = await readFile(path, "utf8");
167
+ const nextStack = [...stack, path];
168
+ const chunks = [];
169
+
170
+ for (const line of css.split(/\r?\n/)) {
171
+ const parsed = parseImportLine(line);
172
+
173
+ if (!parsed) {
174
+ if (IMPORT_PREFIX_RE.test(line)) {
175
+ throw new Error(`Unsupported CSS @import syntax in ${path}: ${line.trim()}`);
176
+ }
177
+ chunks.push(line);
178
+ continue;
179
+ }
180
+
181
+ if (isExternalImportSpecifier(parsed.specifier)) {
182
+ // Hoisting is what keeps a surviving @import valid, but hoisting it out
183
+ // of a layer would silently move its rules to another layer.
184
+ if (layered) {
185
+ throw new Error(
186
+ `Remote CSS @import inside a layered import cannot be hoisted in ${path}: ${line.trim()}`,
187
+ );
188
+ }
189
+
190
+ const statement = preservedImportStatement(line);
191
+ if (!preserved.includes(statement)) preserved.push(statement);
192
+ continue;
193
+ }
194
+
195
+ const layer = parsed.trailer ? parseLayerTrailer(parsed.trailer) : null;
196
+
197
+ if (parsed.trailer && !layer) {
198
+ throw new Error(`Unsupported CSS @import modifiers in ${path}: ${line.trim()}`);
199
+ }
200
+
201
+ const target = resolveImportTarget(path, parsed.specifier);
202
+ const inlined = await inlineFile(target, nextStack, preserved, layered || layer !== null);
203
+ chunks.push(layer ? wrapInLayer(inlined, layer.name) : inlined);
204
+ }
205
+
206
+ return `${chunks.join("\n").trim()}\n`;
207
+ }
208
+
209
+ export async function inlineImports(file) {
210
+ const preserved = [];
211
+ const body = await inlineFile(resolve(file), [], preserved, false);
212
+
213
+ if (preserved.length === 0) return body;
214
+
215
+ // An @import that survives inlining must still precede every rule, so the
216
+ // preserved statements move to the top, after @charset if the entry has one.
217
+ const charset = body.match(CHARSET_RE);
218
+ const head = charset ? `${charset[0].trimEnd()}\n` : "";
219
+ const rest = (charset ? body.slice(charset[0].length) : body).replace(/^\n+/, "");
220
+
221
+ return `${head}${preserved.join("\n")}\n\n${rest}`;
222
+ }
223
+
224
+ export function stripComments(css) {
225
+ let out = "";
226
+ let quote = "";
227
+
228
+ for (let i = 0; i < css.length; i += 1) {
229
+ const char = css[i];
230
+ const next = css[i + 1];
231
+
232
+ if (quote) {
233
+ out += char;
234
+ if (char === "\\") {
235
+ out += next ?? "";
236
+ i += 1;
237
+ } else if (char === quote) {
238
+ quote = "";
239
+ }
240
+ continue;
241
+ }
242
+
243
+ if (char === '"' || char === "'") {
244
+ quote = char;
245
+ out += char;
246
+ continue;
247
+ }
248
+
249
+ if (char === "/" && next === "*") {
250
+ i += 2;
251
+ while (i < css.length && !(css[i] === "*" && css[i + 1] === "/")) i += 1;
252
+ i += 1;
253
+ continue;
254
+ }
255
+
256
+ out += char;
257
+ }
258
+
259
+ return out;
260
+ }
261
+
262
+ export function minifyCss(css) {
263
+ const source = stripComments(css).trim();
264
+ let out = "";
265
+ let quote = "";
266
+ let previousWasSpace = false;
267
+
268
+ for (let i = 0; i < source.length; i += 1) {
269
+ const char = source[i];
270
+ const next = source[i + 1];
271
+
272
+ if (quote) {
273
+ out += char;
274
+ if (char === "\\") {
275
+ out += next ?? "";
276
+ i += 1;
277
+ } else if (char === quote) {
278
+ quote = "";
279
+ }
280
+ previousWasSpace = false;
281
+ continue;
282
+ }
283
+
284
+ if (/\s/.test(char)) {
285
+ if (!previousWasSpace) out += " ";
286
+ previousWasSpace = true;
287
+ continue;
288
+ }
289
+
290
+ out += char;
291
+ previousWasSpace = false;
292
+ }
293
+
294
+ return `${out.trim()}\n`;
295
+ }
296
+
297
+ export function bundledCssIssues(css) {
298
+ const issues = [];
299
+
300
+ if (css.includes("@import")) issues.push("contains unresolved @import");
301
+ if (css.includes("--lightningcss")) issues.push("contains --lightningcss-* variables");
302
+ if (/color-mix\(in srgb/.test(css)) issues.push("contains color-mix(in srgb, ...)");
303
+ if (/@media[^{]*\(\s*width\s*[<>]=/.test(css)) {
304
+ issues.push("contains transpiled media query range syntax");
305
+ }
306
+
307
+ return issues;
308
+ }
@@ -1 +0,0 @@
1
- [data-theme="indigo"] { color-scheme: dark; --primary: hsl(220 90% 66%); --primary-fg: hsl(222 30% 8%); --secondary: hsl(270 65% 70%); --secondary-fg: hsl(270 30% 10%); --success: hsl(145 55% 54%); --success-fg: hsl(145 40% 8%); --warning: hsl(38 92% 62%); --warning-fg: hsl(35 35% 10%); --danger: hsl(0 75% 66%); --danger-fg: hsl(0 35% 10%); --neutral: hsl(220 10% 72%); --neutral-fg: hsl(222 24% 8%); --surface: hsl(222 24% 8%); --surface-raised: hsl(222 22% 11%); --surface-subtle: hsl(222 18% 15%); --surface-solid: hsl(0 0% 96%); --text: hsl(0 0% 96%); --text-muted: hsl(220 10% 68%); --text-subtle: hsl(220 8% 55%); --border: hsl(222 14% 24%); --focus: var(--primary); --focus-ring: hsl(220 90% 66% / 0.28); --hover-overlay: hsl(0 0% 100% / 0.06); } [data-theme="dim"] { color-scheme: dark; --primary: hsl(210 92% 70%); --primary-fg: hsl(218 28% 10%); --secondary: hsl(270 70% 74%); --secondary-fg: hsl(270 30% 10%); --success: hsl(150 56% 58%); --success-fg: hsl(150 38% 9%); --warning: hsl(40 92% 64%); --warning-fg: hsl(35 35% 10%); --danger: hsl(0 74% 68%); --danger-fg: hsl(0 35% 10%); --neutral: hsl(220 10% 72%); --neutral-fg: hsl(220 24% 10%); --surface: hsl(220 20% 13%); --surface-raised: hsl(220 18% 16%); --surface-subtle: hsl(220 16% 20%); --surface-solid: hsl(0 0% 96%); --text: hsl(220 18% 94%); --text-muted: hsl(220 10% 70%); --text-subtle: hsl(220 8% 58%); --border: hsl(220 12% 28%); --focus: var(--primary); --focus-ring: hsl(210 92% 70% / 0.28); --hover-overlay: hsl(0 0% 100% / 0.06); } [data-theme="corporate"] { color-scheme: light dark; --primary: hsl(214 82% 44%); --primary-fg: white; --secondary: hsl(250 45% 48%); --secondary-fg: white; --success: hsl(152 62% 32%); --success-fg: white; --warning: hsl(40 94% 48%); --warning-fg: hsl(35 30% 12%); --danger: hsl(355 70% 50%); --danger-fg: white; --neutral: hsl(220 12% 42%); --neutral-fg: white; --surface: hsl(0 0% 100%); --surface-raised: hsl(214 30% 98%); --surface-subtle: hsl(214 35% 95%); --surface-solid: hsl(220 24% 12%); --text: hsl(220 24% 12%); --text-muted: hsl(220 10% 42%); --text-subtle: hsl(220 8% 55%); --border: hsl(220 16% 86%); --focus: var(--primary); --focus-ring: hsl(214 82% 44% / 0.22); } @supports (color: light-dark(red, blue)) { [data-theme="corporate"] { --primary: light-dark(hsl(214 82% 44%), hsl(214 75% 80%)); --primary-fg: light-dark(white, hsl(214 30% 12%)); --secondary: light-dark(hsl(250 45% 48%), hsl(250 53% 80%)); --secondary-fg: light-dark(white, hsl(250 30% 12%)); --success: light-dark(hsl(152 62% 32%), hsl(152 70% 80%)); --success-fg: light-dark(white, hsl(152 30% 12%)); --warning: light-dark(hsl(40 94% 48%), hsl(40 75% 80%)); --warning-fg: light-dark(hsl(35 30% 12%), hsl(40 30% 12%)); --danger: light-dark(hsl(355 70% 50%), hsl(355 75% 80%)); --danger-fg: light-dark(white, hsl(355 30% 12%)); --neutral: light-dark(hsl(220 12% 42%), hsl(220 20% 80%)); --neutral-fg: light-dark(white, hsl(220 30% 12%)); --surface: light-dark(hsl(0 0% 100%), hsl(214 18% 8%)); --surface-raised: light-dark(hsl(214 30% 98%), hsl(214 16% 11%)); --surface-subtle: light-dark(hsl(214 35% 95%), hsl(214 14% 16%)); --surface-solid: light-dark(hsl(220 24% 12%), hsl(0 0% 96%)); --text: light-dark(hsl(220 24% 12%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(220 10% 42%), hsl(214 8% 68%)); --text-subtle: light-dark(hsl(220 8% 55%), hsl(214 7% 55%)); --border: light-dark(hsl(220 16% 86%), hsl(214 12% 24%)); --focus-ring: light-dark(hsl(214 82% 44% / 0.22), hsl(214 75% 80% / 0.28)); --hover-overlay: light-dark(hsl(220 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(220 30% 10%), hsl(0 0% 0%)); } } [data-theme="forest"] { color-scheme: light dark; --primary: hsl(175 48% 26%); --primary-fg: white; --secondary: hsl(82 42% 34%); --secondary-fg: white; --success: hsl(148 58% 32%); --success-fg: white; --warning: hsl(42 88% 48%); --warning-fg: hsl(35 35% 12%); --danger: hsl(4 62% 48%); --danger-fg: white; --neutral: hsl(120 10% 34%); --neutral-fg: white; --surface: hsl(48 36% 96%); --surface-raised: hsl(45 36% 98%); --surface-subtle: hsl(112 26% 91%); --surface-solid: hsl(130 22% 15%); --text: hsl(130 20% 14%); --text-muted: hsl(125 10% 38%); --text-subtle: hsl(125 8% 50%); --border: hsl(105 18% 78%); --focus: var(--primary); --focus-ring: hsl(175 48% 26% / 0.22); --radius: 0.75rem; --radius-sm: 0.5rem; --radius-lg: 1.25rem; } @supports (color: light-dark(red, blue)) { [data-theme="forest"] { --primary: light-dark(hsl(175 48% 26%), hsl(175 45% 76%)); --primary-fg: light-dark(white, hsl(175 30% 12%)); --secondary: light-dark(hsl(82 42% 34%), hsl(82 50% 80%)); --secondary-fg: light-dark(white, hsl(82 30% 12%)); --success: light-dark(hsl(148 58% 32%), hsl(148 66% 80%)); --success-fg: light-dark(white, hsl(148 30% 12%)); --warning: light-dark(hsl(42 88% 48%), hsl(42 75% 80%)); --warning-fg: light-dark(hsl(35 35% 12%), hsl(42 30% 12%)); --danger: light-dark(hsl(4 62% 48%), hsl(4 70% 80%)); --danger-fg: light-dark(white, hsl(4 30% 12%)); --neutral: light-dark(hsl(120 10% 34%), hsl(120 18% 80%)); --neutral-fg: light-dark(white, hsl(120 30% 12%)); --surface: light-dark(hsl(48 36% 96%), hsl(130 18% 8%)); --surface-raised: light-dark(hsl(45 36% 98%), hsl(130 16% 11%)); --surface-subtle: light-dark(hsl(112 26% 91%), hsl(130 14% 16%)); --surface-solid: light-dark(hsl(130 22% 15%), hsl(0 0% 96%)); --text: light-dark(hsl(130 20% 14%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(125 10% 38%), hsl(130 8% 68%)); --text-subtle: light-dark(hsl(125 8% 50%), hsl(130 7% 55%)); --border: light-dark(hsl(105 18% 78%), hsl(130 12% 24%)); --focus-ring: light-dark(hsl(175 48% 26% / 0.22), hsl(175 45% 76% / 0.28)); --hover-overlay: light-dark(hsl(220 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(220 30% 10%), hsl(0 0% 0%)); } } [data-theme="ocean"] { color-scheme: light dark; --primary: hsl(198 82% 35%); --primary-fg: white; --secondary: hsl(174 58% 30%); --secondary-fg: white; --success: hsl(160 62% 30%); --success-fg: white; --warning: hsl(36 90% 50%); --warning-fg: hsl(35 35% 12%); --danger: hsl(350 70% 52%); --danger-fg: white; --neutral: hsl(205 12% 38%); --neutral-fg: white; --surface: hsl(200 50% 98%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(198 42% 94%); --surface-solid: hsl(204 38% 16%); --text: hsl(205 34% 14%); --text-muted: hsl(205 12% 42%); --text-subtle: hsl(205 10% 56%); --border: hsl(198 30% 84%); --focus: var(--primary); --focus-ring: hsl(198 82% 35% / 0.22); } @supports (color: light-dark(red, blue)) { [data-theme="ocean"] { --primary: light-dark(hsl(198 82% 35%), hsl(198 75% 80%)); --primary-fg: light-dark(white, hsl(198 30% 12%)); --secondary: light-dark(hsl(174 58% 30%), hsl(174 66% 80%)); --secondary-fg: light-dark(white, hsl(174 30% 12%)); --success: light-dark(hsl(160 62% 30%), hsl(160 70% 80%)); --success-fg: light-dark(white, hsl(160 30% 12%)); --warning: light-dark(hsl(36 90% 50%), hsl(36 75% 80%)); --warning-fg: light-dark(hsl(35 35% 12%), hsl(36 30% 12%)); --danger: light-dark(hsl(350 70% 52%), hsl(350 75% 80%)); --danger-fg: light-dark(white, hsl(350 30% 12%)); --neutral: light-dark(hsl(205 12% 38%), hsl(205 20% 80%)); --neutral-fg: light-dark(white, hsl(205 30% 12%)); --surface: light-dark(hsl(200 50% 98%), hsl(200 18% 8%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(200 16% 11%)); --surface-subtle: light-dark(hsl(198 42% 94%), hsl(200 14% 16%)); --surface-solid: light-dark(hsl(204 38% 16%), hsl(0 0% 96%)); --text: light-dark(hsl(205 34% 14%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(205 12% 42%), hsl(200 8% 68%)); --text-subtle: light-dark(hsl(205 10% 56%), hsl(200 7% 55%)); --border: light-dark(hsl(198 30% 84%), hsl(200 12% 24%)); --focus-ring: light-dark(hsl(198 82% 35% / 0.22), hsl(198 75% 80% / 0.28)); --hover-overlay: light-dark(hsl(220 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(220 30% 10%), hsl(0 0% 0%)); } } [data-theme="spruce"] { color-scheme: light dark; --primary: hsl(174 45% 24%); --primary-fg: hsl(160 30% 97%); --secondary: hsl(24 48% 42%); --secondary-fg: hsl(0 0% 98%); --success: hsl(135 38% 33%); --success-fg: hsl(0 0% 98%); --warning: hsl(42 72% 35%); --warning-fg: hsl(0 0% 98%); --danger: hsl(3 55% 44%); --danger-fg: hsl(0 0% 98%); --neutral: hsl(185 8% 40%); --neutral-fg: hsl(0 0% 98%); --surface: hsl(0 0% 100%); --surface-raised: hsl(160 20% 98%); --surface-subtle: hsl(168 18% 95%); --surface-solid: hsl(185 25% 11%); --text: hsl(185 25% 11%); --text-muted: hsl(183 9% 40%); --text-subtle: hsl(182 7% 55%); --border: hsl(170 12% 86%); --focus: var(--primary); --focus-ring: hsl(174 45% 24% / 0.22); } @supports (color: light-dark(red, blue)) { [data-theme="spruce"] { --primary: light-dark(hsl(174 45% 24%), hsl(170 35% 66%)); --primary-fg: light-dark(hsl(160 30% 97%), hsl(174 40% 9%)); --secondary: light-dark(hsl(24 48% 42%), hsl(26 50% 70%)); --secondary-fg: light-dark(hsl(0 0% 98%), hsl(26 35% 11%)); --success: light-dark(hsl(135 38% 33%), hsl(135 35% 60%)); --success-fg: light-dark(hsl(0 0% 98%), hsl(135 35% 8%)); --warning: light-dark(hsl(42 72% 35%), hsl(44 60% 62%)); --warning-fg: light-dark(hsl(0 0% 98%), hsl(42 35% 10%)); --danger: light-dark(hsl(3 55% 44%), hsl(3 58% 69%)); --danger-fg: light-dark(hsl(0 0% 98%), hsl(3 35% 10%)); --neutral: light-dark(hsl(185 8% 40%), hsl(183 10% 70%)); --neutral-fg: light-dark(hsl(0 0% 98%), hsl(185 18% 10%)); --surface: light-dark(hsl(0 0% 100%), hsl(182 20% 8%)); --surface-raised: light-dark(hsl(160 20% 98%), hsl(182 17% 11%)); --surface-subtle: light-dark(hsl(168 18% 95%), hsl(182 14% 15%)); --surface-solid: light-dark(hsl(185 25% 11%), hsl(0 0% 96%)); --text: light-dark(hsl(185 25% 11%), hsl(175 12% 94%)); --text-muted: light-dark(hsl(183 9% 40%), hsl(180 8% 66%)); --text-subtle: light-dark(hsl(182 7% 55%), hsl(180 7% 52%)); --border: light-dark(hsl(170 12% 86%), hsl(182 12% 23%)); --focus-ring: light-dark(hsl(174 45% 24% / 0.22), hsl(170 35% 66% / 0.28)); --hover-overlay: light-dark(hsl(185 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(185 30% 10%), hsl(0 0% 0%)); } } [data-theme="petrol"] { color-scheme: light dark; --primary: hsl(203 55% 30%); --primary-fg: hsl(0 0% 98%); --secondary: hsl(22 55% 47%); --secondary-fg: hsl(0 0% 98%); --success: hsl(155 45% 32%); --success-fg: hsl(0 0% 98%); --warning: hsl(38 70% 36%); --warning-fg: hsl(0 0% 98%); --danger: hsl(357 55% 45%); --danger-fg: hsl(0 0% 98%); --neutral: hsl(210 8% 43%); --neutral-fg: hsl(0 0% 98%); --surface: hsl(0 0% 100%); --surface-raised: hsl(204 30% 98%); --surface-subtle: hsl(204 25% 96%); --surface-solid: hsl(207 25% 12%); --text: hsl(207 25% 12%); --text-muted: hsl(206 9% 42%); --text-subtle: hsl(205 7% 57%); --border: hsl(205 15% 87%); --focus: var(--primary); --focus-ring: hsl(203 55% 30% / 0.22); } @supports (color: light-dark(red, blue)) { [data-theme="petrol"] { --primary: light-dark(hsl(203 55% 30%), hsl(202 55% 72%)); --primary-fg: light-dark(hsl(0 0% 98%), hsl(204 40% 10%)); --secondary: light-dark(hsl(22 55% 47%), hsl(24 55% 70%)); --secondary-fg: light-dark(hsl(0 0% 98%), hsl(24 35% 11%)); --success: light-dark(hsl(155 45% 32%), hsl(155 40% 60%)); --success-fg: light-dark(hsl(0 0% 98%), hsl(155 35% 8%)); --warning: light-dark(hsl(38 70% 36%), hsl(40 62% 63%)); --warning-fg: light-dark(hsl(0 0% 98%), hsl(38 35% 10%)); --danger: light-dark(hsl(357 55% 45%), hsl(357 58% 70%)); --danger-fg: light-dark(hsl(0 0% 98%), hsl(357 35% 10%)); --neutral: light-dark(hsl(210 8% 43%), hsl(208 10% 71%)); --neutral-fg: light-dark(hsl(0 0% 98%), hsl(208 18% 10%)); --surface: light-dark(hsl(0 0% 100%), hsl(206 22% 9%)); --surface-raised: light-dark(hsl(204 30% 98%), hsl(206 19% 12%)); --surface-subtle: light-dark(hsl(204 25% 96%), hsl(206 15% 16%)); --surface-solid: light-dark(hsl(207 25% 12%), hsl(0 0% 96%)); --text: light-dark(hsl(207 25% 12%), hsl(205 12% 95%)); --text-muted: light-dark(hsl(206 9% 42%), hsl(205 8% 67%)); --text-subtle: light-dark(hsl(205 7% 57%), hsl(205 7% 53%)); --border: light-dark(hsl(205 15% 87%), hsl(206 12% 24%)); --focus-ring: light-dark(hsl(203 55% 30% / 0.22), hsl(202 55% 72% / 0.28)); --hover-overlay: light-dark(hsl(207 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(207 30% 10%), hsl(0 0% 0%)); } } [data-theme="sunset"] { color-scheme: light dark; --primary: hsl(16 84% 43%); --primary-fg: white; --secondary: hsl(330 60% 52%); --secondary-fg: white; --success: hsl(142 52% 34%); --success-fg: white; --warning: hsl(42 94% 52%); --warning-fg: hsl(30 35% 12%); --danger: hsl(358 72% 52%); --danger-fg: white; --neutral: hsl(24 12% 38%); --neutral-fg: white; --surface: hsl(34 70% 97%); --surface-raised: hsl(30 80% 99%); --surface-subtle: hsl(28 62% 92%); --surface-solid: hsl(18 34% 18%); --text: hsl(20 34% 14%); --text-muted: hsl(20 12% 42%); --text-subtle: hsl(20 10% 55%); --border: hsl(25 38% 84%); --focus: var(--primary); --focus-ring: hsl(16 84% 43% / 0.22); --selection-bg: var(--secondary); --selection-fg: var(--secondary-fg); --shadow-color: hsl(20 60% 12%); } @supports (color: light-dark(red, blue)) { [data-theme="sunset"] { --primary: light-dark(hsl(16 84% 43%), hsl(16 75% 80%)); --primary-fg: light-dark(white, hsl(16 30% 12%)); --secondary: light-dark(hsl(330 60% 52%), hsl(330 68% 80%)); --secondary-fg: light-dark(white, hsl(330 30% 12%)); --success: light-dark(hsl(142 52% 34%), hsl(142 60% 80%)); --success-fg: light-dark(white, hsl(142 30% 12%)); --warning: light-dark(hsl(42 94% 52%), hsl(42 75% 80%)); --warning-fg: light-dark(hsl(30 35% 12%), hsl(42 30% 12%)); --danger: light-dark(hsl(358 72% 52%), hsl(358 75% 80%)); --danger-fg: light-dark(white, hsl(358 30% 12%)); --neutral: light-dark(hsl(24 12% 38%), hsl(24 20% 80%)); --neutral-fg: light-dark(white, hsl(24 30% 12%)); --surface: light-dark(hsl(34 70% 97%), hsl(20 18% 8%)); --surface-raised: light-dark(hsl(30 80% 99%), hsl(20 16% 11%)); --surface-subtle: light-dark(hsl(28 62% 92%), hsl(20 14% 16%)); --surface-solid: light-dark(hsl(18 34% 18%), hsl(0 0% 96%)); --text: light-dark(hsl(20 34% 14%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(20 12% 42%), hsl(20 8% 68%)); --text-subtle: light-dark(hsl(20 10% 55%), hsl(20 7% 55%)); --border: light-dark(hsl(25 38% 84%), hsl(20 12% 24%)); --focus-ring: light-dark(hsl(16 84% 43% / 0.22), hsl(16 75% 80% / 0.28)); --hover-overlay: light-dark(hsl(220 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(20 60% 12%), hsl(20 40% 2%)); } } [data-theme="lavender"] { color-scheme: light dark; --primary: hsl(258 70% 58%); --primary-fg: white; --secondary: hsl(305 45% 48%); --secondary-fg: white; --success: hsl(150 50% 33%); --success-fg: white; --warning: hsl(42 90% 52%); --warning-fg: hsl(35 35% 12%); --danger: hsl(350 68% 52%); --danger-fg: white; --neutral: hsl(260 10% 42%); --neutral-fg: white; --surface: hsl(260 45% 98%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(260 35% 94%); --surface-solid: hsl(260 30% 18%); --text: hsl(260 28% 15%); --text-muted: hsl(260 12% 43%); --text-subtle: hsl(260 10% 57%); --border: hsl(260 28% 85%); --focus: var(--primary); --focus-ring: hsl(258 70% 58% / 0.22); --soft-bg-mix: 92%; --soft-hover-alpha: 8%; --shadow-color: hsl(260 45% 20%); } @supports (color: light-dark(red, blue)) { [data-theme="lavender"] { --primary: light-dark(hsl(258 70% 58%), hsl(258 75% 80%)); --primary-fg: light-dark(white, hsl(258 30% 12%)); --secondary: light-dark(hsl(305 45% 48%), hsl(305 53% 80%)); --secondary-fg: light-dark(white, hsl(305 30% 12%)); --success: light-dark(hsl(150 50% 33%), hsl(150 58% 80%)); --success-fg: light-dark(white, hsl(150 30% 12%)); --warning: light-dark(hsl(42 90% 52%), hsl(42 75% 80%)); --warning-fg: light-dark(hsl(35 35% 12%), hsl(42 30% 12%)); --danger: light-dark(hsl(350 68% 52%), hsl(350 75% 80%)); --danger-fg: light-dark(white, hsl(350 30% 12%)); --neutral: light-dark(hsl(260 10% 42%), hsl(260 18% 80%)); --neutral-fg: light-dark(white, hsl(260 30% 12%)); --surface: light-dark(hsl(260 45% 98%), hsl(260 18% 8%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(260 16% 11%)); --surface-subtle: light-dark(hsl(260 35% 94%), hsl(260 14% 16%)); --surface-solid: light-dark(hsl(260 30% 18%), hsl(0 0% 96%)); --text: light-dark(hsl(260 28% 15%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(260 12% 43%), hsl(260 8% 68%)); --text-subtle: light-dark(hsl(260 10% 57%), hsl(260 7% 55%)); --border: light-dark(hsl(260 28% 85%), hsl(260 12% 24%)); --focus-ring: light-dark(hsl(258 70% 58% / 0.22), hsl(258 75% 80% / 0.28)); --hover-overlay: light-dark(hsl(220 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(260 45% 20%), hsl(260 40% 2%)); } } [data-theme="mono"] { color-scheme: light dark; --primary: hsl(220 8% 18%); --primary-fg: white; --secondary: hsl(220 7% 34%); --secondary-fg: white; --success: hsl(145 42% 32%); --success-fg: white; --warning: hsl(40 70% 44%); --warning-fg: hsl(35 35% 10%); --danger: hsl(0 56% 46%); --danger-fg: white; --neutral: hsl(220 6% 45%); --neutral-fg: white; --surface: hsl(0 0% 99%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(220 8% 94%); --surface-solid: hsl(220 8% 15%); --text: hsl(220 8% 12%); --text-muted: hsl(220 6% 42%); --text-subtle: hsl(220 5% 58%); --border: hsl(220 8% 84%); --focus: var(--primary); --focus-ring: hsl(220 8% 18% / 0.22); } @supports (color: light-dark(red, blue)) { [data-theme="mono"] { --primary: light-dark(hsl(220 8% 18%), hsl(220 16% 80%)); --primary-fg: light-dark(white, hsl(220 30% 12%)); --secondary: light-dark(hsl(220 7% 34%), hsl(220 15% 80%)); --secondary-fg: light-dark(white, hsl(220 30% 12%)); --success: light-dark(hsl(145 42% 32%), hsl(145 50% 80%)); --success-fg: light-dark(white, hsl(145 30% 12%)); --warning: light-dark(hsl(40 70% 44%), hsl(40 75% 80%)); --warning-fg: light-dark(hsl(35 35% 10%), hsl(40 30% 12%)); --danger: light-dark(hsl(0 56% 46%), hsl(0 64% 80%)); --danger-fg: light-dark(white, hsl(0 30% 12%)); --neutral: light-dark(hsl(220 6% 45%), hsl(220 14% 80%)); --neutral-fg: light-dark(white, hsl(220 30% 12%)); --surface: light-dark(hsl(0 0% 99%), hsl(0 0% 8%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(0 0% 11%)); --surface-subtle: light-dark(hsl(220 8% 94%), hsl(0 0% 16%)); --surface-solid: light-dark(hsl(220 8% 15%), hsl(0 0% 96%)); --text: light-dark(hsl(220 8% 12%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(220 6% 42%), hsl(0 0% 68%)); --text-subtle: light-dark(hsl(220 5% 58%), hsl(0 0% 55%)); --border: light-dark(hsl(220 8% 84%), hsl(0 0% 24%)); --focus-ring: light-dark(hsl(220 8% 18% / 0.22), hsl(220 16% 80% / 0.28)); --hover-overlay: light-dark(hsl(220 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(220 30% 10%), hsl(0 0% 0%)); } } [data-theme="square"] { color-scheme: light dark; --primary: hsl(220 90% 56%); --primary-fg: white; --secondary: hsl(270 60% 56%); --secondary-fg: white; --success: hsl(145 60% 32%); --success-fg: white; --warning: hsl(38 92% 50%); --warning-fg: hsl(35 30% 12%); --danger: hsl(0 75% 51%); --danger-fg: white; --neutral: hsl(220 10% 42%); --neutral-fg: white; --surface: hsl(0 0% 100%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(220 20% 96%); --surface-solid: hsl(222 22% 12%); --text: hsl(222 22% 12%); --text-muted: hsl(220 10% 42%); --text-subtle: hsl(220 8% 55%); --border: hsl(220 15% 88%); --focus: var(--primary); --focus-ring: hsl(220 90% 56% / 0.22); --hover-overlay: hsl(220 30% 10% / 0.04); --radius: 0; --radius-sm: 0; --radius-lg: 0; } @supports (color: light-dark(red, blue)) { [data-theme="square"] { --primary: light-dark(hsl(220 90% 56%), hsl(220 75% 80%)); --primary-fg: light-dark(white, hsl(220 30% 12%)); --secondary: light-dark(hsl(270 60% 56%), hsl(270 68% 80%)); --secondary-fg: light-dark(white, hsl(270 30% 12%)); --success: light-dark(hsl(145 60% 32%), hsl(145 68% 80%)); --success-fg: light-dark(white, hsl(145 30% 12%)); --warning: light-dark(hsl(38 92% 50%), hsl(38 75% 80%)); --warning-fg: light-dark(hsl(35 30% 12%), hsl(38 30% 12%)); --danger: light-dark(hsl(0 75% 51%), hsl(0 75% 80%)); --danger-fg: light-dark(white, hsl(0 30% 12%)); --neutral: light-dark(hsl(220 10% 42%), hsl(220 18% 80%)); --neutral-fg: light-dark(white, hsl(220 30% 12%)); --surface: light-dark(hsl(0 0% 100%), hsl(222 18% 8%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(222 16% 11%)); --surface-subtle: light-dark(hsl(220 20% 96%), hsl(222 14% 16%)); --surface-solid: light-dark(hsl(222 22% 12%), hsl(0 0% 96%)); --text: light-dark(hsl(222 22% 12%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(220 10% 42%), hsl(222 8% 68%)); --text-subtle: light-dark(hsl(220 8% 55%), hsl(222 7% 55%)); --border: light-dark(hsl(220 15% 88%), hsl(222 12% 24%)); --focus-ring: light-dark(hsl(220 90% 56% / 0.22), hsl(220 75% 80% / 0.28)); --hover-overlay: light-dark(hsl(220 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(220 30% 10%), hsl(0 0% 0%)); } } [data-theme="edge"] { color-scheme: light dark; --primary: hsl(224 72% 42%); --primary-fg: white; --secondary: hsl(166 54% 30%); --secondary-fg: white; --success: hsl(145 46% 32%); --success-fg: white; --warning: hsl(36 80% 38%); --warning-fg: hsl(34 35% 10%); --danger: hsl(354 62% 46%); --danger-fg: white; --neutral: hsl(228 9% 42%); --neutral-fg: white; --surface: hsl(40 20% 99%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(220 24% 95%); --surface-solid: hsl(228 26% 12%); --text: hsl(228 24% 12%); --text-muted: hsl(228 9% 42%); --text-subtle: hsl(228 7% 56%); --border: hsl(220 14% 86%); --focus: var(--primary); --focus-ring: hsl(224 72% 42% / 0.22); --hover-overlay: hsl(228 30% 10% / 0.04); --radius: 0.375rem; --radius-sm: 0.25rem; --radius-lg: 0.625rem; --btn-default-bg: var(--surface-subtle); --btn-default-border: transparent; --btn-border-color: transparent; --btn-border-block-end-width: 2px; --control-border-block-end-color: var(--primary); --control-border-block-end-width: 2px; } [data-theme="edge"] .btn:not(.link) { --btn-border-block-end-color: var(--intent, var(--primary)); } [data-theme="edge"] .btn:not(:where(.solid, .soft, .outline, .ghost, .link)) { --btn-default-fg: var(--intent, var(--text)); --ui-hover-bg: var(--surface-raised); } [data-theme="edge"] .btn:not( :where(:disabled, [aria-disabled="true"], .solid, .soft, .outline, .ghost, .link) ):hover { box-shadow: none; } [data-theme="edge"] .alert { --alert-border-inline-start-color: var(--intent, var(--primary)); --alert-border-inline-start-width: 4px; } [data-theme="edge"] :is(.input, .textarea, .select):focus-visible { --control-border: var(--border); --control-border-block-end-width: 3px; } @supports (color: light-dark(red, blue)) { [data-theme="edge"] { --primary: light-dark(hsl(224 72% 42%), hsl(220 80% 76%)); --primary-fg: light-dark(white, hsl(224 35% 10%)); --secondary: light-dark(hsl(166 54% 30%), hsl(166 58% 70%)); --secondary-fg: light-dark(white, hsl(166 35% 9%)); --success: light-dark(hsl(145 46% 32%), hsl(145 48% 64%)); --success-fg: light-dark(white, hsl(145 35% 8%)); --warning: light-dark(hsl(36 80% 38%), hsl(40 72% 66%)); --warning-fg: light-dark(hsl(34 35% 10%), hsl(38 35% 10%)); --danger: light-dark(hsl(354 62% 46%), hsl(354 66% 72%)); --danger-fg: light-dark(white, hsl(354 35% 10%)); --neutral: light-dark(hsl(228 9% 42%), hsl(228 12% 72%)); --neutral-fg: light-dark(white, hsl(228 24% 10%)); --surface: light-dark(hsl(40 20% 99%), hsl(228 20% 8%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(228 17% 11%)); --surface-subtle: light-dark(hsl(220 24% 95%), hsl(228 14% 16%)); --surface-solid: light-dark(hsl(228 26% 12%), hsl(0 0% 96%)); --text: light-dark(hsl(228 24% 12%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(228 9% 42%), hsl(228 8% 68%)); --text-subtle: light-dark(hsl(228 7% 56%), hsl(228 7% 55%)); --border: light-dark(hsl(220 14% 86%), hsl(228 12% 24%)); --focus-ring: light-dark(hsl(224 72% 42% / 0.22), hsl(220 80% 76% / 0.28)); --hover-overlay: light-dark(hsl(228 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(228 30% 10%), hsl(0 0% 0%)); } } [data-theme="cyberpunk"] { color-scheme: light; --primary: hsl(330 100% 60%); --primary-fg: hsl(330 100% 10%); --secondary: hsl(187 100% 42%); --secondary-fg: hsl(187 100% 8%); --success: hsl(150 100% 35%); --success-fg: hsl(150 100% 6%); --warning: hsl(25 95% 50%); --warning-fg: hsl(25 100% 8%); --danger: hsl(0 85% 44%); --danger-fg: hsl(0 0% 98%); --neutral: hsl(56 30% 8%); --neutral-fg: hsl(56 100% 55%); --surface: hsl(56 100% 50%); --surface-raised: hsl(54 100% 64%); --surface-subtle: hsl(53 100% 80%); --surface-solid: hsl(56 30% 8%); --text: hsl(56 60% 6%); --text-muted: hsl(56 25% 22%); --text-subtle: hsl(56 14% 34%); --border: hsl(56 60% 6%); --focus: hsl(330 100% 42%); --focus-ring: hsl(330 100% 42% / 0.3); --hover-overlay: hsl(56 100% 8% / 0.08); --radius: 0; --radius-sm: 0; --radius-lg: 0; --border-width: 2px; --shadow: 0.25rem 0.25rem 0 hsl(56 60% 6%); --shadow-popout: 0.25rem 0.25rem 0 hsl(56 60% 6%); --shadow-color: hsl(56 60% 6%); --soft-bg-mix: 78%; --soft-border-mix: 40%; --selection-bg: hsl(56 60% 6%); --selection-fg: hsl(56 100% 50%); } [data-theme="neon"] { color-scheme: dark; --primary: hsl(315 95% 62%); --primary-fg: hsl(315 100% 8%); --secondary: hsl(182 100% 46%); --secondary-fg: hsl(182 100% 7%); --success: hsl(135 85% 52%); --success-fg: hsl(135 100% 6%); --warning: hsl(55 95% 55%); --warning-fg: hsl(55 100% 7%); --danger: hsl(350 100% 64%); --danger-fg: hsl(350 100% 8%); --neutral: hsl(260 12% 76%); --neutral-fg: hsl(260 25% 8%); --surface: hsl(260 25% 6%); --surface-raised: hsl(260 22% 9%); --surface-subtle: hsl(260 20% 13%); --surface-solid: hsl(0 0% 96%); --text: hsl(0 0% 97%); --text-muted: hsl(260 12% 70%); --text-subtle: hsl(260 8% 55%); --border: hsl(260 18% 24%); --focus: var(--secondary); --focus-ring: hsl(182 100% 46% / 0.35); --hover-overlay: hsl(0 0% 100% / 0.07); --shadow-color: hsl(260 60% 3%); --soft-bg-mix: 80%; --soft-border-mix: 45%; --soft-hover-alpha: 20%; --shadow-popout: 0 0 1rem hsl(315 95% 62% / 0.25); } @media (color-gamut: p3) { @supports (color: oklch(0% 0 0)) { [data-theme="neon"] { --primary: oklch(70% 0.32 340); --secondary: oklch(85% 0.17 195); --success: oklch(85% 0.28 148); --warning: oklch(92% 0.2 105); --danger: oklch(68% 0.26 15); --focus-ring: oklch(85% 0.17 195 / 0.35); --shadow-popout: 0 0 1rem oklch(70% 0.32 340 / 0.3); } } } [data-theme="gradient"] { color-scheme: light dark; --primary: hsl(211 100% 50%); --primary-fg: white; --secondary: hsl(208 7% 46%); --secondary-fg: white; --success: hsl(134 61% 41%); --success-fg: white; --warning: hsl(45 100% 51%); --warning-fg: hsl(45 100% 10%); --danger: hsl(354 70% 54%); --danger-fg: white; --neutral: hsl(210 10% 23%); --neutral-fg: white; --surface: hsl(0 0% 100%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(210 17% 95%); --surface-solid: hsl(210 10% 15%); --text: hsl(210 11% 15%); --text-muted: hsl(208 7% 46%); --text-subtle: hsl(210 9% 62%); --border: hsl(210 14% 89%); --focus: var(--primary); --focus-ring: hsl(211 100% 50% / 0.25); --hover-overlay: hsl(210 30% 10% / 0.04); --radius: 0.25rem; --radius-sm: 0.2rem; --radius-lg: 0.3rem; } @supports (color: light-dark(red, blue)) { [data-theme="gradient"] { --primary: light-dark(hsl(211 100% 50%), hsl(211 90% 72%)); --primary-fg: light-dark(white, hsl(211 40% 10%)); --secondary: light-dark(hsl(208 7% 46%), hsl(208 10% 72%)); --secondary-fg: light-dark(white, hsl(208 15% 10%)); --success: light-dark(hsl(134 61% 41%), hsl(134 55% 65%)); --success-fg: light-dark(white, hsl(134 40% 8%)); --warning: light-dark(hsl(45 100% 51%), hsl(45 90% 70%)); --warning-fg: light-dark(hsl(45 100% 10%), hsl(45 100% 10%)); --danger: light-dark(hsl(354 70% 54%), hsl(354 75% 74%)); --danger-fg: light-dark(white, hsl(354 40% 10%)); --neutral: light-dark(hsl(210 10% 23%), hsl(210 12% 75%)); --neutral-fg: light-dark(white, hsl(210 15% 10%)); --surface: light-dark(hsl(0 0% 100%), hsl(210 15% 8%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(210 13% 11%)); --surface-subtle: light-dark(hsl(210 17% 95%), hsl(210 12% 16%)); --surface-solid: light-dark(hsl(210 10% 15%), hsl(0 0% 96%)); --text: light-dark(hsl(210 11% 15%), hsl(0 0% 96%)); --text-muted: light-dark(hsl(208 7% 46%), hsl(210 8% 68%)); --text-subtle: light-dark(hsl(210 9% 62%), hsl(210 7% 55%)); --border: light-dark(hsl(210 14% 89%), hsl(210 12% 24%)); --focus-ring: light-dark(hsl(211 100% 50% / 0.25), hsl(211 90% 72% / 0.3)); --hover-overlay: light-dark(hsl(210 30% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(210 30% 10%), hsl(0 0% 0%)); } } @supports (color: color-mix(in oklch, red, white)) { [data-theme="gradient"] .btn:not(:where(.soft, .outline, .ghost, .link)) { --gradient-hover-color: color-mix(in oklch, var(--intent, var(--neutral)) 88%, black); --ui-bg: linear-gradient( 180deg, color-mix(in oklch, white 15%, var(--intent, var(--neutral))), var(--intent, var(--neutral)) ); --btn-default-bg: var(--ui-bg); --ui-hover-bg: linear-gradient( 180deg, color-mix(in oklch, white 15%, var(--gradient-hover-color)), var(--gradient-hover-color) ); } }