@lukas_holdings/castdom 1.0.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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +707 -0
  3. package/bin/castdom.js +2 -0
  4. package/dist/astro.cjs +86 -0
  5. package/dist/astro.cjs.map +1 -0
  6. package/dist/astro.d.cts +88 -0
  7. package/dist/astro.d.ts +88 -0
  8. package/dist/astro.js +80 -0
  9. package/dist/astro.js.map +1 -0
  10. package/dist/chunk-COLESJ66.js +57 -0
  11. package/dist/chunk-COLESJ66.js.map +1 -0
  12. package/dist/chunk-EJRNKHL5.js +31 -0
  13. package/dist/chunk-EJRNKHL5.js.map +1 -0
  14. package/dist/chunk-JRQ6EVQP.cjs +35 -0
  15. package/dist/chunk-JRQ6EVQP.cjs.map +1 -0
  16. package/dist/chunk-KGLTVTHU.js +73 -0
  17. package/dist/chunk-KGLTVTHU.js.map +1 -0
  18. package/dist/chunk-O4OOMGGM.cjs +198 -0
  19. package/dist/chunk-O4OOMGGM.cjs.map +1 -0
  20. package/dist/chunk-ONS533CQ.js +104 -0
  21. package/dist/chunk-ONS533CQ.js.map +1 -0
  22. package/dist/chunk-ORY4OMZ5.cjs +110 -0
  23. package/dist/chunk-ORY4OMZ5.cjs.map +1 -0
  24. package/dist/chunk-QLEBTZIB.cjs +64 -0
  25. package/dist/chunk-QLEBTZIB.cjs.map +1 -0
  26. package/dist/chunk-XS5HAU5E.cjs +109 -0
  27. package/dist/chunk-XS5HAU5E.cjs.map +1 -0
  28. package/dist/chunk-YDT4TPB7.cjs +84 -0
  29. package/dist/chunk-YDT4TPB7.cjs.map +1 -0
  30. package/dist/chunk-ZBJB7WVV.js +193 -0
  31. package/dist/chunk-ZBJB7WVV.js.map +1 -0
  32. package/dist/chunk-ZWZ5ZLJE.js +103 -0
  33. package/dist/chunk-ZWZ5ZLJE.js.map +1 -0
  34. package/dist/cli.js +135 -0
  35. package/dist/index.cjs +540 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +176 -0
  38. package/dist/index.d.ts +176 -0
  39. package/dist/index.js +440 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/next.cjs +65 -0
  42. package/dist/next.cjs.map +1 -0
  43. package/dist/next.d.cts +72 -0
  44. package/dist/next.d.ts +72 -0
  45. package/dist/next.js +48 -0
  46. package/dist/next.js.map +1 -0
  47. package/dist/react.cjs +30 -0
  48. package/dist/react.cjs.map +1 -0
  49. package/dist/react.d.cts +70 -0
  50. package/dist/react.d.ts +70 -0
  51. package/dist/react.js +7 -0
  52. package/dist/react.js.map +1 -0
  53. package/dist/renderer-B1R7u2wm.d.ts +30 -0
  54. package/dist/renderer-Bfzjr6l9.d.cts +30 -0
  55. package/dist/ssr.cjs +46 -0
  56. package/dist/ssr.cjs.map +1 -0
  57. package/dist/ssr.d.cts +83 -0
  58. package/dist/ssr.d.ts +83 -0
  59. package/dist/ssr.js +5 -0
  60. package/dist/ssr.js.map +1 -0
  61. package/dist/types-ChD5jENU.d.cts +105 -0
  62. package/dist/types-ChD5jENU.d.ts +105 -0
  63. package/dist/vite.cjs +83 -0
  64. package/dist/vite.cjs.map +1 -0
  65. package/dist/vite.d.cts +81 -0
  66. package/dist/vite.d.ts +81 -0
  67. package/dist/vite.js +77 -0
  68. package/dist/vite.js.map +1 -0
  69. package/package.json +130 -0
@@ -0,0 +1,109 @@
1
+ 'use strict';
2
+
3
+ var chunkJRQ6EVQP_cjs = require('./chunk-JRQ6EVQP.cjs');
4
+
5
+ // src/core/renderer.ts
6
+ function resolveConfig(config) {
7
+ return {
8
+ color: config?.color ?? chunkJRQ6EVQP_cjs.DEFAULTS.color,
9
+ shimmerColor: config?.shimmerColor ?? chunkJRQ6EVQP_cjs.DEFAULTS.shimmerColor,
10
+ animationDuration: config?.animationDuration ?? chunkJRQ6EVQP_cjs.DEFAULTS.animationDuration,
11
+ classPrefix: config?.classPrefix ?? chunkJRQ6EVQP_cjs.DEFAULTS.classPrefix,
12
+ inlineStyles: config?.inlineStyles ?? chunkJRQ6EVQP_cjs.DEFAULTS.inlineStyles
13
+ };
14
+ }
15
+ function renderBone(bone, config, index) {
16
+ const { color, shimmerColor, animationDuration, classPrefix, inlineStyles } = config;
17
+ const radius = bone.r >= 9999 ? "50%" : `${bone.r}px`;
18
+ if (inlineStyles) {
19
+ const gradient = `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;
20
+ return `<div aria-hidden="true" style="position:absolute;left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius};background:${gradient};background-size:200% 100%;animation:${classPrefix}-shimmer ${animationDuration}ms ease-in-out infinite"></div>`;
21
+ }
22
+ return `<div class="${classPrefix}-bone" style="left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius}" aria-hidden="true"></div>`;
23
+ }
24
+ function renderBonesHTML(name, bp, config) {
25
+ const cfg = resolveConfig(config);
26
+ const bones = bp.bones.map((bone, i) => renderBone(bone, cfg)).join("");
27
+ return `<div class="${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${name}" role="status" aria-busy="true" aria-label="Loading ${name}" style="position:relative;width:${bp.containerWidth}px;height:${bp.containerHeight}px;overflow:hidden">${bones}</div>`;
28
+ }
29
+ function renderResponsiveHTML(skeleton, config) {
30
+ const cfg = resolveConfig(config);
31
+ const breakpoints = [...skeleton.breakpoints].sort(
32
+ (a, b) => a.viewport - b.viewport
33
+ );
34
+ if (breakpoints.length === 0) return "";
35
+ if (breakpoints.length === 1) {
36
+ return renderBonesHTML(skeleton.name, breakpoints[0], config);
37
+ }
38
+ const variants = breakpoints.map((bp, idx) => {
39
+ const bonesHTML = bp.bones.map((bone, i) => renderBone(bone, cfg)).join("");
40
+ return `<div class="${cfg.classPrefix}-bp ${cfg.classPrefix}-bp-${bp.viewport}" style="position:relative;width:100%;height:${bp.containerHeight}px;overflow:hidden;display:none">${bonesHTML}</div>`;
41
+ });
42
+ return `<div class="${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${skeleton.name}" role="status" aria-busy="true" aria-label="Loading ${skeleton.name}">${variants.join("")}</div>`;
43
+ }
44
+ function generateCSS(skeleton, config) {
45
+ const cfg = resolveConfig(config);
46
+ const { classPrefix: p, color, shimmerColor, animationDuration } = cfg;
47
+ const breakpoints = [...skeleton.breakpoints].sort(
48
+ (a, b) => a.viewport - b.viewport
49
+ );
50
+ const name = skeleton.name;
51
+ const parts = [];
52
+ parts.push(
53
+ `.${p}-${name} .${p}-bone{position:absolute;background:linear-gradient(90deg,${color} 25%,${shimmerColor} 50%,${color} 75%);background-size:200% 100%;animation:${p}-shimmer ${animationDuration}ms ease-in-out infinite}`
54
+ );
55
+ if (breakpoints.length > 1) {
56
+ for (let i = 0; i < breakpoints.length; i++) {
57
+ const bp = breakpoints[i];
58
+ const next = breakpoints[i + 1];
59
+ const prev = breakpoints[i - 1];
60
+ let query;
61
+ if (i === 0) {
62
+ const max = Math.floor((bp.viewport + next.viewport) / 2) - 1;
63
+ query = `@media(max-width:${max}px)`;
64
+ } else if (i === breakpoints.length - 1) {
65
+ const min = Math.floor((prev.viewport + bp.viewport) / 2);
66
+ query = `@media(min-width:${min}px)`;
67
+ } else {
68
+ const min = Math.floor((prev.viewport + bp.viewport) / 2);
69
+ const max = Math.floor((bp.viewport + next.viewport) / 2) - 1;
70
+ query = `@media(min-width:${min}px) and (max-width:${max}px)`;
71
+ }
72
+ parts.push(
73
+ `${query}{.${p}-${name} .${p}-bp-${bp.viewport}{display:block}}`
74
+ );
75
+ }
76
+ } else if (breakpoints.length === 1) {
77
+ parts.push(
78
+ `.${p}-${name} .${p}-bp-${breakpoints[0].viewport}{display:block}`
79
+ );
80
+ }
81
+ return parts.join("\n");
82
+ }
83
+ function generateCriticalCSS(skeletons, config) {
84
+ const cfg = resolveConfig(config);
85
+ const parts = [];
86
+ parts.push(
87
+ `@keyframes ${cfg.classPrefix}-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}`
88
+ );
89
+ parts.push(
90
+ `@media(prefers-reduced-motion:reduce){.${cfg.classPrefix}-bone{animation:none!important}}`
91
+ );
92
+ for (const skeleton of skeletons) {
93
+ parts.push(generateCSS(skeleton, config));
94
+ }
95
+ return parts.join("\n");
96
+ }
97
+ function renderStandalone(skeleton, config) {
98
+ const css = generateCriticalCSS([skeleton], config);
99
+ const html = renderResponsiveHTML(skeleton, config);
100
+ return `<style>${css}</style>${html}`;
101
+ }
102
+
103
+ exports.generateCSS = generateCSS;
104
+ exports.generateCriticalCSS = generateCriticalCSS;
105
+ exports.renderBonesHTML = renderBonesHTML;
106
+ exports.renderResponsiveHTML = renderResponsiveHTML;
107
+ exports.renderStandalone = renderStandalone;
108
+ //# sourceMappingURL=chunk-XS5HAU5E.cjs.map
109
+ //# sourceMappingURL=chunk-XS5HAU5E.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/renderer.ts"],"names":["DEFAULTS"],"mappings":"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","file":"chunk-XS5HAU5E.cjs","sourcesContent":["import type {\n Bone,\n BreakpointData,\n SkeletonData,\n CastDOMConfig,\n} from \"./types.js\";\nimport { DEFAULTS } from \"./types.js\";\n\ntype RenderConfig = Pick<\n Required<CastDOMConfig>,\n \"color\" | \"shimmerColor\" | \"animationDuration\" | \"classPrefix\" | \"inlineStyles\"\n>;\n\nfunction resolveConfig(config?: Partial<CastDOMConfig>): RenderConfig {\n return {\n color: config?.color ?? DEFAULTS.color,\n shimmerColor: config?.shimmerColor ?? DEFAULTS.shimmerColor,\n animationDuration: config?.animationDuration ?? DEFAULTS.animationDuration,\n classPrefix: config?.classPrefix ?? DEFAULTS.classPrefix,\n inlineStyles: config?.inlineStyles ?? DEFAULTS.inlineStyles,\n };\n}\n\n/** Render a single bone to an HTML string */\nfunction renderBone(bone: Bone, config: RenderConfig, index: number): string {\n const { color, shimmerColor, animationDuration, classPrefix, inlineStyles } = config;\n const radius = bone.r >= 9999 ? \"50%\" : `${bone.r}px`;\n\n if (inlineStyles) {\n const gradient = `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;\n return `<div aria-hidden=\"true\" style=\"position:absolute;left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius};background:${gradient};background-size:200% 100%;animation:${classPrefix}-shimmer ${animationDuration}ms ease-in-out infinite\"></div>`;\n }\n\n return `<div class=\"${classPrefix}-bone\" style=\"left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius}\" aria-hidden=\"true\"></div>`;\n}\n\n/**\n * Render all bones for a breakpoint to HTML.\n * The output is a pure CSS skeleton — no JS needed to display.\n */\nexport function renderBonesHTML(\n name: string,\n bp: BreakpointData,\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const bones = bp.bones\n .map((bone, i) => renderBone(bone, cfg, i))\n .join(\"\");\n\n return `<div class=\"${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${name}\" role=\"status\" aria-busy=\"true\" aria-label=\"Loading ${name}\" style=\"position:relative;width:${bp.containerWidth}px;height:${bp.containerHeight}px;overflow:hidden\">${bones}</div>`;\n}\n\n/**\n * Render a responsive skeleton with all breakpoints.\n * Uses CSS container queries + media queries for breakpoint switching.\n */\nexport function renderResponsiveHTML(\n skeleton: SkeletonData,\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const breakpoints = [...skeleton.breakpoints].sort(\n (a, b) => a.viewport - b.viewport\n );\n\n if (breakpoints.length === 0) return \"\";\n if (breakpoints.length === 1) {\n return renderBonesHTML(skeleton.name, breakpoints[0], config);\n }\n\n // Render each breakpoint variant, hidden by default\n const variants = breakpoints.map((bp, idx) => {\n const bonesHTML = bp.bones\n .map((bone, i) => renderBone(bone, cfg, i))\n .join(\"\");\n\n return `<div class=\"${cfg.classPrefix}-bp ${cfg.classPrefix}-bp-${bp.viewport}\" style=\"position:relative;width:100%;height:${bp.containerHeight}px;overflow:hidden;display:none\">${bonesHTML}</div>`;\n });\n\n return `<div class=\"${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${skeleton.name}\" role=\"status\" aria-busy=\"true\" aria-label=\"Loading ${skeleton.name}\">${variants.join(\"\")}</div>`;\n}\n\n/**\n * Generate CSS for a skeleton definition.\n * Includes responsive media queries to show/hide the correct breakpoint variant.\n */\nexport function generateCSS(\n skeleton: SkeletonData,\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const { classPrefix: p, color, shimmerColor, animationDuration } = cfg;\n const breakpoints = [...skeleton.breakpoints].sort(\n (a, b) => a.viewport - b.viewport\n );\n const name = skeleton.name;\n\n const parts: string[] = [];\n\n // Base bone styles\n parts.push(\n `.${p}-${name} .${p}-bone{position:absolute;background:linear-gradient(90deg,${color} 25%,${shimmerColor} 50%,${color} 75%);background-size:200% 100%;animation:${p}-shimmer ${animationDuration}ms ease-in-out infinite}`\n );\n\n // Responsive breakpoint switching via media queries\n if (breakpoints.length > 1) {\n for (let i = 0; i < breakpoints.length; i++) {\n const bp = breakpoints[i];\n const next = breakpoints[i + 1];\n const prev = breakpoints[i - 1];\n\n let query: string;\n if (i === 0) {\n // Smallest: up to midpoint between this and next\n const max = Math.floor((bp.viewport + next!.viewport) / 2) - 1;\n query = `@media(max-width:${max}px)`;\n } else if (i === breakpoints.length - 1) {\n // Largest: from midpoint between prev and this\n const min = Math.floor((prev!.viewport + bp.viewport) / 2);\n query = `@media(min-width:${min}px)`;\n } else {\n // Middle: between midpoints\n const min = Math.floor((prev!.viewport + bp.viewport) / 2);\n const max = Math.floor((bp.viewport + next!.viewport) / 2) - 1;\n query = `@media(min-width:${min}px) and (max-width:${max}px)`;\n }\n\n parts.push(\n `${query}{.${p}-${name} .${p}-bp-${bp.viewport}{display:block}}`\n );\n }\n } else if (breakpoints.length === 1) {\n // Single breakpoint — always visible\n parts.push(\n `.${p}-${name} .${p}-bp-${breakpoints[0].viewport}{display:block}`\n );\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Generate critical inline CSS for above-the-fold skeletons.\n * This CSS is designed to be embedded in <style> tags for instant rendering.\n */\nexport function generateCriticalCSS(\n skeletons: SkeletonData[],\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const parts: string[] = [];\n\n // Base keyframes (only once)\n parts.push(\n `@keyframes ${cfg.classPrefix}-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}`\n );\n\n // Reduced motion\n parts.push(\n `@media(prefers-reduced-motion:reduce){.${cfg.classPrefix}-bone{animation:none!important}}`\n );\n\n // Per-skeleton styles\n for (const skeleton of skeletons) {\n parts.push(generateCSS(skeleton, config));\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Generate a complete, self-contained HTML skeleton\n * that can be embedded directly in server-rendered pages.\n * Includes inline <style> — no external CSS needed.\n */\nexport function renderStandalone(\n skeleton: SkeletonData,\n config?: Partial<CastDOMConfig>\n): string {\n const css = generateCriticalCSS([skeleton], config);\n const html = renderResponsiveHTML(skeleton, config);\n return `<style>${css}</style>${html}`;\n}\n"]}
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ var chunkXS5HAU5E_cjs = require('./chunk-XS5HAU5E.cjs');
4
+ var chunkJRQ6EVQP_cjs = require('./chunk-JRQ6EVQP.cjs');
5
+
6
+ // src/core/registry.ts
7
+ var store = /* @__PURE__ */ new Map();
8
+ var globalConfig = {
9
+ color: chunkJRQ6EVQP_cjs.DEFAULTS.color,
10
+ shimmerColor: chunkJRQ6EVQP_cjs.DEFAULTS.shimmerColor,
11
+ animationDuration: chunkJRQ6EVQP_cjs.DEFAULTS.animationDuration,
12
+ classPrefix: chunkJRQ6EVQP_cjs.DEFAULTS.classPrefix,
13
+ inlineStyles: chunkJRQ6EVQP_cjs.DEFAULTS.inlineStyles
14
+ };
15
+ function configure(config) {
16
+ globalConfig = { ...globalConfig, ...config };
17
+ for (const [name, entry] of store) {
18
+ const updated = buildEntry(entry.data);
19
+ store.set(name, updated);
20
+ }
21
+ }
22
+ function buildEntry(data) {
23
+ const css = chunkXS5HAU5E_cjs.generateCSS(data, globalConfig);
24
+ const html = {};
25
+ for (const bp of data.breakpoints) {
26
+ html[bp.viewport] = chunkXS5HAU5E_cjs.renderBonesHTML(data.name, bp, globalConfig);
27
+ }
28
+ return { data, css, html };
29
+ }
30
+ function register(data) {
31
+ store.set(data.name, buildEntry(data));
32
+ }
33
+ function registerAll(skeletons) {
34
+ for (const data of skeletons) {
35
+ register(data);
36
+ }
37
+ }
38
+ function loadManifest(manifest) {
39
+ registerAll(manifest.skeletons);
40
+ }
41
+ function get(name) {
42
+ return store.get(name);
43
+ }
44
+ function has(name) {
45
+ return store.has(name);
46
+ }
47
+ function names() {
48
+ return [...store.keys()];
49
+ }
50
+ function remove(name) {
51
+ return store.delete(name);
52
+ }
53
+ function clear() {
54
+ store.clear();
55
+ }
56
+ function getAllCSS() {
57
+ const parts = [];
58
+ const seen = /* @__PURE__ */ new Set();
59
+ const prefix = globalConfig.classPrefix;
60
+ parts.push(getBaseCSS(prefix));
61
+ for (const entry of store.values()) {
62
+ if (!seen.has(entry.data.name)) {
63
+ parts.push(entry.css);
64
+ seen.add(entry.data.name);
65
+ }
66
+ }
67
+ return parts.join("\n");
68
+ }
69
+ function getBaseCSS(prefix, config) {
70
+ return `@keyframes ${prefix}-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}@media(prefers-reduced-motion:reduce){.${prefix}-bone{animation:none!important}}`;
71
+ }
72
+
73
+ exports.clear = clear;
74
+ exports.configure = configure;
75
+ exports.get = get;
76
+ exports.getAllCSS = getAllCSS;
77
+ exports.has = has;
78
+ exports.loadManifest = loadManifest;
79
+ exports.names = names;
80
+ exports.register = register;
81
+ exports.registerAll = registerAll;
82
+ exports.remove = remove;
83
+ //# sourceMappingURL=chunk-YDT4TPB7.cjs.map
84
+ //# sourceMappingURL=chunk-YDT4TPB7.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/registry.ts"],"names":["DEFAULTS","generateCSS","renderBonesHTML"],"mappings":"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","file":"chunk-YDT4TPB7.cjs","sourcesContent":["import type { RegistryEntry, SkeletonData, CastDOMConfig } from \"./types.js\";\nimport { generateCSS } from \"./renderer.js\";\nimport { renderBonesHTML } from \"./renderer.js\";\nimport { DEFAULTS } from \"./types.js\";\n\n/**\n * Global skeleton registry.\n * Stores all registered skeleton data and provides lookup by name.\n * Import once at app entry — all <CastDOM> components auto-resolve from here.\n */\n\nconst store = new Map<string, RegistryEntry>();\nlet globalConfig: Required<\n Pick<CastDOMConfig, \"color\" | \"shimmerColor\" | \"animationDuration\" | \"classPrefix\" | \"inlineStyles\">\n> = {\n color: DEFAULTS.color,\n shimmerColor: DEFAULTS.shimmerColor,\n animationDuration: DEFAULTS.animationDuration,\n classPrefix: DEFAULTS.classPrefix,\n inlineStyles: DEFAULTS.inlineStyles,\n};\n\n/** Configure global registry settings */\nexport function configure(\n config: Partial<typeof globalConfig>\n): void {\n globalConfig = { ...globalConfig, ...config };\n // Re-generate CSS/HTML for all registered skeletons\n for (const [name, entry] of store) {\n const updated = buildEntry(entry.data);\n store.set(name, updated);\n }\n}\n\n/** Build a registry entry from skeleton data */\nfunction buildEntry(data: SkeletonData): RegistryEntry {\n const css = generateCSS(data, globalConfig);\n const html: Record<number, string> = {};\n for (const bp of data.breakpoints) {\n html[bp.viewport] = renderBonesHTML(data.name, bp, globalConfig);\n }\n return { data, css, html };\n}\n\n/** Register a single skeleton */\nexport function register(data: SkeletonData): void {\n store.set(data.name, buildEntry(data));\n}\n\n/** Register multiple skeletons at once */\nexport function registerAll(skeletons: SkeletonData[]): void {\n for (const data of skeletons) {\n register(data);\n }\n}\n\n/** Load and register skeletons from a generated manifest file */\nexport function loadManifest(manifest: { skeletons: SkeletonData[] }): void {\n registerAll(manifest.skeletons);\n}\n\n/** Get a registered skeleton by name */\nexport function get(name: string): RegistryEntry | undefined {\n return store.get(name);\n}\n\n/** Check if a skeleton is registered */\nexport function has(name: string): boolean {\n return store.has(name);\n}\n\n/** Get all registered skeleton names */\nexport function names(): string[] {\n return [...store.keys()];\n}\n\n/** Remove a skeleton from the registry */\nexport function remove(name: string): boolean {\n return store.delete(name);\n}\n\n/** Clear all registered skeletons */\nexport function clear(): void {\n store.clear();\n}\n\n/** Get the combined CSS for all registered skeletons */\nexport function getAllCSS(): string {\n const parts: string[] = [];\n const seen = new Set<string>();\n\n // Add base animation CSS once\n const prefix = globalConfig.classPrefix;\n parts.push(getBaseCSS(prefix, globalConfig));\n\n for (const entry of store.values()) {\n if (!seen.has(entry.data.name)) {\n parts.push(entry.css);\n seen.add(entry.data.name);\n }\n }\n\n return parts.join(\"\\n\");\n}\n\n/** Get base CSS (keyframes + shared styles) */\nfunction getBaseCSS(\n prefix: string,\n config: typeof globalConfig\n): string {\n return `@keyframes ${prefix}-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}@media(prefers-reduced-motion:reduce){.${prefix}-bone{animation:none!important}}`;\n}\n"]}
@@ -0,0 +1,193 @@
1
+ import { selectBreakpoint } from './chunk-ONS533CQ.js';
2
+ import { get } from './chunk-KGLTVTHU.js';
3
+ import { useState, useRef, useEffect, createElement, useCallback, useMemo } from 'react';
4
+
5
+ function CastDOM({
6
+ name,
7
+ children,
8
+ loading,
9
+ fallback,
10
+ animation = "shimmer",
11
+ color,
12
+ shimmerColor,
13
+ duration,
14
+ className,
15
+ style,
16
+ onSkeletonShow,
17
+ onContentShow,
18
+ ariaLabel
19
+ }) {
20
+ const [isLoading, setIsLoading] = useState(loading ?? true);
21
+ const [viewportWidth, setViewportWidth] = useState(
22
+ typeof window !== "undefined" ? window.innerWidth : 1280
23
+ );
24
+ const containerRef = useRef(null);
25
+ const shownRef = useRef(false);
26
+ useEffect(() => {
27
+ if (loading !== void 0) {
28
+ setIsLoading(loading);
29
+ }
30
+ }, [loading]);
31
+ useEffect(() => {
32
+ if (loading === void 0 && children) {
33
+ setIsLoading(false);
34
+ }
35
+ }, [loading, children]);
36
+ useEffect(() => {
37
+ if (typeof window === "undefined") return;
38
+ const onResize = () => setViewportWidth(window.innerWidth);
39
+ window.addEventListener("resize", onResize, { passive: true });
40
+ return () => window.removeEventListener("resize", onResize);
41
+ }, []);
42
+ useEffect(() => {
43
+ if (isLoading && !shownRef.current) {
44
+ shownRef.current = true;
45
+ onSkeletonShow?.();
46
+ }
47
+ if (!isLoading && shownRef.current) {
48
+ onContentShow?.();
49
+ }
50
+ }, [isLoading, onSkeletonShow, onContentShow]);
51
+ const entry = get(name);
52
+ if (!isLoading) {
53
+ return createElement(
54
+ "div",
55
+ {
56
+ ref: containerRef,
57
+ className,
58
+ style,
59
+ "data-castdom": name
60
+ },
61
+ children
62
+ );
63
+ }
64
+ if (!entry) {
65
+ return fallback ? createElement("div", { className, style }, fallback) : null;
66
+ }
67
+ const bp = selectBreakpoint(entry.data, viewportWidth);
68
+ if (!bp) return fallback ? createElement("div", { className, style }, fallback) : null;
69
+ return createElement(SkeletonRenderer, {
70
+ name,
71
+ breakpoint: bp,
72
+ animation,
73
+ color,
74
+ shimmerColor,
75
+ duration,
76
+ className,
77
+ style,
78
+ ariaLabel
79
+ });
80
+ }
81
+ function SkeletonRenderer({
82
+ name,
83
+ breakpoint,
84
+ animation,
85
+ color = "#e0e0e0",
86
+ shimmerColor = "#f0f0f0",
87
+ duration = 1500,
88
+ className,
89
+ style,
90
+ ariaLabel
91
+ }) {
92
+ const bones = breakpoint.bones;
93
+ const containerStyle = {
94
+ position: "relative",
95
+ width: breakpoint.containerWidth,
96
+ height: breakpoint.containerHeight,
97
+ overflow: "hidden",
98
+ ...style
99
+ };
100
+ const getBackground = useCallback(() => {
101
+ switch (animation) {
102
+ case "shimmer":
103
+ return `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;
104
+ case "pulse":
105
+ case "wave":
106
+ case "none":
107
+ return color;
108
+ }
109
+ }, [animation, color, shimmerColor]);
110
+ const getAnimationCSS = useCallback(() => {
111
+ switch (animation) {
112
+ case "shimmer":
113
+ return `castdom-shimmer ${duration}ms ease-in-out infinite`;
114
+ case "pulse":
115
+ return `castdom-pulse ${duration}ms ease-in-out infinite`;
116
+ case "wave":
117
+ return `castdom-wave ${duration}ms ease-in-out infinite`;
118
+ case "none":
119
+ return "none";
120
+ }
121
+ }, [animation, duration]);
122
+ return createElement(
123
+ "div",
124
+ {
125
+ className: `castdom-skeleton castdom-${name}${className ? ` ${className}` : ""}`,
126
+ style: containerStyle,
127
+ role: "status",
128
+ "aria-busy": true,
129
+ "aria-label": ariaLabel ?? `Loading ${name}`
130
+ },
131
+ bones.map(
132
+ (bone, i) => createElement("div", {
133
+ key: i,
134
+ className: "castdom-bone",
135
+ "aria-hidden": true,
136
+ style: {
137
+ position: "absolute",
138
+ left: bone.x,
139
+ top: bone.y,
140
+ width: bone.w,
141
+ height: bone.h,
142
+ borderRadius: bone.r >= 9999 ? "50%" : bone.r,
143
+ background: getBackground(),
144
+ backgroundSize: animation === "shimmer" ? "200% 100%" : void 0,
145
+ animation: getAnimationCSS(),
146
+ animationDelay: animation === "wave" ? `${i * 50}ms` : void 0
147
+ }
148
+ })
149
+ )
150
+ );
151
+ }
152
+ function CastDOMStyle({
153
+ skeletons
154
+ }) {
155
+ const css = useMemo(() => {
156
+ const parts = [
157
+ "@keyframes castdom-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}",
158
+ "@keyframes castdom-pulse{0%,100%{opacity:1}50%{opacity:0.4}}",
159
+ "@keyframes castdom-wave{0%{opacity:0.4}50%{opacity:1}100%{opacity:0.4}}",
160
+ "@media(prefers-reduced-motion:reduce){.castdom-bone{animation:none!important}}"
161
+ ];
162
+ return parts.join("");
163
+ }, []);
164
+ return createElement("style", {
165
+ dangerouslySetInnerHTML: { __html: css },
166
+ "data-castdom": "critical"
167
+ });
168
+ }
169
+ function useCastDOM(name) {
170
+ const entry = get(name);
171
+ const [vw, setVW] = useState(
172
+ typeof window !== "undefined" ? window.innerWidth : 1280
173
+ );
174
+ useEffect(() => {
175
+ if (typeof window === "undefined") return;
176
+ const onResize = () => setVW(window.innerWidth);
177
+ window.addEventListener("resize", onResize, { passive: true });
178
+ return () => window.removeEventListener("resize", onResize);
179
+ }, []);
180
+ const breakpoint = entry ? selectBreakpoint(entry.data, vw) : null;
181
+ return {
182
+ exists: !!entry,
183
+ data: entry?.data ?? null,
184
+ breakpoint,
185
+ css: entry?.css ?? "",
186
+ html: breakpoint ? entry?.html[breakpoint.viewport] ?? "" : ""
187
+ };
188
+ }
189
+ var react_default = CastDOM;
190
+
191
+ export { CastDOM, CastDOMStyle, react_default, useCastDOM };
192
+ //# sourceMappingURL=chunk-ZBJB7WVV.js.map
193
+ //# sourceMappingURL=chunk-ZBJB7WVV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/react.tsx"],"names":[],"mappings":"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","file":"chunk-ZBJB7WVV.js","sourcesContent":["\"use client\";\n\nimport {\n type ReactNode,\n type CSSProperties,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n createElement,\n} from \"react\";\nimport type { SkeletonData, BreakpointData, CastDOMConfig } from \"../core/types.js\";\nimport { get as getFromRegistry } from \"../core/registry.js\";\nimport { selectBreakpoint } from \"../core/responsive.js\";\n\nexport interface CastDOMProps {\n /** Name of the registered skeleton */\n name: string;\n /** Children to render once loaded */\n children: ReactNode;\n /** Whether the content is still loading (default: false when children mount) */\n loading?: boolean;\n /** Custom fallback while skeleton data loads from registry */\n fallback?: ReactNode;\n /** Animation type override */\n animation?: \"shimmer\" | \"pulse\" | \"wave\" | \"none\";\n /** Base color override */\n color?: string;\n /** Shimmer highlight color override */\n shimmerColor?: string;\n /** Animation duration override (ms) */\n duration?: number;\n /** Additional CSS class */\n className?: string;\n /** Additional inline styles */\n style?: CSSProperties;\n /** Callback when skeleton is shown */\n onSkeletonShow?: () => void;\n /** Callback when real content replaces skeleton */\n onContentShow?: () => void;\n /** Aria label for the loading state */\n ariaLabel?: string;\n}\n\n/**\n * <CastDOM> — wraps your component and shows an extracted skeleton while loading.\n *\n * Usage:\n * ```tsx\n * <CastDOM name=\"user-card\" loading={isLoading}>\n * <UserCard data={data} />\n * </CastDOM>\n * ```\n *\n * The skeleton auto-resolves from the global registry.\n * Register skeletons once at app entry with `loadManifest()`.\n */\nexport function CastDOM({\n name,\n children,\n loading,\n fallback,\n animation = \"shimmer\",\n color,\n shimmerColor,\n duration,\n className,\n style,\n onSkeletonShow,\n onContentShow,\n ariaLabel,\n}: CastDOMProps): ReactNode {\n const [isLoading, setIsLoading] = useState(loading ?? true);\n const [viewportWidth, setViewportWidth] = useState<number>(\n typeof window !== \"undefined\" ? window.innerWidth : 1280\n );\n const containerRef = useRef<HTMLDivElement>(null);\n const shownRef = useRef(false);\n\n // Track loading state\n useEffect(() => {\n if (loading !== undefined) {\n setIsLoading(loading);\n }\n }, [loading]);\n\n // Auto-detect loading completion when loading prop is not provided\n useEffect(() => {\n if (loading === undefined && children) {\n setIsLoading(false);\n }\n }, [loading, children]);\n\n // Track viewport width for responsive skeletons\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n const onResize = () => setViewportWidth(window.innerWidth);\n window.addEventListener(\"resize\", onResize, { passive: true });\n return () => window.removeEventListener(\"resize\", onResize);\n }, []);\n\n // Callbacks\n useEffect(() => {\n if (isLoading && !shownRef.current) {\n shownRef.current = true;\n onSkeletonShow?.();\n }\n if (!isLoading && shownRef.current) {\n onContentShow?.();\n }\n }, [isLoading, onSkeletonShow, onContentShow]);\n\n // Get skeleton from registry\n const entry = getFromRegistry(name);\n\n if (!isLoading) {\n return createElement(\n \"div\",\n {\n ref: containerRef,\n className,\n style,\n \"data-castdom\": name,\n },\n children\n );\n }\n\n // No skeleton data — show fallback or nothing\n if (!entry) {\n return fallback\n ? createElement(\"div\", { className, style }, fallback)\n : null;\n }\n\n // Select best breakpoint\n const bp = selectBreakpoint(entry.data, viewportWidth);\n if (!bp) return fallback ? createElement(\"div\", { className, style }, fallback) : null;\n\n // Render skeleton\n return createElement(SkeletonRenderer, {\n name,\n breakpoint: bp,\n animation,\n color,\n shimmerColor,\n duration,\n className,\n style,\n ariaLabel,\n });\n}\n\n/** Internal skeleton renderer — pure CSS, no JS animation runtime */\ninterface SkeletonRendererProps {\n name: string;\n breakpoint: BreakpointData;\n animation: \"shimmer\" | \"pulse\" | \"wave\" | \"none\";\n color?: string;\n shimmerColor?: string;\n duration?: number;\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n}\n\nfunction SkeletonRenderer({\n name,\n breakpoint,\n animation,\n color = \"#e0e0e0\",\n shimmerColor = \"#f0f0f0\",\n duration = 1500,\n className,\n style,\n ariaLabel,\n}: SkeletonRendererProps): ReactNode {\n const bones = breakpoint.bones;\n\n const containerStyle: CSSProperties = {\n position: \"relative\",\n width: breakpoint.containerWidth,\n height: breakpoint.containerHeight,\n overflow: \"hidden\",\n ...style,\n };\n\n const getBackground = useCallback(() => {\n switch (animation) {\n case \"shimmer\":\n return `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;\n case \"pulse\":\n case \"wave\":\n case \"none\":\n return color;\n }\n }, [animation, color, shimmerColor]);\n\n const getAnimationCSS = useCallback((): string => {\n switch (animation) {\n case \"shimmer\":\n return `castdom-shimmer ${duration}ms ease-in-out infinite`;\n case \"pulse\":\n return `castdom-pulse ${duration}ms ease-in-out infinite`;\n case \"wave\":\n return `castdom-wave ${duration}ms ease-in-out infinite`;\n case \"none\":\n return \"none\";\n }\n }, [animation, duration]);\n\n return createElement(\n \"div\",\n {\n className: `castdom-skeleton castdom-${name}${className ? ` ${className}` : \"\"}`,\n style: containerStyle,\n role: \"status\",\n \"aria-busy\": true,\n \"aria-label\": ariaLabel ?? `Loading ${name}`,\n },\n bones.map((bone, i) =>\n createElement(\"div\", {\n key: i,\n className: \"castdom-bone\",\n \"aria-hidden\": true,\n style: {\n position: \"absolute\" as const,\n left: bone.x,\n top: bone.y,\n width: bone.w,\n height: bone.h,\n borderRadius: bone.r >= 9999 ? \"50%\" : bone.r,\n background: getBackground(),\n backgroundSize: animation === \"shimmer\" ? \"200% 100%\" : undefined,\n animation: getAnimationCSS(),\n animationDelay: animation === \"wave\" ? `${i * 50}ms` : undefined,\n } satisfies CSSProperties,\n })\n )\n );\n}\n\n/**\n * <CastDOMStyle> — renders the critical CSS for registered skeletons.\n * Place this in your <head> or layout for optimal performance.\n *\n * ```tsx\n * <head>\n * <CastDOMStyle skeletons={[\"user-card\", \"feed-item\"]} />\n * </head>\n * ```\n */\nexport function CastDOMStyle({\n skeletons,\n}: {\n skeletons?: string[];\n}): ReactNode {\n const css = useMemo(() => {\n const parts: string[] = [\n \"@keyframes castdom-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}\",\n \"@keyframes castdom-pulse{0%,100%{opacity:1}50%{opacity:0.4}}\",\n \"@keyframes castdom-wave{0%{opacity:0.4}50%{opacity:1}100%{opacity:0.4}}\",\n \"@media(prefers-reduced-motion:reduce){.castdom-bone{animation:none!important}}\",\n ];\n return parts.join(\"\");\n }, []);\n\n return createElement(\"style\", {\n dangerouslySetInnerHTML: { __html: css },\n \"data-castdom\": \"critical\",\n });\n}\n\n/**\n * Hook: useCastDOM — programmatic access to skeleton data.\n */\nexport function useCastDOM(name: string) {\n const entry = getFromRegistry(name);\n const [vw, setVW] = useState(\n typeof window !== \"undefined\" ? window.innerWidth : 1280\n );\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const onResize = () => setVW(window.innerWidth);\n window.addEventListener(\"resize\", onResize, { passive: true });\n return () => window.removeEventListener(\"resize\", onResize);\n }, []);\n\n const breakpoint = entry ? selectBreakpoint(entry.data, vw) : null;\n\n return {\n exists: !!entry,\n data: entry?.data ?? null,\n breakpoint,\n css: entry?.css ?? \"\",\n html: breakpoint ? entry?.html[breakpoint.viewport] ?? \"\" : \"\",\n };\n}\n\nexport default CastDOM;\n"]}
@@ -0,0 +1,103 @@
1
+ import { DEFAULTS } from './chunk-EJRNKHL5.js';
2
+
3
+ // src/core/renderer.ts
4
+ function resolveConfig(config) {
5
+ return {
6
+ color: config?.color ?? DEFAULTS.color,
7
+ shimmerColor: config?.shimmerColor ?? DEFAULTS.shimmerColor,
8
+ animationDuration: config?.animationDuration ?? DEFAULTS.animationDuration,
9
+ classPrefix: config?.classPrefix ?? DEFAULTS.classPrefix,
10
+ inlineStyles: config?.inlineStyles ?? DEFAULTS.inlineStyles
11
+ };
12
+ }
13
+ function renderBone(bone, config, index) {
14
+ const { color, shimmerColor, animationDuration, classPrefix, inlineStyles } = config;
15
+ const radius = bone.r >= 9999 ? "50%" : `${bone.r}px`;
16
+ if (inlineStyles) {
17
+ const gradient = `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;
18
+ return `<div aria-hidden="true" style="position:absolute;left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius};background:${gradient};background-size:200% 100%;animation:${classPrefix}-shimmer ${animationDuration}ms ease-in-out infinite"></div>`;
19
+ }
20
+ return `<div class="${classPrefix}-bone" style="left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius}" aria-hidden="true"></div>`;
21
+ }
22
+ function renderBonesHTML(name, bp, config) {
23
+ const cfg = resolveConfig(config);
24
+ const bones = bp.bones.map((bone, i) => renderBone(bone, cfg)).join("");
25
+ return `<div class="${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${name}" role="status" aria-busy="true" aria-label="Loading ${name}" style="position:relative;width:${bp.containerWidth}px;height:${bp.containerHeight}px;overflow:hidden">${bones}</div>`;
26
+ }
27
+ function renderResponsiveHTML(skeleton, config) {
28
+ const cfg = resolveConfig(config);
29
+ const breakpoints = [...skeleton.breakpoints].sort(
30
+ (a, b) => a.viewport - b.viewport
31
+ );
32
+ if (breakpoints.length === 0) return "";
33
+ if (breakpoints.length === 1) {
34
+ return renderBonesHTML(skeleton.name, breakpoints[0], config);
35
+ }
36
+ const variants = breakpoints.map((bp, idx) => {
37
+ const bonesHTML = bp.bones.map((bone, i) => renderBone(bone, cfg)).join("");
38
+ return `<div class="${cfg.classPrefix}-bp ${cfg.classPrefix}-bp-${bp.viewport}" style="position:relative;width:100%;height:${bp.containerHeight}px;overflow:hidden;display:none">${bonesHTML}</div>`;
39
+ });
40
+ return `<div class="${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${skeleton.name}" role="status" aria-busy="true" aria-label="Loading ${skeleton.name}">${variants.join("")}</div>`;
41
+ }
42
+ function generateCSS(skeleton, config) {
43
+ const cfg = resolveConfig(config);
44
+ const { classPrefix: p, color, shimmerColor, animationDuration } = cfg;
45
+ const breakpoints = [...skeleton.breakpoints].sort(
46
+ (a, b) => a.viewport - b.viewport
47
+ );
48
+ const name = skeleton.name;
49
+ const parts = [];
50
+ parts.push(
51
+ `.${p}-${name} .${p}-bone{position:absolute;background:linear-gradient(90deg,${color} 25%,${shimmerColor} 50%,${color} 75%);background-size:200% 100%;animation:${p}-shimmer ${animationDuration}ms ease-in-out infinite}`
52
+ );
53
+ if (breakpoints.length > 1) {
54
+ for (let i = 0; i < breakpoints.length; i++) {
55
+ const bp = breakpoints[i];
56
+ const next = breakpoints[i + 1];
57
+ const prev = breakpoints[i - 1];
58
+ let query;
59
+ if (i === 0) {
60
+ const max = Math.floor((bp.viewport + next.viewport) / 2) - 1;
61
+ query = `@media(max-width:${max}px)`;
62
+ } else if (i === breakpoints.length - 1) {
63
+ const min = Math.floor((prev.viewport + bp.viewport) / 2);
64
+ query = `@media(min-width:${min}px)`;
65
+ } else {
66
+ const min = Math.floor((prev.viewport + bp.viewport) / 2);
67
+ const max = Math.floor((bp.viewport + next.viewport) / 2) - 1;
68
+ query = `@media(min-width:${min}px) and (max-width:${max}px)`;
69
+ }
70
+ parts.push(
71
+ `${query}{.${p}-${name} .${p}-bp-${bp.viewport}{display:block}}`
72
+ );
73
+ }
74
+ } else if (breakpoints.length === 1) {
75
+ parts.push(
76
+ `.${p}-${name} .${p}-bp-${breakpoints[0].viewport}{display:block}`
77
+ );
78
+ }
79
+ return parts.join("\n");
80
+ }
81
+ function generateCriticalCSS(skeletons, config) {
82
+ const cfg = resolveConfig(config);
83
+ const parts = [];
84
+ parts.push(
85
+ `@keyframes ${cfg.classPrefix}-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}`
86
+ );
87
+ parts.push(
88
+ `@media(prefers-reduced-motion:reduce){.${cfg.classPrefix}-bone{animation:none!important}}`
89
+ );
90
+ for (const skeleton of skeletons) {
91
+ parts.push(generateCSS(skeleton, config));
92
+ }
93
+ return parts.join("\n");
94
+ }
95
+ function renderStandalone(skeleton, config) {
96
+ const css = generateCriticalCSS([skeleton], config);
97
+ const html = renderResponsiveHTML(skeleton, config);
98
+ return `<style>${css}</style>${html}`;
99
+ }
100
+
101
+ export { generateCSS, generateCriticalCSS, renderBonesHTML, renderResponsiveHTML, renderStandalone };
102
+ //# sourceMappingURL=chunk-ZWZ5ZLJE.js.map
103
+ //# sourceMappingURL=chunk-ZWZ5ZLJE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/renderer.ts"],"names":[],"mappings":"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","file":"chunk-ZWZ5ZLJE.js","sourcesContent":["import type {\n Bone,\n BreakpointData,\n SkeletonData,\n CastDOMConfig,\n} from \"./types.js\";\nimport { DEFAULTS } from \"./types.js\";\n\ntype RenderConfig = Pick<\n Required<CastDOMConfig>,\n \"color\" | \"shimmerColor\" | \"animationDuration\" | \"classPrefix\" | \"inlineStyles\"\n>;\n\nfunction resolveConfig(config?: Partial<CastDOMConfig>): RenderConfig {\n return {\n color: config?.color ?? DEFAULTS.color,\n shimmerColor: config?.shimmerColor ?? DEFAULTS.shimmerColor,\n animationDuration: config?.animationDuration ?? DEFAULTS.animationDuration,\n classPrefix: config?.classPrefix ?? DEFAULTS.classPrefix,\n inlineStyles: config?.inlineStyles ?? DEFAULTS.inlineStyles,\n };\n}\n\n/** Render a single bone to an HTML string */\nfunction renderBone(bone: Bone, config: RenderConfig, index: number): string {\n const { color, shimmerColor, animationDuration, classPrefix, inlineStyles } = config;\n const radius = bone.r >= 9999 ? \"50%\" : `${bone.r}px`;\n\n if (inlineStyles) {\n const gradient = `linear-gradient(90deg, ${color} 25%, ${shimmerColor} 50%, ${color} 75%)`;\n return `<div aria-hidden=\"true\" style=\"position:absolute;left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius};background:${gradient};background-size:200% 100%;animation:${classPrefix}-shimmer ${animationDuration}ms ease-in-out infinite\"></div>`;\n }\n\n return `<div class=\"${classPrefix}-bone\" style=\"left:${bone.x}px;top:${bone.y}px;width:${bone.w}px;height:${bone.h}px;border-radius:${radius}\" aria-hidden=\"true\"></div>`;\n}\n\n/**\n * Render all bones for a breakpoint to HTML.\n * The output is a pure CSS skeleton — no JS needed to display.\n */\nexport function renderBonesHTML(\n name: string,\n bp: BreakpointData,\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const bones = bp.bones\n .map((bone, i) => renderBone(bone, cfg, i))\n .join(\"\");\n\n return `<div class=\"${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${name}\" role=\"status\" aria-busy=\"true\" aria-label=\"Loading ${name}\" style=\"position:relative;width:${bp.containerWidth}px;height:${bp.containerHeight}px;overflow:hidden\">${bones}</div>`;\n}\n\n/**\n * Render a responsive skeleton with all breakpoints.\n * Uses CSS container queries + media queries for breakpoint switching.\n */\nexport function renderResponsiveHTML(\n skeleton: SkeletonData,\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const breakpoints = [...skeleton.breakpoints].sort(\n (a, b) => a.viewport - b.viewport\n );\n\n if (breakpoints.length === 0) return \"\";\n if (breakpoints.length === 1) {\n return renderBonesHTML(skeleton.name, breakpoints[0], config);\n }\n\n // Render each breakpoint variant, hidden by default\n const variants = breakpoints.map((bp, idx) => {\n const bonesHTML = bp.bones\n .map((bone, i) => renderBone(bone, cfg, i))\n .join(\"\");\n\n return `<div class=\"${cfg.classPrefix}-bp ${cfg.classPrefix}-bp-${bp.viewport}\" style=\"position:relative;width:100%;height:${bp.containerHeight}px;overflow:hidden;display:none\">${bonesHTML}</div>`;\n });\n\n return `<div class=\"${cfg.classPrefix}-skeleton ${cfg.classPrefix}-${skeleton.name}\" role=\"status\" aria-busy=\"true\" aria-label=\"Loading ${skeleton.name}\">${variants.join(\"\")}</div>`;\n}\n\n/**\n * Generate CSS for a skeleton definition.\n * Includes responsive media queries to show/hide the correct breakpoint variant.\n */\nexport function generateCSS(\n skeleton: SkeletonData,\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const { classPrefix: p, color, shimmerColor, animationDuration } = cfg;\n const breakpoints = [...skeleton.breakpoints].sort(\n (a, b) => a.viewport - b.viewport\n );\n const name = skeleton.name;\n\n const parts: string[] = [];\n\n // Base bone styles\n parts.push(\n `.${p}-${name} .${p}-bone{position:absolute;background:linear-gradient(90deg,${color} 25%,${shimmerColor} 50%,${color} 75%);background-size:200% 100%;animation:${p}-shimmer ${animationDuration}ms ease-in-out infinite}`\n );\n\n // Responsive breakpoint switching via media queries\n if (breakpoints.length > 1) {\n for (let i = 0; i < breakpoints.length; i++) {\n const bp = breakpoints[i];\n const next = breakpoints[i + 1];\n const prev = breakpoints[i - 1];\n\n let query: string;\n if (i === 0) {\n // Smallest: up to midpoint between this and next\n const max = Math.floor((bp.viewport + next!.viewport) / 2) - 1;\n query = `@media(max-width:${max}px)`;\n } else if (i === breakpoints.length - 1) {\n // Largest: from midpoint between prev and this\n const min = Math.floor((prev!.viewport + bp.viewport) / 2);\n query = `@media(min-width:${min}px)`;\n } else {\n // Middle: between midpoints\n const min = Math.floor((prev!.viewport + bp.viewport) / 2);\n const max = Math.floor((bp.viewport + next!.viewport) / 2) - 1;\n query = `@media(min-width:${min}px) and (max-width:${max}px)`;\n }\n\n parts.push(\n `${query}{.${p}-${name} .${p}-bp-${bp.viewport}{display:block}}`\n );\n }\n } else if (breakpoints.length === 1) {\n // Single breakpoint — always visible\n parts.push(\n `.${p}-${name} .${p}-bp-${breakpoints[0].viewport}{display:block}`\n );\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Generate critical inline CSS for above-the-fold skeletons.\n * This CSS is designed to be embedded in <style> tags for instant rendering.\n */\nexport function generateCriticalCSS(\n skeletons: SkeletonData[],\n config?: Partial<CastDOMConfig>\n): string {\n const cfg = resolveConfig(config);\n const parts: string[] = [];\n\n // Base keyframes (only once)\n parts.push(\n `@keyframes ${cfg.classPrefix}-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}`\n );\n\n // Reduced motion\n parts.push(\n `@media(prefers-reduced-motion:reduce){.${cfg.classPrefix}-bone{animation:none!important}}`\n );\n\n // Per-skeleton styles\n for (const skeleton of skeletons) {\n parts.push(generateCSS(skeleton, config));\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Generate a complete, self-contained HTML skeleton\n * that can be embedded directly in server-rendered pages.\n * Includes inline <style> — no external CSS needed.\n */\nexport function renderStandalone(\n skeleton: SkeletonData,\n config?: Partial<CastDOMConfig>\n): string {\n const css = generateCriticalCSS([skeleton], config);\n const html = renderResponsiveHTML(skeleton, config);\n return `<style>${css}</style>${html}`;\n}\n"]}