@askrjs/themes 0.0.16 → 0.0.19

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 (179) hide show
  1. package/README.md +25 -0
  2. package/THEMING.md +6 -5
  3. package/dist/components/_internal/block-layout.d.ts +1 -0
  4. package/dist/components/_internal/block-layout.js +14 -11
  5. package/dist/components/_internal/block-layout.js.map +1 -1
  6. package/dist/components/_internal/classes.js +8 -1
  7. package/dist/components/_internal/classes.js.map +1 -1
  8. package/dist/components/_internal/jsx.js.map +1 -1
  9. package/dist/components/_internal/style.js +185 -28
  10. package/dist/components/_internal/style.js.map +1 -1
  11. package/dist/components/alert/alert.d.ts +1 -0
  12. package/dist/components/alert/alert.js.map +1 -1
  13. package/dist/components/alert/alert.types.d.ts +1 -0
  14. package/dist/components/aside/aside.d.ts +1 -0
  15. package/dist/components/aside/aside.js.map +1 -1
  16. package/dist/components/aspect-ratio/aspect-ratio.d.ts +1 -0
  17. package/dist/components/aspect-ratio/aspect-ratio.js.map +1 -1
  18. package/dist/components/aspect-ratio/aspect-ratio.types.d.ts +1 -0
  19. package/dist/components/badge/badge.d.ts +1 -0
  20. package/dist/components/badge/badge.js.map +1 -1
  21. package/dist/components/badge/badge.types.d.ts +1 -0
  22. package/dist/components/block/block.d.ts +1 -0
  23. package/dist/components/block/block.js +2 -2
  24. package/dist/components/block/block.js.map +1 -1
  25. package/dist/components/block/block.types.d.ts +1 -0
  26. package/dist/components/brand/brand.d.ts +1 -0
  27. package/dist/components/brand/brand.js +2 -2
  28. package/dist/components/brand/brand.js.map +1 -1
  29. package/dist/components/brand/brand.types.d.ts +1 -0
  30. package/dist/components/button-group/button-group.d.ts +1 -0
  31. package/dist/components/button-group/button-group.js.map +1 -1
  32. package/dist/components/button-group/button-group.types.d.ts +1 -0
  33. package/dist/components/card/card.d.ts +1 -0
  34. package/dist/components/card/card.js.map +1 -1
  35. package/dist/components/card/card.types.d.ts +1 -0
  36. package/dist/components/catalog.d.ts +1 -0
  37. package/dist/components/catalog.js +22 -13
  38. package/dist/components/catalog.js.map +1 -1
  39. package/dist/components/close/close.d.ts +1 -0
  40. package/dist/components/close/close.js.map +1 -1
  41. package/dist/components/container/container.d.ts +1 -0
  42. package/dist/components/container/container.js.map +1 -1
  43. package/dist/components/empty-state/empty-state.d.ts +1 -0
  44. package/dist/components/empty-state/empty-state.js.map +1 -1
  45. package/dist/components/field/field.d.ts +1 -0
  46. package/dist/components/field/field.js.map +1 -1
  47. package/dist/components/field/field.types.d.ts +1 -0
  48. package/dist/components/footer/footer.d.ts +1 -0
  49. package/dist/components/footer/footer.js.map +1 -1
  50. package/dist/components/footer/footer.types.d.ts +1 -0
  51. package/dist/components/grid/grid.d.ts +1 -0
  52. package/dist/components/grid/grid.js.map +1 -1
  53. package/dist/components/grid/grid.types.d.ts +1 -0
  54. package/dist/components/header/header.d.ts +1 -0
  55. package/dist/components/header/header.js.map +1 -1
  56. package/dist/components/input-group/input-group.d.ts +1 -0
  57. package/dist/components/input-group/input-group.js.map +1 -1
  58. package/dist/components/input-group/input-group.types.d.ts +1 -0
  59. package/dist/components/jsx-types.d.ts +12 -0
  60. package/dist/components/main/main.d.ts +1 -0
  61. package/dist/components/main/main.js.map +1 -1
  62. package/dist/components/nav/nav.d.ts +1 -0
  63. package/dist/components/nav/nav.js +6 -2
  64. package/dist/components/nav/nav.js.map +1 -1
  65. package/dist/components/nav/nav.types.d.ts +2 -0
  66. package/dist/components/navbar/navbar.d.ts +1 -0
  67. package/dist/components/navbar/navbar.js +15 -10
  68. package/dist/components/navbar/navbar.js.map +1 -1
  69. package/dist/components/overlays/dropdown-content.d.ts +1 -0
  70. package/dist/components/overlays/dropdown-content.js.map +1 -1
  71. package/dist/components/page/page.d.ts +1 -0
  72. package/dist/components/page/page.js.map +1 -1
  73. package/dist/components/page-header/page-header.d.ts +1 -0
  74. package/dist/components/page-header/page-header.js.map +1 -1
  75. package/dist/components/section/section.d.ts +1 -0
  76. package/dist/components/section/section.js.map +1 -1
  77. package/dist/components/separator/separator.d.ts +1 -0
  78. package/dist/components/separator/separator.js.map +1 -1
  79. package/dist/components/separator/separator.types.d.ts +1 -0
  80. package/dist/components/sidebar/index.d.ts +2 -2
  81. package/dist/components/sidebar/sidebar.d.ts +2 -1
  82. package/dist/components/sidebar/sidebar.js +39 -13
  83. package/dist/components/sidebar/sidebar.js.map +1 -1
  84. package/dist/components/sidebar/sidebar.types.d.ts +3 -1
  85. package/dist/components/skeleton/skeleton.d.ts +1 -0
  86. package/dist/components/skeleton/skeleton.js.map +1 -1
  87. package/dist/components/skeleton/skeleton.types.d.ts +1 -0
  88. package/dist/components/spinner/spinner.d.ts +1 -0
  89. package/dist/components/spinner/spinner.js.map +1 -1
  90. package/dist/components/stat/stat.d.ts +1 -0
  91. package/dist/components/stat/stat.js.map +1 -1
  92. package/dist/components/stat/stat.types.d.ts +1 -0
  93. package/dist/components/text/text.d.ts +1 -0
  94. package/dist/components/text/text.js.map +1 -1
  95. package/dist/components/text/text.types.d.ts +1 -0
  96. package/dist/components/theme/theme.d.ts +2 -0
  97. package/dist/components/theme/theme.js +58 -19
  98. package/dist/components/theme/theme.js.map +1 -1
  99. package/dist/components/toolbar/toolbar.d.ts +1 -0
  100. package/dist/components/toolbar/toolbar.js.map +1 -1
  101. package/dist/components.d.ts +6 -3
  102. package/dist/core.d.ts +2 -2
  103. package/dist/ssr.d.ts +18 -0
  104. package/dist/ssr.js +63 -0
  105. package/dist/ssr.js.map +1 -0
  106. package/dist/themes/default/index.css +43 -7
  107. package/package.json +7 -3
  108. package/src/components/_internal/block-layout.ts +27 -16
  109. package/src/components/_internal/classes.ts +15 -2
  110. package/src/components/_internal/jsx.ts +1 -1
  111. package/src/components/_internal/style.ts +248 -27
  112. package/src/components/alert/alert.tsx +1 -0
  113. package/src/components/alert/alert.types.ts +1 -0
  114. package/src/components/aside/aside.tsx +1 -0
  115. package/src/components/aspect-ratio/aspect-ratio.tsx +1 -0
  116. package/src/components/aspect-ratio/aspect-ratio.types.ts +1 -0
  117. package/src/components/badge/badge.tsx +1 -0
  118. package/src/components/badge/badge.types.ts +1 -0
  119. package/src/components/block/block.tsx +4 -1
  120. package/src/components/block/block.types.ts +1 -0
  121. package/src/components/brand/brand.tsx +6 -2
  122. package/src/components/brand/brand.types.ts +1 -0
  123. package/src/components/button-group/button-group.tsx +1 -0
  124. package/src/components/button-group/button-group.types.ts +1 -0
  125. package/src/components/card/card.tsx +1 -0
  126. package/src/components/card/card.types.ts +1 -0
  127. package/src/components/catalog.tsx +15 -4
  128. package/src/components/close/close.tsx +1 -0
  129. package/src/components/container/container.tsx +1 -0
  130. package/src/components/empty-state/empty-state.tsx +1 -0
  131. package/src/components/field/field.tsx +1 -0
  132. package/src/components/field/field.types.ts +1 -0
  133. package/src/components/footer/footer.tsx +1 -0
  134. package/src/components/footer/footer.types.ts +1 -0
  135. package/src/components/grid/grid.tsx +1 -0
  136. package/src/components/grid/grid.types.ts +1 -0
  137. package/src/components/header/header.tsx +1 -0
  138. package/src/components/input-group/input-group.tsx +1 -0
  139. package/src/components/input-group/input-group.types.ts +1 -0
  140. package/src/components/jsx-types.ts +16 -0
  141. package/src/components/main/main.tsx +1 -0
  142. package/src/components/nav/nav.tsx +16 -2
  143. package/src/components/nav/nav.types.ts +2 -0
  144. package/src/components/navbar/navbar.tsx +19 -8
  145. package/src/components/overlays/dropdown-content.tsx +1 -0
  146. package/src/components/page/page.tsx +1 -0
  147. package/src/components/page-header/page-header.tsx +1 -0
  148. package/src/components/section/section.tsx +1 -0
  149. package/src/components/separator/separator.tsx +1 -0
  150. package/src/components/separator/separator.types.ts +1 -0
  151. package/src/components/sidebar/index.ts +1 -0
  152. package/src/components/sidebar/sidebar.tsx +45 -20
  153. package/src/components/sidebar/sidebar.types.ts +3 -0
  154. package/src/components/skeleton/skeleton.tsx +1 -0
  155. package/src/components/skeleton/skeleton.types.ts +1 -0
  156. package/src/components/spinner/spinner.tsx +1 -0
  157. package/src/components/stat/stat.tsx +1 -0
  158. package/src/components/stat/stat.types.ts +1 -0
  159. package/src/components/text/text.tsx +1 -0
  160. package/src/components/text/text.types.ts +1 -0
  161. package/src/components/theme/theme.tsx +85 -14
  162. package/src/components/toolbar/toolbar.tsx +1 -0
  163. package/src/components.ts +9 -0
  164. package/src/ssr.ts +102 -0
  165. package/src/themes/default/styles/base/text.css +4 -4
  166. package/src/themes/default/styles/display/coverage.css +3 -1
  167. package/src/themes/default/styles/layout/block.css +4 -0
  168. package/src/themes/default/styles/layout/layout.css +108 -0
  169. package/src/themes/default/styles/shell/navbar.css +4 -1
  170. package/src/themes/default/tokens.css +1 -1
  171. package/src/themes/presets/ginger.css +3 -3
  172. package/templates/theme/styles/base/text.css +4 -4
  173. package/templates/theme/styles/display/progress-circle.css +2 -0
  174. package/templates/theme/styles/forms/input-group.css +2 -1
  175. package/templates/theme/styles/layout/layout.css +108 -0
  176. package/templates/theme/styles/overlays/toast.css +11 -0
  177. package/templates/theme/styles/shell/navbar.css +4 -1
  178. package/templates/theme/styles/shell/sidebar.css +18 -1
  179. package/templates/theme/tokens.css +1 -1
@@ -1,6 +1,110 @@
1
- import { cspNonce } from "@askrjs/askr";
1
+ import * as Askr from "@askrjs/askr";
2
2
 
3
3
  const cssPropertyNameCache = new Map<string, string>();
4
+ const MAX_PROPERTY_CACHE = 256;
5
+
6
+ const CSS_UNSAFE_RE = /[{}<>\\]/;
7
+ const CSS_URI_SCHEME_RE = /(?:^|[\s(,])([a-z][a-z0-9+.-]*):/i;
8
+ const CSS_FUNCTION_NAME_RE = /([a-z-][a-z0-9-]*)\s*\(/gi;
9
+ const CSS_ALLOWED_FUNCTIONS = new Set([
10
+ "var",
11
+ "calc",
12
+ "min",
13
+ "max",
14
+ "clamp",
15
+ "minmax",
16
+ "repeat",
17
+ "rgb",
18
+ "rgba",
19
+ "hsl",
20
+ "hsla",
21
+ "lab",
22
+ "lch",
23
+ "oklab",
24
+ "oklch",
25
+ "color",
26
+ "color-mix",
27
+ "translate",
28
+ "translatex",
29
+ "translatey",
30
+ "translatez",
31
+ "scale",
32
+ "scalex",
33
+ "scaley",
34
+ "scalez",
35
+ "rotate",
36
+ "rotatex",
37
+ "rotatey",
38
+ "rotatez",
39
+ "skew",
40
+ "skewx",
41
+ "skewy",
42
+ "matrix",
43
+ "matrix3d",
44
+ "linear-gradient",
45
+ "radial-gradient",
46
+ "conic-gradient",
47
+ "repeating-linear-gradient",
48
+ "repeating-radial-gradient",
49
+ "repeating-conic-gradient",
50
+ "cubic-bezier",
51
+ "steps",
52
+ ]);
53
+
54
+ function isSafeCssPropertyName(name: string): boolean {
55
+ if (name.startsWith("--")) return /^--[a-zA-Z0-9_-]+$/.test(name);
56
+ return /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name);
57
+ }
58
+
59
+ function isSafeCssValue(value: string): boolean {
60
+ if (CSS_UNSAFE_RE.test(value) || CSS_URI_SCHEME_RE.test(value)) return false;
61
+
62
+ for (const match of value.matchAll(CSS_FUNCTION_NAME_RE)) {
63
+ if (!CSS_ALLOWED_FUNCTIONS.has(match[1]!.toLowerCase())) return false;
64
+ }
65
+
66
+ return true;
67
+ }
68
+
69
+ function splitCssDeclarations(value: string): string[] {
70
+ const declarations: string[] = [];
71
+ let start = 0;
72
+ let depth = 0;
73
+ let quote: string | undefined;
74
+
75
+ for (let index = 0; index < value.length; index += 1) {
76
+ const char = value[index]!;
77
+ if (quote) {
78
+ if (char === quote && value[index - 1] !== "\\") quote = undefined;
79
+ continue;
80
+ }
81
+ if (char === '"' || char === "'") {
82
+ quote = char;
83
+ continue;
84
+ }
85
+ if (char === "(") depth += 1;
86
+ if (char === ")" && depth > 0) depth -= 1;
87
+ if (char === ";" && depth === 0) {
88
+ declarations.push(value.slice(start, index));
89
+ start = index + 1;
90
+ }
91
+ }
92
+
93
+ declarations.push(value.slice(start));
94
+ return declarations;
95
+ }
96
+
97
+ function serializeCssString(value: string): string {
98
+ const entries: Array<[string, string]> = [];
99
+ for (const candidate of splitCssDeclarations(value)) {
100
+ const separator = candidate.indexOf(":");
101
+ if (separator <= 0) continue;
102
+ const key = candidate.slice(0, separator).trim();
103
+ const cssValue = candidate.slice(separator + 1).trim();
104
+ if (key && cssValue) entries.push([key, cssValue]);
105
+ }
106
+ return serializeCssDeclarations(Object.fromEntries(entries));
107
+ }
4
108
 
5
109
  function cssPropertyName(name: string): string {
6
110
  let cached = cssPropertyNameCache.get(name);
@@ -20,7 +124,9 @@ function cssPropertyName(name: string): string {
20
124
  }
21
125
  }
22
126
 
23
- cssPropertyNameCache.set(name, result);
127
+ if (cssPropertyNameCache.size < MAX_PROPERTY_CACHE) {
128
+ cssPropertyNameCache.set(name, result);
129
+ }
24
130
  return result;
25
131
  }
26
132
 
@@ -34,7 +140,17 @@ export function serializeCssDeclarations(styles: Record<string, unknown>): strin
34
140
  continue;
35
141
  }
36
142
 
37
- const declaration = `${cssPropertyName(key)}:${String(value)}`;
143
+ const rawProperty = key.trim();
144
+ if (!isSafeCssPropertyName(rawProperty)) {
145
+ continue;
146
+ }
147
+ const property = cssPropertyName(rawProperty).trim();
148
+ const cssValue = String(value).trim();
149
+ if (!cssValue || !isSafeCssValue(cssValue)) {
150
+ continue;
151
+ }
152
+
153
+ const declaration = `${property}:${cssValue}`;
38
154
  result = result ? `${result};${declaration}` : declaration;
39
155
  }
40
156
 
@@ -59,16 +175,86 @@ export function mergeCssVar(style: unknown, name: string, value: string): string
59
175
 
60
176
  const STYLE_REGISTRY_ATTR = "data-askr-style-registry";
61
177
  const STYLE_CLASS_PREFIX = "ak-style-";
178
+ const MAX_STYLE_RULES = 512;
179
+ const MAX_SSR_STYLE_CACHE = 4096;
62
180
 
63
- const styleClassCache = new Map<string, string>();
181
+ type StyleRule = {
182
+ className: string;
183
+ declarations: string;
184
+ rule: string;
185
+ };
186
+
187
+ const styleRulesByClass = new Map<string, StyleRule>();
64
188
  type StyleRegistry = {
65
189
  element: HTMLStyleElement;
66
- rules: Map<string, { className: string; rule: string }>;
190
+ ruleCount: number;
191
+ rules: Map<string, StyleRule>;
67
192
  };
68
193
  const registries = new WeakMap<Document, Map<string, StyleRegistry>>();
69
- const MAX_STYLE_RULES = 512;
194
+ function countRegisteredRules(value: string | null): number {
195
+ return value?.match(/\.ak-style-[a-z0-9]+\{/g)?.length ?? 0;
196
+ }
197
+
198
+ function styleClassName(declarations: string): string {
199
+ let first = 0xdeadbeef ^ declarations.length;
200
+ let second = 0x41c6ce57 ^ declarations.length;
201
+
202
+ for (let index = 0; index < declarations.length; index += 1) {
203
+ const code = declarations.charCodeAt(index);
204
+ first = Math.imul(first ^ code, 2_654_435_761);
205
+ second = Math.imul(second ^ code, 1_597_334_677);
206
+ }
207
+
208
+ first =
209
+ Math.imul(first ^ (first >>> 16), 2_246_822_507) ^
210
+ Math.imul(second ^ (second >>> 13), 3_266_489_909);
211
+ second =
212
+ Math.imul(second ^ (second >>> 16), 2_246_822_507) ^
213
+ Math.imul(first ^ (first >>> 13), 3_266_489_909);
214
+
215
+ return `${STYLE_CLASS_PREFIX}${(second >>> 0).toString(36)}${(first >>> 0).toString(36)}`;
216
+ }
217
+
218
+ function escapeStyleRawText(value: string): string {
219
+ return value.replace(/<\//g, "<\\/");
220
+ }
221
+
222
+ function styleRuleFor(declarations: string): StyleRule {
223
+ const className = styleClassName(declarations);
224
+ const existing = styleRulesByClass.get(className);
225
+ if (existing) {
226
+ if (existing.declarations !== declarations) {
227
+ throw new RangeError("Theme style class collision detected.");
228
+ }
229
+ return existing;
230
+ }
231
+
232
+ const entry = {
233
+ className,
234
+ declarations,
235
+ rule: `.${className}{${escapeStyleRawText(declarations)}}`,
236
+ };
237
+ return entry;
238
+ }
239
+
240
+ function rememberStyleRule(entry: StyleRule): void {
241
+ styleRulesByClass.delete(entry.className);
242
+ styleRulesByClass.set(entry.className, entry);
243
+ while (styleRulesByClass.size > MAX_SSR_STYLE_CACHE) {
244
+ const oldest = styleRulesByClass.keys().next().value;
245
+ if (oldest === undefined) break;
246
+ styleRulesByClass.delete(oldest);
247
+ }
248
+ }
70
249
 
71
- let nextStyleClassId = 0;
250
+ function registerSSRStyle(entry: StyleRule): void {
251
+ const register = (
252
+ Askr as typeof Askr & {
253
+ registerSSRStyle?: (id: string, cssText: string) => void;
254
+ }
255
+ ).registerSSRStyle;
256
+ register?.(entry.className, entry.rule);
257
+ }
72
258
 
73
259
  function ensureStyleRegistry(nonce: string | undefined): StyleRegistry | null {
74
260
  if (typeof document === "undefined") return null;
@@ -81,17 +267,31 @@ function ensureStyleRegistry(nonce: string | undefined): StyleRegistry | null {
81
267
  const current = documentRegistries.get(key);
82
268
  if (current?.element.isConnected) return current;
83
269
 
84
- const styleElement = document.createElement("style");
85
- styleElement.setAttribute(STYLE_REGISTRY_ATTR, "true");
86
- if (nonce !== undefined) styleElement.nonce = nonce;
87
- (document.head ?? document.documentElement).append(styleElement);
88
- const registry: StyleRegistry = { element: styleElement, rules: new Map() };
270
+ const existingStyleElements = Array.from(
271
+ document.querySelectorAll<HTMLStyleElement>(`style[${STYLE_REGISTRY_ATTR}]`),
272
+ );
273
+ const styleElement =
274
+ existingStyleElements.find((element) => (element.nonce || undefined) === nonce) ??
275
+ (nonce === undefined && existingStyleElements.length === 1
276
+ ? existingStyleElements[0]
277
+ : undefined) ??
278
+ document.createElement("style");
279
+ if (!styleElement.isConnected) {
280
+ styleElement.setAttribute(STYLE_REGISTRY_ATTR, "true");
281
+ if (nonce !== undefined) styleElement.nonce = nonce;
282
+ (document.head ?? document.documentElement).append(styleElement);
283
+ }
284
+ const registry: StyleRegistry = {
285
+ element: styleElement,
286
+ ruleCount: countRegisteredRules(styleElement.textContent),
287
+ rules: new Map(),
288
+ };
89
289
  documentRegistries.set(key, registry);
90
290
  return registry;
91
291
  }
92
292
 
93
293
  function normalizeDeclarations(declarations: string): string {
94
- return declarations.trim().replace(/;+\s*$/, "");
294
+ return serializeCssString(declarations);
95
295
  }
96
296
 
97
297
  export function styleDeclarationsToClass(declarations: string | undefined): string | undefined {
@@ -100,27 +300,48 @@ export function styleDeclarationsToClass(declarations: string | undefined): stri
100
300
  const normalized = normalizeDeclarations(declarations);
101
301
  if (!normalized) return undefined;
102
302
 
103
- const nonce = cspNonce();
303
+ const entry = styleRuleFor(normalized);
304
+ const nonce = Askr.cspNonce();
104
305
  const registry = ensureStyleRegistry(nonce);
105
306
  const registered = registry?.rules.get(normalized);
106
- if (registered) return registered.className;
107
-
108
- let className = styleClassCache.get(normalized);
109
- if (className === undefined) {
110
- className = `${STYLE_CLASS_PREFIX}${++nextStyleClassId}`;
111
- styleClassCache.set(normalized, className);
307
+ if (registered) {
308
+ registerSSRStyle(registered);
309
+ return registered.className;
112
310
  }
113
311
 
114
312
  if (registry) {
115
- const styleElement = registry.element;
116
- if (styleElement) {
117
- if (registry.rules.size >= MAX_STYLE_RULES)
313
+ if (!registry.rules.has(normalized) && !registry.element.textContent?.includes(entry.rule)) {
314
+ if (registry.ruleCount >= MAX_STYLE_RULES) {
315
+ throw new RangeError("Theme style registry capacity exceeded.");
316
+ }
317
+ }
318
+ if (!registry.element.textContent?.includes(entry.rule)) {
319
+ registry.element.append(entry.rule, "\n");
320
+ registry.ruleCount += 1;
321
+ }
322
+ registry.rules.set(normalized, entry);
323
+ }
324
+
325
+ rememberStyleRule(entry);
326
+ registerSSRStyle(entry);
327
+
328
+ return entry.className;
329
+ }
330
+
331
+ export function styleRulesForHtml(html: string): string[] {
332
+ const rules = new Map<string, string>();
333
+ const classAttributePattern = /\sclass=(?:"([^"]*)"|'([^']*)')/g;
334
+
335
+ for (const attribute of html.matchAll(classAttributePattern)) {
336
+ const value = attribute[1] ?? attribute[2] ?? "";
337
+ for (const className of value.split(/\s+/)) {
338
+ const entry = styleRulesByClass.get(className);
339
+ if (entry) rules.set(className, entry.rule);
340
+ if (rules.size > MAX_STYLE_RULES) {
118
341
  throw new RangeError("Theme style registry capacity exceeded.");
119
- const rule = `.${className}{${normalized}}`;
120
- registry.rules.set(normalized, { className, rule });
121
- styleElement.append(rule, "\n");
342
+ }
122
343
  }
123
344
  }
124
345
 
125
- return className;
346
+ return Array.from(rules.values());
126
347
  }
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { classes } from "../_internal/classes";
2
3
  import { Close } from "../close";
3
4
  import type { AlertProps } from "./alert.types";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { Ref } from "@askrjs/askr/foundations/utilities";
2
3
 
3
4
  export type AlertVariant = "default" | "info" | "success" | "warning" | "danger";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Block } from "../block";
2
3
  import type { AsideProps } from "./aside.types";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Slot } from "@askrjs/askr/foundations";
2
3
  import { mergeProps } from "../_internal/merge-props";
3
4
  import { mergeLayoutStyles } from "../_internal/block-layout";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { JSXElement } from "@askrjs/askr/foundations";
2
3
  import type { Ref } from "@askrjs/askr/foundations/utilities";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Slot } from "@askrjs/askr/foundations";
2
3
  import { classes } from "../_internal/classes";
3
4
  import { mergeProps } from "../_internal/merge-props";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { JSXElement } from "@askrjs/askr/foundations";
2
3
  import type { Ref } from "@askrjs/askr/foundations/utilities";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Slot } from "@askrjs/askr/foundations";
2
3
  import { classes } from "../_internal/classes";
3
4
  import {
@@ -15,8 +16,8 @@ const DEFAULT_ELEMENT = "div";
15
16
  export function Block(props: BlockNativeProps): JSX.Element;
16
17
  export function Block(props: BlockAsChildProps): JSX.Element;
17
18
  export function Block(props: BlockNativeProps | BlockAsChildProps) {
18
- const as = "as" in props ? props.as : DEFAULT_ELEMENT;
19
19
  const {
20
+ as: asProp,
20
21
  asChild,
21
22
  children,
22
23
  ref,
@@ -25,10 +26,12 @@ export function Block(props: BlockNativeProps | BlockAsChildProps) {
25
26
  style: userStyle,
26
27
  ...rest
27
28
  } = props as (BlockNativeProps | BlockAsChildProps) & {
29
+ as?: BlockNativeProps["as"];
28
30
  class?: unknown;
29
31
  className?: unknown;
30
32
  style?: unknown;
31
33
  };
34
+ const as = asProp ?? DEFAULT_ELEMENT;
32
35
 
33
36
  const { blockProps, rest: passthroughProps } = splitBlockLayoutProps(rest);
34
37
  const layoutStyle: Record<string, string | number> = {};
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { JSXElement } from "@askrjs/askr/foundations";
2
3
  import type { Ref } from "@askrjs/askr/foundations/utilities";
3
4
  import type {
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Slot } from "@askrjs/askr/foundations";
2
3
  import { classes } from "../_internal/classes";
3
4
  import { mergeProps } from "../_internal/merge-props";
@@ -16,14 +17,17 @@ export function Brand<TElement extends "div" | "a" | "span" = "div">(
16
17
  props: BrandProps<TElement>,
17
18
  ): JSX.Element {
18
19
  const {
20
+ as: asProp,
19
21
  asChild,
20
22
  children,
21
23
  class: classProp,
22
24
  className,
23
25
  ref,
24
26
  ...rest
25
- } = props as BrandNativeProps | BrandAsChildProps;
26
- const as = "as" in props ? props.as : undefined;
27
+ } = props as (BrandNativeProps | BrandAsChildProps) & {
28
+ as?: "div" | "a" | "span";
29
+ };
30
+ const as = asProp;
27
31
 
28
32
  const finalProps = mergeProps(rest, {
29
33
  ref,
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { Ref } from "@askrjs/askr/foundations/utilities";
2
3
 
3
4
  export type BrandElement = "div" | "a" | "span";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { classes } from "../_internal/classes";
2
3
  import { ButtonGroupProps } from "./button-group.types";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { Ref } from "@askrjs/askr/foundations/utilities";
2
3
 
3
4
  export type ButtonGroupOrientation = "horizontal" | "vertical";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { classes } from "../_internal/classes";
2
3
  import { mergeProps } from "../_internal/merge-props";
3
4
  import type {
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { Ref } from "@askrjs/askr/foundations/utilities";
2
3
 
3
4
  export type CardVariant = "default" | "raised";
@@ -1,11 +1,16 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Slot } from "@askrjs/askr/foundations";
2
3
  import type { Ref } from "@askrjs/askr/foundations/utilities";
3
- import { DialogContent } from "@askrjs/ui";
4
+ import { DialogContent, DialogDescription, DialogTitle } from "@askrjs/ui";
4
5
  import { Block } from "./block";
5
6
  import { classes } from "./_internal/classes";
6
7
  import { mergeProps } from "./_internal/merge-props";
7
8
 
8
9
  const CatalogDialogContent = DialogContent as (props: Record<string, unknown>) => JSX.Element;
10
+ const CatalogDialogTitle = DialogTitle as (props: Record<string, unknown>) => JSX.Element;
11
+ const CatalogDialogDescription = DialogDescription as (
12
+ props: Record<string, unknown>,
13
+ ) => JSX.Element;
9
14
 
10
15
  type CatalogElement = keyof JSX.IntrinsicElements;
11
16
 
@@ -42,13 +47,18 @@ function catalogPart(props: CatalogComponentProps, defaults: CatalogDefaults): J
42
47
  }
43
48
 
44
49
  function buttonPart(props: CatalogComponentProps, slot: string, className?: string): JSX.Element {
45
- const { children, ref, class: classProp, type = "button", ...rest } = props;
50
+ const { as, asChild, children, ref, class: classProp, type = "button", ...rest } = props;
51
+ void as;
46
52
  const finalProps = mergeProps(rest, {
47
53
  ref,
48
54
  class: classes(className, classProp),
49
55
  "data-slot": slot,
50
56
  });
51
57
 
58
+ if (asChild) {
59
+ return <Slot asChild {...finalProps} children={children as JSX.Element} />;
60
+ }
61
+
52
62
  return (
53
63
  <button type={type as "button" | "submit" | "reset"} {...finalProps}>
54
64
  {children}
@@ -249,6 +259,7 @@ export function CalendarDay(
249
259
  const { disabled, outside, rangeEnd, rangeMiddle, rangeStart, selected, today, ...rest } = props;
250
260
  return buttonPart(
251
261
  {
262
+ disabled,
252
263
  "aria-disabled": disabled ? "true" : undefined,
253
264
  "aria-selected": selected ? "true" : undefined,
254
265
  "data-disabled": disabled ? "" : undefined,
@@ -661,11 +672,11 @@ export function SheetFooter(props: CatalogComponentProps): JSX.Element {
661
672
  }
662
673
 
663
674
  export function SheetTitle(props: CatalogComponentProps): JSX.Element {
664
- return catalogPart(props, { slot: "sheet-title", element: "h2" });
675
+ return <CatalogDialogTitle {...props} data-slot="sheet-title" />;
665
676
  }
666
677
 
667
678
  export function SheetDescription(props: CatalogComponentProps): JSX.Element {
668
- return catalogPart(props, { slot: "sheet-description", element: "p" });
679
+ return <CatalogDialogDescription {...props} data-slot="sheet-description" />;
669
680
  }
670
681
 
671
682
  export function Toaster(props: CatalogComponentProps): JSX.Element {
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Button, type ButtonNativeProps } from "@askrjs/ui";
2
3
  import { classes } from "../_internal/classes";
3
4
  import type { CloseNativeProps } from "./close.types";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Block } from "../block";
2
3
  import type { ContainerProps } from "./container.types";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Block } from "../block";
2
3
  import type { EmptyStateProps } from "./empty-state.types";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { classes } from "../_internal/classes";
2
3
  import { mergeProps } from "../_internal/merge-props";
3
4
  import type { FieldErrorProps, FieldHintProps, FieldProps } from "./field.types";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { Ref } from "@askrjs/askr/foundations/utilities";
2
3
 
3
4
  type DivProps = Omit<JSX.IntrinsicElements["div"], "children" | "ref">;
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Block } from "../block";
2
3
  import { classes } from "../_internal/classes";
3
4
  import { mergeProps } from "../_internal/merge-props";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { BlockElementProps } from "../block";
2
3
  import type { Ref } from "@askrjs/askr/foundations/utilities";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { classes } from "../_internal/classes";
2
3
  import { mergeLayoutStyles } from "../_internal/block-layout";
3
4
  import { mergeProps } from "../_internal/merge-props";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { Ref } from "@askrjs/askr/foundations/utilities";
2
3
  import type { BlockSpace, ResponsiveValue } from "../_internal/block-layout";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Block } from "../block";
2
3
  import type { HeaderProps } from "./header.types";
3
4
 
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Slot } from "@askrjs/askr/foundations";
2
3
  import { mergeProps } from "@askrjs/askr/foundations/utilities";
3
4
  import { classes } from "../_internal/classes";
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import type { JSXElement } from "@askrjs/askr/foundations";
2
3
  import type { Ref } from "@askrjs/askr/foundations/utilities";
3
4
 
@@ -0,0 +1,16 @@
1
+ import type { JSXElement } from "@askrjs/askr/foundations/structures";
2
+
3
+ declare global {
4
+ namespace JSX {
5
+ interface Element extends JSXElement {
6
+ readonly __askrThemesJsxElementBrand?: never;
7
+ }
8
+
9
+ interface IntrinsicElements {
10
+ // @ts-ignore The Askr source types already provide a compatible fallback index.
11
+ [element: string]: Record<string, unknown>;
12
+ }
13
+ }
14
+ }
15
+
16
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "@askrjs/askr/jsx-runtime";
1
2
  import { Block } from "../block";
2
3
  import type { MainProps } from "./main.types";
3
4