@usequark/quark-create-app 1.24.2 → 1.24.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usequark/quark-create-app",
3
- "version": "1.24.2",
3
+ "version": "1.24.3",
4
4
  "description": "CLI to scaffold new Quark projects with Next.js, Prisma, BullMQ, and Railway deployment",
5
5
  "keywords": [
6
6
  "quark",
@@ -15,6 +15,38 @@ const tsExtensions = new Set([".ts", ".tsx"]);
15
15
  const throwNewErrorPattern = /\bthrow new Error\s*\(/;
16
16
  const consolePattern = /\bconsole\.(?:log|warn|error|info)\s*\(/;
17
17
 
18
+ /**
19
+ * Tailwind v4 removed the bare `[--var]` shorthand that v3 accepted. Under v4 a
20
+ * class such as `text-[--navbar-text-muted]` is still a *valid* utility name, so
21
+ * Tailwind emits a rule for it - but the declaration body is the bare token
22
+ * `color: --navbar-text-muted`, which is not a valid CSS value. The browser
23
+ * discards the declaration and the element silently inherits instead.
24
+ *
25
+ * Nothing warns: the build exits 0 and the rule is present in the stylesheet, so
26
+ * the breakage is only visible by rendering both themes and reading computed
27
+ * styles. In light mode `body` sets no colour and the inherited default happens
28
+ * to be black, which reads as "working".
29
+ *
30
+ * The v4 forms are `text-(--navbar-text-muted)` or, when the utility namespace
31
+ * would be ambiguous (e.g. `text-` shared between colour and font-size),
32
+ * `text-[color:var(--navbar-text-muted)]`.
33
+ *
34
+ * The pattern matches the *shape* of the v3 shorthand rather than an
35
+ * enumerated list of utilities. An earlier version listed the colour, border and
36
+ * radius families explicitly, which was a false economy: the same mistake is
37
+ * equally reachable through `w-[--panel-width]`, `leading-[--line-height]` or
38
+ * `p-[--gutter]`, and any list has to be extended every time someone reaches
39
+ * for a family it omits.
40
+ *
41
+ * The `-` immediately before `[` is what separates this from ordinary
42
+ * JavaScript. `rows[--i]` (decrement-then-index) and `obj[key--1]` have no
43
+ * hyphen there and do not match. The one shape that is indistinguishable from a
44
+ * real class is `<ident>-[--ident]`, which would require JavaScript that
45
+ * subtracts from a decrement expression - it does not occur in this codebase.
46
+ */
47
+ const tailwindV3VarPattern =
48
+ /(?:^|[\s"'`(])(?:[a-z][a-z0-9-]*:)*[a-z][a-z0-9-]*-\[--[a-zA-Z][a-zA-Z0-9-]*\]/;
49
+
18
50
  const consoleAllowlist = [
19
51
  /^packages\/config\/src\/validate-env\.js$/,
20
52
  /^packages\/core\/src\/testing\//,
@@ -159,6 +191,17 @@ async function main() {
159
191
  }
160
192
  }
161
193
 
194
+ if (isSourceFile(relativePath)) {
195
+ for (const lineNumber of findMatchingLines(
196
+ content,
197
+ tailwindV3VarPattern,
198
+ )) {
199
+ violations.push(
200
+ `${relativePath}:${lineNumber}: Tailwind v4 ignores the v3 \`[--var]\` shorthand - the utility is emitted with an invalid value and silently does nothing. Use \`utility-(--var)\`, or \`utility-[color:var(--var)]\` when the namespace is ambiguous.`,
201
+ );
202
+ }
203
+ }
204
+
162
205
  if (
163
206
  isSourceFile(relativePath) &&
164
207
  !isTestFile(relativePath) &&
@@ -50,8 +50,11 @@ function collectTests(targetPath) {
50
50
  const files = roots.flatMap((rootPath) => collectTests(path.resolve(rootPath)));
51
51
 
52
52
  if (files.length === 0) {
53
- console.error(`No test files found in: ${roots.join(", ")}`);
54
- process.exit(1);
53
+ // An empty suite is not a failure. `node --test` exits 0 when it matches no
54
+ // files, so exiting 1 here made a freshly scaffolded project fail its own
55
+ // `pnpm test` and therefore its pre-push hook.
56
+ console.log(`No test files found in: ${roots.join(", ")} - nothing to run.`);
57
+ process.exit(0);
55
58
  }
56
59
 
57
60
  const result = spawnSync(
@@ -1,10 +1,10 @@
1
1
  import React from "react";
2
2
 
3
3
  const base =
4
- "inline-flex items-center justify-center border font-medium bg-[--badge-bg] border-[--badge-border] text-[--badge-text]";
4
+ "inline-flex items-center justify-center border font-medium bg-(--badge-bg) border-(--badge-border) text-(--badge-text)";
5
5
 
6
6
  export function Badge({ variant = "default", className = "", ...props }) {
7
- const cls = `${base} rounded-[--radius-default] ${className}`.trim();
7
+ const cls = `${base} rounded-(--radius-default) ${className}`.trim();
8
8
  return React.createElement("span", {
9
9
  "data-badge-variant": variant,
10
10
  className: cls,
@@ -6,7 +6,7 @@ const base =
6
6
  "inline-flex items-center justify-center font-medium tracking-wide transition-all duration-200 linear cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 disabled:opacity-40 disabled:cursor-not-allowed active:opacity-80";
7
7
 
8
8
  const variantCls =
9
- "bg-[--btn-bg] border border-[--btn-border] text-[--btn-text] rounded-[--btn-radius] hover:brightness-[var(--btn-hover-brightness)] hover:shadow-[var(--btn-hover-shadow)] hover:bg-[var(--btn-hover-bg)] hover:text-[var(--btn-hover-text)] hover:opacity-[var(--btn-hover-opacity)] focus-visible:ring-[var(--btn-ring)]";
9
+ "bg-(--btn-bg) border border-(--btn-border) text-(--btn-text) rounded-(--btn-radius) hover:brightness-[var(--btn-hover-brightness)] hover:shadow-[var(--btn-hover-shadow)] hover:bg-[var(--btn-hover-bg)] hover:text-[var(--btn-hover-text)] hover:opacity-[var(--btn-hover-opacity)] focus-visible:ring-[var(--btn-ring)]";
10
10
 
11
11
  const sizes = {
12
12
  sm: "h-8 px-3 text-sm",
@@ -4,19 +4,19 @@ import { ChevronDown } from "lucide-react";
4
4
  import React, { useState } from "react";
5
5
 
6
6
  const cardCls =
7
- "border border-[--card-border] bg-[--card-bg] transition-colors duration-200 linear";
7
+ "border border-(--card-border) bg-(--card-bg) transition-colors duration-200 linear";
8
8
 
9
9
  const headerCls = "flex flex-col space-y-1.5 p-6";
10
10
 
11
11
  const titleCls =
12
- "text-lg font-bold leading-none tracking-tight text-[--card-title-text]";
12
+ "text-lg font-bold leading-none tracking-tight text-(--card-title-text)";
13
13
 
14
14
  const contentCls = "p-6 pt-0";
15
15
 
16
16
  const footerCls = "flex items-center p-6 pt-0";
17
17
 
18
18
  const collapsibleTriggerCls =
19
- "flex w-full cursor-pointer items-center justify-between p-6 text-[--card-title-text] transition-colors duration-200 hover:bg-[--card-trigger-hover-bg] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--card-trigger-ring)]";
19
+ "flex w-full cursor-pointer items-center justify-between p-6 text-(--card-title-text) transition-colors duration-200 hover:bg-(--card-trigger-hover-bg) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--card-trigger-ring)]";
20
20
 
21
21
  function CollapsibleCard({
22
22
  className = "",
@@ -31,7 +31,7 @@ function CollapsibleCard({
31
31
  "div",
32
32
  {
33
33
  className:
34
- `${cardCls} h-fit self-start rounded-[--radius-default] ${className}`.trim(),
34
+ `${cardCls} h-fit self-start rounded-(--radius-default) ${className}`.trim(),
35
35
  ...props,
36
36
  },
37
37
  React.createElement(
@@ -51,7 +51,7 @@ function CollapsibleCard({
51
51
  "span",
52
52
  {
53
53
  "aria-hidden": "true",
54
- className: `pointer-events-none ml-3 shrink-0 text-[--card-text-muted] transition-transform duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] ${isOpen ? "rotate-180" : "rotate-0"}`,
54
+ className: `pointer-events-none ml-3 shrink-0 text-(--card-text-muted) transition-transform duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] ${isOpen ? "rotate-180" : "rotate-0"}`,
55
55
  },
56
56
  React.createElement(ChevronDown, { size: 16 }),
57
57
  ),
@@ -59,7 +59,7 @@ function CollapsibleCard({
59
59
  React.createElement(
60
60
  "div",
61
61
  {
62
- className: `border-t border-[--card-border] text-[--card-text-muted] grid transition-[grid-template-rows,opacity,transform] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] motion-reduce:transition-none ${isOpen ? "translate-y-0 opacity-100" : "-translate-y-2 opacity-0"}`,
62
+ className: `border-t border-(--card-border) text-(--card-text-muted) grid transition-[grid-template-rows,opacity,transform] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] motion-reduce:transition-none ${isOpen ? "translate-y-0 opacity-100" : "-translate-y-2 opacity-0"}`,
63
63
  style: { gridTemplateRows: isOpen ? "1fr" : "0fr" },
64
64
  },
65
65
  React.createElement(
@@ -92,7 +92,7 @@ export function Card({
92
92
  return React.createElement(
93
93
  "div",
94
94
  {
95
- className: `${cardCls} rounded-[--radius-default] ${className}`.trim(),
95
+ className: `${cardCls} rounded-(--radius-default) ${className}`.trim(),
96
96
  ...props,
97
97
  },
98
98
  children,
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
 
3
3
  const inputCls =
4
- "h-4 w-4 rounded border-[--checkbox-border] bg-[--checkbox-bg] accent-[--checkbox-accent] transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-[var(--checkbox-ring)] disabled:cursor-not-allowed disabled:opacity-[var(--checkbox-disabled-opacity)]";
4
+ "h-4 w-4 rounded border-(--checkbox-border) bg-(--checkbox-bg) accent-(--checkbox-accent) transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-[var(--checkbox-ring)] disabled:cursor-not-allowed disabled:opacity-[var(--checkbox-disabled-opacity)]";
5
5
 
6
- const labelCls = "text-sm text-[--checkbox-label-text] select-none";
6
+ const labelCls = "text-sm text-(--checkbox-label-text) select-none";
7
7
 
8
8
  export function Checkbox({ id, label, className = "", ...props }) {
9
9
  return React.createElement(
@@ -5,7 +5,7 @@ export function Container({ className = "", children, ...props }) {
5
5
  "section",
6
6
  {
7
7
  className:
8
- `relative overflow-hidden rounded-[--radius-default] border border-[--container-border] bg-[--container-bg] ${className}`.trim(),
8
+ `relative overflow-hidden rounded-(--radius-default) border border-(--container-border) bg-(--container-bg) ${className}`.trim(),
9
9
  ...props,
10
10
  },
11
11
  children,
@@ -28,18 +28,18 @@ import React, {
28
28
  */
29
29
 
30
30
  const dialogCls =
31
- "backdrop:bg-[var(--dialog-backdrop)] border border-[--dialog-border] bg-[--dialog-bg] p-0 w-full max-w-lg rounded-[--radius-default]";
31
+ "backdrop:bg-[var(--dialog-backdrop)] border border-(--dialog-border) bg-(--dialog-bg) p-0 w-full max-w-lg rounded-(--radius-default)";
32
32
 
33
33
  const headerCls =
34
- "flex items-center justify-between border-b border-[--dialog-border] px-5 py-4";
34
+ "flex items-center justify-between border-b border-(--dialog-border) px-5 py-4";
35
35
 
36
36
  const titleCls =
37
- "text-base font-bold tracking-tight text-[--dialog-title-text]";
37
+ "text-base font-bold tracking-tight text-(--dialog-title-text)";
38
38
 
39
39
  const closeCls =
40
- "flex h-8 w-8 cursor-pointer items-center justify-center text-lg text-[--dialog-text-muted] transition-colors duration-200 linear hover:text-[--dialog-title-text] hover:bg-[--dialog-close-hover-bg] active:bg-[--dialog-close-hover-bg] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--dialog-close-ring)] rounded-[--radius-default]";
40
+ "flex h-8 w-8 cursor-pointer items-center justify-center text-lg text-(--dialog-text-muted) transition-colors duration-200 linear hover:text-(--dialog-title-text) hover:bg-(--dialog-close-hover-bg) active:bg-(--dialog-close-hover-bg) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--dialog-close-ring)] rounded-(--radius-default)";
41
41
 
42
- const bodyCls = "px-5 py-4 text-[--dialog-text-muted]";
42
+ const bodyCls = "px-5 py-4 text-(--dialog-text-muted)";
43
43
 
44
44
  export function Dialog({ open, onClose, title, children, className = "" }) {
45
45
  const ref = useRef(null);
@@ -8,7 +8,7 @@ export function ErrorBanner({ message, className = "" }) {
8
8
  role: "alert",
9
9
  "aria-live": "polite",
10
10
  className:
11
- `mb-4 border border-[--error-border] bg-[--error-bg] px-3 py-2 text-sm text-[--error-text] rounded-[--radius-default] ${className}`.trim(),
11
+ `mb-4 border border-(--error-border) bg-(--error-bg) px-3 py-2 text-sm text-(--error-text) rounded-(--radius-default) ${className}`.trim(),
12
12
  },
13
13
  message,
14
14
  );
@@ -1,24 +1,24 @@
1
1
  import React from "react";
2
2
 
3
- const footerCls = "border-t border-[--footer-border] bg-[--footer-bg]";
3
+ const footerCls = "border-t border-(--footer-border) bg-(--footer-bg)";
4
4
  const containerCls = "mx-auto";
5
5
  const topGridCls =
6
6
  "grid gap-8 sm:gap-10 md:grid-cols-2 lg:grid-cols-[minmax(0,1.45fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)]";
7
7
  const brandRowCls = "flex items-center gap-3";
8
8
  const markCls =
9
- "flex shrink-0 items-center justify-center rounded-2xl bg-[--footer-mark-bg] font-bold text-[--footer-mark-text]";
9
+ "flex shrink-0 items-center justify-center rounded-2xl bg-(--footer-mark-bg) font-bold text-(--footer-mark-text)";
10
10
  const brandNameCls =
11
- "text-2xl font-bold tracking-tight text-[--footer-text] sm:text-3xl";
12
- const brandTextCls = "max-w-md text-sm leading-7 text-[--footer-text-muted]";
11
+ "text-2xl font-bold tracking-tight text-(--footer-text) sm:text-3xl";
12
+ const brandTextCls = "max-w-md text-sm leading-7 text-(--footer-text-muted)";
13
13
  const ctaCls =
14
- "inline-flex w-full items-center justify-center rounded-full border border-[--footer-cta-border] font-semibold text-[--footer-mark-text] transition-colors hover:bg-[--footer-cta-hover-bg] sm:w-auto";
15
- const columnTitleCls = "mb-4 font-semibold text-[--footer-text-faint]";
14
+ "inline-flex w-full items-center justify-center rounded-full border border-(--footer-cta-border) font-semibold text-(--footer-mark-text) transition-colors hover:bg-(--footer-cta-hover-bg) sm:w-auto";
15
+ const columnTitleCls = "mb-4 font-semibold text-(--footer-text-faint)";
16
16
  const columnListCls = "space-y-2.5";
17
17
  const linkCls =
18
- "break-words text-[--footer-text-muted] transition-colors hover:text-[--footer-link-hover]";
19
- const textItemCls = "break-words text-[--footer-text-muted]";
18
+ "break-words text-(--footer-text-muted) transition-colors hover:text-(--footer-link-hover)";
19
+ const textItemCls = "break-words text-(--footer-text-muted)";
20
20
  const bottomBarCls =
21
- "mt-8 border-t border-[--footer-border] pt-5 flex flex-col gap-3 text-sm text-[--footer-text-faint] sm:mt-10 sm:pt-6 lg:flex-row lg:items-center lg:justify-between";
21
+ "mt-8 border-t border-(--footer-border) pt-5 flex flex-col gap-3 text-sm text-(--footer-text-faint) sm:mt-10 sm:pt-6 lg:flex-row lg:items-center lg:justify-between";
22
22
  const legalCls = "flex flex-wrap items-center gap-2 sm:gap-3";
23
23
  const sepCls = "text-[var(--footer-sep)]";
24
24
 
@@ -40,7 +40,7 @@ export function FormField({
40
40
  "p",
41
41
  {
42
42
  id: errorId,
43
- className: "text-xs text-[--error-text]",
43
+ className: "text-xs text-(--error-text)",
44
44
  role: "alert",
45
45
  },
46
46
  error,
@@ -1,11 +1,11 @@
1
1
  import React from "react";
2
2
 
3
3
  const base =
4
- "block h-10 w-full border border-[--input-border] bg-[--input-bg] px-3 text-sm text-[--input-text] placeholder:text-[--input-placeholder] transition-colors duration-200 linear hover:border-[--input-border-hover] focus-visible:border-[--input-border-focus] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--input-ring-focus)] disabled:opacity-[--input-disabled-opacity] disabled:cursor-not-allowed";
4
+ "block h-10 w-full border border-(--input-border) bg-(--input-bg) px-3 text-sm text-(--input-text) placeholder:text-(--input-placeholder) transition-colors duration-200 linear hover:border-(--input-border-hover) focus-visible:border-(--input-border-focus) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--input-ring-focus)] disabled:opacity-(--input-disabled-opacity) disabled:cursor-not-allowed";
5
5
 
6
6
  export function Input({ className = "", ...props }) {
7
7
  return React.createElement("input", {
8
- className: `${base} rounded-[--radius-default] ${className}`.trim(),
8
+ className: `${base} rounded-(--radius-default) ${className}`.trim(),
9
9
  ...props,
10
10
  });
11
11
  }
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- const base = "block text-xs font-medium text-[--label-text]";
3
+ const base = "block text-xs font-medium text-(--label-text)";
4
4
 
5
5
  export function Label({ className = "", ...props }) {
6
6
  return React.createElement("label", {
@@ -3,16 +3,16 @@ import { ChevronLeft, ChevronRight, X } from "lucide-react";
3
3
  import React, { useEffect, useRef, useState } from "react";
4
4
 
5
5
  const BACKDROP_CLS =
6
- "absolute inset-0 z-0 bg-[--lightbox-backdrop] px-3 py-4 transition-opacity duration-200 ease-out sm:px-6 sm:py-8 cursor-pointer";
6
+ "absolute inset-0 z-0 bg-(--lightbox-backdrop) px-3 py-4 transition-opacity duration-200 ease-out sm:px-6 sm:py-8 cursor-pointer";
7
7
  const DIALOG_CLS =
8
8
  "relative z-10 mx-auto flex h-full w-full max-w-6xl flex-col p-3 transition-all duration-200 ease-out sm:p-5";
9
9
  const NAV_BTN_CLS =
10
- "inline-flex items-center gap-1.5 rounded-[--radius-default] border border-[--lightbox-btn-border] px-3 py-1.5 text-sm font-medium text-[--lightbox-btn-text] transition-colors duration-200 hover:bg-[--lightbox-btn-hover-bg] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--lightbox-btn-ring)] cursor-pointer";
10
+ "inline-flex items-center gap-1.5 rounded-(--radius-default) border border-(--lightbox-btn-border) px-3 py-1.5 text-sm font-medium text-(--lightbox-btn-text) transition-colors duration-200 hover:bg-(--lightbox-btn-hover-bg) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--lightbox-btn-ring)] cursor-pointer";
11
11
  const CLOSE_BTN_CLS =
12
- "rounded-[--radius-default] border border-[--lightbox-btn-border] font-medium text-[--lightbox-btn-text] transition-colors duration-200 hover:bg-[--lightbox-btn-hover-bg] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--lightbox-btn-ring)] flex items-center cursor-pointer";
12
+ "rounded-(--radius-default) border border-(--lightbox-btn-border) font-medium text-(--lightbox-btn-text) transition-colors duration-200 hover:bg-(--lightbox-btn-hover-bg) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--lightbox-btn-ring)] flex items-center cursor-pointer";
13
13
 
14
14
  const IMG_CLS =
15
- "max-h-[72vh] w-auto max-w-full rounded-[--radius-default] object-contain transition-all duration-200 ease-out";
15
+ "max-h-[72vh] w-auto max-w-full rounded-(--radius-default) object-contain transition-all duration-200 ease-out";
16
16
 
17
17
  export function Lightbox({
18
18
  src,
@@ -129,7 +129,7 @@ export function Lightbox({
129
129
  "p",
130
130
  {
131
131
  key: "caption",
132
- className: "mt-3 text-sm leading-6 text-[--lightbox-caption-text]",
132
+ className: "mt-3 text-sm leading-6 text-(--lightbox-caption-text)",
133
133
  },
134
134
  caption,
135
135
  ),
@@ -169,7 +169,7 @@ export function Lightbox({
169
169
  "p",
170
170
  {
171
171
  key: "counter",
172
- className: "text-center text-[--lightbox-counter-text]",
172
+ className: "text-center text-(--lightbox-counter-text)",
173
173
  style: {
174
174
  minWidth: "var(--lightbox-counter-min-width)",
175
175
  fontSize: "var(--lightbox-counter-font-size)",
@@ -2,47 +2,47 @@
2
2
  import React, { useEffect, useMemo, useRef, useState } from "react";
3
3
 
4
4
  const shellCls =
5
- "relative w-full border-b border-[--navbar-border] bg-[--navbar-bg] shadow-[var(--navbar-inset-shadow)] backdrop-blur";
5
+ "relative w-full border-b border-(--navbar-border) bg-(--navbar-bg) shadow-[var(--navbar-inset-shadow)] backdrop-blur";
6
6
  const mobileShellCls =
7
- "relative w-full border-b border-[--navbar-border] bg-[--navbar-mobile-bg] shadow-[var(--navbar-inset-shadow)]";
7
+ "relative w-full border-b border-(--navbar-border) bg-(--navbar-mobile-bg) shadow-[var(--navbar-inset-shadow)]";
8
8
  const mobileShellOpenCls =
9
- "relative w-full border-b border-transparent bg-[--navbar-mobile-bg] shadow-[var(--navbar-inset-shadow)]";
9
+ "relative w-full border-b border-transparent bg-(--navbar-mobile-bg) shadow-[var(--navbar-inset-shadow)]";
10
10
  const containerCls = "mx-auto w-full px-4 sm:px-6 lg:px-8";
11
11
  const desktopInnerCls = "flex h-[4.5rem] items-center justify-center gap-6";
12
12
  const leftZoneCls = "flex items-center mr-auto";
13
13
  const rightZoneCls = "flex items-center justify-end ml-auto";
14
14
  const logoCls =
15
- "inline-flex items-center gap-2.5 rounded-[--radius-default] px-1.5 py-1 font-semibold tracking-wide text-[--navbar-text] transition-colors hover:text-[--navbar-logo-hover]";
15
+ "inline-flex items-center gap-2.5 rounded-(--radius-default) px-1.5 py-1 font-semibold tracking-wide text-(--navbar-text) transition-colors hover:text-(--navbar-logo-hover)";
16
16
  const markCls =
17
- "inline-flex h-8 w-8 items-center justify-center rounded-[--radius-default] border border-[--navbar-mark-border] bg-[--navbar-mark-bg] font-bold text-[--navbar-mark-text]";
17
+ "inline-flex h-8 w-8 items-center justify-center rounded-(--radius-default) border border-(--navbar-mark-border) bg-(--navbar-mark-bg) font-bold text-(--navbar-mark-text)";
18
18
  const centerNavCls = "flex min-w-0 justify-center";
19
19
  const desktopListCls = "flex items-center gap-3";
20
20
  const desktopLinkCls =
21
- "inline-flex h-10 items-center rounded-[--radius-default] px-3.5 font-medium text-[--navbar-text-muted] transition-colors hover:bg-[--navbar-hover-bg] hover:text-[--navbar-hover-text]";
21
+ "inline-flex h-10 items-center rounded-(--radius-default) px-3.5 font-medium text-(--navbar-text-muted) transition-colors hover:bg-(--navbar-hover-bg) hover:text-(--navbar-hover-text)";
22
22
 
23
23
  const desktopDropdownWrapCls =
24
- "group inline-flex cursor-pointer items-center rounded-[--radius-default] transition-colors hover:bg-[--navbar-hover-bg]";
24
+ "group inline-flex cursor-pointer items-center rounded-(--radius-default) transition-colors hover:bg-(--navbar-hover-bg)";
25
25
  const desktopDropdownLinkCls =
26
- "flex items-center font-medium text-[--navbar-text-muted] transition-colors group-hover:text-[--navbar-hover-text]";
26
+ "flex items-center font-medium text-(--navbar-text-muted) transition-colors group-hover:text-(--navbar-hover-text)";
27
27
  const desktopDropdownChevronCls =
28
- "flex items-center px-2 pt-1 text-[--navbar-text-muted] transition-colors group-hover:text-[--navbar-hover-text] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)] rounded-r-[--radius-default]";
28
+ "flex items-center px-2 pt-1 text-(--navbar-text-muted) transition-colors group-hover:text-(--navbar-hover-text) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)] rounded-r-(--radius-default)";
29
29
  const actionCls =
30
- "inline-flex h-11 items-center justify-center whitespace-nowrap rounded-[--radius-default] border border-[--navbar-action-border] bg-[--navbar-action-bg] px-5 font-semibold text-[--navbar-action-text] transition-colors hover:border-[--navbar-action-hover-border] hover:bg-[--navbar-action-hover-bg] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)]";
30
+ "inline-flex h-11 items-center justify-center whitespace-nowrap rounded-(--radius-default) border border-(--navbar-action-border) bg-(--navbar-action-bg) px-5 font-semibold text-(--navbar-action-text) transition-colors hover:border-(--navbar-action-hover-border) hover:bg-(--navbar-action-hover-bg) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)]";
31
31
 
32
32
  const mobileInnerCls = "flex h-14 items-center justify-between gap-4";
33
33
  const mobileToggleCls =
34
- "inline-flex h-10 w-10 items-center justify-center rounded-[--radius-default] text-[--navbar-text-muted] transition-colors hover:text-[--navbar-hover-text] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)]";
34
+ "inline-flex h-10 w-10 items-center justify-center rounded-(--radius-default) text-(--navbar-text-muted) transition-colors hover:text-(--navbar-hover-text) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)]";
35
35
  const mobilePanelWrapCls =
36
36
  "absolute inset-x-0 top-full z-[80] overflow-y-auto overscroll-contain [touch-action:pan-y] [-webkit-overflow-scrolling:touch] origin-top";
37
37
  const mobilePanelCls =
38
- "border-y border-[--navbar-border] bg-[--navbar-mobile-panel-bg] will-change-transform transition-all duration-300 ease-out";
38
+ "border-y border-(--navbar-border) bg-(--navbar-mobile-panel-bg) will-change-transform transition-all duration-300 ease-out";
39
39
  const mobileLinkCls =
40
- "flex min-h-12 w-full items-center justify-between px-4 text-left font-medium text-[--navbar-text-muted] transition-colors hover:bg-[--navbar-hover-bg] hover:text-[--navbar-hover-text]";
40
+ "flex min-h-12 w-full items-center justify-between px-4 text-left font-medium text-(--navbar-text-muted) transition-colors hover:bg-(--navbar-hover-bg) hover:text-(--navbar-hover-text)";
41
41
  const mobileSubLinkCls =
42
- "block px-6 py-2 text-[--navbar-text-muted] transition-colors hover:bg-[--navbar-hover-bg] hover:text-[--navbar-hover-text]";
42
+ "block px-6 py-2 text-(--navbar-text-muted) transition-colors hover:bg-(--navbar-hover-bg) hover:text-(--navbar-hover-text)";
43
43
  const mobileActionWrapCls = "px-3 pb-3 pt-2";
44
44
  const mobileActionCls =
45
- "inline-flex h-10 w-full items-center justify-center rounded-[--radius-default] border border-[--navbar-action-border] bg-[--navbar-action-bg] px-4 font-semibold text-[--navbar-action-text] transition-colors hover:border-[--navbar-action-hover-border] hover:bg-[--navbar-action-hover-bg] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)]";
45
+ "inline-flex h-10 w-full items-center justify-center rounded-(--radius-default) border border-(--navbar-action-border) bg-(--navbar-action-bg) px-4 font-semibold text-(--navbar-action-text) transition-colors hover:border-(--navbar-action-hover-border) hover:bg-(--navbar-action-hover-bg) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--navbar-ring)]";
46
46
 
47
47
  const DEFAULT_LINKS = [
48
48
  { label: "Home", href: "#" },
@@ -239,7 +239,7 @@ export function Navbar({
239
239
  "a",
240
240
  {
241
241
  href: link.href ?? "#",
242
- className: `${desktopDropdownLinkCls} rounded-l-[--radius-default] px-3.5 py-2`,
242
+ className: `${desktopDropdownLinkCls} rounded-l-(--radius-default) px-3.5 py-2`,
243
243
  },
244
244
  link.label,
245
245
  ),
@@ -262,7 +262,7 @@ export function Navbar({
262
262
  {
263
263
  role: "menu",
264
264
  className:
265
- "absolute left-0 top-[calc(100%+0.6rem)] z-40 w-48 rounded-[--radius-default] border border-[--navbar-border] bg-[--navbar-dropdown-bg] shadow-[var(--navbar-dropdown-shadow)]",
265
+ "absolute left-0 top-[calc(100%+0.6rem)] z-40 w-48 rounded-(--radius-default) border border-(--navbar-border) bg-(--navbar-dropdown-bg) shadow-[var(--navbar-dropdown-shadow)]",
266
266
  },
267
267
  ...(link.items ?? []).map((item, itemIndex) =>
268
268
  React.createElement(
@@ -272,7 +272,7 @@ export function Navbar({
272
272
  href: item.href ?? "#",
273
273
  role: "menuitem",
274
274
  className:
275
- "block rounded-[--radius-default] px-3.5 py-2.5 text-base text-[--navbar-text-muted] transition-colors hover:bg-[--navbar-hover-bg] hover:text-[--navbar-hover-text]",
275
+ "block rounded-(--radius-default) px-3.5 py-2.5 text-base text-(--navbar-text-muted) transition-colors hover:bg-(--navbar-hover-bg) hover:text-(--navbar-hover-text)",
276
276
  onClick: () => setOpenIndex(null),
277
277
  },
278
278
  item.label,
@@ -476,7 +476,7 @@ export function MobileNavbar({
476
476
  },
477
477
  React.createElement(
478
478
  "ul",
479
- { className: "divide-y divide-[--navbar-divide-border]" },
479
+ { className: "divide-y divide-(--navbar-divide-border)" },
480
480
  ...navLinks.map((link, index) => {
481
481
  if (!hasSubItems(link)) {
482
482
  return React.createElement(
@@ -23,7 +23,7 @@ export function PasswordInput({ className = "", ...props }) {
23
23
  onClick: () => setVisible((v) => !v),
24
24
  "aria-label": visible ? "Hide password" : "Show password",
25
25
  className:
26
- "absolute right-2.5 top-1/2 -translate-y-1/2 text-[--input-icon] hover:text-[--input-icon-hover] transition-colors",
26
+ "absolute right-2.5 top-1/2 -translate-y-1/2 text-(--input-icon) hover:text-(--input-icon-hover) transition-colors",
27
27
  },
28
28
  React.createElement(Icon, { size: 16, "aria-hidden": true }),
29
29
  ),
@@ -27,24 +27,24 @@ import { Input } from "./input.js";
27
27
  */
28
28
 
29
29
  const wrapperCls =
30
- "rounded-[--radius-default] border border-[--richtext-border] bg-[--richtext-bg] text-sm text-[--richtext-text] transition-colors duration-200 focus-within:border-[--richtext-border-focus] focus-within:ring-2 focus-within:ring-[var(--richtext-ring)] overflow-hidden";
30
+ "rounded-(--radius-default) border border-(--richtext-border) bg-(--richtext-bg) text-sm text-(--richtext-text) transition-colors duration-200 focus-within:border-(--richtext-border-focus) focus-within:ring-2 focus-within:ring-[var(--richtext-ring)] overflow-hidden";
31
31
 
32
32
  const toolbarCls =
33
- "flex flex-wrap items-center border-b border-[--richtext-border] bg-[--richtext-toolbar-bg]";
33
+ "flex flex-wrap items-center border-b border-(--richtext-border) bg-(--richtext-toolbar-bg)";
34
34
 
35
35
  const btnBase =
36
- "inline-flex items-center justify-center rounded-[--radius-default] text-[--richtext-btn-text] transition-colors cursor-pointer hover:bg-[--richtext-btn-hover-bg] hover:text-[--richtext-btn-hover-text] disabled:opacity-30 disabled:cursor-not-allowed";
36
+ "inline-flex items-center justify-center rounded-(--radius-default) text-(--richtext-btn-text) transition-colors cursor-pointer hover:bg-(--richtext-btn-hover-bg) hover:text-(--richtext-btn-hover-text) disabled:opacity-30 disabled:cursor-not-allowed";
37
37
 
38
38
  const btnActive =
39
- "bg-[--richtext-btn-active-bg] text-[--richtext-btn-active-text]";
39
+ "bg-(--richtext-btn-active-bg) text-(--richtext-btn-active-text)";
40
40
 
41
- const separatorCls = "bg-[--richtext-separator]";
41
+ const separatorCls = "bg-(--richtext-separator)";
42
42
 
43
43
  const editorCls =
44
- "outline-none px-3 py-2 overflow-y-auto prose-sm [&_h1]:text-xl [&_h1]:font-bold [&_h1]:mt-4 [&_h1]:mb-2 [&_h2]:text-lg [&_h2]:font-semibold [&_h2]:mt-3 [&_h2]:mb-1.5 [&_h3]:text-base [&_h3]:font-semibold [&_h3]:mt-2 [&_h3]:mb-1 [&_ul]:list-disc [&_ul]:pl-5 [&_ul]:my-2 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:my-2 [&_li]:my-0.5 [&_a]:text-[--richtext-link] [&_a]:underline [&_blockquote]:border-l-2 [&_blockquote]:border-[--richtext-blockquote-border] [&_blockquote]:pl-3 [&_blockquote]:italic [&_blockquote]:text-[--richtext-blockquote-text] [&_blockquote]:my-2 [&_code]:bg-[--richtext-code-bg] [&_code]:px-1 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono";
44
+ "outline-none px-3 py-2 overflow-y-auto prose-sm [&_h1]:text-xl [&_h1]:font-bold [&_h1]:mt-4 [&_h1]:mb-2 [&_h2]:text-lg [&_h2]:font-semibold [&_h2]:mt-3 [&_h2]:mb-1.5 [&_h3]:text-base [&_h3]:font-semibold [&_h3]:mt-2 [&_h3]:mb-1 [&_ul]:list-disc [&_ul]:pl-5 [&_ul]:my-2 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:my-2 [&_li]:my-0.5 [&_a]:text-(--richtext-link) [&_a]:underline [&_blockquote]:border-l-2 [&_blockquote]:border-(--richtext-blockquote-border) [&_blockquote]:pl-3 [&_blockquote]:italic [&_blockquote]:text-(--richtext-blockquote-text) [&_blockquote]:my-2 [&_code]:bg-(--richtext-code-bg) [&_code]:px-1 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono";
45
45
 
46
46
  const placeholderCls =
47
- "before:content-[attr(data-placeholder)] before:text-[--richtext-placeholder] before:pointer-events-none before:absolute before:left-3 before:top-2";
47
+ "before:content-[attr(data-placeholder)] before:text-(--richtext-placeholder) before:pointer-events-none before:absolute before:left-3 before:top-2";
48
48
 
49
49
  const TOOLBAR_ITEMS = [
50
50
  { command: "bold", icon: "B", title: "Bold", style: "font-bold" },
@@ -433,7 +433,7 @@ export function RichText({
433
433
  linkError
434
434
  ? React.createElement(
435
435
  "p",
436
- { className: "mt-1.5 text-xs text-[--color-danger]" },
436
+ { className: "mt-1.5 text-xs text-(--color-danger)" },
437
437
  linkError,
438
438
  )
439
439
  : null,
@@ -10,19 +10,19 @@ import React, {
10
10
  } from "react";
11
11
 
12
12
  const triggerCls =
13
- "flex h-10 w-full items-center justify-between rounded-[--radius-default] border border-[--select-border] bg-[--select-bg] px-3 text-sm text-[--select-text] shadow-sm transition-all duration-200 cursor-pointer hover:border-[--select-border-hover] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--select-ring)] disabled:cursor-not-allowed disabled:opacity-50";
13
+ "flex h-10 w-full items-center justify-between rounded-(--radius-default) border border-(--select-border) bg-(--select-bg) px-3 text-sm text-(--select-text) shadow-sm transition-all duration-200 cursor-pointer hover:border-(--select-border-hover) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--select-ring)] disabled:cursor-not-allowed disabled:opacity-50";
14
14
 
15
15
  const panelCls =
16
- "absolute z-30 max-h-60 w-full overflow-auto rounded-[--radius-default] border border-[--select-border] bg-[--select-panel-bg] shadow-xl origin-top transition-all duration-200 ease-out";
16
+ "absolute z-30 max-h-60 w-full overflow-auto rounded-(--radius-default) border border-(--select-border) bg-(--select-panel-bg) shadow-xl origin-top transition-all duration-200 ease-out";
17
17
 
18
18
  const optionBaseCls =
19
- "flex w-full items-center justify-between rounded-[--radius-default] px-2.5 py-2 text-left text-sm transition-colors duration-150";
19
+ "flex w-full items-center justify-between rounded-(--radius-default) px-2.5 py-2 text-left text-sm transition-colors duration-150";
20
20
 
21
21
  const optionIdleCls =
22
- "cursor-pointer text-[--select-text] hover:bg-[--select-option-hover-bg] hover:text-[--select-option-hover-text]";
22
+ "cursor-pointer text-(--select-text) hover:bg-(--select-option-hover-bg) hover:text-(--select-option-hover-text)";
23
23
 
24
24
  const optionActiveCls =
25
- "bg-[--select-option-active-bg] text-[--select-option-active-text]";
25
+ "bg-(--select-option-active-bg) text-(--select-option-active-text)";
26
26
 
27
27
  function normalizeValue(value) {
28
28
  if (value === undefined || value === null) return "";
@@ -249,8 +249,8 @@ export function Select({
249
249
  "span",
250
250
  {
251
251
  className: selectedOption
252
- ? "text-[--select-text]"
253
- : "text-[--select-text-faint]",
252
+ ? "text-(--select-text)"
253
+ : "text-(--select-text-faint)",
254
254
  },
255
255
  selectedOption?.label ??
256
256
  (options.length === 0 ? "No options" : placeholder),
@@ -260,7 +260,7 @@ export function Select({
260
260
  {
261
261
  "aria-hidden": "true",
262
262
  className:
263
- `ml-2 text-[--select-text-faint] transition-transform duration-200 ease-in-out ${open ? "rotate-180" : "rotate-0"}`.trim(),
263
+ `ml-2 text-(--select-text-faint) transition-transform duration-200 ease-in-out ${open ? "rotate-180" : "rotate-0"}`.trim(),
264
264
  },
265
265
  React.createElement(ChevronDown, { size: 16 }),
266
266
  ),
@@ -276,7 +276,7 @@ export function Select({
276
276
  options.length === 0
277
277
  ? React.createElement(
278
278
  "p",
279
- { className: "px-2.5 py-2 text-sm text-[--select-text-faint]" },
279
+ { className: "px-2.5 py-2 text-sm text-(--select-text-faint)" },
280
280
  "No options",
281
281
  )
282
282
  : options.map((option) => {
@@ -305,7 +305,7 @@ export function Select({
305
305
  "span",
306
306
  {
307
307
  "aria-hidden": "true",
308
- className: "text-[--select-option-active-text]",
308
+ className: "text-(--select-option-active-text)",
309
309
  },
310
310
  React.createElement(Check, { size: 16 }),
311
311
  )
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- const base = "animate-pulse rounded-lg bg-[--skeleton-bg]";
3
+ const base = "animate-pulse rounded-lg bg-(--skeleton-bg)";
4
4
 
5
5
  export function Skeleton({ className = "", ...props }) {
6
6
  return React.createElement("div", {
@@ -2,11 +2,11 @@ import { ChevronDown, ChevronsUpDown, ChevronUp } from "lucide-react";
2
2
  import React from "react";
3
3
 
4
4
  const wrapperCls =
5
- "w-full overflow-auto rounded-[--radius-default] border border-[--table-border] bg-[--table-bg]";
5
+ "w-full overflow-auto rounded-(--radius-default) border border-(--table-border) bg-(--table-bg)";
6
6
 
7
7
  const tableCls = "w-full caption-bottom text-sm";
8
8
 
9
- const headerCls = "border-b border-[--table-border] bg-[--table-header-bg]";
9
+ const headerCls = "border-b border-(--table-border) bg-(--table-header-bg)";
10
10
 
11
11
  const bodyCls = "[&_tr:last-child]:border-0";
12
12
 
@@ -14,15 +14,15 @@ const rowCls =
14
14
  "border-b border-[var(--table-border)] transition-colors hover:bg-[var(--table-row-hover-bg)] active:bg-[var(--table-row-active-bg)]";
15
15
 
16
16
  const headCls =
17
- "h-11 text-left align-middle font-medium text-[--table-head-text]";
17
+ "h-11 text-left align-middle font-medium text-(--table-head-text)";
18
18
 
19
- const cellCls = "align-middle text-[--table-cell-text]";
19
+ const cellCls = "align-middle text-(--table-cell-text)";
20
20
 
21
21
  const sortButtonCls =
22
- "group inline-flex w-full items-center justify-between gap-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-[var(--table-ring)] rounded-[--radius-default]";
22
+ "group inline-flex w-full items-center justify-between gap-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-[var(--table-ring)] rounded-(--radius-default)";
23
23
 
24
24
  const sortIconCls =
25
- "text-[--table-sort-icon] transition-colors group-hover:text-[--table-sort-icon-hover]";
25
+ "text-(--table-sort-icon) transition-colors group-hover:text-(--table-sort-icon-hover)";
26
26
 
27
27
  function toAriaSort(direction) {
28
28
  if (direction === "asc") return "ascending";
@@ -148,7 +148,7 @@ export function TableHead({
148
148
  {
149
149
  "aria-hidden": "true",
150
150
  className:
151
- `${sortIconCls} ${direction !== "none" ? "text-[--table-sort-active]" : ""}`.trim(),
151
+ `${sortIconCls} ${direction !== "none" ? "text-(--table-sort-active)" : ""}`.trim(),
152
152
  },
153
153
  sortIndicator(direction),
154
154
  ),
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
 
3
3
  const base =
4
- "block w-full rounded-[--radius-default] border border-[--input-border] bg-[--input-bg] px-3 py-2 text-sm text-[--input-text] placeholder:text-[--input-placeholder] transition-colors duration-200 hover:border-[--input-border-hover] focus-visible:border-[--input-border-focus] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--input-ring-focus)] disabled:opacity-[--input-disabled-opacity] disabled:cursor-not-allowed resize-y";
4
+ "block w-full rounded-(--radius-default) border border-(--input-border) bg-(--input-bg) px-3 py-2 text-sm text-(--input-text) placeholder:text-(--input-placeholder) transition-colors duration-200 hover:border-(--input-border-hover) focus-visible:border-(--input-border-focus) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--input-ring-focus)] disabled:opacity-(--input-disabled-opacity) disabled:cursor-not-allowed resize-y";
5
5
 
6
6
  export function Textarea({ className = "", ...props }) {
7
7
  return React.createElement("textarea", {
@@ -21,7 +21,7 @@ export function Toast({
21
21
  const [shouldRender, setShouldRender] = useState(visible);
22
22
 
23
23
  const variantCls =
24
- "bg-[--toast-bg] border-[--toast-border] text-[--toast-text] shadow-[var(--toast-shadow)]";
24
+ "bg-(--toast-bg) border-(--toast-border) text-(--toast-text) shadow-[var(--toast-shadow)]";
25
25
 
26
26
  const remainingRef = useRef(duration);
27
27
  const startRef = useRef(null);
@@ -98,7 +98,7 @@ export function Toast({
98
98
  "data-toast-variant": variant,
99
99
  onMouseEnter: () => setHovered(true),
100
100
  onMouseLeave: () => setHovered(false),
101
- className: `fixed bottom-4 right-4 z-50 flex items-center gap-3 rounded-[--radius-default] px-4 py-3 text-sm transition-colors duration-200 ${variantCls}`,
101
+ className: `fixed bottom-4 right-4 z-50 flex items-center gap-3 rounded-(--radius-default) px-4 py-3 text-sm transition-colors duration-200 ${variantCls}`,
102
102
  style: animStyle,
103
103
  },
104
104
  React.createElement("span", null, message),
@@ -116,7 +116,7 @@ export function Toast({
116
116
  "aria-label": "Dismiss notification",
117
117
  onClick: onClose,
118
118
  className:
119
- "flex h-6 w-6 cursor-pointer items-center justify-center rounded-[--radius-default] text-base leading-none opacity-80 transition-all hover:bg-black/10 dark:hover:bg-white/10 hover:opacity-100 active:bg-black/20 dark:active:bg-white/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
119
+ "flex h-6 w-6 cursor-pointer items-center justify-center rounded-(--radius-default) text-base leading-none opacity-80 transition-all hover:bg-black/10 dark:hover:bg-white/10 hover:opacity-100 active:bg-black/20 dark:active:bg-white/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
120
120
  },
121
121
  React.createElement(
122
122
  "svg",