@marvalt/dstyler 0.1.19 → 0.1.21

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 (52) hide show
  1. package/dist/components/AutoBlock.d.ts +8 -0
  2. package/dist/components/AutoBlock.d.ts.map +1 -1
  3. package/dist/components/AutoSection.d.ts +4 -1
  4. package/dist/components/AutoSection.d.ts.map +1 -1
  5. package/dist/components/StyledPage.d.ts +6 -0
  6. package/dist/components/StyledPage.d.ts.map +1 -1
  7. package/dist/components/index.d.ts +1 -1
  8. package/dist/components/index.d.ts.map +1 -1
  9. package/dist/engine/detectSections.d.ts.map +1 -1
  10. package/dist/engine/index.d.ts.map +1 -1
  11. package/dist/engine/renderBlock.d.ts +0 -3
  12. package/dist/engine/renderBlock.d.ts.map +1 -1
  13. package/dist/engine/selectTemplate.d.ts.map +1 -1
  14. package/dist/index.d.ts +159 -90
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.esm.js +478 -502
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +481 -501
  19. package/dist/index.js.map +1 -1
  20. package/dist/providers/ThemeProvider.d.ts.map +1 -1
  21. package/dist/providers/index.d.ts.map +1 -1
  22. package/dist/templates/FooterMinimal.d.ts.map +1 -1
  23. package/dist/templates/HeroDefault.d.ts.map +1 -1
  24. package/dist/templates/SectionBase.d.ts.map +1 -1
  25. package/dist/templates/SectionWave.d.ts.map +1 -1
  26. package/dist/templates/blocks/BlockButton.d.ts +6 -2
  27. package/dist/templates/blocks/BlockButton.d.ts.map +1 -1
  28. package/dist/templates/blocks/BlockForm.d.ts +6 -2
  29. package/dist/templates/blocks/BlockForm.d.ts.map +1 -1
  30. package/dist/templates/blocks/BlockImage.d.ts +6 -2
  31. package/dist/templates/blocks/BlockImage.d.ts.map +1 -1
  32. package/dist/templates/blocks/BlockText.d.ts +6 -2
  33. package/dist/templates/blocks/BlockText.d.ts.map +1 -1
  34. package/dist/templates/blocks/index.d.ts.map +1 -1
  35. package/dist/templates/index.d.ts +1 -0
  36. package/dist/templates/index.d.ts.map +1 -1
  37. package/dist/tokens/css-vars.d.ts.map +1 -1
  38. package/dist/tokens/index.d.ts.map +1 -1
  39. package/dist/tokens/tokens.d.ts.map +1 -1
  40. package/dist/tokens/wordpress-to-tokens.d.ts.map +1 -1
  41. package/dist/types/block.d.ts +2 -0
  42. package/dist/types/block.d.ts.map +1 -1
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types/section.d.ts.map +1 -1
  46. package/dist/types/theme.d.ts.map +1 -1
  47. package/dist/utils/createColorMapper.d.ts.map +1 -1
  48. package/dist/utils/index.d.ts +1 -1
  49. package/dist/utils/index.d.ts.map +1 -1
  50. package/dist/utils/responsive.d.ts.map +1 -1
  51. package/package.json +52 -69
  52. package/README.md +0 -76
package/dist/index.js CHANGED
@@ -2,241 +2,6 @@
2
2
 
3
3
  var React = require('react');
4
4
 
5
- /**
6
- * @license GPL-3.0-or-later
7
- *
8
- * This file is part of the MarVAlt Open SDK.
9
- * Copyright (c) 2025 Vibune Pty Ltd.
10
- */
11
- const defaultTokens = {
12
- colors: {
13
- primary: '#1a73e8',
14
- secondary: '#34a853',
15
- background: '#ffffff',
16
- text: '#222222',
17
- heading: '#222222',
18
- default: '#ffffff',
19
- alternate: '#f5f5f5',
20
- highlight: '#e8f0fe',
21
- },
22
- spacing: {
23
- sm: 8,
24
- md: 16,
25
- lg: 32,
26
- xl: 64,
27
- },
28
- typography: {
29
- body: 'Inter, sans-serif',
30
- heading: 'Inter, sans-serif',
31
- scale: 1.2,
32
- },
33
- };
34
-
35
- /**
36
- * @license GPL-3.0-or-later
37
- *
38
- * This file is part of the MarVAlt Open SDK.
39
- * Copyright (c) 2025 Vibune Pty Ltd.
40
- */
41
- /**
42
- * Convert WordPress theme styles to design tokens
43
- */
44
- function convertWordPressToTokens(wpStyles) {
45
- if (!wpStyles) {
46
- return defaultTokens;
47
- }
48
- const palette = wpStyles.theme_palette || {};
49
- // Typography priority: Customizer (wpStyles.typography) > Theme Editor fonts (if present separately) > defaults
50
- // Note: Theme Editor fonts are not explicitly separated in current schema; assume Customizer holds them if provided.
51
- const tokens = {
52
- ...defaultTokens,
53
- colors: {
54
- ...defaultTokens.colors,
55
- ...(wpStyles.colors?.primary && { primary: wpStyles.colors.primary }),
56
- ...(wpStyles.colors?.secondary && { secondary: wpStyles.colors.secondary }),
57
- ...(wpStyles.colors?.text && { text: wpStyles.colors.text }),
58
- // heading handled below to allow palette fallback
59
- ...(wpStyles.colors?.background && {
60
- background: wpStyles.colors.background,
61
- default: wpStyles.colors.background,
62
- }),
63
- },
64
- typography: {
65
- ...defaultTokens.typography,
66
- ...(wpStyles.typography?.heading_font_family && {
67
- heading: wpStyles.typography.heading_font_family
68
- }),
69
- ...(wpStyles.typography?.body_font_family && {
70
- body: wpStyles.typography.body_font_family
71
- }),
72
- },
73
- };
74
- // Explicit heading override (may be undefined/empty), then fallback below
75
- tokens.colors.heading = wpStyles.colors?.heading || undefined;
76
- // Ensure heading color falls back to text if not provided
77
- if (!tokens.colors.heading) {
78
- tokens.colors.heading =
79
- // Prefer explicit heading color if provided by Theme Editor
80
- wpStyles.colors?.heading ||
81
- // Then Theme Editor palette contrast (matches heading color choice in editor)
82
- palette['contrast'] ||
83
- // Then palette primary
84
- palette['primary'] ||
85
- // Then explicit primary fallback
86
- wpStyles.colors?.primary ||
87
- tokens.colors.text;
88
- }
89
- // Generate alternate and highlight colors from primary/background
90
- if (tokens.colors.primary && tokens.colors.background) {
91
- // Alternate: slightly darker/lighter than background
92
- tokens.colors.alternate = adjustBrightness(tokens.colors.background, -0.05);
93
- // Highlight: tinted with primary color
94
- tokens.colors.highlight = blendColors(tokens.colors.background, tokens.colors.primary, 0.1);
95
- }
96
- return tokens;
97
- }
98
- /**
99
- * Adjust brightness of a hex color
100
- */
101
- function adjustBrightness(hex, amount) {
102
- const num = parseInt(hex.replace('#', ''), 16);
103
- const r = Math.max(0, Math.min(255, ((num >> 16) & 0xff) + (amount * 255)));
104
- const g = Math.max(0, Math.min(255, ((num >> 8) & 0xff) + (amount * 255)));
105
- const b = Math.max(0, Math.min(255, (num & 0xff) + (amount * 255)));
106
- return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
107
- }
108
- /**
109
- * Blend two colors
110
- */
111
- function blendColors(color1, color2, ratio) {
112
- const c1 = hexToRgb(color1);
113
- const c2 = hexToRgb(color2);
114
- if (!c1 || !c2)
115
- return color1;
116
- const r = Math.round(c1.r + (c2.r - c1.r) * ratio);
117
- const g = Math.round(c1.g + (c2.g - c1.g) * ratio);
118
- const b = Math.round(c1.b + (c2.b - c1.b) * ratio);
119
- return `rgb(${r}, ${g}, ${b})`;
120
- }
121
- function hexToRgb(hex) {
122
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
123
- return result
124
- ? {
125
- r: parseInt(result[1], 16),
126
- g: parseInt(result[2], 16),
127
- b: parseInt(result[3], 16),
128
- }
129
- : null;
130
- }
131
-
132
- /**
133
- * @license GPL-3.0-or-later
134
- *
135
- * This file is part of the MarVAlt Open SDK.
136
- * Copyright (c) 2025 Vibune Pty Ltd.
137
- */
138
- const THEME_STYLE_ID = 'dstyler-theme-vars';
139
- function tokensToCSS(tokens) {
140
- return `
141
- --dstyler-color-primary: ${tokens.colors.primary};
142
- --dstyler-color-secondary: ${tokens.colors.secondary};
143
- --dstyler-color-background: ${tokens.colors.background};
144
- --dstyler-color-text: ${tokens.colors.text};
145
- --dstyler-color-heading: ${tokens.colors.heading || tokens.colors.text};
146
- --dstyler-color-default: ${tokens.colors.default};
147
- --dstyler-color-alternate: ${tokens.colors.alternate};
148
- --dstyler-color-highlight: ${tokens.colors.highlight};
149
- --dstyler-spacing-sm: ${tokens.spacing.sm}px;
150
- --dstyler-spacing-md: ${tokens.spacing.md}px;
151
- --dstyler-spacing-lg: ${tokens.spacing.lg}px;
152
- --dstyler-spacing-xl: ${tokens.spacing.xl}px;
153
- --dstyler-font-body: ${tokens.typography.body};
154
- --dstyler-font-heading: ${tokens.typography.heading};
155
- --dstyler-font-scale: ${tokens.typography.scale};
156
- `;
157
- }
158
- function ensureStyleElement() {
159
- if (typeof document === 'undefined')
160
- return null;
161
- let el = document.getElementById(THEME_STYLE_ID);
162
- if (!el) {
163
- el = document.createElement('style');
164
- el.id = THEME_STYLE_ID;
165
- document.head.appendChild(el);
166
- }
167
- return el;
168
- }
169
- /**
170
- * Inject CSS variables from design tokens into :root
171
- */
172
- function injectCSSVariables(tokens) {
173
- const el = ensureStyleElement();
174
- if (!el)
175
- return;
176
- el.textContent = `:root { ${tokensToCSS(tokens)} }`;
177
- }
178
- /**
179
- * Inject CSS variables for both light and dark themes.
180
- * Light applies to :root, dark applies when [data-theme="dark"] is present.
181
- */
182
- function injectDualThemeCSS(light, dark) {
183
- const el = ensureStyleElement();
184
- if (!el)
185
- return;
186
- el.textContent = `
187
- :root { ${tokensToCSS(light)} }
188
- :root[data-theme="dark"] { ${tokensToCSS(dark)} }
189
- `;
190
- }
191
-
192
- /**
193
- * @license GPL-3.0-or-later
194
- *
195
- * This file is part of the MarVAlt Open SDK.
196
- * Copyright (c) 2025 Vibune Pty Ltd.
197
- */
198
- /**
199
- * ThemeProvider - Provides design tokens to the application
200
- *
201
- * Priority order:
202
- * 1. customTokens (highest priority - overrides everything)
203
- * 2. WordPress styles (converted to tokens)
204
- * 3. Default tokens (fallback)
205
- */
206
- const ThemeProvider = ({ children, wpStyles, customTokens, darkWpStyles, darkTokens, mode = 'light', }) => {
207
- React.useEffect(() => {
208
- // Light tokens: custom -> wpStyles -> default
209
- const lightTokens = customTokens || convertWordPressToTokens(wpStyles);
210
- // Dark tokens: provided -> wpStylesDark -> fallback to light
211
- const resolvedDarkTokens = darkTokens || (darkWpStyles ? convertWordPressToTokens(darkWpStyles) : lightTokens);
212
- if (mode === 'dark') {
213
- injectDualThemeCSS(lightTokens, resolvedDarkTokens);
214
- if (typeof document !== 'undefined') {
215
- document.documentElement.setAttribute('data-theme', 'dark');
216
- }
217
- return;
218
- }
219
- if (mode === 'system') {
220
- const prefersDark = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
221
- injectDualThemeCSS(lightTokens, resolvedDarkTokens);
222
- if (typeof document !== 'undefined') {
223
- document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
224
- }
225
- return;
226
- }
227
- // Default to light
228
- injectDualThemeCSS(lightTokens, resolvedDarkTokens);
229
- if (typeof document !== 'undefined') {
230
- document.documentElement.setAttribute('data-theme', 'light');
231
- }
232
- }, [wpStyles, customTokens, darkWpStyles, darkTokens, mode]);
233
- // Return children if provided, otherwise return null (theme still applies via useEffect)
234
- if (children === undefined || children === null) {
235
- return null;
236
- }
237
- return React.createElement(React.Fragment, null, children);
238
- };
239
-
240
5
  var jsxRuntime = {exports: {}};
241
6
 
242
7
  var reactJsxRuntime_production = {};
@@ -652,21 +417,80 @@ if (process.env.NODE_ENV === 'production') {
652
417
  var jsxRuntimeExports = jsxRuntime.exports;
653
418
 
654
419
  /**
655
- * @license GPL-3.0-or-later
656
- *
657
- * This file is part of the MarVAlt Open SDK.
658
- * Copyright (c) 2025 Vibune Pty Ltd.
420
+ * Detect sections from WordPress blocks
421
+ * Sections are defined by core/group blocks (with or without alignment)
422
+ * Sub-sections are core/columns blocks inside groups
659
423
  */
424
+ function detectSections(blocks) {
425
+ const sections = [];
426
+ let sectionIndex = 0;
427
+ for (const block of blocks) {
428
+ // core/group blocks are sections
429
+ if (block.name === 'core/group') {
430
+ const attrs = block.attributes || {};
431
+ const className = attrs.className;
432
+ const customId = attrs.id;
433
+ // Extract blocks from innerBlocks
434
+ const innerBlocks = block.innerBlocks || [];
435
+ sections.push({
436
+ id: `section-${sectionIndex}`,
437
+ type: 'section',
438
+ block,
439
+ blocks: innerBlocks,
440
+ index: sectionIndex,
441
+ customClass: className,
442
+ customId,
443
+ });
444
+ sectionIndex++;
445
+ }
446
+ }
447
+ return sections;
448
+ }
660
449
  /**
661
- * Select template component based on section properties
450
+ * Determine section style based on alternating logic and custom classes
451
+ * Rules:
452
+ * - default is always first
453
+ * - after default follows alternate
454
+ * - highlight resets - next section is default
662
455
  */
663
- function selectTemplate(section) {
664
- const block = section.block;
665
- const attrs = block.attributes || {};
456
+ function determineSectionStyle(section, previousStyle) {
666
457
  const className = section.customClass?.toLowerCase() || '';
667
- // Check for hero/cover blocks
668
- if (block.name === 'core/cover') {
669
- return 'HeroDefault';
458
+ // Check for custom class overrides first
459
+ if (className.includes('highlight') || className.includes('highlight-section')) {
460
+ return 'highlight';
461
+ }
462
+ if (className.includes('dark') || className.includes('dark-section')) {
463
+ return 'alternate';
464
+ }
465
+ if (className.includes('default') || className.includes('default-section')) {
466
+ return 'default';
467
+ }
468
+ // Alternating logic
469
+ if (section.index === 0) {
470
+ return 'default';
471
+ }
472
+ if (previousStyle === 'highlight') {
473
+ return 'default';
474
+ }
475
+ if (previousStyle === 'default') {
476
+ return 'alternate';
477
+ }
478
+ if (previousStyle === 'alternate') {
479
+ return 'default';
480
+ }
481
+ return 'default';
482
+ }
483
+
484
+ /**
485
+ * Select template component based on section properties
486
+ */
487
+ function selectTemplate(section) {
488
+ const block = section.block;
489
+ const attrs = block.attributes || {};
490
+ const className = section.customClass?.toLowerCase() || '';
491
+ // Check for hero/cover blocks
492
+ if (block.name === 'core/cover') {
493
+ return 'HeroDefault';
670
494
  }
671
495
  // Check for footer (could be detected by block name or class)
672
496
  if (className.includes('footer') || block.name === 'core/footer') {
@@ -680,101 +504,6 @@ function selectTemplate(section) {
680
504
  return 'SectionBase';
681
505
  }
682
506
 
683
- const SectionBase = ({ section, children }) => {
684
- const style = section.style || 'default';
685
- const bgColor = `var(--dstyler-color-${style})`;
686
- const className = [
687
- 'dstyler-section',
688
- `dstyler-section-${style}`,
689
- section.customClass,
690
- ].filter(Boolean).join(' ');
691
- return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
692
- backgroundColor: bgColor,
693
- padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
694
- }, children: jsxRuntimeExports.jsx("div", { style: {
695
- maxWidth: 'var(--dstyler-container-max-width, 1200px)',
696
- margin: '0 auto',
697
- }, children: children }) }));
698
- };
699
-
700
- const SectionWave = ({ section, children, waveTop = true, waveBottom = true, }) => {
701
- const style = section.style || 'default';
702
- const bgColor = `var(--dstyler-color-${style})`;
703
- const className = [
704
- 'dstyler-section',
705
- 'dstyler-section-wave',
706
- `dstyler-section-${style}`,
707
- section.customClass,
708
- ].filter(Boolean).join(' ');
709
- // Wave SVG
710
- const waveSvg = (jsxRuntimeExports.jsx("svg", { viewBox: "0 0 1440 120", preserveAspectRatio: "none", style: {
711
- width: '100%',
712
- height: '60px',
713
- display: 'block',
714
- fill: bgColor,
715
- }, children: jsxRuntimeExports.jsx("path", { d: "M0,60 C240,0 480,120 720,60 C960,0 1200,120 1440,60 L1440,120 L0,120 Z" }) }));
716
- return (jsxRuntimeExports.jsxs("section", { className: className, id: section.customId, style: {
717
- backgroundColor: bgColor,
718
- position: 'relative',
719
- }, children: [waveTop && (jsxRuntimeExports.jsx("div", { style: { marginTop: '-1px' }, children: waveSvg })), jsxRuntimeExports.jsx("div", { style: {
720
- padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
721
- }, children: jsxRuntimeExports.jsx("div", { style: {
722
- maxWidth: 'var(--dstyler-container-max-width, 1200px)',
723
- margin: '0 auto',
724
- }, children: children }) }), waveBottom && (jsxRuntimeExports.jsx("div", { style: { marginBottom: '-1px', transform: 'rotate(180deg)' }, children: waveSvg }))] }));
725
- };
726
-
727
- const HeroDefault = ({ section, children }) => {
728
- const style = section.style || 'default';
729
- const bgColor = `var(--dstyler-color-${style})`;
730
- const className = [
731
- 'dstyler-hero',
732
- 'dstyler-hero-default',
733
- `dstyler-hero-${style}`,
734
- section.customClass,
735
- ].filter(Boolean).join(' ');
736
- return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
737
- backgroundColor: bgColor,
738
- padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
739
- minHeight: '50vh',
740
- display: 'flex',
741
- alignItems: 'center',
742
- justifyContent: 'center',
743
- }, children: jsxRuntimeExports.jsx("div", { style: {
744
- maxWidth: 'var(--dstyler-container-max-width, 1200px)',
745
- margin: '0 auto',
746
- textAlign: 'center',
747
- }, children: children }) }));
748
- };
749
-
750
- const FooterMinimal = ({ section, children }) => {
751
- const style = section.style || 'default';
752
- const bgColor = `var(--dstyler-color-${style})`;
753
- const className = [
754
- 'dstyler-footer',
755
- 'dstyler-footer-minimal',
756
- `dstyler-footer-${style}`,
757
- section.customClass,
758
- ].filter(Boolean).join(' ');
759
- return (jsxRuntimeExports.jsx("footer", { className: className, id: section.customId, style: {
760
- backgroundColor: bgColor,
761
- padding: 'var(--dstyler-spacing-lg) var(--dstyler-spacing-md)',
762
- borderTop: '1px solid var(--dstyler-color-alternate)',
763
- }, children: jsxRuntimeExports.jsx("div", { style: {
764
- maxWidth: 'var(--dstyler-container-max-width, 1200px)',
765
- margin: '0 auto',
766
- textAlign: 'center',
767
- fontSize: '0.875rem',
768
- color: 'var(--dstyler-color-text)',
769
- }, children: children }) }));
770
- };
771
-
772
- /**
773
- * @license GPL-3.0-or-later
774
- *
775
- * This file is part of the MarVAlt Open SDK.
776
- * Copyright (c) 2025 Vibune Pty Ltd.
777
- */
778
507
  /**
779
508
  * Select block renderer based on block type
780
509
  */
@@ -795,7 +524,7 @@ function selectBlockRenderer(block) {
795
524
  case 'core/shortcode':
796
525
  // Check if it's a form shortcode
797
526
  const attrs = block.attributes || {};
798
- const content = attrs.content || attrs.html || '';
527
+ const content = (attrs.content || attrs.html || '');
799
528
  if (content.includes('[gravityform') || content.includes('[mauticform')) {
800
529
  return 'BlockForm';
801
530
  }
@@ -807,7 +536,7 @@ function selectBlockRenderer(block) {
807
536
 
808
537
  const BlockText = ({ block, className }) => {
809
538
  const attrs = block.attributes || {};
810
- const content = attrs.content || attrs.text || '';
539
+ const content = (attrs.content || attrs.text || '');
811
540
  const tagName = block.name === 'core/heading'
812
541
  ? `h${attrs.level || 2}`
813
542
  : 'p';
@@ -827,40 +556,38 @@ const BlockText = ({ block, className }) => {
827
556
 
828
557
  const BlockImage = ({ block, className }) => {
829
558
  const attrs = block.attributes || {};
830
- const url = attrs.url || attrs.sourceUrl || '';
831
- const alt = attrs.alt || attrs.altText || '';
559
+ const url = (attrs.url || attrs.sourceUrl || '');
560
+ const alt = (attrs.alt || attrs.altText || '');
832
561
  const width = attrs.width;
833
562
  const height = attrs.height;
834
563
  if (!url)
835
564
  return null;
836
- const style = {
837
- maxWidth: '100%',
838
- height: 'auto',
839
- marginBottom: 'var(--dstyler-spacing-md)',
840
- borderRadius: '8px',
841
- };
842
- return (jsxRuntimeExports.jsx("img", { src: url, alt: alt, width: width, height: height, className: className, style: style, loading: "lazy" }));
565
+ return (jsxRuntimeExports.jsx("img", { src: url, alt: alt, width: width, height: height, className: className, style: {
566
+ maxWidth: '100%',
567
+ height: 'auto',
568
+ marginBottom: 'var(--dstyler-spacing-md)',
569
+ borderRadius: '8px',
570
+ }, loading: "lazy" }));
843
571
  };
844
572
 
845
573
  const BlockButton = ({ block, className }) => {
846
574
  const attrs = block.attributes || {};
847
- const text = attrs.text || attrs.label || 'Button';
848
- const url = attrs.url || attrs.linkUrl || '#';
575
+ const text = (attrs.text || attrs.label || 'Button');
576
+ const url = (attrs.url || attrs.linkUrl || '#');
849
577
  const isExternal = typeof window !== 'undefined'
850
578
  ? url.startsWith('http') && !url.includes(window.location.hostname)
851
579
  : url.startsWith('http');
852
- const style = {
853
- display: 'inline-block',
854
- padding: 'var(--dstyler-spacing-md) var(--dstyler-spacing-lg)',
855
- backgroundColor: 'var(--dstyler-color-primary)',
856
- color: '#ffffff',
857
- textDecoration: 'none',
858
- borderRadius: '4px',
859
- fontWeight: '500',
860
- marginBottom: 'var(--dstyler-spacing-md)',
861
- transition: 'opacity 0.2s',
862
- };
863
- return (jsxRuntimeExports.jsx("a", { href: url, target: isExternal ? '_blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, className: className, style: style, onMouseEnter: (e) => {
580
+ return (jsxRuntimeExports.jsx("a", { href: url, target: isExternal ? '_blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, className: className, style: {
581
+ display: 'inline-block',
582
+ padding: 'var(--dstyler-spacing-md) var(--dstyler-spacing-lg)',
583
+ backgroundColor: 'var(--dstyler-color-primary)',
584
+ color: '#ffffff',
585
+ textDecoration: 'none',
586
+ borderRadius: '4px',
587
+ fontWeight: '500',
588
+ marginBottom: 'var(--dstyler-spacing-md)',
589
+ transition: 'opacity 0.2s',
590
+ }, onMouseEnter: (e) => {
864
591
  e.currentTarget.style.opacity = '0.9';
865
592
  }, onMouseLeave: (e) => {
866
593
  e.currentTarget.style.opacity = '1';
@@ -869,16 +596,22 @@ const BlockButton = ({ block, className }) => {
869
596
 
870
597
  const BlockForm = ({ block, className }) => {
871
598
  const attrs = block.attributes || {};
872
- const content = attrs.content || attrs.html || '';
599
+ const content = (attrs.content || attrs.html || '');
873
600
  // For now, just render the HTML content
874
601
  // In the future, this could parse and render using Gravity Forms or Mautic components
875
- const style = {
876
- marginBottom: 'var(--dstyler-spacing-lg)',
877
- };
878
- return (jsxRuntimeExports.jsx("div", { className: className, style: style, dangerouslySetInnerHTML: { __html: content } }));
602
+ return (jsxRuntimeExports.jsx("div", { className: className, style: {
603
+ marginBottom: 'var(--dstyler-spacing-lg)',
604
+ }, dangerouslySetInnerHTML: { __html: content } }));
879
605
  };
880
606
 
881
- const AutoBlock = ({ block, registry }) => {
607
+ /**
608
+ * AutoBlock - Automatically selects and renders the appropriate block renderer
609
+ *
610
+ * Priority:
611
+ * 1. Use registry renderer if available (integrates with wparser)
612
+ * 2. Use dstyler's built-in block renderers
613
+ */
614
+ const AutoBlock = ({ block, registry, className }) => {
882
615
  // If registry is provided, use it for rendering (integrates with wparser)
883
616
  if (registry && registry.renderers[block.name]) {
884
617
  const Renderer = registry.renderers[block.name];
@@ -888,19 +621,111 @@ const AutoBlock = ({ block, registry }) => {
888
621
  const rendererName = selectBlockRenderer(block);
889
622
  switch (rendererName) {
890
623
  case 'BlockText':
891
- return jsxRuntimeExports.jsx(BlockText, { block: block });
624
+ return jsxRuntimeExports.jsx(BlockText, { block: block, className: className });
892
625
  case 'BlockImage':
893
- return jsxRuntimeExports.jsx(BlockImage, { block: block });
626
+ return jsxRuntimeExports.jsx(BlockImage, { block: block, className: className });
894
627
  case 'BlockButton':
895
- return jsxRuntimeExports.jsx(BlockButton, { block: block });
628
+ return jsxRuntimeExports.jsx(BlockButton, { block: block, className: className });
896
629
  case 'BlockForm':
897
- return jsxRuntimeExports.jsx(BlockForm, { block: block });
630
+ return jsxRuntimeExports.jsx(BlockForm, { block: block, className: className });
898
631
  default:
899
632
  // Fallback: render block name or content
900
633
  return (jsxRuntimeExports.jsx("div", { style: { marginBottom: 'var(--dstyler-spacing-md)' }, children: jsxRuntimeExports.jsxs("pre", { style: { fontSize: '0.875rem', color: '#666' }, children: [block.name, ": ", JSON.stringify(block.attributes, null, 2)] }) }));
901
634
  }
902
635
  };
903
636
 
637
+ const SectionBase = ({ section, children }) => {
638
+ const style = section.style || 'default';
639
+ const bgColor = `var(--dstyler-color-${style})`;
640
+ const className = [
641
+ 'dstyler-section',
642
+ `dstyler-section-${style}`,
643
+ section.customClass,
644
+ ].filter(Boolean).join(' ');
645
+ return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
646
+ backgroundColor: bgColor,
647
+ padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
648
+ }, children: jsxRuntimeExports.jsx("div", { style: {
649
+ maxWidth: 'var(--dstyler-container-max-width, 1200px)',
650
+ margin: '0 auto',
651
+ }, children: children }) }));
652
+ };
653
+
654
+ const SectionWave = ({ section, children, waveTop = true, waveBottom = true, }) => {
655
+ const style = section.style || 'default';
656
+ const bgColor = `var(--dstyler-color-${style})`;
657
+ const className = [
658
+ 'dstyler-section',
659
+ 'dstyler-section-wave',
660
+ `dstyler-section-${style}`,
661
+ section.customClass,
662
+ ].filter(Boolean).join(' ');
663
+ // Wave SVG
664
+ const waveSvg = (jsxRuntimeExports.jsx("svg", { viewBox: "0 0 1440 120", preserveAspectRatio: "none", style: {
665
+ width: '100%',
666
+ height: '60px',
667
+ display: 'block',
668
+ fill: bgColor,
669
+ }, children: jsxRuntimeExports.jsx("path", { d: "M0,60 C240,0 480,120 720,60 C960,0 1200,120 1440,60 L1440,120 L0,120 Z" }) }));
670
+ return (jsxRuntimeExports.jsxs("section", { className: className, id: section.customId, style: {
671
+ backgroundColor: bgColor,
672
+ position: 'relative',
673
+ }, children: [waveTop && jsxRuntimeExports.jsx("div", { style: { marginTop: '-1px' }, children: waveSvg }), jsxRuntimeExports.jsx("div", { style: {
674
+ padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
675
+ }, children: jsxRuntimeExports.jsx("div", { style: {
676
+ maxWidth: 'var(--dstyler-container-max-width, 1200px)',
677
+ margin: '0 auto',
678
+ }, children: children }) }), waveBottom && (jsxRuntimeExports.jsx("div", { style: { marginBottom: '-1px', transform: 'rotate(180deg)' }, children: waveSvg }))] }));
679
+ };
680
+
681
+ const HeroDefault = ({ section, children }) => {
682
+ const style = section.style || 'default';
683
+ const bgColor = `var(--dstyler-color-${style})`;
684
+ const className = [
685
+ 'dstyler-hero',
686
+ 'dstyler-hero-default',
687
+ `dstyler-hero-${style}`,
688
+ section.customClass,
689
+ ].filter(Boolean).join(' ');
690
+ return (jsxRuntimeExports.jsx("section", { className: className, id: section.customId, style: {
691
+ backgroundColor: bgColor,
692
+ padding: 'var(--dstyler-spacing-xl) var(--dstyler-spacing-md)',
693
+ minHeight: '50vh',
694
+ display: 'flex',
695
+ alignItems: 'center',
696
+ justifyContent: 'center',
697
+ }, children: jsxRuntimeExports.jsx("div", { style: {
698
+ maxWidth: 'var(--dstyler-container-max-width, 1200px)',
699
+ margin: '0 auto',
700
+ textAlign: 'center',
701
+ }, children: children }) }));
702
+ };
703
+
704
+ const FooterMinimal = ({ section, children }) => {
705
+ const style = section.style || 'default';
706
+ const bgColor = `var(--dstyler-color-${style})`;
707
+ const className = [
708
+ 'dstyler-footer',
709
+ 'dstyler-footer-minimal',
710
+ `dstyler-footer-${style}`,
711
+ section.customClass,
712
+ ].filter(Boolean).join(' ');
713
+ return (jsxRuntimeExports.jsx("footer", { className: className, id: section.customId, style: {
714
+ backgroundColor: bgColor,
715
+ padding: 'var(--dstyler-spacing-lg) var(--dstyler-spacing-md)',
716
+ borderTop: '1px solid var(--dstyler-color-alternate)',
717
+ }, children: jsxRuntimeExports.jsx("div", { style: {
718
+ maxWidth: 'var(--dstyler-container-max-width, 1200px)',
719
+ margin: '0 auto',
720
+ textAlign: 'center',
721
+ fontSize: '0.875rem',
722
+ color: 'var(--dstyler-color-text)',
723
+ }, children: children }) }));
724
+ };
725
+
726
+ /**
727
+ * AutoSection - Automatically selects and renders the appropriate section template
728
+ */
904
729
  const AutoSection = ({ section, registry }) => {
905
730
  const templateName = selectTemplate(section);
906
731
  // Render blocks using AutoBlock or registry
@@ -921,85 +746,20 @@ const AutoSection = ({ section, registry }) => {
921
746
  return (jsxRuntimeExports.jsx(SectionWave, { section: section, waveTop: waveTop, waveBottom: waveBottom, children: renderBlocks() }));
922
747
  }
923
748
  case 'HeroDefault':
924
- return (jsxRuntimeExports.jsx(HeroDefault, { section: section, children: renderBlocks() }));
749
+ return jsxRuntimeExports.jsx(HeroDefault, { section: section, children: renderBlocks() });
925
750
  case 'FooterMinimal':
926
- return (jsxRuntimeExports.jsx(FooterMinimal, { section: section, children: renderBlocks() }));
751
+ return jsxRuntimeExports.jsx(FooterMinimal, { section: section, children: renderBlocks() });
927
752
  default:
928
- return (jsxRuntimeExports.jsx(SectionBase, { section: section, children: renderBlocks() }));
753
+ return jsxRuntimeExports.jsx(SectionBase, { section: section, children: renderBlocks() });
929
754
  }
930
755
  };
931
756
 
932
757
  /**
933
- * @license GPL-3.0-or-later
758
+ * StyledPage - Main entry point for dstyler
934
759
  *
935
- * This file is part of the MarVAlt Open SDK.
936
- * Copyright (c) 2025 Vibune Pty Ltd.
937
- */
938
- /**
939
- * Detect sections from WordPress blocks
940
- * Sections are defined by core/group blocks (with or without alignment)
941
- * Sub-sections are core/columns blocks inside groups
760
+ * Detects sections from WordPress blocks and applies alternating styles
761
+ * Integrates with wparser's registry for block rendering
942
762
  */
943
- function detectSections(blocks) {
944
- const sections = [];
945
- let sectionIndex = 0;
946
- for (const block of blocks) {
947
- // core/group blocks are sections
948
- if (block.name === 'core/group') {
949
- const attrs = block.attributes || {};
950
- const className = attrs.className;
951
- const customId = attrs.id;
952
- // Extract blocks from innerBlocks
953
- const innerBlocks = block.innerBlocks || [];
954
- sections.push({
955
- id: `section-${sectionIndex}`,
956
- type: 'section',
957
- block,
958
- blocks: innerBlocks,
959
- index: sectionIndex,
960
- customClass: className,
961
- customId,
962
- });
963
- sectionIndex++;
964
- }
965
- }
966
- return sections;
967
- }
968
- /**
969
- * Determine section style based on alternating logic and custom classes
970
- * Rules:
971
- * - default is always first
972
- * - after default follows alternate
973
- * - highlight resets - next section is default
974
- */
975
- function determineSectionStyle(section, previousStyle) {
976
- const className = section.customClass?.toLowerCase() || '';
977
- // Check for custom class overrides first
978
- if (className.includes('highlight') || className.includes('highlight-section')) {
979
- return 'highlight';
980
- }
981
- if (className.includes('dark') || className.includes('dark-section')) {
982
- return 'alternate';
983
- }
984
- if (className.includes('default') || className.includes('default-section')) {
985
- return 'default';
986
- }
987
- // Alternating logic
988
- if (section.index === 0) {
989
- return 'default';
990
- }
991
- if (previousStyle === 'highlight') {
992
- return 'default';
993
- }
994
- if (previousStyle === 'default') {
995
- return 'alternate';
996
- }
997
- if (previousStyle === 'alternate') {
998
- return 'default';
999
- }
1000
- return 'default';
1001
- }
1002
-
1003
763
  const StyledPage = ({ page, registry }) => {
1004
764
  const blocks = page.blocks || [];
1005
765
  // Detect sections from blocks
@@ -1014,25 +774,175 @@ const StyledPage = ({ page, registry }) => {
1014
774
  style,
1015
775
  };
1016
776
  });
1017
- return (jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: styledSections.map((section) => (jsxRuntimeExports.jsx(AutoSection, { section: section, registry: registry }, section.id))) }));
777
+ return (jsxRuntimeExports.jsx(React.Fragment, { children: styledSections.map((section) => (jsxRuntimeExports.jsx(AutoSection, { section: section, registry: registry }, section.id))) }));
778
+ };
779
+
780
+ const defaultTokens = {
781
+ colors: {
782
+ primary: '#1a73e8',
783
+ secondary: '#34a853',
784
+ background: '#ffffff',
785
+ text: '#222222',
786
+ heading: '#222222',
787
+ default: '#ffffff',
788
+ alternate: '#f5f5f5',
789
+ highlight: '#e8f0fe',
790
+ },
791
+ spacing: {
792
+ sm: 8,
793
+ md: 16,
794
+ lg: 32,
795
+ xl: 64,
796
+ },
797
+ typography: {
798
+ body: 'Inter, sans-serif',
799
+ heading: 'Inter, sans-serif',
800
+ scale: 1.2,
801
+ },
1018
802
  };
1019
803
 
1020
804
  /**
1021
- * @license GPL-3.0-or-later
1022
- *
1023
- * This file is part of the MarVAlt Open SDK.
1024
- * Copyright (c) 2025 Vibune Pty Ltd.
805
+ * Adjust brightness of a hex color
806
+ */
807
+ function adjustBrightness(hex, amount) {
808
+ const num = parseInt(hex.replace('#', ''), 16);
809
+ const r = Math.max(0, Math.min(255, ((num >> 16) & 0xff) + (amount * 255)));
810
+ const g = Math.max(0, Math.min(255, ((num >> 8) & 0xff) + (amount * 255)));
811
+ const b = Math.max(0, Math.min(255, (num & 0xff) + (amount * 255)));
812
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
813
+ }
814
+ /**
815
+ * Blend two colors
1025
816
  */
817
+ function blendColors(color1, color2, ratio) {
818
+ const c1 = hexToRgb(color1);
819
+ const c2 = hexToRgb(color2);
820
+ if (!c1 || !c2)
821
+ return color1;
822
+ const r = Math.round(c1.r + (c2.r - c1.r) * ratio);
823
+ const g = Math.round(c1.g + (c2.g - c1.g) * ratio);
824
+ const b = Math.round(c1.b + (c2.b - c1.b) * ratio);
825
+ return `rgb(${r}, ${g}, ${b})`;
826
+ }
827
+ function hexToRgb(hex) {
828
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
829
+ return result
830
+ ? {
831
+ r: parseInt(result[1], 16),
832
+ g: parseInt(result[2], 16),
833
+ b: parseInt(result[3], 16),
834
+ }
835
+ : null;
836
+ }
1026
837
  /**
1027
- * Responsive utility functions
838
+ * Convert WordPress theme styles to design tokens
1028
839
  */
1029
- function getResponsiveValue(mobile, tablet, desktop) {
1030
- if (!tablet && !desktop) {
1031
- return typeof mobile === 'number' ? `${mobile}px` : mobile;
840
+ function convertWordPressToTokens(wpStyles) {
841
+ if (!wpStyles) {
842
+ return defaultTokens;
1032
843
  }
1033
- // For now, return mobile value
1034
- // In the future, this could use CSS custom properties with media queries
1035
- return typeof mobile === 'number' ? `${mobile}px` : mobile;
844
+ const palette = wpStyles.theme_palette || {};
845
+ // Typography priority: Customizer (wpStyles.typography) > Theme Editor fonts > defaults
846
+ const tokens = {
847
+ ...defaultTokens,
848
+ colors: {
849
+ ...defaultTokens.colors,
850
+ ...(wpStyles.colors?.primary && { primary: wpStyles.colors.primary }),
851
+ ...(wpStyles.colors?.secondary && { secondary: wpStyles.colors.secondary }),
852
+ ...(wpStyles.colors?.text && { text: wpStyles.colors.text }),
853
+ // heading handled below to allow palette fallback
854
+ ...(wpStyles.colors?.background && {
855
+ background: wpStyles.colors.background,
856
+ default: wpStyles.colors.background,
857
+ }),
858
+ },
859
+ typography: {
860
+ ...defaultTokens.typography,
861
+ ...(wpStyles.typography?.heading_font_family && {
862
+ heading: wpStyles.typography.heading_font_family,
863
+ }),
864
+ ...(wpStyles.typography?.body_font_family && {
865
+ body: wpStyles.typography.body_font_family,
866
+ }),
867
+ },
868
+ };
869
+ // Explicit heading override (may be undefined/empty), then fallback below
870
+ tokens.colors.heading = wpStyles.colors?.heading || undefined;
871
+ // Ensure heading color falls back to text if not provided
872
+ if (!tokens.colors.heading) {
873
+ tokens.colors.heading =
874
+ // Prefer explicit heading color if provided by Theme Editor
875
+ wpStyles.colors?.heading ||
876
+ // Then Theme Editor palette contrast (matches heading color choice in editor)
877
+ palette['contrast'] ||
878
+ // Then palette primary
879
+ palette['primary'] ||
880
+ // Then explicit primary fallback
881
+ wpStyles.colors?.primary ||
882
+ tokens.colors.text;
883
+ }
884
+ // Generate alternate and highlight colors from primary/background
885
+ if (tokens.colors.primary && tokens.colors.background) {
886
+ // Alternate: slightly darker/lighter than background
887
+ tokens.colors.alternate = adjustBrightness(tokens.colors.background, -0.05);
888
+ // Highlight: tinted with primary color
889
+ tokens.colors.highlight = blendColors(tokens.colors.background, tokens.colors.primary, 0.1);
890
+ }
891
+ return tokens;
892
+ }
893
+
894
+ const THEME_STYLE_ID = 'dstyler-theme-vars';
895
+ function tokensToCSS(tokens) {
896
+ return `
897
+ --dstyler-color-primary: ${tokens.colors.primary};
898
+ --dstyler-color-secondary: ${tokens.colors.secondary};
899
+ --dstyler-color-background: ${tokens.colors.background};
900
+ --dstyler-color-text: ${tokens.colors.text};
901
+ --dstyler-color-heading: ${tokens.colors.heading || tokens.colors.text};
902
+ --dstyler-color-default: ${tokens.colors.default};
903
+ --dstyler-color-alternate: ${tokens.colors.alternate};
904
+ --dstyler-color-highlight: ${tokens.colors.highlight};
905
+ --dstyler-spacing-sm: ${tokens.spacing.sm}px;
906
+ --dstyler-spacing-md: ${tokens.spacing.md}px;
907
+ --dstyler-spacing-lg: ${tokens.spacing.lg}px;
908
+ --dstyler-spacing-xl: ${tokens.spacing.xl}px;
909
+ --dstyler-font-body: ${tokens.typography.body};
910
+ --dstyler-font-heading: ${tokens.typography.heading};
911
+ --dstyler-font-scale: ${tokens.typography.scale};
912
+ `;
913
+ }
914
+ function ensureStyleElement() {
915
+ if (typeof document === 'undefined')
916
+ return null;
917
+ let el = document.getElementById(THEME_STYLE_ID);
918
+ if (!el) {
919
+ el = document.createElement('style');
920
+ el.id = THEME_STYLE_ID;
921
+ document.head.appendChild(el);
922
+ }
923
+ return el;
924
+ }
925
+ /**
926
+ * Inject CSS variables from design tokens into :root
927
+ */
928
+ function injectCSSVariables(tokens) {
929
+ const el = ensureStyleElement();
930
+ if (!el)
931
+ return;
932
+ el.textContent = `:root { ${tokensToCSS(tokens)} }`;
933
+ }
934
+ /**
935
+ * Inject CSS variables for both light and dark themes.
936
+ * Light applies to :root, dark applies when [data-theme="dark"] is present.
937
+ */
938
+ function injectDualThemeCSS(light, dark) {
939
+ const el = ensureStyleElement();
940
+ if (!el)
941
+ return;
942
+ el.textContent = `
943
+ :root { ${tokensToCSS(light)} }
944
+ :root[data-theme="dark"] { ${tokensToCSS(dark)} }
945
+ `;
1036
946
  }
1037
947
 
1038
948
  /**
@@ -1041,6 +951,78 @@ function getResponsiveValue(mobile, tablet, desktop) {
1041
951
  * This file is part of the MarVAlt Open SDK.
1042
952
  * Copyright (c) 2025 Vibune Pty Ltd.
1043
953
  */
954
+ /**
955
+ * ThemeProvider - Provides design tokens to the application
956
+ *
957
+ * Priority order:
958
+ * 1. customTokens (highest priority - overrides everything)
959
+ * 2. WordPress styles (converted to tokens)
960
+ * 3. Default tokens (fallback)
961
+ */
962
+ const ThemeProvider = ({ children, wpStyles, customTokens, darkWpStyles, darkTokens, mode = 'light', }) => {
963
+ React.useEffect(() => {
964
+ // Light tokens: custom -> wpStyles -> default
965
+ const lightTokens = customTokens || convertWordPressToTokens(wpStyles);
966
+ // Dark tokens: provided -> wpStylesDark -> fallback to light
967
+ const resolvedDarkTokens = darkTokens || (darkWpStyles ? convertWordPressToTokens(darkWpStyles) : lightTokens);
968
+ if (mode === 'dark') {
969
+ injectDualThemeCSS(lightTokens, resolvedDarkTokens);
970
+ if (typeof document !== 'undefined') {
971
+ document.documentElement.setAttribute('data-theme', 'dark');
972
+ }
973
+ return;
974
+ }
975
+ if (mode === 'system') {
976
+ const prefersDark = typeof window !== 'undefined' &&
977
+ window.matchMedia &&
978
+ window.matchMedia('(prefers-color-scheme: dark)').matches;
979
+ injectDualThemeCSS(lightTokens, resolvedDarkTokens);
980
+ if (typeof document !== 'undefined') {
981
+ document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
982
+ }
983
+ return;
984
+ }
985
+ // Default to light
986
+ injectDualThemeCSS(lightTokens, resolvedDarkTokens);
987
+ if (typeof document !== 'undefined') {
988
+ document.documentElement.setAttribute('data-theme', 'light');
989
+ }
990
+ }, [wpStyles, customTokens, darkWpStyles, darkTokens, mode]);
991
+ // Return children if provided, otherwise return null (theme still applies via useEffect)
992
+ if (children === undefined || children === null) {
993
+ return null;
994
+ }
995
+ return React.createElement(React.Fragment, null, children);
996
+ };
997
+
998
+ /**
999
+ * Check if a color is dark (for determining text color)
1000
+ */
1001
+ function isColorDark(color) {
1002
+ // Remove # if present
1003
+ const hex = color.replace('#', '');
1004
+ // Handle RGB/RGBA format
1005
+ if (color.startsWith('rgb')) {
1006
+ const match = color.match(/\d+/g);
1007
+ if (match && match.length >= 3) {
1008
+ const r = parseInt(match[0], 10);
1009
+ const g = parseInt(match[1], 10);
1010
+ const b = parseInt(match[2], 10);
1011
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
1012
+ return brightness < 128;
1013
+ }
1014
+ }
1015
+ // Handle hex format
1016
+ if (hex.length === 6) {
1017
+ const r = parseInt(hex.substring(0, 2), 16);
1018
+ const g = parseInt(hex.substring(2, 4), 16);
1019
+ const b = parseInt(hex.substring(4, 6), 16);
1020
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
1021
+ return brightness < 128;
1022
+ }
1023
+ // Default to light background
1024
+ return false;
1025
+ }
1044
1026
  /**
1045
1027
  * Create a colorMapper from design tokens and WordPress theme palette
1046
1028
  *
@@ -1067,7 +1049,9 @@ function getResponsiveValue(mobile, tablet, desktop) {
1067
1049
  */
1068
1050
  function createColorMapperFromTokens(tokens, themePalette, overrides) {
1069
1051
  // Debug logging
1070
- if (typeof window !== 'undefined' && typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
1052
+ if (typeof window !== 'undefined' &&
1053
+ typeof process !== 'undefined' &&
1054
+ process.env.NODE_ENV === 'development') {
1071
1055
  console.log('🎨 createColorMapperFromTokens - themePalette:', themePalette);
1072
1056
  console.log('🎨 createColorMapperFromTokens - tokens:', tokens);
1073
1057
  }
@@ -1084,7 +1068,9 @@ function createColorMapperFromTokens(tokens, themePalette, overrides) {
1084
1068
  const textColor = isDark ? 'text-white' : 'text-gray-900';
1085
1069
  // Include both bg (for background mapping) and text-[color] (for text mapping), plus contrast helper
1086
1070
  const result = `bg-[${color}] text-[${color}] ${textColor}`;
1087
- if (typeof window !== 'undefined' && typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
1071
+ if (typeof window !== 'undefined' &&
1072
+ typeof process !== 'undefined' &&
1073
+ process.env.NODE_ENV === 'development') {
1088
1074
  console.log(`🎨 Color mapper - ${wpColorName}:`, result, '(from theme palette)');
1089
1075
  }
1090
1076
  return result;
@@ -1092,53 +1078,25 @@ function createColorMapperFromTokens(tokens, themePalette, overrides) {
1092
1078
  // Fallback: Map WordPress theme colors to design tokens
1093
1079
  // Using Tailwind's arbitrary value syntax for hex colors
1094
1080
  const defaultMap = {
1095
- 'base': `bg-[${tokens.colors.background}] text-[${tokens.colors.text}]`,
1096
- 'background': `bg-[${tokens.colors.background}] text-[${tokens.colors.text}]`,
1097
- 'foreground': `text-[${tokens.colors.text}]`,
1098
- 'contrast': `bg-[${themePalette?.contrast || tokens.colors.text}] text-[${tokens.colors.background}]`,
1099
- 'primary': `bg-[${themePalette?.primary || tokens.colors.primary}] text-[${themePalette?.primary || tokens.colors.primary}]`,
1100
- 'secondary': `bg-[${themePalette?.secondary || tokens.colors.secondary}] text-[${themePalette?.secondary || tokens.colors.secondary}]`,
1101
- 'heading': `text-[${tokens.colors.heading || tokens.colors.text}]`,
1102
- 'accent': `bg-[${tokens.colors.highlight}] text-[${tokens.colors.highlight}]`,
1081
+ base: `bg-[${tokens.colors.background}] text-[${tokens.colors.text}]`,
1082
+ background: `bg-[${tokens.colors.background}] text-[${tokens.colors.text}]`,
1083
+ foreground: `text-[${tokens.colors.text}]`,
1084
+ contrast: `bg-[${themePalette?.contrast || tokens.colors.text}] text-[${tokens.colors.background}]`,
1085
+ primary: `bg-[${themePalette?.primary || tokens.colors.primary}] text-[${themePalette?.primary || tokens.colors.primary}]`,
1086
+ secondary: `bg-[${themePalette?.secondary || tokens.colors.secondary}] text-[${themePalette?.secondary || tokens.colors.secondary}]`,
1087
+ heading: `text-[${tokens.colors.heading || tokens.colors.text}]`,
1088
+ accent: `bg-[${tokens.colors.highlight}] text-[${tokens.colors.highlight}]`,
1103
1089
  'accent-1': `bg-[${themePalette?.['accent-1'] || tokens.colors.primary}] text-[${themePalette?.['accent-1'] || tokens.colors.primary}]`,
1104
1090
  'accent-2': `bg-[${themePalette?.['accent-2'] || tokens.colors.secondary}] text-[${themePalette?.['accent-2'] || tokens.colors.secondary}]`,
1105
1091
  'accent-3': `bg-[${themePalette?.['accent-3'] || tokens.colors.highlight}] text-[${themePalette?.['accent-3'] || tokens.colors.highlight}]`,
1106
1092
  'accent-4': `bg-[${themePalette?.['accent-4'] || tokens.colors.alternate}] text-[${themePalette?.['accent-4'] || tokens.colors.alternate}]`,
1107
1093
  'accent-5': `bg-[${themePalette?.['accent-5'] || tokens.colors.default}] text-[${themePalette?.['accent-5'] || tokens.colors.default}]`,
1108
1094
  'accent-6': `bg-[${themePalette?.['accent-6'] || themePalette?.primary || tokens.colors.primary}] text-[${themePalette?.['accent-6'] || themePalette?.primary || tokens.colors.primary}]`,
1109
- 'transparent': 'bg-transparent',
1095
+ transparent: 'bg-transparent',
1110
1096
  };
1111
1097
  return wpColorName ? defaultMap[wpColorName] || null : null;
1112
1098
  };
1113
1099
  }
1114
- /**
1115
- * Check if a color is dark (for determining text color)
1116
- */
1117
- function isColorDark(color) {
1118
- // Remove # if present
1119
- const hex = color.replace('#', '');
1120
- // Handle RGB/RGBA format
1121
- if (color.startsWith('rgb')) {
1122
- const match = color.match(/\d+/g);
1123
- if (match && match.length >= 3) {
1124
- const r = parseInt(match[0], 10);
1125
- const g = parseInt(match[1], 10);
1126
- const b = parseInt(match[2], 10);
1127
- const brightness = (r * 299 + g * 587 + b * 114) / 1000;
1128
- return brightness < 128;
1129
- }
1130
- }
1131
- // Handle hex format
1132
- if (hex.length === 6) {
1133
- const r = parseInt(hex.substring(0, 2), 16);
1134
- const g = parseInt(hex.substring(2, 4), 16);
1135
- const b = parseInt(hex.substring(4, 6), 16);
1136
- const brightness = (r * 299 + g * 587 + b * 114) / 1000;
1137
- return brightness < 128;
1138
- }
1139
- // Default to light background
1140
- return false;
1141
- }
1142
1100
  /**
1143
1101
  * Create a colorMapper with semantic Tailwind classes
1144
1102
  *
@@ -1166,9 +1124,9 @@ function createColorMapperWithSemanticClasses(tokens, overrides) {
1166
1124
  // Map WordPress theme colors to semantic Tailwind classes
1167
1125
  // Assumes your Tailwind config has primary, secondary, muted, etc. defined
1168
1126
  const defaultMap = {
1169
- 'base': 'bg-white',
1170
- 'contrast': 'bg-gray-900 text-white',
1171
- 'transparent': 'bg-transparent',
1127
+ base: 'bg-white',
1128
+ contrast: 'bg-gray-900 text-white',
1129
+ transparent: 'bg-transparent',
1172
1130
  'accent-1': 'bg-primary text-primary-foreground',
1173
1131
  'accent-2': 'bg-secondary text-secondary-foreground',
1174
1132
  'accent-3': 'bg-muted text-muted-foreground',
@@ -1180,8 +1138,30 @@ function createColorMapperWithSemanticClasses(tokens, overrides) {
1180
1138
  };
1181
1139
  }
1182
1140
 
1141
+ /**
1142
+ * @license GPL-3.0-or-later
1143
+ *
1144
+ * This file is part of the MarVAlt Open SDK.
1145
+ * Copyright (c) 2025 Vibune Pty Ltd.
1146
+ */
1147
+ /**
1148
+ * Responsive utility functions
1149
+ */
1150
+ function getResponsiveValue(mobile, tablet, desktop) {
1151
+ if (!tablet && !desktop) {
1152
+ return typeof mobile === 'number' ? `${mobile}px` : mobile;
1153
+ }
1154
+ // For now, return mobile value
1155
+ // In the future, this could use CSS custom properties with media queries
1156
+ return typeof mobile === 'number' ? `${mobile}px` : mobile;
1157
+ }
1158
+
1183
1159
  exports.AutoBlock = AutoBlock;
1184
1160
  exports.AutoSection = AutoSection;
1161
+ exports.BlockButton = BlockButton;
1162
+ exports.BlockForm = BlockForm;
1163
+ exports.BlockImage = BlockImage;
1164
+ exports.BlockText = BlockText;
1185
1165
  exports.FooterMinimal = FooterMinimal;
1186
1166
  exports.HeroDefault = HeroDefault;
1187
1167
  exports.SectionBase = SectionBase;