@vertz/ui 0.2.15 → 0.2.17

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 (33) hide show
  1. package/README.md +49 -0
  2. package/dist/shared/{chunk-dksg08fq.js → chunk-07bh4m1e.js} +1 -1
  3. package/dist/shared/chunk-14eqne2a.js +10 -0
  4. package/dist/shared/{chunk-nn9v1zmk.js → chunk-2wtb9x81.js} +83 -20
  5. package/dist/shared/{chunk-8hsz5y4a.js → chunk-4fwcwxn6.js} +14 -4
  6. package/dist/shared/{chunk-4txc67nd.js → chunk-6jyt4ycw.js} +67 -2
  7. package/dist/shared/{chunk-83g4h38e.js → chunk-6wd36w21.js} +1 -0
  8. package/dist/shared/{chunk-h89w580h.js → chunk-afawz764.js} +1 -1
  9. package/dist/shared/{chunk-1wby7nex.js → chunk-dhehvmj0.js} +161 -9
  10. package/dist/shared/{chunk-wymw818z.js → chunk-fkbgbf3n.js} +48 -9
  11. package/dist/shared/{chunk-hw67ckr3.js → chunk-fs3eec4b.js} +230 -19
  12. package/dist/shared/{chunk-5dbq8jp9.js → chunk-j09yyh34.js} +72 -6
  13. package/dist/shared/chunk-mtsvrj9e.js +23 -0
  14. package/dist/shared/{chunk-j6qyxfdc.js → chunk-vndfjfdy.js} +3 -3
  15. package/dist/src/auth/public.d.ts +40 -24
  16. package/dist/src/auth/public.js +110 -52
  17. package/dist/src/css/public.d.ts +110 -2
  18. package/dist/src/css/public.js +8 -4
  19. package/dist/src/form/public.d.ts +29 -6
  20. package/dist/src/form/public.js +2 -2
  21. package/dist/src/index.d.ts +284 -13
  22. package/dist/src/index.js +160 -14
  23. package/dist/src/internals.d.ts +168 -5
  24. package/dist/src/internals.js +14 -8
  25. package/dist/src/jsx-runtime/index.d.ts +5 -0
  26. package/dist/src/jsx-runtime/index.js +8 -1
  27. package/dist/src/query/public.js +4 -3
  28. package/dist/src/router/public.d.ts +17 -4
  29. package/dist/src/router/public.js +16 -11
  30. package/dist/src/test/index.d.ts +5 -0
  31. package/dist/src/test/index.js +4 -3
  32. package/package.json +3 -3
  33. package/reactivity.json +1 -11
package/README.md CHANGED
@@ -255,6 +255,53 @@ const compiled = compileTheme(theme);
255
255
  ThemeProvider({ theme: 'dark', children: [<App />] });
256
256
  ```
257
257
 
258
+ ### Fonts
259
+
260
+ Declare font families with `font()` and compile them into `@font-face` CSS, custom properties, and preload tags with `compileFonts()`. Only woff2 format is supported.
261
+
262
+ ```tsx
263
+ import { font, compileFonts } from '@vertz/ui/css';
264
+
265
+ const sans = font('DM Sans', {
266
+ weight: '100..1000',
267
+ src: '/fonts/dm-sans.woff2',
268
+ fallback: ['system-ui', 'sans-serif'],
269
+ });
270
+
271
+ const mono = font('JetBrains Mono', {
272
+ weight: '100..800',
273
+ src: '/fonts/jb-mono.woff2',
274
+ fallback: ['monospace'],
275
+ });
276
+
277
+ const compiled = compileFonts({ sans, mono });
278
+
279
+ // compiled.fontFaceCss — @font-face declarations
280
+ // compiled.cssVarsCss — :root { --font-sans: ...; --font-mono: ...; }
281
+ // compiled.cssVarLines — individual lines for merging into an existing :root
282
+ // compiled.preloadTags — <link rel="preload" ...> HTML tags
283
+ ```
284
+
285
+ **Multiple font files** (e.g., normal + italic):
286
+
287
+ ```tsx
288
+ const sans = font('DM Sans', {
289
+ weight: '100..1000',
290
+ src: [
291
+ { path: '/fonts/dm-sans.woff2', weight: '100..1000', style: 'normal' },
292
+ { path: '/fonts/dm-sans-italic.woff2', weight: '100..1000', style: 'italic' },
293
+ ],
294
+ fallback: ['system-ui', 'sans-serif'],
295
+ });
296
+ ```
297
+
298
+ **Font without `src`** (system font with CSS var only):
299
+
300
+ ```tsx
301
+ const system = font('system-ui', { weight: '400' });
302
+ // compileFonts({ system }) generates --font-system: 'system-ui'; but no @font-face
303
+ ```
304
+
258
305
  ---
259
306
 
260
307
  ## Forms
@@ -624,6 +671,8 @@ onMount(() => {
624
671
  | `compileTheme` | Compile a theme to CSS |
625
672
  | `ThemeProvider` | Provide a theme to descendants |
626
673
  | `globalCss` | Inject global CSS |
674
+ | `font` | Declare a font family descriptor |
675
+ | `compileFonts` | Compile font descriptors into CSS, vars, and preload tags |
627
676
 
628
677
  ### Forms
629
678
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  domEffect
3
- } from "./chunk-8hsz5y4a.js";
3
+ } from "./chunk-4fwcwxn6.js";
4
4
 
5
5
  // src/dom/attributes.ts
6
6
  function __attr(el, name, fn) {
@@ -0,0 +1,10 @@
1
+ import {
2
+ hasSSRResolver
3
+ } from "./chunk-4fwcwxn6.js";
4
+
5
+ // src/env/is-browser.ts
6
+ function isBrowser() {
7
+ return typeof window !== "undefined" && !hasSSRResolver();
8
+ }
9
+
10
+ export { isBrowser };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __classList,
3
3
  __on
4
- } from "./chunk-dksg08fq.js";
4
+ } from "./chunk-07bh4m1e.js";
5
5
  import {
6
6
  __append,
7
7
  __element,
@@ -9,18 +9,25 @@ import {
9
9
  __exitChildren,
10
10
  __staticText,
11
11
  getIsHydrating
12
- } from "./chunk-j6qyxfdc.js";
12
+ } from "./chunk-vndfjfdy.js";
13
+ import {
14
+ RouterContext
15
+ } from "./chunk-mtsvrj9e.js";
16
+ import {
17
+ isBrowser
18
+ } from "./chunk-14eqne2a.js";
13
19
  import {
14
20
  _tryOnCleanup,
15
21
  createContext,
16
22
  domEffect,
23
+ getSSRContext,
17
24
  popScope,
18
25
  pushScope,
19
26
  runCleanups,
20
27
  signal,
21
28
  untrack,
22
29
  useContext
23
- } from "./chunk-8hsz5y4a.js";
30
+ } from "./chunk-4fwcwxn6.js";
24
31
 
25
32
  // src/router/link.ts
26
33
  var DANGEROUS_SCHEMES = ["javascript:", "data:", "vbscript:"];
@@ -65,8 +72,10 @@ function createLink(currentPath, navigate, factoryOptions) {
65
72
  } else {
66
73
  __append(el, result);
67
74
  }
68
- } else {
75
+ } else if (typeof children === "string") {
69
76
  __append(el, __staticText(children));
77
+ } else {
78
+ __append(el, children);
70
79
  }
71
80
  __exitChildren();
72
81
  if (activeClass) {
@@ -88,22 +97,49 @@ function createLink(currentPath, navigate, factoryOptions) {
88
97
  return el;
89
98
  };
90
99
  }
91
-
92
- // src/router/router-context.ts
93
- var RouterContext = createContext(undefined, "@vertz/ui::RouterContext");
94
- function useRouter() {
100
+ function Link({ href, children, activeClass, className }) {
95
101
  const router = useContext(RouterContext);
96
102
  if (!router) {
97
- throw new Error("useRouter() must be called within RouterContext.Provider");
103
+ throw new Error("Link must be used within a RouterContext.Provider (via createRouter)");
98
104
  }
99
- return router;
100
- }
101
- function useParams() {
102
- const router = useContext(RouterContext);
103
- if (!router) {
104
- throw new Error("useParams() must be called within RouterContext.Provider");
105
+ const safeHref = isSafeUrl(href) ? href : "#";
106
+ const handleClick = (event) => {
107
+ const mouseEvent = event;
108
+ if (mouseEvent.ctrlKey || mouseEvent.metaKey || mouseEvent.shiftKey || mouseEvent.altKey) {
109
+ return;
110
+ }
111
+ mouseEvent.preventDefault();
112
+ router.navigate({ to: safeHref });
113
+ };
114
+ const props = { href: safeHref };
115
+ if (className) {
116
+ props.class = className;
117
+ }
118
+ const el = __element("a", props);
119
+ __on(el, "click", handleClick);
120
+ __enterChildren(el);
121
+ if (typeof children === "function") {
122
+ const result = children();
123
+ if (typeof result === "string") {
124
+ __append(el, __staticText(result));
125
+ } else {
126
+ __append(el, result);
127
+ }
128
+ } else if (typeof children === "string") {
129
+ __append(el, __staticText(children));
130
+ } else {
131
+ __append(el, children);
132
+ }
133
+ __exitChildren();
134
+ if (activeClass) {
135
+ __classList(el, {
136
+ [activeClass]: () => {
137
+ router.current;
138
+ return isBrowser() ? window.location.pathname === safeHref : false;
139
+ }
140
+ });
105
141
  }
106
- return router.current?.parsedParams ?? router.current?.params ?? {};
142
+ return el;
107
143
  }
108
144
 
109
145
  // src/router/outlet.ts
@@ -249,17 +285,44 @@ function buildLevels(matched) {
249
285
  }));
250
286
  }
251
287
  function buildInsideOutFactory(matched, levels, startAt, router) {
252
- let factory = matched[matched.length - 1].route.component;
288
+ const ssrCtx = getSSRContext();
289
+ const wrapForSSR = (route, routeIndex) => {
290
+ if (!ssrCtx)
291
+ return route.component;
292
+ const resolved = ssrCtx.resolvedComponents?.get(route);
293
+ if (resolved)
294
+ return resolved;
295
+ return () => {
296
+ const result = route.component();
297
+ if (result instanceof Promise) {
298
+ if (!ssrCtx.pendingRouteComponents) {
299
+ ssrCtx.pendingRouteComponents = new Map;
300
+ }
301
+ ssrCtx.pendingRouteComponents.set(route, result);
302
+ for (let j = routeIndex + 1;j < matched.length; j++) {
303
+ const childRoute = matched[j].route;
304
+ if (!ssrCtx.pendingRouteComponents.has(childRoute)) {
305
+ const childResult = childRoute.component();
306
+ if (childResult instanceof Promise) {
307
+ ssrCtx.pendingRouteComponents.set(childRoute, childResult);
308
+ }
309
+ }
310
+ }
311
+ }
312
+ return result;
313
+ };
314
+ };
315
+ let factory = wrapForSSR(matched[matched.length - 1].route, matched.length - 1);
253
316
  for (let i = matched.length - 2;i >= startAt; i--) {
254
317
  const level = levels[i];
255
318
  const childFactory = factory;
256
319
  level.childSignal.value = childFactory;
257
- const parentRoute = level.route;
320
+ const parentComponent = wrapForSSR(level.route, i);
258
321
  const cs = level.childSignal;
259
322
  factory = () => {
260
323
  let result;
261
324
  OutletContext.Provider({ childComponent: cs, router }, () => {
262
- result = parentRoute.component();
325
+ result = parentComponent();
263
326
  });
264
327
  return result;
265
328
  };
@@ -285,4 +348,4 @@ function useSearchParams(searchSignal) {
285
348
  return searchSignal.value;
286
349
  }
287
350
 
288
- export { createLink, RouterContext, useRouter, useParams, OutletContext, Outlet, RouterView, parseSearchParams, useSearchParams };
351
+ export { createLink, Link, OutletContext, Outlet, RouterView, parseSearchParams, useSearchParams };
@@ -1,10 +1,20 @@
1
1
  // src/ssr/ssr-render-context.ts
2
- var _ssrResolver = null;
2
+ var RESOLVER_KEY = "__VERTZ_SSR_RESOLVER__";
3
+ function getResolver() {
4
+ return globalThis[RESOLVER_KEY] ?? null;
5
+ }
3
6
  function registerSSRResolver(resolver) {
4
- _ssrResolver = resolver;
7
+ if (resolver === null) {
8
+ delete globalThis[RESOLVER_KEY];
9
+ } else {
10
+ globalThis[RESOLVER_KEY] = resolver;
11
+ }
5
12
  }
6
13
  function getSSRContext() {
7
- return _ssrResolver?.();
14
+ return getResolver()?.();
15
+ }
16
+ function hasSSRResolver() {
17
+ return getResolver() !== null;
8
18
  }
9
19
 
10
20
  // src/component/context.ts
@@ -472,4 +482,4 @@ function lifecycleEffect(fn) {
472
482
  return dispose;
473
483
  }
474
484
 
475
- export { registerSSRResolver, getSSRContext, createContext, useContext, getContextScope, setContextScope, DisposalScopeError, onCleanup, _tryOnCleanup, pushScope, popScope, runCleanups, setReadValueCallback, untrack, batch, startSignalCollection, stopSignalCollection, signal, computed, domEffect, lifecycleEffect };
485
+ export { registerSSRResolver, getSSRContext, hasSSRResolver, createContext, useContext, getContextScope, setContextScope, DisposalScopeError, onCleanup, _tryOnCleanup, pushScope, popScope, runCleanups, setReadValueCallback, untrack, batch, startSignalCollection, stopSignalCollection, signal, computed, domEffect, lifecycleEffect };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  injectCSS
3
- } from "./chunk-1wby7nex.js";
3
+ } from "./chunk-dhehvmj0.js";
4
4
 
5
5
  // src/dom/animation.ts
6
6
  function onAnimationsComplete(el, callback) {
@@ -479,7 +479,72 @@ function hydrate(registry) {
479
479
  autoStrategy(el, doHydrate);
480
480
  }
481
481
  }
482
+
483
+ // src/hydrate/island-hydrate.ts
484
+ var hydrationQueue = [];
485
+ var isProcessing = false;
486
+ async function processQueue() {
487
+ if (isProcessing)
488
+ return;
489
+ isProcessing = true;
490
+ while (hydrationQueue.length > 0) {
491
+ const task = hydrationQueue.shift();
492
+ await task();
493
+ }
494
+ isProcessing = false;
495
+ }
496
+ function deserializeIslandProps(container) {
497
+ const script = container.querySelector(":scope > script[data-v-island-props]");
498
+ if (!script || !script.textContent) {
499
+ return {};
500
+ }
501
+ try {
502
+ return JSON.parse(script.textContent);
503
+ } catch {
504
+ return {};
505
+ }
506
+ }
507
+ function hydrateIslands(registry) {
508
+ const elements = document.querySelectorAll("[data-v-island]");
509
+ const registryKeys = Object.keys(registry);
510
+ for (const el of elements) {
511
+ if (el.hasAttribute("data-v-hydrated"))
512
+ continue;
513
+ const islandId = el.getAttribute("data-v-island");
514
+ if (!islandId)
515
+ continue;
516
+ const loader = registry[islandId];
517
+ if (!loader) {
518
+ console.error(`[vertz] Island "${islandId}" not found in registry. Available: [${registryKeys.join(", ")}]`);
519
+ continue;
520
+ }
521
+ const props = deserializeIslandProps(el);
522
+ const doHydrate = () => {
523
+ hydrationQueue.push(async () => {
524
+ try {
525
+ const mod = await loader();
526
+ const result = mod.default(props, el);
527
+ if (result instanceof Node) {
528
+ const children = Array.from(el.childNodes);
529
+ for (const child of children) {
530
+ if (child instanceof Element && child.hasAttribute("data-v-island-props")) {
531
+ continue;
532
+ }
533
+ el.removeChild(child);
534
+ }
535
+ el.appendChild(result);
536
+ }
537
+ el.setAttribute("data-v-hydrated", "");
538
+ } catch (error) {
539
+ console.error(`[vertz] Failed to hydrate island "${islandId}":`, error);
540
+ }
541
+ });
542
+ processQueue();
543
+ };
544
+ autoStrategy(el, doHydrate);
545
+ }
546
+ }
482
547
  // src/query/index.ts
483
548
  import { isQueryDescriptor } from "@vertz/fetch";
484
549
 
485
- export { onAnimationsComplete, keyframes, ANIMATION_DURATION, ANIMATION_EASING, fadeIn, fadeOut, zoomIn, zoomOut, slideInFromTop, slideInFromBottom, slideOutToTop, slideOutToBottom, slideInFromLeft, slideInFromRight, slideOutToLeft, slideOutToRight, accordionDown, accordionUp, palettes, resolveComponent, deserializeProps, hydrate, isQueryDescriptor };
550
+ export { onAnimationsComplete, keyframes, ANIMATION_DURATION, ANIMATION_EASING, fadeIn, fadeOut, zoomIn, zoomOut, slideInFromTop, slideInFromBottom, slideOutToTop, slideOutToBottom, slideInFromLeft, slideInFromRight, slideOutToLeft, slideOutToRight, accordionDown, accordionUp, palettes, resolveComponent, deserializeProps, hydrate, hydrateIslands, isQueryDescriptor };
@@ -44,6 +44,7 @@ function defineRoutes(map) {
44
44
  loader: config.loader,
45
45
  params: config.params,
46
46
  pattern,
47
+ prerender: config.prerender,
47
48
  searchParams: config.searchParams
48
49
  };
49
50
  if (config.children) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getSSRContext
3
- } from "./chunk-8hsz5y4a.js";
3
+ } from "./chunk-4fwcwxn6.js";
4
4
 
5
5
  // src/dom/dom-adapter.ts
6
6
  function createDOMAdapter() {
@@ -3,10 +3,10 @@ import {
3
3
  __element,
4
4
  __enterChildren,
5
5
  __exitChildren
6
- } from "./chunk-j6qyxfdc.js";
6
+ } from "./chunk-vndfjfdy.js";
7
7
  import {
8
8
  getSSRContext
9
- } from "./chunk-8hsz5y4a.js";
9
+ } from "./chunk-4fwcwxn6.js";
10
10
 
11
11
  // src/component/children.ts
12
12
  var MAX_RESOLVE_DEPTH = 100;
@@ -757,6 +757,143 @@ ${props}
757
757
  }`;
758
758
  }
759
759
 
760
+ // src/css/sanitize.ts
761
+ function sanitizeCssValue(value) {
762
+ return value.replace(/[;{}<>']/g, "").replace(/url\s*\(/gi, "").replace(/expression\s*\(/gi, "").replace(/@import/gi, "");
763
+ }
764
+ function escapeHtmlAttr(value) {
765
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, "&#39;");
766
+ }
767
+
768
+ // src/css/font.ts
769
+ function font(family, options) {
770
+ return {
771
+ __brand: "FontDescriptor",
772
+ family,
773
+ weight: String(options.weight),
774
+ style: options.style ?? "normal",
775
+ display: options.display ?? "swap",
776
+ src: options.src,
777
+ fallback: options.fallback ?? [],
778
+ subsets: options.subsets ?? ["latin"],
779
+ unicodeRange: options.unicodeRange,
780
+ adjustFontFallback: options.adjustFontFallback ?? true
781
+ };
782
+ }
783
+ function toCssWeight(weight) {
784
+ return sanitizeCssValue(weight).replace("..", " ");
785
+ }
786
+ function buildFontFace(family, src, weight, style, display, unicodeRange) {
787
+ const safeFamily = sanitizeCssValue(family);
788
+ const safeSrc = sanitizeCssValue(src);
789
+ const safeStyle = sanitizeCssValue(style);
790
+ const safeDisplay = sanitizeCssValue(display);
791
+ const lines = [
792
+ "@font-face {",
793
+ ` font-family: '${safeFamily}';`,
794
+ ` font-style: ${safeStyle};`,
795
+ ` font-weight: ${toCssWeight(weight)};`,
796
+ ` font-display: ${safeDisplay};`,
797
+ ` src: url(${safeSrc}) format('woff2');`
798
+ ];
799
+ if (unicodeRange) {
800
+ lines.push(` unicode-range: ${sanitizeCssValue(unicodeRange)};`);
801
+ }
802
+ lines.push("}");
803
+ return lines.join(`
804
+ `);
805
+ }
806
+ var PERCENT_RE = /^\d+\.\d+%$/;
807
+ function sanitizeMetricValue(value) {
808
+ if (!PERCENT_RE.test(value)) {
809
+ throw new Error(`Invalid font metric override value: "${value}"`);
810
+ }
811
+ return value;
812
+ }
813
+ function buildFallbackFontFace(family, metrics) {
814
+ const safeFamily = sanitizeCssValue(family);
815
+ const lines = [
816
+ "@font-face {",
817
+ ` font-family: '${safeFamily} Fallback';`,
818
+ ` src: local('${metrics.fallbackFont}');`,
819
+ ` ascent-override: ${sanitizeMetricValue(metrics.ascentOverride)};`,
820
+ ` descent-override: ${sanitizeMetricValue(metrics.descentOverride)};`,
821
+ ` line-gap-override: ${sanitizeMetricValue(metrics.lineGapOverride)};`,
822
+ ` size-adjust: ${sanitizeMetricValue(metrics.sizeAdjust)};`,
823
+ "}"
824
+ ];
825
+ return lines.join(`
826
+ `);
827
+ }
828
+ function validateWoff2Src(path) {
829
+ if (!path.toLowerCase().endsWith(".woff2")) {
830
+ throw new Error(`Font src "${path}" is not a .woff2 file. Only woff2 format is currently supported.`);
831
+ }
832
+ }
833
+ function compileFonts(fonts, options) {
834
+ const fontFaces = [];
835
+ const cssVars = [];
836
+ const preloadPaths = [];
837
+ const fallbackMetrics = options?.fallbackMetrics;
838
+ for (const [key, descriptor] of Object.entries(fonts)) {
839
+ if (!/^[a-zA-Z0-9-]+$/.test(key)) {
840
+ throw new Error(`Font key "${key}" contains invalid CSS identifier characters. Use only [a-zA-Z0-9-].`);
841
+ }
842
+ const { family, weight, style, display, src, fallback, unicodeRange } = descriptor;
843
+ const adjustFontFallback = descriptor.adjustFontFallback ?? true;
844
+ const metrics = fallbackMetrics?.[key];
845
+ const shouldGenerateFallback = metrics && src && adjustFontFallback !== false;
846
+ const safeFamily = sanitizeCssValue(family);
847
+ const safeFallbacks = fallback.map(sanitizeCssValue);
848
+ const fallbackFontName = shouldGenerateFallback ? `'${safeFamily} Fallback'` : undefined;
849
+ const familyParts = [
850
+ `'${safeFamily}'`,
851
+ ...fallbackFontName ? [fallbackFontName] : [],
852
+ ...safeFallbacks
853
+ ];
854
+ const familyValue = familyParts.join(", ");
855
+ cssVars.push(` --font-${sanitizeCssValue(key)}: ${familyValue};`);
856
+ if (!src)
857
+ continue;
858
+ if (typeof src === "string") {
859
+ validateWoff2Src(src);
860
+ fontFaces.push(buildFontFace(family, src, weight, style, display, unicodeRange));
861
+ preloadPaths.push(src);
862
+ } else {
863
+ for (const entry of src) {
864
+ validateWoff2Src(entry.path);
865
+ const entryWeight = entry.weight != null ? String(entry.weight) : weight;
866
+ const entryStyle = entry.style ?? style;
867
+ fontFaces.push(buildFontFace(family, entry.path, entryWeight, entryStyle, display, unicodeRange));
868
+ }
869
+ const first = src[0];
870
+ if (first) {
871
+ preloadPaths.push(first.path);
872
+ }
873
+ }
874
+ if (shouldGenerateFallback) {
875
+ fontFaces.push(buildFallbackFontFace(family, metrics));
876
+ }
877
+ }
878
+ const fontFaceCss = fontFaces.join(`
879
+
880
+ `);
881
+ const cssVarLines = cssVars;
882
+ const cssVarsCss = cssVars.length > 0 ? `:root {
883
+ ${cssVars.join(`
884
+ `)}
885
+ }` : "";
886
+ const preloadTags = preloadPaths.map((p) => `<link rel="preload" href="${escapeHtmlAttr(p)}" as="font" type="font/woff2" crossorigin>`).join(`
887
+ `);
888
+ const preloadItems = preloadPaths.map((href) => ({
889
+ href,
890
+ as: "font",
891
+ type: "font/woff2",
892
+ crossorigin: true
893
+ }));
894
+ return { fontFaceCss, cssVarsCss, cssVarLines, preloadTags, preloadItems };
895
+ }
896
+
760
897
  // src/css/global-css.ts
761
898
  function globalCss(input) {
762
899
  const rules = [];
@@ -810,16 +947,14 @@ class InlineStyleError extends Error {
810
947
  }
811
948
 
812
949
  // src/css/theme.ts
813
- function sanitizeCssValue(value) {
814
- return value.replace(/[;{}]/g, "").replace(/url\s*\(/gi, "").replace(/expression\s*\(/gi, "").replace(/@import/gi, "");
815
- }
816
950
  function defineTheme(input) {
817
951
  return {
818
952
  colors: input.colors,
819
- spacing: input.spacing
953
+ spacing: input.spacing,
954
+ fonts: input.fonts
820
955
  };
821
956
  }
822
- function compileTheme(theme) {
957
+ function compileTheme(theme, options) {
823
958
  const rootVars = [];
824
959
  const darkVars = [];
825
960
  const tokenPaths = [];
@@ -864,7 +999,22 @@ function compileTheme(theme) {
864
999
  tokenPaths.push(`spacing.${name}`);
865
1000
  }
866
1001
  }
1002
+ let fontFaceCss = "";
1003
+ let preloadTags = "";
1004
+ let preloadItems = [];
1005
+ if (theme.fonts) {
1006
+ const compiled = compileFonts(theme.fonts, {
1007
+ fallbackMetrics: options?.fallbackMetrics
1008
+ });
1009
+ fontFaceCss = compiled.fontFaceCss;
1010
+ preloadTags = compiled.preloadTags;
1011
+ preloadItems = compiled.preloadItems;
1012
+ rootVars.push(...compiled.cssVarLines);
1013
+ }
867
1014
  const blocks = [];
1015
+ if (fontFaceCss) {
1016
+ blocks.push(fontFaceCss);
1017
+ }
868
1018
  if (rootVars.length > 0) {
869
1019
  blocks.push(`:root {
870
1020
  ${rootVars.join(`
@@ -880,7 +1030,9 @@ ${darkVars.join(`
880
1030
  return {
881
1031
  css: blocks.join(`
882
1032
  `),
883
- tokens: tokenPaths
1033
+ tokens: tokenPaths,
1034
+ preloadTags,
1035
+ preloadItems
884
1036
  };
885
1037
  }
886
1038
 
@@ -1017,4 +1169,4 @@ function variants(config) {
1017
1169
  return fn;
1018
1170
  }
1019
1171
 
1020
- export { resolveChildren, children, PROPERTY_MAP, KEYWORD_MAP, DISPLAY_MAP, SPACING_SCALE, RADIUS_SCALE, SHADOW_SCALE, FONT_SIZE_SCALE, FONT_WEIGHT_SCALE, LINE_HEIGHT_SCALE, ALIGNMENT_MAP, SIZE_KEYWORDS, HEIGHT_AXIS_PROPERTIES, COLOR_NAMESPACES, CSS_COLOR_KEYWORDS, CONTENT_MAP, PSEUDO_PREFIXES, PSEUDO_MAP, injectCSS, resetInjectedStyles, getInjectedCSS, css, globalCss, s, defineTheme, compileTheme, ThemeProvider, variants };
1172
+ export { resolveChildren, children, PROPERTY_MAP, KEYWORD_MAP, DISPLAY_MAP, SPACING_SCALE, RADIUS_SCALE, SHADOW_SCALE, FONT_SIZE_SCALE, FONT_WEIGHT_SCALE, LINE_HEIGHT_SCALE, ALIGNMENT_MAP, SIZE_KEYWORDS, HEIGHT_AXIS_PROPERTIES, COLOR_NAMESPACES, CSS_COLOR_KEYWORDS, CONTENT_MAP, PSEUDO_PREFIXES, PSEUDO_MAP, injectCSS, resetInjectedStyles, getInjectedCSS, css, font, compileFonts, globalCss, s, defineTheme, compileTheme, ThemeProvider, variants };