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