@praxisjs/css 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +21 -0
  3. package/dist/__tests__/css-builder.test.d.ts +2 -0
  4. package/dist/__tests__/css-builder.test.d.ts.map +1 -0
  5. package/dist/__tests__/css-builder.test.js +198 -0
  6. package/dist/__tests__/css-builder.test.js.map +1 -0
  7. package/dist/__tests__/cx.test.d.ts +2 -0
  8. package/dist/__tests__/cx.test.d.ts.map +1 -0
  9. package/dist/__tests__/cx.test.js +47 -0
  10. package/dist/__tests__/cx.test.js.map +1 -0
  11. package/dist/__tests__/decorators.test.d.ts +2 -0
  12. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  13. package/dist/__tests__/decorators.test.js +268 -0
  14. package/dist/__tests__/decorators.test.js.map +1 -0
  15. package/dist/__tests__/extract.test.d.ts +2 -0
  16. package/dist/__tests__/extract.test.d.ts.map +1 -0
  17. package/dist/__tests__/extract.test.js +420 -0
  18. package/dist/__tests__/extract.test.js.map +1 -0
  19. package/dist/__tests__/global.test.d.ts +2 -0
  20. package/dist/__tests__/global.test.d.ts.map +1 -0
  21. package/dist/__tests__/global.test.js +117 -0
  22. package/dist/__tests__/global.test.js.map +1 -0
  23. package/dist/__tests__/keyframes.test.d.ts +2 -0
  24. package/dist/__tests__/keyframes.test.d.ts.map +1 -0
  25. package/dist/__tests__/keyframes.test.js +90 -0
  26. package/dist/__tests__/keyframes.test.js.map +1 -0
  27. package/dist/__tests__/preflight.test.d.ts +2 -0
  28. package/dist/__tests__/preflight.test.d.ts.map +1 -0
  29. package/dist/__tests__/preflight.test.js +55 -0
  30. package/dist/__tests__/preflight.test.js.map +1 -0
  31. package/dist/__tests__/registry.test.d.ts +2 -0
  32. package/dist/__tests__/registry.test.d.ts.map +1 -0
  33. package/dist/__tests__/registry.test.js +132 -0
  34. package/dist/__tests__/registry.test.js.map +1 -0
  35. package/dist/__tests__/style.test.d.ts +2 -0
  36. package/dist/__tests__/style.test.d.ts.map +1 -0
  37. package/dist/__tests__/style.test.js +257 -0
  38. package/dist/__tests__/style.test.js.map +1 -0
  39. package/dist/__tests__/styled.test.d.ts +2 -0
  40. package/dist/__tests__/styled.test.d.ts.map +1 -0
  41. package/dist/__tests__/styled.test.js +951 -0
  42. package/dist/__tests__/styled.test.js.map +1 -0
  43. package/dist/__tests__/theme-instance.test.d.ts +2 -0
  44. package/dist/__tests__/theme-instance.test.d.ts.map +1 -0
  45. package/dist/__tests__/theme-instance.test.js +317 -0
  46. package/dist/__tests__/theme-instance.test.js.map +1 -0
  47. package/dist/__tests__/theme-uninit.test.d.ts +2 -0
  48. package/dist/__tests__/theme-uninit.test.d.ts.map +1 -0
  49. package/dist/__tests__/theme-uninit.test.js +10 -0
  50. package/dist/__tests__/theme-uninit.test.js.map +1 -0
  51. package/dist/__tests__/token-sheet.test.d.ts +2 -0
  52. package/dist/__tests__/token-sheet.test.d.ts.map +1 -0
  53. package/dist/__tests__/token-sheet.test.js +74 -0
  54. package/dist/__tests__/token-sheet.test.js.map +1 -0
  55. package/dist/base/stylesheet.d.ts +30 -0
  56. package/dist/base/stylesheet.d.ts.map +1 -0
  57. package/dist/base/stylesheet.js +29 -0
  58. package/dist/base/stylesheet.js.map +1 -0
  59. package/dist/builder/css-builder.d.ts +98 -0
  60. package/dist/builder/css-builder.d.ts.map +1 -0
  61. package/dist/builder/css-builder.js +79 -0
  62. package/dist/builder/css-builder.js.map +1 -0
  63. package/dist/builder/keyframes.d.ts +23 -0
  64. package/dist/builder/keyframes.d.ts.map +1 -0
  65. package/dist/builder/keyframes.js +54 -0
  66. package/dist/builder/keyframes.js.map +1 -0
  67. package/dist/cx.d.ts +3 -0
  68. package/dist/cx.d.ts.map +1 -0
  69. package/dist/cx.js +23 -0
  70. package/dist/cx.js.map +1 -0
  71. package/dist/decorators/param.d.ts +23 -0
  72. package/dist/decorators/param.d.ts.map +1 -0
  73. package/dist/decorators/param.js +39 -0
  74. package/dist/decorators/param.js.map +1 -0
  75. package/dist/decorators/style.d.ts +15 -0
  76. package/dist/decorators/style.d.ts.map +1 -0
  77. package/dist/decorators/style.js +53 -0
  78. package/dist/decorators/style.js.map +1 -0
  79. package/dist/decorators/styled.d.ts +37 -0
  80. package/dist/decorators/styled.d.ts.map +1 -0
  81. package/dist/decorators/styled.js +138 -0
  82. package/dist/decorators/styled.js.map +1 -0
  83. package/dist/extract.d.ts +14 -0
  84. package/dist/extract.d.ts.map +1 -0
  85. package/dist/extract.js +124 -0
  86. package/dist/extract.js.map +1 -0
  87. package/dist/global.d.ts +27 -0
  88. package/dist/global.d.ts.map +1 -0
  89. package/dist/global.js +49 -0
  90. package/dist/global.js.map +1 -0
  91. package/dist/index.d.ts +11 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +10 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/internal/collector.d.ts +15 -0
  96. package/dist/internal/collector.d.ts.map +1 -0
  97. package/dist/internal/collector.js +33 -0
  98. package/dist/internal/collector.js.map +1 -0
  99. package/dist/internal/hash.d.ts +5 -0
  100. package/dist/internal/hash.d.ts.map +1 -0
  101. package/dist/internal/hash.js +11 -0
  102. package/dist/internal/hash.js.map +1 -0
  103. package/dist/internal/registry.d.ts +3 -0
  104. package/dist/internal/registry.d.ts.map +1 -0
  105. package/dist/internal/registry.js +41 -0
  106. package/dist/internal/registry.js.map +1 -0
  107. package/dist/preflight.d.ts +18 -0
  108. package/dist/preflight.d.ts.map +1 -0
  109. package/dist/preflight.js +191 -0
  110. package/dist/preflight.js.map +1 -0
  111. package/dist/server.d.ts +8 -0
  112. package/dist/server.d.ts.map +1 -0
  113. package/dist/server.js +8 -0
  114. package/dist/server.js.map +1 -0
  115. package/dist/tokens/decorators.d.ts +41 -0
  116. package/dist/tokens/decorators.d.ts.map +1 -0
  117. package/dist/tokens/decorators.js +73 -0
  118. package/dist/tokens/decorators.js.map +1 -0
  119. package/dist/tokens/index.d.ts +4 -0
  120. package/dist/tokens/index.d.ts.map +1 -0
  121. package/dist/tokens/index.js +4 -0
  122. package/dist/tokens/index.js.map +1 -0
  123. package/dist/tokens/theme-instance.d.ts +33 -0
  124. package/dist/tokens/theme-instance.d.ts.map +1 -0
  125. package/dist/tokens/theme-instance.js +130 -0
  126. package/dist/tokens/theme-instance.js.map +1 -0
  127. package/dist/tokens/token-sheet.d.ts +46 -0
  128. package/dist/tokens/token-sheet.d.ts.map +1 -0
  129. package/dist/tokens/token-sheet.js +55 -0
  130. package/dist/tokens/token-sheet.js.map +1 -0
  131. package/package.json +33 -0
  132. package/src/__tests__/css-builder.test.ts +230 -0
  133. package/src/__tests__/cx.test.ts +63 -0
  134. package/src/__tests__/decorators.test.ts +127 -0
  135. package/src/__tests__/extract.test.ts +418 -0
  136. package/src/__tests__/global.test.ts +140 -0
  137. package/src/__tests__/keyframes.test.ts +103 -0
  138. package/src/__tests__/preflight.test.ts +63 -0
  139. package/src/__tests__/registry.test.ts +151 -0
  140. package/src/__tests__/style.test.ts +153 -0
  141. package/src/__tests__/styled.test.ts +427 -0
  142. package/src/__tests__/theme-instance.test.ts +374 -0
  143. package/src/__tests__/theme-uninit.test.ts +12 -0
  144. package/src/__tests__/token-sheet.test.ts +87 -0
  145. package/src/base/stylesheet.ts +35 -0
  146. package/src/builder/css-builder.ts +204 -0
  147. package/src/builder/keyframes.ts +66 -0
  148. package/src/cx.ts +26 -0
  149. package/src/decorators/param.ts +40 -0
  150. package/src/decorators/style.ts +55 -0
  151. package/src/decorators/styled.ts +167 -0
  152. package/src/extract.ts +133 -0
  153. package/src/global.ts +60 -0
  154. package/src/index.ts +10 -0
  155. package/src/internal/collector.ts +38 -0
  156. package/src/internal/hash.ts +10 -0
  157. package/src/internal/registry.ts +51 -0
  158. package/src/preflight.ts +192 -0
  159. package/src/server.ts +12 -0
  160. package/src/tokens/decorators.ts +108 -0
  161. package/src/tokens/index.ts +3 -0
  162. package/src/tokens/theme-instance.ts +149 -0
  163. package/src/tokens/token-sheet.ts +86 -0
  164. package/tsconfig.json +8 -0
@@ -0,0 +1,98 @@
1
+ import type { Properties } from "csstype";
2
+ /**
3
+ * Typed CSS property object — backed by `csstype`.
4
+ * All ~300 CSS properties with full value autocomplete.
5
+ * Used as the argument to `this.css({...})` and all nesting methods.
6
+ */
7
+ export type CSSProperties = Properties<string | number> & Record<string, string | number | undefined>;
8
+ /**
9
+ * Fluent CSS builder returned by `this.css({...})`.
10
+ * TypeScript sees it as `string` — works in JSX `class=`, `cx()`, computed keys.
11
+ * Serialises to a CSS rule block via `toString()`.
12
+ *
13
+ * @example
14
+ * class BtnStyles extends Stylesheet {
15
+ * $base = this.css({ display: 'inline-flex', padding: '8px 16px' })
16
+ * .hover({ opacity: 0.9 })
17
+ * .disabled({ opacity: 0.4, pointerEvents: 'none' })
18
+ * .before({ content: '""', display: 'block' })
19
+ * .media('max-width: 640px', { width: '100%' })
20
+ * }
21
+ */
22
+ export type CSSBuilder = string & {
23
+ /** `&:hover { ... }` */
24
+ hover(props: CSSProperties): CSSBuilder;
25
+ /** `&:focus { ... }` */
26
+ focus(props: CSSProperties): CSSBuilder;
27
+ /** `&:focus-within { ... }` */
28
+ focusWithin(props: CSSProperties): CSSBuilder;
29
+ /** `&:focus-visible { ... }` */
30
+ focusVisible(props: CSSProperties): CSSBuilder;
31
+ /** `&:active { ... }` */
32
+ active(props: CSSProperties): CSSBuilder;
33
+ /** `&:visited { ... }` */
34
+ visited(props: CSSProperties): CSSBuilder;
35
+ /** `&:target { ... }` */
36
+ target(props: CSSProperties): CSSBuilder;
37
+ /** `&:disabled, &[disabled], &[aria-disabled="true"] { ... }` */
38
+ disabled(props: CSSProperties): CSSBuilder;
39
+ /** `&:enabled { ... }` */
40
+ enabled(props: CSSProperties): CSSBuilder;
41
+ /** `&:checked { ... }` */
42
+ checked(props: CSSProperties): CSSBuilder;
43
+ /** `&:indeterminate { ... }` */
44
+ indeterminate(props: CSSProperties): CSSBuilder;
45
+ /** `&:required { ... }` */
46
+ required(props: CSSProperties): CSSBuilder;
47
+ /** `&:optional { ... }` */
48
+ optional(props: CSSProperties): CSSBuilder;
49
+ /** `&:valid { ... }` */
50
+ valid(props: CSSProperties): CSSBuilder;
51
+ /** `&:invalid { ... }` */
52
+ invalid(props: CSSProperties): CSSBuilder;
53
+ /** `&:read-only { ... }` */
54
+ readOnly(props: CSSProperties): CSSBuilder;
55
+ /** `&:first-child { ... }` */
56
+ first(props: CSSProperties): CSSBuilder;
57
+ /** `&:last-child { ... }` */
58
+ last(props: CSSProperties): CSSBuilder;
59
+ /** `&:nth-child(n) { ... }` */
60
+ nthChild(n: string | number, props: CSSProperties): CSSBuilder;
61
+ /** `&:empty { ... }` */
62
+ empty(props: CSSProperties): CSSBuilder;
63
+ /** `&:has(selector) { ... }` */
64
+ has(selector: string, props: CSSProperties): CSSBuilder;
65
+ /** `&:is(...) { ... }` */
66
+ is(selector: string, props: CSSProperties): CSSBuilder;
67
+ /** `&:where(...) { ... }` — zero specificity */
68
+ where(selector: string, props: CSSProperties): CSSBuilder;
69
+ /** `&:not(selector) { ... }` */
70
+ not(selector: string, props: CSSProperties): CSSBuilder;
71
+ /** `&::before { ... }` */
72
+ before(props: CSSProperties): CSSBuilder;
73
+ /** `&::after { ... }` */
74
+ after(props: CSSProperties): CSSBuilder;
75
+ /** `&::placeholder { ... }` */
76
+ placeholder(props: CSSProperties): CSSBuilder;
77
+ /** `&::selection { ... }` */
78
+ selection(props: CSSProperties): CSSBuilder;
79
+ /** `&::first-line { ... }` */
80
+ firstLine(props: CSSProperties): CSSBuilder;
81
+ /** `&::first-letter { ... }` */
82
+ firstLetter(props: CSSProperties): CSSBuilder;
83
+ /** `&::marker { ... }` */
84
+ marker(props: CSSProperties): CSSBuilder;
85
+ /** `&::backdrop { ... }` */
86
+ backdrop(props: CSSProperties): CSSBuilder;
87
+ /** `@media (query) { ... }` */
88
+ media(query: string, props: CSSProperties): CSSBuilder;
89
+ /** `@container (query) { ... }` */
90
+ container(query: string, props: CSSProperties): CSSBuilder;
91
+ /** `@supports (condition) { ... }` */
92
+ supports(condition: string, props: CSSProperties): CSSBuilder;
93
+ /** Any arbitrary nested selector or at-rule. */
94
+ on(selector: string, props: CSSProperties): CSSBuilder;
95
+ };
96
+ export declare function isCSSBuilder(value: unknown): boolean;
97
+ export declare function createCSSBuilder(initialProps: CSSProperties): CSSBuilder;
98
+ //# sourceMappingURL=css-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-builder.d.ts","sourceRoot":"","sources":["../../src/builder/css-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1C;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;AAItG;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAEhC,wBAAwB;IACxB,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACxC,wBAAwB;IACxB,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACxC,+BAA+B;IAC/B,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC9C,gCAAgC;IAChC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC/C,yBAAyB;IACzB,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACzC,0BAA0B;IAC1B,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC1C,yBAAyB;IACzB,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAGzC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC3C,0BAA0B;IAC1B,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC1C,0BAA0B;IAC1B,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC1C,gCAAgC;IAChC,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAChD,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC3C,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC3C,wBAAwB;IACxB,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACxC,0BAA0B;IAC1B,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC1C,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAG3C,8BAA8B;IAC9B,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACxC,6BAA6B;IAC7B,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACvC,+BAA+B;IAC/B,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC/D,wBAAwB;IACxB,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAGxC,gCAAgC;IAChC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACxD,0BAA0B;IAC1B,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACvD,gDAAgD;IAChD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC1D,gCAAgC;IAChC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAGxD,0BAA0B;IAC1B,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACzC,yBAAyB;IACzB,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACxC,+BAA+B;IAC/B,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC9C,6BAA6B;IAC7B,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC5C,8BAA8B;IAC9B,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC5C,gCAAgC;IAChC,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC9C,0BAA0B;IAC1B,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACzC,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAG3C,+BAA+B;IAC/B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IACvD,mCAAmC;IACnC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC3D,sCAAsC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAC9D,gDAAgD;IAChD,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;CACxD,CAAC;AAMF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGpD;AAaD,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,aAAa,GAAG,UAAU,CAsExE"}
@@ -0,0 +1,79 @@
1
+ // ─── Runtime ──────────────────────────────────────────────────────────────────
2
+ const CSS_BUILDER = Symbol("css-builder");
3
+ export function isCSSBuilder(value) {
4
+ return typeof value === "object" && value !== null &&
5
+ value[CSS_BUILDER] === true;
6
+ }
7
+ function toKebab(camel) {
8
+ return camel.replace(/([A-Z])/g, "-$1").toLowerCase();
9
+ }
10
+ function propsToCSS(props) {
11
+ return Object.entries(props)
12
+ .filter(([, v]) => v != null)
13
+ .map(([k, v]) => `${toKebab(k)}: ${String(v)};`)
14
+ .join("\n");
15
+ }
16
+ export function createCSSBuilder(initialProps) {
17
+ const parts = [];
18
+ const rootCSS = propsToCSS(initialProps);
19
+ if (rootCSS)
20
+ parts.push(rootCSS);
21
+ const addNested = (selector, props) => {
22
+ const rawCSS = propsToCSS(props);
23
+ if (!rawCSS)
24
+ return builder;
25
+ const inner = rawCSS.split("\n").map((l) => ` ${l}`).join("\n");
26
+ parts.push(`${selector} {\n${inner}\n}`);
27
+ return builder;
28
+ };
29
+ const toCSS = () => parts.join("\n");
30
+ const builder = Object.assign(Object.create(null), {
31
+ [CSS_BUILDER]: true,
32
+ toString: toCSS,
33
+ [Symbol.toPrimitive]: toCSS,
34
+ // Pseudo-classes — interaction
35
+ hover: (p) => addNested("&:hover", p),
36
+ focus: (p) => addNested("&:focus", p),
37
+ focusWithin: (p) => addNested("&:focus-within", p),
38
+ focusVisible: (p) => addNested("&:focus-visible", p),
39
+ active: (p) => addNested("&:active", p),
40
+ visited: (p) => addNested("&:visited", p),
41
+ target: (p) => addNested("&:target", p),
42
+ // Pseudo-classes — form state
43
+ disabled: (p) => addNested('&:disabled, &[disabled], &[aria-disabled="true"]', p),
44
+ enabled: (p) => addNested("&:enabled", p),
45
+ checked: (p) => addNested("&:checked", p),
46
+ indeterminate: (p) => addNested("&:indeterminate", p),
47
+ required: (p) => addNested("&:required", p),
48
+ optional: (p) => addNested("&:optional", p),
49
+ valid: (p) => addNested("&:valid", p),
50
+ invalid: (p) => addNested("&:invalid", p),
51
+ readOnly: (p) => addNested("&:read-only", p),
52
+ // Pseudo-classes — structural
53
+ first: (p) => addNested("&:first-child", p),
54
+ last: (p) => addNested("&:last-child", p),
55
+ nthChild: (n, p) => addNested(`&:nth-child(${String(n)})`, p),
56
+ empty: (p) => addNested("&:empty", p),
57
+ // Pseudo-classes — relational (Selectors Level 4)
58
+ has: (sel, p) => addNested(`&:has(${sel})`, p),
59
+ is: (sel, p) => addNested(`&:is(${sel})`, p),
60
+ where: (sel, p) => addNested(`&:where(${sel})`, p),
61
+ not: (sel, p) => addNested(`&:not(${sel})`, p),
62
+ // Pseudo-elements
63
+ before: (p) => addNested("&::before", p),
64
+ after: (p) => addNested("&::after", p),
65
+ placeholder: (p) => addNested("&::placeholder", p),
66
+ selection: (p) => addNested("&::selection", p),
67
+ firstLine: (p) => addNested("&::first-line", p),
68
+ firstLetter: (p) => addNested("&::first-letter", p),
69
+ marker: (p) => addNested("&::marker", p),
70
+ backdrop: (p) => addNested("&::backdrop", p),
71
+ // At-rules
72
+ media: (q, p) => addNested(`@media (${q})`, p),
73
+ container: (q, p) => addNested(`@container (${q})`, p),
74
+ supports: (cond, p) => addNested(`@supports (${cond})`, p),
75
+ on: (sel, p) => addNested(sel, p),
76
+ });
77
+ return builder;
78
+ }
79
+ //# sourceMappingURL=css-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-builder.js","sourceRoot":"","sources":["../../src/builder/css-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { CSSProperties } from "./css-builder.js";
2
+ type KeyframeStops = Record<string, CSSProperties>;
3
+ /**
4
+ * Defines a scoped `@keyframes` animation and injects it into `document.head`.
5
+ * Returns the scoped animation name — use it in the `animation` or
6
+ * `animationName` property of a stylesheet field.
7
+ *
8
+ * The name is content-hashed so two identical animations share one injection.
9
+ * Safe to call at module level (outside components).
10
+ *
11
+ * @example
12
+ * const pulse = keyframes('pulse', {
13
+ * from: { opacity: 1 },
14
+ * to: { opacity: 0.4 },
15
+ * })
16
+ *
17
+ * class LoaderStyles extends Stylesheet {
18
+ * $spinner = this.css({ animation: `${pulse} 1.2s ease-in-out infinite` })
19
+ * }
20
+ */
21
+ export declare function keyframes(name: string, stops: KeyframeStops): string;
22
+ export {};
23
+ //# sourceMappingURL=keyframes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyframes.d.ts","sourceRoot":"","sources":["../../src/builder/keyframes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOtD,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAenD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,CAsBpE"}
@@ -0,0 +1,54 @@
1
+ import { getCollector } from "../internal/collector.js";
2
+ import { hashCSS } from "../internal/hash.js";
3
+ const STATIC_MODE = typeof __PRAXIS_CSS_STATIC__ !== "undefined" && __PRAXIS_CSS_STATIC__;
4
+ const injected = new Set();
5
+ function toKebab(camel) {
6
+ return camel.replace(/([A-Z])/g, "-$1").toLowerCase();
7
+ }
8
+ function stopToCSS(props) {
9
+ return Object.entries(props)
10
+ .filter(([, v]) => v != null)
11
+ .map(([k, v]) => ` ${toKebab(k)}: ${String(v)};`)
12
+ .join("\n");
13
+ }
14
+ /**
15
+ * Defines a scoped `@keyframes` animation and injects it into `document.head`.
16
+ * Returns the scoped animation name — use it in the `animation` or
17
+ * `animationName` property of a stylesheet field.
18
+ *
19
+ * The name is content-hashed so two identical animations share one injection.
20
+ * Safe to call at module level (outside components).
21
+ *
22
+ * @example
23
+ * const pulse = keyframes('pulse', {
24
+ * from: { opacity: 1 },
25
+ * to: { opacity: 0.4 },
26
+ * })
27
+ *
28
+ * class LoaderStyles extends Stylesheet {
29
+ * $spinner = this.css({ animation: `${pulse} 1.2s ease-in-out infinite` })
30
+ * }
31
+ */
32
+ export function keyframes(name, stops) {
33
+ const stopsCSS = Object.entries(stops)
34
+ .map(([stop, props]) => ` ${stop} {\n${stopToCSS(props)}\n }`)
35
+ .join("\n");
36
+ const scopedName = `prx-${name}-${hashCSS(stopsCSS)}`;
37
+ const kfCSS = `@keyframes ${scopedName} {\n${stopsCSS}\n}`;
38
+ if (STATIC_MODE)
39
+ return scopedName;
40
+ const col = getCollector();
41
+ if (col) {
42
+ col.addKeyframes(scopedName, kfCSS);
43
+ return scopedName;
44
+ }
45
+ if (!injected.has(scopedName) && typeof document !== "undefined") {
46
+ const el = document.createElement("style");
47
+ el.setAttribute("data-praxis-keyframes", scopedName);
48
+ el.textContent = kfCSS;
49
+ document.head.appendChild(el);
50
+ injected.add(scopedName);
51
+ }
52
+ return scopedName;
53
+ }
54
+ //# sourceMappingURL=keyframes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyframes.js","sourceRoot":"","sources":["../../src/builder/keyframes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAM9C,MAAM,WAAW,GACf,OAAO,qBAAqB,KAAK,WAAW,IAAI,qBAAqB,CAAC;AAIxE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;AAEnC,SAAS,OAAO,CAAC,KAAa;IAC5B,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AACxD,CAAC;AAED,SAAS,SAAS,CAAC,KAAoB;IACrC,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;SACzB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;SAC5B,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;SACnD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAoB;IAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;SACnC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,OAAO,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;SAC/D,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,UAAU,GAAG,OAAO,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;IACtD,MAAM,KAAK,GAAG,cAAc,UAAU,OAAO,QAAQ,KAAK,CAAC;IAE3D,IAAI,WAAW;QAAE,OAAO,UAAU,CAAC;IAEnC,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,IAAI,GAAG,EAAE,CAAC;QAAC,GAAG,CAAC,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAAC,OAAO,UAAU,CAAC;IAAC,CAAC;IAEpE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACjE,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC3C,EAAE,CAAC,YAAY,CAAC,uBAAuB,EAAE,UAAU,CAAC,CAAC;QACrD,EAAE,CAAC,WAAW,GAAG,KAAK,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC9B,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
package/dist/cx.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type ClassValue = string | number | boolean | null | undefined | ClassValue[] | Record<string, unknown>;
2
+ export declare function cx(...args: ClassValue[]): string;
3
+ //# sourceMappingURL=cx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cx.d.ts","sourceRoot":"","sources":["../src/cx.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,UAAU,EAAE,GACZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE5B,wBAAgB,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,CAgBhD"}
package/dist/cx.js ADDED
@@ -0,0 +1,23 @@
1
+ export function cx(...args) {
2
+ const classes = [];
3
+ for (const arg of args) {
4
+ if (!arg && arg !== 0)
5
+ continue;
6
+ if (typeof arg === "string" || typeof arg === "number") {
7
+ classes.push(String(arg));
8
+ }
9
+ else if (Array.isArray(arg)) {
10
+ const inner = cx(...arg);
11
+ if (inner)
12
+ classes.push(inner);
13
+ }
14
+ else if (typeof arg === "object") {
15
+ for (const [key, val] of Object.entries(arg)) {
16
+ if (val)
17
+ classes.push(key);
18
+ }
19
+ }
20
+ }
21
+ return classes.join(" ");
22
+ }
23
+ //# sourceMappingURL=cx.js.map
package/dist/cx.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cx.js","sourceRoot":"","sources":["../src/cx.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,EAAE,CAAC,GAAG,IAAkB;IACtC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;YAAE,SAAS;QAChC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5B,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,EAAE,CAAC,GAAI,GAAoB,CAAC,CAAC;YAC3C,IAAI,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACnC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7C,IAAI,GAAG;oBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { ReactiveStylesheet } from "../base/stylesheet.js";
2
+ export declare function getParamNames(ctor: object): Set<string>;
3
+ /**
4
+ * Marks a field in a {@link ReactiveStylesheet} subclass as a reactive CSS custom property.
5
+ * The field name becomes `--fieldName` on the component's container element.
6
+ * Setting the field updates the CSS var reactively — zero re-renders.
7
+ *
8
+ * Only valid inside a `ReactiveStylesheet` subclass. TypeScript enforces this at
9
+ * compile time via the `createFieldDecorator<ReactiveStylesheet>` constraint.
10
+ *
11
+ * @example
12
+ * class CardStyles extends ReactiveStylesheet {
13
+ * @Param() color = '#3b82f6' // → --color on the element
14
+ * @Param() radius = '8px' // → --radius on the element
15
+ *
16
+ * $root = `border-radius: var(--radius); background: var(--color);`
17
+ * }
18
+ *
19
+ * // In the component:
20
+ * this.$card.color = '#ef4444' // updates --color reactively
21
+ */
22
+ export declare function Param(): <This extends ReactiveStylesheet, Value = unknown>(_value: undefined, context: ClassFieldDecoratorContext<This, Value>) => void;
23
+ //# sourceMappingURL=param.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"param.d.ts","sourceRoot":"","sources":["../../src/decorators/param.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAIhE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAEvD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,KAAK,oIAUpB"}
@@ -0,0 +1,39 @@
1
+ import { createFieldDecorator } from "@praxisjs/decorators";
2
+ const PARAM_META = new WeakMap();
3
+ export function getParamNames(ctor) {
4
+ return PARAM_META.get(ctor) ?? new Set();
5
+ }
6
+ /**
7
+ * Marks a field in a {@link ReactiveStylesheet} subclass as a reactive CSS custom property.
8
+ * The field name becomes `--fieldName` on the component's container element.
9
+ * Setting the field updates the CSS var reactively — zero re-renders.
10
+ *
11
+ * Only valid inside a `ReactiveStylesheet` subclass. TypeScript enforces this at
12
+ * compile time via the `createFieldDecorator<ReactiveStylesheet>` constraint.
13
+ *
14
+ * @example
15
+ * class CardStyles extends ReactiveStylesheet {
16
+ * @Param() color = '#3b82f6' // → --color on the element
17
+ * @Param() radius = '8px' // → --radius on the element
18
+ *
19
+ * $root = `border-radius: var(--radius); background: var(--color);`
20
+ * }
21
+ *
22
+ * // In the component:
23
+ * this.$card.color = '#ef4444' // updates --color reactively
24
+ */
25
+ export function Param() {
26
+ return createFieldDecorator({
27
+ bind(instance, name) {
28
+ const ctor = instance.constructor;
29
+ let meta = PARAM_META.get(ctor);
30
+ if (!meta) {
31
+ meta = new Set();
32
+ PARAM_META.set(ctor, meta);
33
+ }
34
+ meta.add(name);
35
+ return {};
36
+ },
37
+ });
38
+ }
39
+ //# sourceMappingURL=param.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"param.js","sourceRoot":"","sources":["../../src/decorators/param.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAI5D,MAAM,UAAU,GAAG,IAAI,OAAO,EAAuB,CAAC;AAEtD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,KAAK;IACnB,OAAO,oBAAoB,CAAqB;QAC9C,IAAI,CAAC,QAAQ,EAAE,IAAI;YACjB,MAAM,IAAI,GAAI,QAAmB,CAAC,WAAqB,CAAC;YACxD,IAAI,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,EAAE,CAAC;gBAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;gBAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAAC,CAAC;YAC5D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Reactive field decorator that binds a signal value to a CSS custom property
3
+ * on the component's container element — with zero re-renders.
4
+ *
5
+ * Only CSS custom properties (variables) are supported: the property name must
6
+ * start with `--`.
7
+ *
8
+ * @example
9
+ * class ThemeCard extends StatefulComponent {
10
+ * @Style('--accent') accent = '#3b82f6'
11
+ * @Style('--radius') radius = '8px'
12
+ * }
13
+ */
14
+ export declare function Style(property: `--${string}`): <This extends object, Value = unknown>(_value: undefined, context: ClassFieldDecoratorContext<This, Value>) => void;
15
+ //# sourceMappingURL=style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/decorators/style.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,KAAK,MAAM,EAAE,uHAsC5C"}
@@ -0,0 +1,53 @@
1
+ import { effect, signal } from "@praxisjs/core/internal";
2
+ import { createFieldDecorator } from "@praxisjs/decorators";
3
+ /**
4
+ * Reactive field decorator that binds a signal value to a CSS custom property
5
+ * on the component's container element — with zero re-renders.
6
+ *
7
+ * Only CSS custom properties (variables) are supported: the property name must
8
+ * start with `--`.
9
+ *
10
+ * @example
11
+ * class ThemeCard extends StatefulComponent {
12
+ * @Style('--accent') accent = '#3b82f6'
13
+ * @Style('--radius') radius = '8px'
14
+ * }
15
+ */
16
+ export function Style(property) {
17
+ return createFieldDecorator({
18
+ bind(instance, _name, initialValue) {
19
+ const sig = signal(initialValue);
20
+ let stopEffect = null;
21
+ return {
22
+ descriptor: {
23
+ get() {
24
+ return sig();
25
+ },
26
+ set(v) {
27
+ sig.set(v);
28
+ },
29
+ },
30
+ onMount() {
31
+ // _anchor is the "[/ComponentName]" comment node inserted by the runtime.
32
+ // Its parentElement is the container where the component lives.
33
+ const el = instance._anchor
34
+ ?.parentElement;
35
+ if (!el)
36
+ return;
37
+ stopEffect = effect(() => {
38
+ el.style.setProperty(property, String(sig()));
39
+ });
40
+ },
41
+ onUnmount() {
42
+ stopEffect?.();
43
+ stopEffect = null;
44
+ const el = instance._anchor
45
+ ?.parentElement;
46
+ if (el)
47
+ el.style.removeProperty(property);
48
+ },
49
+ };
50
+ },
51
+ });
52
+ }
53
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/decorators/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAsB,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,KAAK,CAAC,QAAuB;IAC3C,OAAO,oBAAoB,CAAS;QAClC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,YAAY;YAChC,MAAM,GAAG,GAAG,MAAM,CAAC,YAA+B,CAAC,CAAC;YACpD,IAAI,UAAU,GAAwB,IAAI,CAAC;YAE3C,OAAO;gBACL,UAAU,EAAE;oBACV,GAAG;wBACD,OAAO,GAAG,EAAE,CAAC;oBACf,CAAC;oBACD,GAAG,CAAC,CAAkB;wBACpB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBACb,CAAC;iBACF;gBAED,OAAO;oBACL,0EAA0E;oBAC1E,gEAAgE;oBAChE,MAAM,EAAE,GAAI,QAAqC,CAAC,OAAO;wBACvD,EAAE,aAAmC,CAAC;oBACxC,IAAI,CAAC,EAAE;wBAAE,OAAO;oBAEhB,UAAU,GAAG,MAAM,CAAC,GAAG,EAAE;wBACvB,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;oBAChD,CAAC,CAAC,CAAC;gBACL,CAAC;gBAED,SAAS;oBACP,UAAU,EAAE,EAAE,CAAC;oBACf,UAAU,GAAG,IAAI,CAAC;oBAClB,MAAM,EAAE,GAAI,QAAqC,CAAC,OAAO;wBACvD,EAAE,aAAmC,CAAC;oBACxC,IAAI,EAAE;wBAAE,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;gBAC5C,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { StatefulComponent } from "@praxisjs/core";
2
+ import type { ReactiveStylesheet, Stylesheet } from "../base/stylesheet.js";
3
+ /**
4
+ * Scoped CSS field decorator. Receives a {@link Stylesheet} or
5
+ * {@link ReactiveStylesheet} subclass and builds a stylesheet object with:
6
+ *
7
+ * - `$xxx` fields → scoped class name strings (static, cached per class)
8
+ * - `@Param() xxx` fields → reactive CSS custom property setters
9
+ * (requires {@link ReactiveStylesheet} and a `StatefulComponent` field)
10
+ *
11
+ * **TypeScript enforcement:**
12
+ * - `@Styled(ReactiveStylesheetSubclass)` can only be applied to fields of
13
+ * classes that extend `StatefulComponent`.
14
+ * - `@Styled(StylesheetSubclass)` (no `@Param()`) can be applied to any component.
15
+ *
16
+ * @example — static CSS (any component)
17
+ * class IconStyles extends Stylesheet {
18
+ * $root = `display: inline-flex; align-items: center;`
19
+ * }
20
+ *
21
+ * class Icon extends StatelessComponent {
22
+ * @Styled(IconStyles) $s!: IconStyles
23
+ * }
24
+ *
25
+ * @example — reactive CSS vars (StatefulComponent only)
26
+ * class CardStyles extends ReactiveStylesheet {
27
+ * @Param() color = '#3b82f6'
28
+ * $root = `border: 2px solid var(--color);`
29
+ * }
30
+ *
31
+ * @Component()
32
+ * class Card extends StatefulComponent {
33
+ * @Styled(CardStyles) $card!: CardStyles
34
+ * }
35
+ */
36
+ export declare function Styled<T extends Stylesheet>(StyleClass: new () => T): [T] extends [ReactiveStylesheet] ? (value: undefined, context: ClassFieldDecoratorContext<StatefulComponent, T>) => void : (value: undefined, context: ClassFieldDecoratorContext<object, T>) => void;
37
+ //# sourceMappingURL=styled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styled.d.ts","sourceRoot":"","sources":["../../src/decorators/styled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AASxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAoD5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,UAAU,EACzC,UAAU,EAAE,UAAU,CAAC,GACtB,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,GAC/B,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,iBAAiB,EAAE,CAAC,CAAC,KAAK,IAAI,GACrF,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,IAAI,CAoE7E"}
@@ -0,0 +1,138 @@
1
+ import { effect, signal } from "@praxisjs/core/internal";
2
+ import { createFieldDecorator } from "@praxisjs/decorators";
3
+ import { getParamNames } from "./param.js";
4
+ import { isCSSBuilder } from "../builder/css-builder.js";
5
+ import { hashCSS } from "../internal/hash.js";
6
+ import { injectStyle, releaseStyle } from "../internal/registry.js";
7
+ const styleClassCache = new Map();
8
+ function getStyleClassData(StyleClass) {
9
+ const cached = styleClassCache.get(StyleClass);
10
+ if (cached)
11
+ return cached;
12
+ // Constructing the template fires @Param() addInitializers, populating PARAM_META.
13
+ const template = new StyleClass();
14
+ const paramNames = getParamNames(StyleClass);
15
+ const classMap = {};
16
+ const blocks = [];
17
+ const paramDefaults = {};
18
+ for (const [key, value] of Object.entries(template)) {
19
+ if (key.startsWith("$")) {
20
+ // Accept plain strings or fluent CSSBuilder values.
21
+ const css = typeof value === "string" ? value : isCSSBuilder(value) ? String(value) : null;
22
+ if (css !== null) {
23
+ const safeName = key.slice(1).replace(/[^\w-]/g, "-");
24
+ const className = `prx-${safeName}-${hashCSS(css)}`;
25
+ classMap[key] = className;
26
+ blocks.push(`.${className} { ${css.trim()} }`);
27
+ }
28
+ }
29
+ else if (paramNames.has(key) && (typeof value === "string" || typeof value === "number")) {
30
+ paramDefaults[key] = value;
31
+ }
32
+ }
33
+ const data = {
34
+ classMap: Object.freeze(classMap),
35
+ rawCSS: blocks.join("\n"),
36
+ registryKey: blocks.length ? hashCSS(blocks.join("\n")) : "",
37
+ paramDefaults: Object.freeze(paramDefaults),
38
+ };
39
+ styleClassCache.set(StyleClass, data);
40
+ return data;
41
+ }
42
+ // ─── @Styled field decorator ──────────────────────────────────────────────────
43
+ /**
44
+ * Scoped CSS field decorator. Receives a {@link Stylesheet} or
45
+ * {@link ReactiveStylesheet} subclass and builds a stylesheet object with:
46
+ *
47
+ * - `$xxx` fields → scoped class name strings (static, cached per class)
48
+ * - `@Param() xxx` fields → reactive CSS custom property setters
49
+ * (requires {@link ReactiveStylesheet} and a `StatefulComponent` field)
50
+ *
51
+ * **TypeScript enforcement:**
52
+ * - `@Styled(ReactiveStylesheetSubclass)` can only be applied to fields of
53
+ * classes that extend `StatefulComponent`.
54
+ * - `@Styled(StylesheetSubclass)` (no `@Param()`) can be applied to any component.
55
+ *
56
+ * @example — static CSS (any component)
57
+ * class IconStyles extends Stylesheet {
58
+ * $root = `display: inline-flex; align-items: center;`
59
+ * }
60
+ *
61
+ * class Icon extends StatelessComponent {
62
+ * @Styled(IconStyles) $s!: IconStyles
63
+ * }
64
+ *
65
+ * @example — reactive CSS vars (StatefulComponent only)
66
+ * class CardStyles extends ReactiveStylesheet {
67
+ * @Param() color = '#3b82f6'
68
+ * $root = `border: 2px solid var(--color);`
69
+ * }
70
+ *
71
+ * @Component()
72
+ * class Card extends StatefulComponent {
73
+ * @Styled(CardStyles) $card!: CardStyles
74
+ * }
75
+ */
76
+ export function Styled(StyleClass) {
77
+ const data = getStyleClassData(StyleClass);
78
+ // Eagerly inject CSS at decoration time (module load) so it is in the DOM
79
+ // before any component mounts — avoids layout-measurement timing issues.
80
+ // The per-instance onMount/onUnmount still manage the ref count on top of
81
+ // this base reference, which intentionally stays for the page lifetime.
82
+ if (data.rawCSS)
83
+ injectStyle(data.registryKey, data.rawCSS);
84
+ return createFieldDecorator({
85
+ bind(instance) {
86
+ const paramSignals = {};
87
+ const hasParams = Object.keys(data.paramDefaults).length > 0;
88
+ const stylesheetObj = hasParams
89
+ ? { ...data.classMap }
90
+ : data.classMap;
91
+ for (const [paramName, defaultVal] of Object.entries(data.paramDefaults)) {
92
+ const sig = signal(defaultVal);
93
+ paramSignals[paramName] = sig;
94
+ Object.defineProperty(stylesheetObj, paramName, {
95
+ get() { return sig(); },
96
+ set(v) { sig.set(v); },
97
+ enumerable: true,
98
+ configurable: true,
99
+ });
100
+ }
101
+ let stopFns = [];
102
+ return {
103
+ descriptor: { value: stylesheetObj, writable: false },
104
+ onMount() {
105
+ // Increment ref-count for this instance (base ref injected at decoration time).
106
+ if (data.rawCSS)
107
+ injectStyle(data.registryKey, data.rawCSS);
108
+ if (hasParams) {
109
+ const el = instance._anchor?.parentElement;
110
+ if (el) {
111
+ for (const [paramName, sig] of Object.entries(paramSignals)) {
112
+ stopFns.push(effect(() => {
113
+ el.style.setProperty(`--${paramName}`, String(sig()));
114
+ }));
115
+ }
116
+ }
117
+ }
118
+ },
119
+ onUnmount() {
120
+ if (data.rawCSS)
121
+ releaseStyle(data.registryKey);
122
+ for (const stop of stopFns)
123
+ stop();
124
+ stopFns = [];
125
+ if (hasParams) {
126
+ const el = instance._anchor?.parentElement;
127
+ if (el) {
128
+ for (const paramName of Object.keys(paramSignals)) {
129
+ el.style.removeProperty(`--${paramName}`);
130
+ }
131
+ }
132
+ }
133
+ },
134
+ };
135
+ },
136
+ });
137
+ }
138
+ //# sourceMappingURL=styled.js.map