@stigmer/theme 3.10.0 → 3.11.1-dev.20260812192248

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": "@stigmer/theme",
3
- "version": "3.10.0",
3
+ "version": "3.11.1-dev.20260812192248",
4
4
  "description": "Design tokens, CSS custom properties, and utility functions for Stigmer UI components",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { cn } from "../utils";
3
+
4
+ /**
5
+ * Pins the #454 override contract: the `stg:` utility prefix must be
6
+ * TRANSPARENT for tailwind-merge conflict grouping, so a host's unprefixed
7
+ * `className` utilities still displace the SDK's prefixed defaults. If these
8
+ * pins fail, `cn("stg:p-2", hostClassName)` keeps both classes and the SDK's
9
+ * higher cascade layer silently wins — the documented override channel
10
+ * (DD-019) breaks without any visible error.
11
+ */
12
+ describe("cn: stg: prefix is transparent for conflict grouping", () => {
13
+ it("host unprefixed utility displaces the SDK's prefixed default", () => {
14
+ expect(cn("stg:p-2", "p-4")).toBe("p-4");
15
+ });
16
+
17
+ it("SDK-internal merges keep the prefixed spelling", () => {
18
+ expect(cn("stg:p-2", "stg:p-4")).toBe("stg:p-4");
19
+ });
20
+
21
+ it("non-conflicting utilities from both spellings all survive", () => {
22
+ expect(cn("stg:flex stg:gap-2", "p-4")).toBe("stg:flex stg:gap-2 p-4");
23
+ });
24
+
25
+ it("variant conflicts resolve across spellings", () => {
26
+ expect(cn("stg:hover:bg-red-500", "hover:bg-blue-500")).toBe(
27
+ "hover:bg-blue-500",
28
+ );
29
+ });
30
+
31
+ it("SDK prefixed utility displaces an earlier host utility", () => {
32
+ // SDK call sites put the host className LAST (cn(base, className)), but
33
+ // the grouping must be symmetric.
34
+ expect(cn("p-4", "stg:p-2")).toBe("stg:p-2");
35
+ });
36
+
37
+ it("non-Tailwind classes pass through untouched", () => {
38
+ expect(cn("stg:p-2", "custom-class")).toBe("stg:p-2 custom-class");
39
+ expect(cn("stgm-thread-item-enter", "stg:opacity-50")).toBe(
40
+ "stgm-thread-item-enter stg:opacity-50",
41
+ );
42
+ });
43
+
44
+ it("conditional falsy inputs are dropped (clsx semantics)", () => {
45
+ expect(cn("stg:flex", false, undefined, null, "stg:gap-2")).toBe(
46
+ "stg:flex stg:gap-2",
47
+ );
48
+ });
49
+ });
package/src/utils.ts CHANGED
@@ -1,8 +1,48 @@
1
1
  import { type ClassValue, clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
2
+ import { extendTailwindMerge } from "tailwind-merge";
3
3
 
4
4
  export type { ClassValue };
5
5
 
6
+ /**
7
+ * SDK utility class prefix (stigmer/stigmer#454).
8
+ *
9
+ * Every Tailwind utility authored inside the SDK carries this variant-style
10
+ * prefix (`stg:flex`), so the published stylesheet shares no class names with
11
+ * a host application's own Tailwind build. The prefix is deliberately NOT
12
+ * `stgm`: Tailwind v4 prefixes theme variables too, and a `stgm` prefix would
13
+ * collide with the `--stgm-*` token namespace (`--stgm-font-sans`,
14
+ * `--stgm-shadow-sm`) that `@stigmer/theme` already owns.
15
+ */
16
+ export const UTILITY_PREFIX = "stg:";
17
+
18
+ /**
19
+ * tailwind-merge configured so the `stg:` prefix is TRANSPARENT for conflict
20
+ * grouping. This preserves the SDK's `className` override contract: hosts
21
+ * pass their own unprefixed utilities (`p-4`), and they must still displace
22
+ * the SDK's prefixed defaults (`stg:p-2`) — the two spellings target the same
23
+ * CSS property, so they must resolve as the same conflict group even though
24
+ * their class names differ. Without this hook, both classes would survive the
25
+ * merge and the SDK's higher cascade layer would silently win, breaking the
26
+ * documented override channel (DD-019).
27
+ */
28
+ const twMergeWithPrefix = extendTailwindMerge({
29
+ experimentalParseClassName: ({ className, parseClassName }) =>
30
+ parseClassName(
31
+ className.startsWith(UTILITY_PREFIX)
32
+ ? className.slice(UTILITY_PREFIX.length)
33
+ : className,
34
+ ),
35
+ });
36
+
37
+ /**
38
+ * Merge class values with Tailwind-aware conflict resolution.
39
+ *
40
+ * Later inputs win conflicts, across BOTH spellings of a utility: the SDK's
41
+ * prefixed form (`stg:p-2`) and a host's unprefixed form (`p-4`) are the same
42
+ * conflict group, so `cn("stg:p-2", className)` lets a host `className`
43
+ * override SDK defaults exactly as it did before the #454 prefix migration.
44
+ * Non-Tailwind classes pass through untouched.
45
+ */
6
46
  export function cn(...inputs: ClassValue[]) {
7
- return twMerge(clsx(inputs));
47
+ return twMergeWithPrefix(clsx(inputs));
8
48
  }
package/utils.d.ts CHANGED
@@ -1,4 +1,24 @@
1
1
  import { type ClassValue } from "clsx";
2
2
  export type { ClassValue };
3
+ /**
4
+ * SDK utility class prefix (stigmer/stigmer#454).
5
+ *
6
+ * Every Tailwind utility authored inside the SDK carries this variant-style
7
+ * prefix (`stg:flex`), so the published stylesheet shares no class names with
8
+ * a host application's own Tailwind build. The prefix is deliberately NOT
9
+ * `stgm`: Tailwind v4 prefixes theme variables too, and a `stgm` prefix would
10
+ * collide with the `--stgm-*` token namespace (`--stgm-font-sans`,
11
+ * `--stgm-shadow-sm`) that `@stigmer/theme` already owns.
12
+ */
13
+ export declare const UTILITY_PREFIX = "stg:";
14
+ /**
15
+ * Merge class values with Tailwind-aware conflict resolution.
16
+ *
17
+ * Later inputs win conflicts, across BOTH spellings of a utility: the SDK's
18
+ * prefixed form (`stg:p-2`) and a host's unprefixed form (`p-4`) are the same
19
+ * conflict group, so `cn("stg:p-2", className)` lets a host `className`
20
+ * override SDK defaults exactly as it did before the #454 prefix migration.
21
+ * Non-Tailwind classes pass through untouched.
22
+ */
3
23
  export declare function cn(...inputs: ClassValue[]): string;
4
24
  //# sourceMappingURL=utils.d.ts.map
package/utils.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAC;AAG7C,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAC;AAG7C,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,SAAS,CAAC;AAqBrC;;;;;;;;GAQG;AACH,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
package/utils.js CHANGED
@@ -1,6 +1,41 @@
1
1
  import { clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
2
+ import { extendTailwindMerge } from "tailwind-merge";
3
+ /**
4
+ * SDK utility class prefix (stigmer/stigmer#454).
5
+ *
6
+ * Every Tailwind utility authored inside the SDK carries this variant-style
7
+ * prefix (`stg:flex`), so the published stylesheet shares no class names with
8
+ * a host application's own Tailwind build. The prefix is deliberately NOT
9
+ * `stgm`: Tailwind v4 prefixes theme variables too, and a `stgm` prefix would
10
+ * collide with the `--stgm-*` token namespace (`--stgm-font-sans`,
11
+ * `--stgm-shadow-sm`) that `@stigmer/theme` already owns.
12
+ */
13
+ export const UTILITY_PREFIX = "stg:";
14
+ /**
15
+ * tailwind-merge configured so the `stg:` prefix is TRANSPARENT for conflict
16
+ * grouping. This preserves the SDK's `className` override contract: hosts
17
+ * pass their own unprefixed utilities (`p-4`), and they must still displace
18
+ * the SDK's prefixed defaults (`stg:p-2`) — the two spellings target the same
19
+ * CSS property, so they must resolve as the same conflict group even though
20
+ * their class names differ. Without this hook, both classes would survive the
21
+ * merge and the SDK's higher cascade layer would silently win, breaking the
22
+ * documented override channel (DD-019).
23
+ */
24
+ const twMergeWithPrefix = extendTailwindMerge({
25
+ experimentalParseClassName: ({ className, parseClassName }) => parseClassName(className.startsWith(UTILITY_PREFIX)
26
+ ? className.slice(UTILITY_PREFIX.length)
27
+ : className),
28
+ });
29
+ /**
30
+ * Merge class values with Tailwind-aware conflict resolution.
31
+ *
32
+ * Later inputs win conflicts, across BOTH spellings of a utility: the SDK's
33
+ * prefixed form (`stg:p-2`) and a host's unprefixed form (`p-4`) are the same
34
+ * conflict group, so `cn("stg:p-2", className)` lets a host `className`
35
+ * override SDK defaults exactly as it did before the #454 prefix migration.
36
+ * Non-Tailwind classes pass through untouched.
37
+ */
3
38
  export function cn(...inputs) {
4
- return twMerge(clsx(inputs));
39
+ return twMergeWithPrefix(clsx(inputs));
5
40
  }
6
41
  //# sourceMappingURL=utils.js.map
package/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,IAAI,EAAE,MAAM,MAAM,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAIzC,MAAM,UAAU,EAAE,CAAC,GAAG,MAAoB;IACxC,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,IAAI,EAAE,MAAM,MAAM,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAIrD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AAErC;;;;;;;;;GASG;AACH,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;IAC5C,0BAA0B,EAAE,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,CAC5D,cAAc,CACZ,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC;QAClC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;QACxC,CAAC,CAAC,SAAS,CACd;CACJ,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,EAAE,CAAC,GAAG,MAAoB;IACxC,OAAO,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;AACzC,CAAC"}