@o2vend/theme-cli 1.0.32

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 (116) hide show
  1. package/README.md +425 -0
  2. package/assets/Logo_o2vend.png +0 -0
  3. package/assets/favicon.png +0 -0
  4. package/assets/logo-white.png +0 -0
  5. package/bin/o2vend +42 -0
  6. package/config/widget-map.json +50 -0
  7. package/lib/commands/check.js +201 -0
  8. package/lib/commands/generate.js +33 -0
  9. package/lib/commands/init.js +214 -0
  10. package/lib/commands/optimize.js +216 -0
  11. package/lib/commands/package.js +208 -0
  12. package/lib/commands/serve.js +105 -0
  13. package/lib/commands/validate.js +191 -0
  14. package/lib/lib/api-client.js +357 -0
  15. package/lib/lib/dev-server.js +2618 -0
  16. package/lib/lib/file-watcher.js +80 -0
  17. package/lib/lib/hot-reload.js +106 -0
  18. package/lib/lib/liquid-engine.js +822 -0
  19. package/lib/lib/liquid-filters.js +671 -0
  20. package/lib/lib/mock-api-server.js +989 -0
  21. package/lib/lib/mock-data.js +1468 -0
  22. package/lib/lib/widget-service.js +321 -0
  23. package/package.json +70 -0
  24. package/test-theme/README.md +27 -0
  25. package/test-theme/assets/async-sections.js +446 -0
  26. package/test-theme/assets/cart-drawer.js +463 -0
  27. package/test-theme/assets/cart-manager.js +223 -0
  28. package/test-theme/assets/checkout-price-handler.js +368 -0
  29. package/test-theme/assets/components.css +4629 -0
  30. package/test-theme/assets/delivery-zone.css +299 -0
  31. package/test-theme/assets/delivery-zone.js +396 -0
  32. package/test-theme/assets/logo.png +0 -0
  33. package/test-theme/assets/sections.css +48 -0
  34. package/test-theme/assets/theme.css +3500 -0
  35. package/test-theme/assets/theme.js +3745 -0
  36. package/test-theme/config/settings_data.json +292 -0
  37. package/test-theme/config/settings_schema.json +1050 -0
  38. package/test-theme/layout/theme.liquid +195 -0
  39. package/test-theme/locales/en.default.json +260 -0
  40. package/test-theme/sections/content-fallback.liquid +53 -0
  41. package/test-theme/sections/content.liquid +57 -0
  42. package/test-theme/sections/footer-fallback.liquid +328 -0
  43. package/test-theme/sections/footer.liquid +278 -0
  44. package/test-theme/sections/header-fallback.liquid +1805 -0
  45. package/test-theme/sections/header.liquid +1145 -0
  46. package/test-theme/sections/hero-fallback.liquid +212 -0
  47. package/test-theme/sections/hero.liquid +136 -0
  48. package/test-theme/snippets/account-sidebar.liquid +200 -0
  49. package/test-theme/snippets/add-to-cart-modal.liquid +484 -0
  50. package/test-theme/snippets/breadcrumbs.liquid +134 -0
  51. package/test-theme/snippets/cart-drawer.liquid +467 -0
  52. package/test-theme/snippets/delivery-zone-city-selector.liquid +79 -0
  53. package/test-theme/snippets/delivery-zone-modal.liquid +337 -0
  54. package/test-theme/snippets/delivery-zone-search.liquid +78 -0
  55. package/test-theme/snippets/icon.liquid +105 -0
  56. package/test-theme/snippets/login-modal.liquid +346 -0
  57. package/test-theme/snippets/mega-menu.liquid +812 -0
  58. package/test-theme/snippets/news-thumbnail.liquid +187 -0
  59. package/test-theme/snippets/pagination.liquid +120 -0
  60. package/test-theme/snippets/price.liquid +92 -0
  61. package/test-theme/snippets/product-card-related.liquid +78 -0
  62. package/test-theme/snippets/product-card-simple.liquid +41 -0
  63. package/test-theme/snippets/product-card.liquid +697 -0
  64. package/test-theme/snippets/rating.liquid +85 -0
  65. package/test-theme/snippets/skeleton-collection-grid.liquid +114 -0
  66. package/test-theme/snippets/skeleton-product-card.liquid +124 -0
  67. package/test-theme/snippets/skeleton-product-grid.liquid +34 -0
  68. package/test-theme/snippets/social-sharing.liquid +185 -0
  69. package/test-theme/templates/account/dashboard.liquid +401 -0
  70. package/test-theme/templates/account/loyalty-redemption.liquid +405 -0
  71. package/test-theme/templates/account/loyalty.liquid +588 -0
  72. package/test-theme/templates/account/order-detail.liquid +230 -0
  73. package/test-theme/templates/account/orders.liquid +349 -0
  74. package/test-theme/templates/account/profile.liquid +758 -0
  75. package/test-theme/templates/account/register.liquid +232 -0
  76. package/test-theme/templates/account/return-orders.liquid +348 -0
  77. package/test-theme/templates/account/store-credit.liquid +464 -0
  78. package/test-theme/templates/account/subscriptions.liquid +601 -0
  79. package/test-theme/templates/account/wishlist.liquid +419 -0
  80. package/test-theme/templates/address-book.liquid +1092 -0
  81. package/test-theme/templates/categories.liquid +452 -0
  82. package/test-theme/templates/checkout.liquid +4511 -0
  83. package/test-theme/templates/error.liquid +384 -0
  84. package/test-theme/templates/index.liquid +11 -0
  85. package/test-theme/templates/login.liquid +185 -0
  86. package/test-theme/templates/order-confirmation.liquid +720 -0
  87. package/test-theme/templates/page.liquid +297 -0
  88. package/test-theme/templates/product-detail.liquid +4363 -0
  89. package/test-theme/templates/products.liquid +518 -0
  90. package/test-theme/templates/search.liquid +922 -0
  91. package/test-theme/theme.json.example +19 -0
  92. package/test-theme/widgets/brand-carousel.liquid +676 -0
  93. package/test-theme/widgets/brand.liquid +245 -0
  94. package/test-theme/widgets/carousel.liquid +843 -0
  95. package/test-theme/widgets/category-list-carousel.liquid +656 -0
  96. package/test-theme/widgets/category-list.liquid +340 -0
  97. package/test-theme/widgets/category.liquid +475 -0
  98. package/test-theme/widgets/discount-time.liquid +176 -0
  99. package/test-theme/widgets/footer-menu.liquid +695 -0
  100. package/test-theme/widgets/footer.liquid +179 -0
  101. package/test-theme/widgets/gallery.liquid +271 -0
  102. package/test-theme/widgets/header-menu.liquid +932 -0
  103. package/test-theme/widgets/header.liquid +159 -0
  104. package/test-theme/widgets/html.liquid +214 -0
  105. package/test-theme/widgets/news.liquid +217 -0
  106. package/test-theme/widgets/product-canvas.liquid +235 -0
  107. package/test-theme/widgets/product-carousel.liquid +502 -0
  108. package/test-theme/widgets/product.liquid +45 -0
  109. package/test-theme/widgets/recently-viewed.liquid +26 -0
  110. package/test-theme/widgets/shared/product-grid.liquid +339 -0
  111. package/test-theme/widgets/simple-product.liquid +42 -0
  112. package/test-theme/widgets/single-product.liquid +610 -0
  113. package/test-theme/widgets/spacebar-carousel.liquid +663 -0
  114. package/test-theme/widgets/spacebar.liquid +279 -0
  115. package/test-theme/widgets/splash.liquid +378 -0
  116. package/test-theme/widgets/testimonial-carousel.liquid +709 -0
@@ -0,0 +1,279 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_data = widget.data
4
+
5
+ comment
6
+ Static Widget: Content comes from widget_data.content or widget.content
7
+ Settings come from widget_settings
8
+ endcomment
9
+
10
+ assign content_data = widget_data.content | default: widget.content
11
+ comment
12
+ Handle both formats:
13
+ 1. Direct array: content_data is an array (when widget-service stores array directly)
14
+ 2. Object with Items: content_data.Items or content_data.items (when stored as object)
15
+ endcomment
16
+ if content_data.Items
17
+ assign items = content_data.Items
18
+ elsif content_data.items
19
+ assign items = content_data.items
20
+ else
21
+ assign items = content_data
22
+ endif
23
+ assign background_color = content_data.backgroundColor | default: content_data.BackgroundColor | default: widget_settings.backgroundColor | default: '#ffffff'
24
+ assign text_color = content_data.textColor | default: content_data.TextColor | default: widget_settings.textColor | default: '#000000'
25
+ %}
26
+
27
+ <section class="widget widget-spacebar" data-widget-id="{{ widget.id }}" style="background-color: {{ background_color }};">
28
+ <div class="spacebar-inner">
29
+ {% if items and items.size > 0 %}
30
+ <div class="spacebar-grid">
31
+ {% for item in items %}
32
+ {% liquid
33
+ assign item_title = item.title | default: item.Title
34
+ assign item_description = item.description | default: item.Description
35
+ assign item_icon = item.iconHtml | default: item.IconHtml
36
+ assign item_image = item.imageUrl | default: item.ImageUrl | default: item.image | default: item.Image
37
+ assign item_bg = item.backgroundColor | default: item.BackgroundColor | default: '#ffffff'
38
+ assign item_text = item.textColor | default: item.TextColor | default: text_color
39
+ %}
40
+
41
+ <div class="spacebar-item" style="background-color: {{ item_bg }};">
42
+ {% if item_image and item_image != blank %}
43
+ {% comment %} Handle external URLs vs local assets {% endcomment %}
44
+ {% if item_image contains 'http://' or item_image contains 'https://' %}
45
+ {% assign item_image_src = item_image %}
46
+ {% else %}
47
+ {% assign item_image_src = item_image | asset_url %}
48
+ {% endif %}
49
+ <div class="spacebar-item__icon spacebar-item__icon--image">
50
+ <img src="{{ item_image_src }}" alt="{{ item_title | escape }}" loading="lazy">
51
+ </div>
52
+ {% elsif item_icon and item_icon != blank %}
53
+ <div class="spacebar-item__icon spacebar-item__icon--html">
54
+ {{ item_icon }}
55
+ </div>
56
+ {% endif %}
57
+
58
+ <div class="spacebar-item__content" style="color: {{ item_text }};">
59
+ {% if item_title and item_title != blank %}
60
+ <h4 class="spacebar-item__title">{{ item_title }}</h4>
61
+ {% endif %}
62
+ {% if item_description and item_description != blank %}
63
+ <p class="spacebar-item__desc">{{ item_description }}</p>
64
+ {% endif %}
65
+ </div>
66
+ </div>
67
+ {% endfor %}
68
+ </div>
69
+ {% else %}
70
+ <div class="widget-empty">
71
+ <p style="color: {{ text_color }};">No items to display.</p>
72
+ </div>
73
+ {% endif %}
74
+ </div>
75
+
76
+ <style>
77
+ .widget-spacebar {
78
+ padding: 32px 0;
79
+ width: 100%;
80
+ }
81
+
82
+ .widget-spacebar .spacebar-inner {
83
+ max-width: 1400px;
84
+ margin: 0 auto;
85
+ padding: 0 24px;
86
+ }
87
+
88
+ .widget-spacebar .spacebar-grid {
89
+ display: grid;
90
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
91
+ gap: 24px;
92
+ align-items: stretch;
93
+ }
94
+
95
+ .widget-spacebar .spacebar-item {
96
+ display: flex;
97
+ align-items: flex-start;
98
+ gap: 16px;
99
+ padding: 20px;
100
+ border-radius: 12px;
101
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
102
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
103
+ cursor: pointer;
104
+ }
105
+
106
+ .widget-spacebar .spacebar-item:hover {
107
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
108
+ transform: translateY(-4px);
109
+ }
110
+
111
+ .widget-spacebar .spacebar-item__icon {
112
+ flex-shrink: 0;
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ width: 72px;
117
+ height: 72px;
118
+ border-radius: 10px;
119
+ overflow: hidden;
120
+ }
121
+
122
+ .widget-spacebar .spacebar-item__icon--image {
123
+ background: transparent;
124
+ padding: 0;
125
+ }
126
+
127
+ .widget-spacebar .spacebar-item__icon--image img {
128
+ width: 100%;
129
+ height: 100%;
130
+ object-fit: cover;
131
+ display: block;
132
+ }
133
+
134
+ .widget-spacebar .spacebar-item__icon--html {
135
+ font-size: 36px;
136
+ background: rgba(0, 0, 0, 0.05);
137
+ }
138
+
139
+ .widget-spacebar .spacebar-item__icon--html svg {
140
+ width: 40px;
141
+ height: 40px;
142
+ }
143
+
144
+ .widget-spacebar .spacebar-item__content {
145
+ display: flex;
146
+ flex-direction: column;
147
+ gap: 8px;
148
+ flex: 1;
149
+ min-width: 0;
150
+ }
151
+
152
+ .widget-spacebar .spacebar-item__title {
153
+ font-size: 18px;
154
+ font-weight: 600;
155
+ margin: 0;
156
+ line-height: 1.4;
157
+ word-wrap: break-word;
158
+ overflow-wrap: break-word;
159
+ }
160
+
161
+ .widget-spacebar .spacebar-item__desc {
162
+ font-size: 14px;
163
+ margin: 0;
164
+ line-height: 1.6;
165
+ opacity: 0.85;
166
+ word-wrap: break-word;
167
+ overflow-wrap: break-word;
168
+ }
169
+
170
+ .widget-spacebar .widget-empty {
171
+ text-align: center;
172
+ padding: 40px 20px;
173
+ }
174
+
175
+ .widget-spacebar .widget-empty p {
176
+ margin: 0;
177
+ font-size: 16px;
178
+ opacity: 0.6;
179
+ }
180
+
181
+ /* Tablet (768px - 1024px) */
182
+ @media (max-width: 1024px) {
183
+ .widget-spacebar .spacebar-grid {
184
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
185
+ gap: 20px;
186
+ }
187
+
188
+ .widget-spacebar .spacebar-item {
189
+ padding: 18px;
190
+ }
191
+ }
192
+
193
+ /* Mobile (481px - 768px) */
194
+ @media (max-width: 768px) {
195
+ .widget-spacebar {
196
+ padding: 24px 0;
197
+ }
198
+
199
+ .widget-spacebar .spacebar-inner {
200
+ padding: 0 16px;
201
+ }
202
+
203
+ .widget-spacebar .spacebar-grid {
204
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
205
+ gap: 16px;
206
+ }
207
+
208
+ .widget-spacebar .spacebar-item {
209
+ gap: 14px;
210
+ padding: 16px;
211
+ }
212
+
213
+ .widget-spacebar .spacebar-item__icon {
214
+ width: 64px;
215
+ height: 64px;
216
+ }
217
+
218
+ .widget-spacebar .spacebar-item__icon--html {
219
+ font-size: 32px;
220
+ }
221
+
222
+ .widget-spacebar .spacebar-item__icon--html svg {
223
+ width: 36px;
224
+ height: 36px;
225
+ }
226
+
227
+ .widget-spacebar .spacebar-item__title {
228
+ font-size: 16px;
229
+ }
230
+
231
+ .widget-spacebar .spacebar-item__desc {
232
+ font-size: 13px;
233
+ }
234
+ }
235
+
236
+ /* Small Mobile (max 480px) */
237
+ @media (max-width: 480px) {
238
+ .widget-spacebar {
239
+ padding: 20px 0;
240
+ }
241
+
242
+ .widget-spacebar .spacebar-inner {
243
+ padding: 0 12px;
244
+ }
245
+
246
+ .widget-spacebar .spacebar-grid {
247
+ grid-template-columns: 1fr;
248
+ gap: 12px;
249
+ }
250
+
251
+ .widget-spacebar .spacebar-item {
252
+ gap: 12px;
253
+ padding: 14px;
254
+ }
255
+
256
+ .widget-spacebar .spacebar-item__icon {
257
+ width: 56px;
258
+ height: 56px;
259
+ }
260
+
261
+ .widget-spacbar .spacebar-item__icon--html {
262
+ font-size: 28px;
263
+ }
264
+
265
+ .widget-spacebar .spacebar-item__icon--html svg {
266
+ width: 32px;
267
+ height: 32px;
268
+ }
269
+
270
+ .widget-spacebar .spacebar-item__title {
271
+ font-size: 15px;
272
+ }
273
+
274
+ .widget-spacebar .spacebar-item__desc {
275
+ font-size: 13px;
276
+ }
277
+ }
278
+ </style>
279
+ </section>
@@ -0,0 +1,378 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_data = widget.data
4
+
5
+ comment
6
+ Static Widget: Content comes from widget_data.content (splash configuration)
7
+ Settings come from widget_settings
8
+ endcomment
9
+
10
+ assign content_data = widget_data.content | default: widget.content
11
+ if content_data.size > 0 and content_data.first.ImageUrl
12
+ assign splash_config = content_data.first
13
+ else
14
+ assign splash_config = content_data
15
+ endif
16
+
17
+ assign image_url = splash_config.ImageUrl | default: splash_config.imageUrl | default: splash_config.Image | default: splash_config.image
18
+ if image_url == 'null' or image_url == ''
19
+ assign image_url = nil
20
+ endif
21
+
22
+ assign link_text = splash_config.LinkText | default: splash_config.linkText | default: splash_config.ButtonText | default: splash_config.buttonText
23
+ if link_text == 'null' or link_text == ''
24
+ assign link_text = nil
25
+ endif
26
+
27
+ assign target_url = splash_config.TargetUrl | default: splash_config.targetUrl | default: splash_config.Link | default: splash_config.link
28
+ if target_url == 'null' or target_url == ''
29
+ assign target_url = nil
30
+ endif
31
+
32
+ assign title = splash_config.Title | default: splash_config.title
33
+ if title == 'null' or title == ''
34
+ assign title = nil
35
+ endif
36
+
37
+ assign description = splash_config.Description | default: splash_config.description
38
+ if description == 'null' or description == ''
39
+ assign description = nil
40
+ endif
41
+
42
+ assign background_color = widget_settings.backgroundColor | default: splash_config.BackgroundColor | default: splash_config.backgroundColor
43
+ if background_color == 'null' or background_color == '' or background_color == nil
44
+ assign background_color = '#ffffff'
45
+ endif
46
+
47
+ assign text_color = widget_settings.textColor | default: splash_config.TextColor | default: splash_config.textColor
48
+ if text_color == 'null' or text_color == '' or text_color == nil
49
+ assign text_color = '#111111'
50
+ endif
51
+
52
+ assign delay_seconds = widget_settings.seconds | default: splash_config.Seconds | default: splash_config.seconds | default: splash_config.DelaySeconds | default: splash_config.delaySeconds
53
+ if delay_seconds == 'null' or delay_seconds == '' or delay_seconds == nil
54
+ assign delay_seconds = 1
55
+ endif
56
+
57
+ assign show_once = widget_settings.showOnce | default: splash_config.ShowOnce | default: splash_config.showOnce | default: true
58
+ assign overlay_color = widget_settings.overlayColor | default: splash_config.OverlayColor | default: splash_config.overlayColor | default: 'rgba(0,0,0,0.6)'
59
+ assign event_type = widget_settings.eventType | default: splash_config.EventType | default: splash_config.eventType | default: 'Onload'
60
+ %}
61
+
62
+ {% assign splash_id = widget.id | default: widget._id | default: 'splash-default' %}
63
+
64
+ <!-- DEBUG: Splash Widget Loaded - ID: {{ splash_id }}, Image: {{ image_url }}, Delay: {{ delay_seconds }} -->
65
+
66
+ <div class="widget-splash-overlay" data-splash-widget data-widget-id="{{ splash_id }}" data-delay="{{ delay_seconds }}" data-show-once="false" data-event-type="{{ event_type }}">
67
+ <div class="widget-splash-modal" style="background-color: {{ background_color }};">
68
+ <!-- Close Button -->
69
+ <button type="button" class="splash-close" data-splash-close aria-label="Close">
70
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
71
+ <line x1="18" y1="6" x2="6" y2="18"></line>
72
+ <line x1="6" y1="6" x2="18" y2="18"></line>
73
+ </svg>
74
+ </button>
75
+
76
+ <div class="splash-content">
77
+ {% if image_url %}
78
+ <div class="splash-image">
79
+ {% if target_url %}
80
+ <a href="{{ target_url }}">
81
+ <img src="{{ image_url }}" alt="{{ title | default: 'Special Offer' }}" width="800" height="450" loading="lazy">
82
+ </a>
83
+ {% else %}
84
+ <img src="{{ image_url }}" alt="{{ title | default: 'Special Offer' }}" width="800" height="450" loading="lazy">
85
+ {% endif %}
86
+ </div>
87
+ {% endif %}
88
+
89
+ {% if title or description or link_text %}
90
+ <div class="splash-body" style="color: {{ text_color }};">
91
+ {% if title %}
92
+ <h2 class="splash-title">{{ title }}</h2>
93
+ {% endif %}
94
+
95
+ {% if description %}
96
+ <p class="splash-description">{{ description }}</p>
97
+ {% endif %}
98
+
99
+ {% if link_text and target_url %}
100
+ <a href="{{ target_url }}" class="splash-cta">{{ link_text }}</a>
101
+ {% endif %}
102
+ </div>
103
+ {% endif %}
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <style>
109
+ /* Splash Overlay */
110
+ .widget-splash-overlay {
111
+ display: none;
112
+ position: fixed;
113
+ top: 0;
114
+ left: 0;
115
+ right: 0;
116
+ bottom: 0;
117
+ background: {{ overlay_color }};
118
+ z-index: 99999;
119
+ align-items: center;
120
+ justify-content: center;
121
+ padding: 20px;
122
+ opacity: 0;
123
+ transition: opacity 0.3s ease;
124
+ }
125
+
126
+ .widget-splash-overlay.active {
127
+ display: flex;
128
+ }
129
+
130
+ .widget-splash-overlay.visible {
131
+ opacity: 1;
132
+ }
133
+
134
+ /* Modal */
135
+ .widget-splash-modal {
136
+ position: relative;
137
+ max-width: 500px;
138
+ width: 100%;
139
+ max-height: 90vh;
140
+ overflow: hidden;
141
+ border-radius: var(--border-radius-medium);
142
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
143
+ transform: scale(0.9) translateY(20px);
144
+ transition: transform 0.3s ease;
145
+ }
146
+
147
+ .widget-splash-overlay.visible .widget-splash-modal {
148
+ transform: scale(1) translateY(0);
149
+ }
150
+
151
+ /* Close Button */
152
+ .splash-close {
153
+ position: absolute;
154
+ top: 12px;
155
+ right: 12px;
156
+ width: 36px;
157
+ height: 36px;
158
+ border: none;
159
+ background: rgba(255, 255, 255, 0.9);
160
+ border-radius: 50%;
161
+ cursor: pointer;
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ color: #333;
166
+ z-index: 10;
167
+ transition: all 0.2s ease;
168
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
169
+ }
170
+
171
+ .splash-close:hover {
172
+ background: #fff;
173
+ transform: scale(1.1);
174
+ }
175
+
176
+ /* Content */
177
+ .splash-content {
178
+ display: flex;
179
+ flex-direction: column;
180
+ }
181
+
182
+ /* Image */
183
+ .splash-image {
184
+ width: 100%;
185
+ overflow: hidden;
186
+ }
187
+
188
+ .splash-image img {
189
+ width: 100%;
190
+ height: auto;
191
+ display: block;
192
+ object-fit: cover;
193
+ }
194
+
195
+ .splash-image a {
196
+ display: block;
197
+ }
198
+
199
+ /* Body */
200
+ .splash-body {
201
+ padding: 24px;
202
+ text-align: center;
203
+ }
204
+
205
+ .splash-title {
206
+ font-size: 24px;
207
+ font-weight: 700;
208
+ margin: 0 0 12px 0;
209
+ line-height: 1.3;
210
+ }
211
+
212
+ .splash-description {
213
+ font-size: 15px;
214
+ line-height: 1.6;
215
+ margin: 0 0 20px 0;
216
+ opacity: 0.85;
217
+ }
218
+
219
+ /* CTA Button */
220
+ .splash-cta {
221
+ display: inline-block;
222
+ padding: 14px 32px;
223
+ background: #111;
224
+ color: #fff !important;
225
+ text-decoration: none;
226
+ font-size: 14px;
227
+ font-weight: 600;
228
+ border-radius: 8px;
229
+ transition: all 0.2s ease;
230
+ text-transform: uppercase;
231
+ letter-spacing: 0.5px;
232
+ }
233
+
234
+ .splash-cta:hover {
235
+ background: #333;
236
+ transform: translateY(-2px);
237
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
238
+ }
239
+
240
+ /* Image Only Mode (no title/description) */
241
+ .splash-body:empty {
242
+ display: none;
243
+ }
244
+
245
+ .splash-image + .splash-body:not(:has(.splash-title)):not(:has(.splash-description)):not(:has(.splash-cta)) {
246
+ display: none;
247
+ }
248
+
249
+ /* Mobile */
250
+ @media (max-width: 576px) {
251
+ .widget-splash-overlay {
252
+ padding: 16px;
253
+ }
254
+
255
+ .widget-splash-modal {
256
+ max-width: 100%;
257
+ border-radius: 12px;
258
+ }
259
+
260
+ .splash-close {
261
+ top: 8px;
262
+ right: 8px;
263
+ width: 32px;
264
+ height: 32px;
265
+ }
266
+
267
+ .splash-close svg {
268
+ width: 20px;
269
+ height: 20px;
270
+ }
271
+
272
+ .splash-body {
273
+ padding: 20px 16px;
274
+ }
275
+
276
+ .splash-title {
277
+ font-size: 20px;
278
+ }
279
+
280
+ .splash-description {
281
+ font-size: 14px;
282
+ }
283
+
284
+ .splash-cta {
285
+ padding: 12px 24px;
286
+ font-size: 13px;
287
+ width: 100%;
288
+ text-align: center;
289
+ }
290
+ }
291
+ </style>
292
+
293
+ <script>
294
+ (function() {
295
+ console.log('[Splash] Script loaded for widget {{ splash_id }}');
296
+
297
+ // Wait for DOM to be ready
298
+ function initSplash() {
299
+ console.log('[Splash] initSplash called');
300
+
301
+ // Try multiple selectors
302
+ let splashWidget = document.querySelector('[data-splash-widget][data-widget-id="{{ splash_id }}"]');
303
+ if (!splashWidget) {
304
+ splashWidget = document.querySelector('[data-splash-widget]');
305
+ }
306
+ if (!splashWidget) {
307
+ splashWidget = document.querySelector('.widget-splash-overlay');
308
+ }
309
+
310
+ if (!splashWidget) {
311
+ console.error('[Splash] Widget element NOT FOUND in DOM!');
312
+ console.log('[Splash] Looking for: [data-splash-widget][data-widget-id="{{ splash_id }}"]');
313
+ return;
314
+ }
315
+
316
+ console.log('[Splash] Widget element FOUND:', splashWidget);
317
+
318
+ const delaySeconds = parseInt(splashWidget.dataset.delay) || 1;
319
+ console.log('[Splash] Delay seconds:', delaySeconds);
320
+
321
+ // Show splash after delay
322
+ console.log('[Splash] Setting timeout for', delaySeconds, 'second(s)...');
323
+ setTimeout(function() {
324
+ console.log('[Splash] TIMEOUT FIRED - Showing popup NOW!');
325
+ splashWidget.style.display = 'flex';
326
+ splashWidget.classList.add('active');
327
+ // Trigger animation after a frame
328
+ setTimeout(function() {
329
+ splashWidget.classList.add('visible');
330
+ console.log('[Splash] Added visible class');
331
+ }, 50);
332
+ }, delaySeconds * 1000);
333
+
334
+ // Close function
335
+ function closeSplash() {
336
+ console.log('[Splash] Closing popup');
337
+ splashWidget.classList.remove('visible');
338
+ setTimeout(function() {
339
+ splashWidget.classList.remove('active');
340
+ splashWidget.style.display = 'none';
341
+ }, 300);
342
+ }
343
+
344
+ // Close button click
345
+ const closeBtn = splashWidget.querySelector('[data-splash-close]');
346
+ if (closeBtn) {
347
+ closeBtn.addEventListener('click', function(e) {
348
+ e.preventDefault();
349
+ e.stopPropagation();
350
+ closeSplash();
351
+ });
352
+ }
353
+
354
+ // Close on overlay click
355
+ splashWidget.addEventListener('click', function(e) {
356
+ if (e.target === splashWidget) {
357
+ closeSplash();
358
+ }
359
+ });
360
+
361
+ // Close on Escape key
362
+ document.addEventListener('keydown', function(e) {
363
+ if (e.key === 'Escape' && splashWidget.classList.contains('active')) {
364
+ closeSplash();
365
+ }
366
+ });
367
+ }
368
+
369
+ // Initialize when DOM is ready
370
+ if (document.readyState === 'loading') {
371
+ console.log('[Splash] DOM still loading, waiting...');
372
+ document.addEventListener('DOMContentLoaded', initSplash);
373
+ } else {
374
+ console.log('[Splash] DOM ready, initializing in 100ms...');
375
+ setTimeout(initSplash, 100);
376
+ }
377
+ })();
378
+ </script>