@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 +1 -1
- package/src/__tests__/cn-prefix-merge.test.ts +49 -0
- package/src/utils.ts +42 -2
- package/utils.d.ts +20 -0
- package/utils.d.ts.map +1 -1
- package/utils.js +37 -2
- package/utils.js.map +1 -1
package/package.json
CHANGED
|
@@ -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 {
|
|
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
|
|
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 {
|
|
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
|
|
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,
|
|
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"}
|