@o2vend/theme-cli 1.0.36 → 1.0.38

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 (90) hide show
  1. package/README.md +4 -0
  2. package/lib/lib/dev-server.js +344 -48
  3. package/lib/lib/liquid-engine.js +3 -1
  4. package/lib/lib/mock-data.js +473 -119
  5. package/lib/lib/widget-service.js +12 -4
  6. package/package.json +2 -2
  7. package/test-theme/assets/async-sections.js +32 -24
  8. package/test-theme/assets/cart-drawer.js +20 -22
  9. package/test-theme/assets/cart-manager.js +1 -15
  10. package/test-theme/assets/checkout-price-handler.js +12 -11
  11. package/test-theme/assets/checkout.css +1415 -0
  12. package/test-theme/assets/checkout.js +3174 -0
  13. package/test-theme/assets/components.css +178 -29
  14. package/test-theme/assets/delivery-zone.js +1 -1
  15. package/test-theme/assets/product-detail.css +1050 -0
  16. package/test-theme/assets/product-detail.js +2940 -0
  17. package/test-theme/assets/theme.css +95 -120
  18. package/test-theme/assets/theme.js +781 -186
  19. package/test-theme/layout/theme.liquid +91 -17
  20. package/test-theme/sections/content.liquid +64 -57
  21. package/test-theme/sections/footer-fallback.liquid +57 -7
  22. package/test-theme/sections/footer.liquid +63 -12
  23. package/test-theme/sections/header-fallback.liquid +41 -41
  24. package/test-theme/sections/header.liquid +41 -51
  25. package/test-theme/sections/hero-fallback.liquid +1 -1
  26. package/test-theme/sections/hero.liquid +159 -136
  27. package/test-theme/snippets/account-sidebar.liquid +121 -29
  28. package/test-theme/snippets/add-to-cart-modal.liquid +258 -206
  29. package/test-theme/snippets/breadcrumbs.liquid +98 -11
  30. package/test-theme/snippets/cart-drawer.liquid +93 -0
  31. package/test-theme/snippets/delivery-zone-city-selector.liquid +101 -15
  32. package/test-theme/snippets/delivery-zone-modal.liquid +529 -84
  33. package/test-theme/snippets/delivery-zone-search.liquid +104 -18
  34. package/test-theme/snippets/login-modal.liquid +269 -82
  35. package/test-theme/snippets/mega-menu.liquid +130 -43
  36. package/test-theme/snippets/news-thumbnail.liquid +120 -28
  37. package/test-theme/snippets/pagination.liquid +1 -1
  38. package/test-theme/snippets/price.liquid +100 -9
  39. package/test-theme/snippets/product-card-related.liquid +22 -4
  40. package/test-theme/snippets/product-card-simple.liquid +521 -25
  41. package/test-theme/snippets/product-card.liquid +145 -232
  42. package/test-theme/snippets/rating.liquid +100 -9
  43. package/test-theme/snippets/skeleton-collection-grid.liquid +94 -8
  44. package/test-theme/snippets/skeleton-product-card.liquid +102 -16
  45. package/test-theme/snippets/skeleton-product-grid.liquid +87 -1
  46. package/test-theme/snippets/social-sharing.liquid +133 -32
  47. package/test-theme/templates/account/dashboard.liquid +30 -0
  48. package/test-theme/templates/account/loyalty-redemption.liquid +29 -28
  49. package/test-theme/templates/account/loyalty.liquid +45 -43
  50. package/test-theme/templates/account/order-detail.liquid +15 -8
  51. package/test-theme/templates/account/orders.liquid +189 -35
  52. package/test-theme/templates/account/profile.liquid +509 -114
  53. package/test-theme/templates/account/register.liquid +18 -8
  54. package/test-theme/templates/account/return-orders.liquid +31 -30
  55. package/test-theme/templates/account/store-credit.liquid +27 -26
  56. package/test-theme/templates/account/subscriptions.liquid +22 -5
  57. package/test-theme/templates/account/wishlist.liquid +88 -19
  58. package/test-theme/templates/address-book.liquid +166 -69
  59. package/test-theme/templates/categories.liquid +90 -30
  60. package/test-theme/templates/checkout.liquid +137 -3834
  61. package/test-theme/templates/error.liquid +23 -21
  62. package/test-theme/templates/index.liquid +29 -0
  63. package/test-theme/templates/login.liquid +33 -6
  64. package/test-theme/templates/order-confirmation.liquid +67 -9
  65. package/test-theme/templates/page.liquid +418 -206
  66. package/test-theme/templates/product-detail.liquid +124 -3878
  67. package/test-theme/templates/products.liquid +155 -30
  68. package/test-theme/templates/search.liquid +739 -225
  69. package/test-theme/widgets/brand-carousel.liquid +102 -82
  70. package/test-theme/widgets/brand.liquid +78 -50
  71. package/test-theme/widgets/carousel.liquid +253 -121
  72. package/test-theme/widgets/category-list-carousel.liquid +32 -8
  73. package/test-theme/widgets/category-list.liquid +21 -6
  74. package/test-theme/widgets/category.liquid +104 -37
  75. package/test-theme/widgets/discount-time.liquid +326 -119
  76. package/test-theme/widgets/footer-menu.liquid +115 -23
  77. package/test-theme/widgets/footer.liquid +118 -5
  78. package/test-theme/widgets/gallery.liquid +29 -5
  79. package/test-theme/widgets/header-menu.liquid +25 -13
  80. package/test-theme/widgets/header.liquid +64 -26
  81. package/test-theme/widgets/html.liquid +29 -6
  82. package/test-theme/widgets/news.liquid +6 -0
  83. package/test-theme/widgets/product-canvas.liquid +20 -12
  84. package/test-theme/widgets/product-carousel.liquid +118 -56
  85. package/test-theme/widgets/shared/product-grid.liquid +12 -0
  86. package/test-theme/widgets/single-product.liquid +688 -250
  87. package/test-theme/widgets/spacebar-carousel.liquid +39 -10
  88. package/test-theme/widgets/spacebar.liquid +77 -6
  89. package/test-theme/widgets/splash.liquid +40 -30
  90. package/test-theme/widgets/testimonial-carousel.liquid +111 -67
@@ -14,17 +14,50 @@
14
14
  assign show_bottom_margin = widget_settings.showWidgetBottomMargin | default: 'Yes'
15
15
  assign background_color = widget_settings.backgroundColor
16
16
  assign text_color = widget_settings.textColor
17
- assign image_height = widget_settings.imageHeight
18
- assign image_width = widget_settings.imageWidth
19
17
  assign hide_dot = widget_settings.hideDot | default: false
20
18
  assign hide_arrow = widget_settings.hideArrow | default: false
21
19
  assign autoplay = widget_settings.autoplay | default: true
22
20
  assign interval = widget_settings.interval | default: 5000
21
+ assign caption_horizontal = widget_settings.captionHorizontalAlignment | default: 'center'
22
+ assign caption_vertical = widget_settings.captionVerticalAlignment | default: 'center'
23
+
24
+ comment
25
+ ShowWidgetInMobileOrDesktop: mobile, desktop, or other (all devices)
26
+ endcomment
27
+ assign show_widget_device = widget_settings.showWidgetInMobileOrDesktop | default: widget_settings.ShowWidgetInMobileOrDesktop | default: ''
28
+
29
+ assign items_count = items.size
30
+ if items_count > 1
31
+ assign has_multiple_items = true
32
+ else
33
+ assign has_multiple_items = false
34
+ endif
35
+ if autoplay and has_multiple_items
36
+ assign enable_autoplay = true
37
+ else
38
+ assign enable_autoplay = false
39
+ endif
23
40
  %}
24
41
 
25
- <section class="modern-carousel"
42
+ {% assign show_widget_device_lower = show_widget_device | default: '' | downcase | strip %}
43
+ {% assign needs_device_wrapper = false %}
44
+ {% assign is_hero_priority_widget = false %}
45
+ {% if is_hero_first == true or is_hero_first == 'true' or is_hero_first == 1 or is_hero_first == '1' %}
46
+ {% assign is_hero_priority_widget = true %}
47
+ {% endif %}
48
+ {% if show_widget_device_lower == 'mobile' or show_widget_device_lower == 'desktop' %}
49
+ {% assign needs_device_wrapper = true %}
50
+ {% endif %}
51
+
52
+ {% if needs_device_wrapper and show_widget_device_lower == 'mobile' %}
53
+ <div class="carousel-device-wrapper carousel-device-wrapper--mobile-only">
54
+ {% elsif needs_device_wrapper and show_widget_device_lower == 'desktop' %}
55
+ <div class="carousel-device-wrapper carousel-device-wrapper--desktop-only">
56
+ {% endif %}
57
+
58
+ <section class="modern-carousel{% if show_bottom_margin == 'Yes' %} modern-carousel--bottom-margin{% endif %}"
26
59
  data-widget-id="{{ widget.id }}"
27
- data-autoplay="{{ autoplay }}"
60
+ data-autoplay="{{ enable_autoplay }}"
28
61
  data-interval="{{ interval }}"
29
62
  {% if background_color and background_color != 'null' %}style="background-color: {{ background_color }};"{% endif %}>
30
63
  {% if items and items.size > 0 %}
@@ -32,7 +65,8 @@
32
65
  <div class="modern-carousel__viewport" data-carousel-viewport>
33
66
  <div class="modern-carousel__track" data-carousel-track>
34
67
  {% for item in items %}
35
- <article class="modern-carousel__slide{% if forloop.first %} is-active{% endif %}" data-slide="{{ forloop.index0 }}">
68
+ {% assign target_url = item.targetUrl | default: item.TargetUrl | default: item.target_url | default: item.cta_link %}
69
+ <article class="modern-carousel__slide{% if forloop.first %} is-active{% endif %}{% if target_url and target_url != 'null' and target_url != '' %} has-target{% endif %}" data-slide="{{ forloop.index0 }}" {% if target_url and target_url != 'null' and target_url != '' %}data-target-url="{{ target_url }}"{% endif %}>
36
70
  {% assign image_url = item.imageUrl | default: item.ImageUrl | default: item.image | default: item.Image %}
37
71
 
38
72
  {% if image_url and image_url != 'null' %}
@@ -41,14 +75,16 @@
41
75
  {% else %}
42
76
  {% assign image_src = image_url | asset_url %}
43
77
  {% endif %}
44
-
78
+ {% if forloop.first and is_hero_priority_widget %}
79
+ <link rel="preload" as="image" href="{{ image_src }}" fetchpriority="high">
80
+ {% endif %}
45
81
  <div class="modern-carousel__image-wrapper">
46
- <img src="{{ image_src }}"
47
- alt="{{ item.caption | default: item.Caption | default: item.title | default: 'Carousel image' }}"
82
+ <img src="{{ image_src }}"
83
+ alt="{{ item.caption | default: item.Caption | default: item.title | default: 'Carousel image' }}"
48
84
  class="modern-carousel__image"
49
85
  loading="{% if forloop.first %}eager{% else %}lazy{% endif %}"
50
- {% if image_height %}style="height: {{ image_height }}px;"{% endif %}>
51
- <div class="modern-carousel__overlay"></div>
86
+ decoding="async"
87
+ {% if forloop.first and is_hero_priority_widget %}fetchpriority="high"{% endif %}>
52
88
  </div>
53
89
  {% endif %}
54
90
 
@@ -57,8 +93,29 @@
57
93
  {% assign link_text = item.linkText | default: item.LinkText | default: item.link_text | default: item.cta_label %}
58
94
  {% assign target_url = item.targetUrl | default: item.TargetUrl | default: item.target_url | default: item.cta_link %}
59
95
 
96
+ {% comment %} Button color and border styling properties per item {% endcomment %}
97
+ {% assign btn_bg_color = item.ButtonBackgroundColor | default: item.buttonBackgroundColor %}
98
+ {% assign btn_text_color = item.ButtonTextColor | default: item.buttonTextColor %}
99
+ {% assign btn_border_color = item.ButtonBorderColor | default: item.buttonBorderColor %}
100
+ {% assign btn_border_radius = item.ButtonBorderRadius | default: item.buttonBorderRadius %}
101
+
102
+ {% comment %} Build button inline styles {% endcomment %}
103
+ {% assign btn_style = '' %}
104
+ {% if btn_bg_color and btn_bg_color != '' and btn_bg_color != 'null' %}
105
+ {% assign btn_style = btn_style | append: 'background-color: ' | append: btn_bg_color | append: '; ' %}
106
+ {% endif %}
107
+ {% if btn_text_color and btn_text_color != '' and btn_text_color != 'null' %}
108
+ {% assign btn_style = btn_style | append: 'color: ' | append: btn_text_color | append: '; ' %}
109
+ {% endif %}
110
+ {% if btn_border_color and btn_border_color != '' and btn_border_color != 'null' %}
111
+ {% assign btn_style = btn_style | append: 'border: 1px solid ' | append: btn_border_color | append: '; ' %}
112
+ {% endif %}
113
+ {% if btn_border_radius and btn_border_radius != '' and btn_border_radius != 'null' %}
114
+ {% assign btn_style = btn_style | append: 'border-radius: ' | append: btn_border_radius | append: 'px; ' %}
115
+ {% endif %}
116
+
60
117
  {% if caption or sub_caption or link_text %}
61
- <div class="modern-carousel__content" {% if text_color and text_color != 'null' %}style="color: {{ text_color }};"{% endif %}>
118
+ <div class="modern-carousel__content modern-carousel__content--{{ caption_horizontal }}-{{ caption_vertical }}" {% if text_color and text_color != 'null' %}style="color: {{ text_color }};"{% endif %}>
62
119
  <div class="modern-carousel__content-inner">
63
120
  {% if caption %}
64
121
  <h2 class="modern-carousel__title animate-fade-up">{{ caption }}</h2>
@@ -67,7 +124,7 @@
67
124
  <p class="modern-carousel__subtitle animate-fade-up animate-delay-1">{{ sub_caption }}</p>
68
125
  {% endif %}
69
126
  {% if link_text and link_text != '' and target_url and target_url != 'null' %}
70
- <a href="{{ target_url }}" class="modern-carousel__cta animate-fade-up animate-delay-2">
127
+ <a href="{{ target_url }}" class="modern-carousel__cta animate-fade-up animate-delay-2"{% if btn_style != '' %} style="{{ btn_style }}"{% endif %}>
71
128
  <span>{{ link_text }}</span>
72
129
  <svg class="modern-carousel__cta-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
73
130
  <path d="M5 12h14M12 5l7 7-7 7"/>
@@ -82,42 +139,44 @@
82
139
  </div>
83
140
  </div>
84
141
 
85
- {% unless hide_arrow == true %}
86
- <div class="modern-carousel__controls">
87
- <button class="modern-carousel__arrow modern-carousel__arrow--prev"
88
- data-carousel-prev
89
- aria-label="Previous slide">
90
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
91
- <path d="M15 18l-6-6 6-6"/>
92
- </svg>
93
- </button>
94
- <button class="modern-carousel__arrow modern-carousel__arrow--next"
95
- data-carousel-next
96
- aria-label="Next slide">
97
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
98
- <path d="M9 18l6-6-6-6"/>
99
- </svg>
100
- </button>
101
- </div>
102
- {% endunless %}
103
-
104
- {% unless hide_dot == true %}
105
- <div class="modern-carousel__pagination" data-carousel-dots>
106
- {% for item in items %}
107
- <button class="modern-carousel__dot{% if forloop.first %} is-active{% endif %}"
108
- data-index="{{ forloop.index0 }}"
109
- aria-label="Go to slide {{ forloop.index }}">
110
- <span class="modern-carousel__dot-inner"></span>
142
+ {% if has_multiple_items %}
143
+ {% unless hide_arrow == true %}
144
+ <div class="modern-carousel__controls">
145
+ <button class="modern-carousel__arrow modern-carousel__arrow--prev"
146
+ data-carousel-prev
147
+ aria-label="Previous slide">
148
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
149
+ <path d="M15 18l-6-6 6-6"/>
150
+ </svg>
111
151
  </button>
112
- {% endfor %}
113
- </div>
114
- {% endunless %}
115
-
116
- <!-- Progress Bar -->
117
- {% if autoplay %}
118
- <div class="modern-carousel__progress">
119
- <div class="modern-carousel__progress-bar" data-progress-bar></div>
120
- </div>
152
+ <button class="modern-carousel__arrow modern-carousel__arrow--next"
153
+ data-carousel-next
154
+ aria-label="Next slide">
155
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
156
+ <path d="M9 18l6-6-6-6"/>
157
+ </svg>
158
+ </button>
159
+ </div>
160
+ {% endunless %}
161
+
162
+ {% unless hide_dot == true %}
163
+ <div class="modern-carousel__pagination" data-carousel-dots>
164
+ {% for item in items %}
165
+ <button class="modern-carousel__dot{% if forloop.first %} is-active{% endif %}"
166
+ data-index="{{ forloop.index0 }}"
167
+ aria-label="Go to slide {{ forloop.index }}">
168
+ <span class="modern-carousel__dot-inner"></span>
169
+ </button>
170
+ {% endfor %}
171
+ </div>
172
+ {% endunless %}
173
+
174
+ <!-- Progress Bar -->
175
+ {% if enable_autoplay %}
176
+ <div class="modern-carousel__progress">
177
+ <div class="modern-carousel__progress-bar" data-progress-bar></div>
178
+ </div>
179
+ {% endif %}
121
180
  {% endif %}
122
181
  </div>
123
182
  {% else %}
@@ -132,19 +191,36 @@
132
191
  {% endif %}
133
192
  </section>
134
193
 
194
+ {% if needs_device_wrapper %}
195
+ </div>
196
+ {% endif %}
197
+
135
198
  <style>
199
+ /* Device visibility wrapper classes */
200
+ .carousel-device-wrapper--mobile-only {
201
+ display: block;
202
+ }
203
+ .carousel-device-wrapper--desktop-only {
204
+ display: none;
205
+ }
206
+ @media (min-width: 768px) {
207
+ .carousel-device-wrapper--mobile-only {
208
+ display: none;
209
+ }
210
+ .carousel-device-wrapper--desktop-only {
211
+ display: block;
212
+ }
213
+ }
136
214
  /* ============================================
137
215
  MODERN CAROUSEL - ROOT VARIABLES
138
216
  ============================================ */
139
217
 
140
218
  :root {
141
- --carousel-height-desktop: 600px;
142
- --carousel-height-tablet: 500px;
143
- --carousel-height-mobile: 400px;
144
219
  --carousel-radius: 20px;
145
220
  --carousel-padding: 60px;
221
+ --carousel-padding-mobile: 24px;
222
+ --carousel-bottom-margin: 40px;
146
223
  --carousel-gap: 24px;
147
- --overlay-gradient: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 100%);
148
224
  --transition-smooth: cubic-bezier(0.4, 0, 0.2, 1);
149
225
  --color-white: #ffffff;
150
226
  --color-black: #000000;
@@ -158,12 +234,13 @@
158
234
  .modern-carousel {
159
235
  position: relative;
160
236
  width: 100%;
161
- {% if show_bottom_margin == 'Yes' %}
162
- margin-bottom: 60px;
163
- {% endif %}
164
237
  overflow: hidden;
165
238
  }
166
239
 
240
+ .modern-carousel--bottom-margin {
241
+ margin-bottom: var(--carousel-bottom-margin);
242
+ }
243
+
167
244
  .modern-carousel__container {
168
245
  position: relative;
169
246
  width: 100%;
@@ -185,7 +262,6 @@
185
262
  overflow: hidden;
186
263
  {% if show_container == 'Yes' %}
187
264
  border-radius: var(--carousel-radius);
188
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
189
265
  {% endif %}
190
266
  }
191
267
 
@@ -203,43 +279,25 @@
203
279
  position: relative;
204
280
  flex: 0 0 100%;
205
281
  min-width: 100%;
206
- height: var(--carousel-height-desktop);
207
282
  display: flex;
208
- align-items: center;
209
- justify-content: center;
210
283
  overflow: hidden;
211
284
  border-radius: var(--border-radius-medium);
212
285
  }
213
286
 
287
+ .modern-carousel__slide.has-target {
288
+ cursor: pointer;
289
+ }
290
+
214
291
  .modern-carousel__image-wrapper {
215
- position: absolute;
216
- top: 0;
217
- left: 0;
292
+ position: relative;
218
293
  width: 100%;
219
- height: 100%;
220
294
  z-index: 1;
221
295
  }
222
296
 
223
297
  .modern-carousel__image {
224
298
  width: 100%;
225
- height: 100%;
226
- {% comment %} object-fit: cover; {% endcomment %}
227
- transform: scale(1);
228
- transition: transform 8s var(--transition-smooth);
229
- }
230
-
231
- .modern-carousel__slide.is-active .modern-carousel__image {
232
- transform: scale(1.08);
233
- }
234
-
235
- .modern-carousel__overlay {
236
- position: absolute;
237
- top: 0;
238
- left: 0;
239
- right: 0;
240
- bottom: 0;
241
- background: var(--overlay-gradient);
242
- z-index: 2;
299
+ height: auto;
300
+ display: block;
243
301
  }
244
302
 
245
303
  /* ============================================
@@ -253,24 +311,83 @@
253
311
  width: 100%;
254
312
  height: 100%;
255
313
  display: flex;
256
- align-items: center;
257
- justify-content: center;
258
314
  z-index: 3;
259
315
  padding: var(--carousel-padding);
260
316
  }
261
317
 
318
+ .modern-carousel__content--left-top {
319
+ align-items: flex-start;
320
+ justify-content: flex-start;
321
+ }
322
+
323
+ .modern-carousel__content--left-center {
324
+ align-items: center;
325
+ justify-content: flex-start;
326
+ }
327
+
328
+ .modern-carousel__content--left-bottom {
329
+ align-items: flex-end;
330
+ justify-content: flex-start;
331
+ }
332
+
333
+ .modern-carousel__content--center-top {
334
+ align-items: flex-start;
335
+ justify-content: center;
336
+ }
337
+
338
+ .modern-carousel__content--center-center {
339
+ align-items: center;
340
+ justify-content: center;
341
+ }
342
+
343
+ .modern-carousel__content--center-bottom {
344
+ align-items: flex-end;
345
+ justify-content: center;
346
+ }
347
+
348
+ .modern-carousel__content--right-top {
349
+ align-items: flex-start;
350
+ justify-content: flex-end;
351
+ }
352
+
353
+ .modern-carousel__content--right-center {
354
+ align-items: center;
355
+ justify-content: flex-end;
356
+ }
357
+
358
+ .modern-carousel__content--right-bottom {
359
+ align-items: flex-end;
360
+ justify-content: flex-end;
361
+ }
362
+
262
363
  .modern-carousel__content-inner {
263
364
  max-width: 800px;
365
+ }
366
+
367
+ .modern-carousel__content--left-top .modern-carousel__content-inner,
368
+ .modern-carousel__content--left-center .modern-carousel__content-inner,
369
+ .modern-carousel__content--left-bottom .modern-carousel__content-inner {
370
+ text-align: left;
371
+ }
372
+
373
+ .modern-carousel__content--center-top .modern-carousel__content-inner,
374
+ .modern-carousel__content--center-center .modern-carousel__content-inner,
375
+ .modern-carousel__content--center-bottom .modern-carousel__content-inner {
264
376
  text-align: center;
265
377
  }
266
378
 
379
+ .modern-carousel__content--right-top .modern-carousel__content-inner,
380
+ .modern-carousel__content--right-center .modern-carousel__content-inner,
381
+ .modern-carousel__content--right-bottom .modern-carousel__content-inner {
382
+ text-align: right;
383
+ }
384
+
267
385
  .modern-carousel__title {
268
386
  margin: 0 0 20px;
269
387
  font-size: clamp(2rem, 5vw, 4.5rem);
270
388
  font-weight: 800;
271
389
  line-height: 1.1;
272
390
  color: var(--color-white);
273
- text-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
274
391
  letter-spacing: -0.03em;
275
392
  }
276
393
 
@@ -279,12 +396,8 @@
279
396
  font-size: clamp(1rem, 2vw, 1.5rem);
280
397
  font-weight: 400;
281
398
  line-height: 1.6;
282
- color: var(--color-white);
283
399
  opacity: 0.95;
284
- text-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
285
400
  max-width: 600px;
286
- margin-left: auto;
287
- margin-right: auto;
288
401
  }
289
402
 
290
403
  .modern-carousel__cta {
@@ -300,7 +413,6 @@
300
413
  letter-spacing: 0.5px;
301
414
  border-radius: 50px;
302
415
  transition: all 0.4s var(--transition-smooth);
303
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
304
416
  text-transform: uppercase;
305
417
  position: relative;
306
418
  overflow: hidden;
@@ -320,7 +432,6 @@
320
432
 
321
433
  .modern-carousel__cta:hover {
322
434
  transform: translateY(-4px);
323
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
324
435
  color: var(--color-white);
325
436
  }
326
437
 
@@ -397,7 +508,6 @@
397
508
  cursor: pointer;
398
509
  pointer-events: all;
399
510
  transition: all 0.3s var(--transition-smooth);
400
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
401
511
  }
402
512
 
403
513
  .modern-carousel__arrow svg {
@@ -410,7 +520,6 @@
410
520
  .modern-carousel__arrow:hover {
411
521
  background: var(--color-white);
412
522
  transform: scale(1.1);
413
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
414
523
  }
415
524
 
416
525
  .modern-carousel__arrow--prev:hover svg {
@@ -524,10 +633,6 @@
524
633
  ============================================ */
525
634
 
526
635
  @media screen and (max-width: 1024px) and (min-width: 769px) {
527
- .modern-carousel__slide {
528
- height: var(--carousel-height-tablet);
529
- }
530
-
531
636
  .modern-carousel__content {
532
637
  padding: 50px 40px;
533
638
  }
@@ -563,16 +668,13 @@
563
668
 
564
669
  @media screen and (max-width: 768px) {
565
670
  :root {
566
- --carousel-padding: 30px 20px;
567
671
  --carousel-radius: 16px;
568
- }
569
-
570
- .modern-carousel__slide {
571
- height: var(--carousel-height-mobile);
672
+ --carousel-padding-mobile: 20px;
673
+ --carousel-bottom-margin: 24px;
572
674
  }
573
675
 
574
676
  .modern-carousel__content {
575
- padding: var(--carousel-padding);
677
+ padding: var(--carousel-padding-mobile);
576
678
  }
577
679
 
578
680
  .modern-carousel__content-inner {
@@ -640,10 +742,6 @@
640
742
  ============================================ */
641
743
 
642
744
  @media screen and (max-width: 480px) {
643
- .modern-carousel__slide {
644
- height: 350px;
645
- }
646
-
647
745
  .modern-carousel__title {
648
746
  font-size: clamp(1.25rem, 7vw, 2rem);
649
747
  }
@@ -685,7 +783,7 @@
685
783
  </style>
686
784
 
687
785
  <script>
688
- document.addEventListener('DOMContentLoaded', function() {
786
+ document.addEventListener('DOMContentLoaded', () => {
689
787
  const carouselEl = document.querySelector('[data-widget-id="{{ widget.id }}"]');
690
788
  if (!carouselEl) return;
691
789
 
@@ -697,30 +795,42 @@
697
795
  const autoplay = carouselEl.dataset.autoplay === 'true';
698
796
  const interval = parseInt(carouselEl.dataset.interval || '5000', 10);
699
797
 
700
- if (!track || dots.length === 0) return;
798
+ if (!track) return;
799
+
800
+ const slides = carouselEl.querySelectorAll('.modern-carousel__slide');
801
+ const totalSlides = slides.length;
802
+
803
+ if (totalSlides === 0) return;
701
804
 
702
805
  let currentIndex = 0;
703
- const totalSlides = dots.length;
704
806
  let autoplayTimer = null;
705
807
  let progressTimer = null;
706
808
  let progressWidth = 0;
707
809
 
708
810
  // Go to specific slide
709
811
  function goToSlide(index) {
710
- // Remove active class from all slides and dots
711
- const slides = carouselEl.querySelectorAll('.modern-carousel__slide');
812
+ // Remove active class from all slides
712
813
  slides.forEach(slide => slide.classList.remove('is-active'));
713
- dots.forEach(dot => dot.classList.remove('is-active'));
814
+
815
+ // Remove active class from dots if they exist
816
+ if (dots.length > 0) {
817
+ dots.forEach(dot => dot.classList.remove('is-active'));
818
+ }
714
819
 
715
820
  // Set new index
716
821
  currentIndex = (index + totalSlides) % totalSlides;
717
822
 
718
- // Update transform
719
- track.style.transform = `translateX(-${currentIndex * 100}%)`;
823
+ // Update transform only if there are multiple slides
824
+ if (totalSlides > 1) {
825
+ track.style.transform = `translateX(-${currentIndex * 100}%)`;
826
+ }
720
827
 
721
828
  // Add active class
722
829
  slides[currentIndex].classList.add('is-active');
723
- dots[currentIndex].classList.add('is-active');
830
+
831
+ if (dots.length > 0 && dots[currentIndex]) {
832
+ dots[currentIndex].classList.add('is-active');
833
+ }
724
834
 
725
835
  // Reset progress
726
836
  if (progressBar) {
@@ -729,7 +839,7 @@
729
839
  }
730
840
 
731
841
  // Restart autoplay
732
- if (autoplay) {
842
+ if (autoplay && totalSlides > 1) {
733
843
  startAutoplay();
734
844
  }
735
845
  }
@@ -790,12 +900,14 @@
790
900
  }
791
901
 
792
902
  // Event listeners for dots
793
- dots.forEach((dot, index) => {
794
- dot.addEventListener('click', () => goToSlide(index));
795
- });
903
+ if (dots.length > 0) {
904
+ dots.forEach((dot, index) => {
905
+ dot.addEventListener('click', () => goToSlide(index));
906
+ });
907
+ }
796
908
 
797
- // Pause on hover
798
- if (autoplay) {
909
+ // Pause on hover and start autoplay
910
+ if (autoplay && totalSlides > 1) {
799
911
  carouselEl.addEventListener('mouseenter', stopAutoplay);
800
912
  carouselEl.addEventListener('mouseleave', startAutoplay);
801
913
 
@@ -839,5 +951,25 @@
839
951
  }
840
952
  }
841
953
  }
954
+
955
+ // Make whole slide clickable (if slide has data-target-url). Ignore clicks that happen on anchors, buttons or form elements.
956
+ slides.forEach(slide => {
957
+ const url = slide.dataset.targetUrl;
958
+ if (!url) return;
959
+
960
+ slide.addEventListener('click', (e) => {
961
+ // If click was on an interactive element (link/button/input), don't handle here
962
+ if (e.target.closest('a, button, input, textarea')) return;
963
+
964
+ const cta = slide.querySelector('.modern-carousel__cta');
965
+ const ctaTarget = cta ? cta.getAttribute('target') : null;
966
+
967
+ if (ctaTarget === '_blank') {
968
+ window.open(url, '_blank');
969
+ } else {
970
+ window.location.href = url;
971
+ }
972
+ });
973
+ });
842
974
  });
843
- </script>
975
+ </script>