@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
@@ -12,16 +12,19 @@
12
12
  <!-- Favicon -->
13
13
  <link rel="icon" href="{{ shop.favicon }}">
14
14
 
15
- <!-- Resource hints for fonts -->
15
+ <!-- Resource hints for fonts and CDN (intl-tel-input) -->
16
16
  <link rel="preconnect" href="https://fonts.googleapis.com">
17
17
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
18
+ <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
18
19
 
19
- <!-- CSS -->
20
+ <!-- Critical CSS (required for above-the-fold) -->
20
21
  <link rel="stylesheet" href="/assets/theme.min.css">
21
22
  <link rel="stylesheet" href="/assets/components.min.css">
22
- <link rel="stylesheet" href="/assets/delivery-zone.min.css">
23
- <!-- intl-tel-input CSS -->
24
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/intl-tel-input@23.0.0/build/css/intlTelInput.min.css">
23
+ <!-- Non-critical CSS: load async to avoid blocking LCP (delivery zone modal, intl-tel-input used on checkout/account) -->
24
+ <link rel="stylesheet" href="/assets/delivery-zone.min.css" media="print" onload="this.media='all'">
25
+ <noscript><link rel="stylesheet" href="/assets/delivery-zone.min.css"></noscript>
26
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/intl-tel-input@23.0.0/build/css/intlTelInput.min.css" media="print" onload="this.media='all'">
27
+ <noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/intl-tel-input@23.0.0/build/css/intlTelInput.min.css"></noscript>
25
28
 
26
29
  <!-- Font Loading - Non-render-blocking -->
27
30
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Playfair+Display:wght@400;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'">
@@ -34,8 +37,44 @@
34
37
  {% if additional_head %}
35
38
  {{ additional_head }}
36
39
  {% endif %}
40
+
41
+ <style>
42
+ @media (max-width: 768px) {
43
+ body {
44
+ padding-bottom: calc(76px + env(safe-area-inset-bottom));
45
+ }
46
+
47
+ .main-content {
48
+ padding-bottom: calc(84px + env(safe-area-inset-bottom)) !important;
49
+ }
50
+ }
51
+
52
+ .wishlist-btn,
53
+ .action-btn.wishlist-btn {
54
+ color: #111827;
55
+ background: rgba(255, 255, 255, 0.92);
56
+ }
57
+
58
+ .wishlist-btn svg,
59
+ .action-btn.wishlist-btn svg {
60
+ fill: none;
61
+ stroke: currentColor;
62
+ }
63
+
64
+ .wishlist-btn.in-wishlist,
65
+ .action-btn.wishlist-btn.in-wishlist {
66
+ background: rgba(239, 68, 68, 0.95) !important;
67
+ color: #ffffff !important;
68
+ }
69
+
70
+ .wishlist-btn.in-wishlist svg,
71
+ .action-btn.wishlist-btn.in-wishlist svg {
72
+ fill: #ffffff;
73
+ stroke: #ffffff;
74
+ }
75
+ </style>
37
76
 
38
- <!-- Plugin hooks for theme head -->
77
+ <!-- App hooks for theme head -->
39
78
  {% hook 'theme_head' %}
40
79
 
41
80
  {%- comment -%}
@@ -64,14 +103,14 @@
64
103
  data-shop-currency="{{ shop.currency | default: 'USD' }}"
65
104
  data-shop-currency-symbol="{{ shop.settings.currencySymbol | default: shop.currency | default: '$' }}"
66
105
  data-shop-locale="{{ shop.locale | default: shop.language | default: 'en-US' }}"
67
- style="font-family: var(--font-primary); font-size: {{ settings.font_size_base | default: 16 }}px;"
106
+ style="font-family: var(--font-primary); font-size: {{ settings.font_size_base | default: 14 }}px;"
68
107
  >
69
108
  <script>
70
109
  window.__SHOP_CURRENCY__ = '{{ shop.currency | default: 'USD' }}';
71
110
  window.__SHOP_CURRENCY_SYMBOL__ = '{{ shop.settings.currencySymbol | default: shop.currency | default: '$' }}';
72
111
  window.__SHOP_LOCALE__ = '{{ shop.locale | default: shop.language | default: 'en-US' }}';
73
112
  </script>
74
- <!-- Plugin hooks for body begin -->
113
+ <!-- app hooks for body begin -->
75
114
  {% hook 'theme_body_begin' %}
76
115
  <!-- Skip Links for Accessibility -->
77
116
  <a href="#main-content" class="skip-link">Skip to main content</a>
@@ -85,7 +124,7 @@
85
124
 
86
125
  <!-- Main content -->
87
126
  <main class="main-content" id="main-content" role="main">
88
- <div class="container" style="max-width: {{ settings.container_width | default: 1400 }}px; padding: 0 {{ settings.container_padding | default: 24 }}px;">
127
+ <div class="container" style="max-width: {{ settings.container_width | default: 1200 }}px; padding: 0 {{ settings.container_padding | default: 16 }}px;">
89
128
  {%- comment -%}
90
129
  Use Shopify-compatible {{ content_for_layout }} for layout content,
91
130
  but keep support for our existing {{ content }} variable as a fallback.
@@ -105,21 +144,41 @@
105
144
  </footer>
106
145
  {% hook 'footer_after' %}
107
146
 
147
+ <!-- Image error fallback (replaces inline onerror for CSP/security) -->
148
+ <script>
149
+ document.addEventListener('DOMContentLoaded', () => {
150
+ document.querySelectorAll('img[data-img-fallback]').forEach((img) => {
151
+ img.addEventListener('error', () => {
152
+ img.style.display = 'none';
153
+ const action = img.getAttribute('data-img-fallback');
154
+ if (action === 'next' && img.nextElementSibling) {
155
+ img.nextElementSibling.style.display = img.getAttribute('data-img-fallback-display') || 'flex';
156
+ } else if (action === 'parent-next' && img.parentElement && img.parentElement.nextElementSibling) {
157
+ img.parentElement.nextElementSibling.style.display = 'flex';
158
+ } else if (action && action.startsWith('query:') && img.parentNode) {
159
+ const sel = action.slice(6);
160
+ const el = img.parentNode.querySelector(sel);
161
+ if (el) el.style.display = 'flex';
162
+ }
163
+ });
164
+ });
165
+ });
166
+ </script>
108
167
  <!-- JavaScript -->
109
168
  <script src="/assets/cart-manager.min.js" defer></script>
110
- <script src="/assets/theme.min.js" defer></script>
169
+ <script src="/assets/theme.js" defer></script>
111
170
  <script src="/assets/cart-drawer.min.js" defer></script>
112
171
  <script src="/assets/delivery-zone.min.js" defer></script>
113
- <!-- intl-tel-input JS -->
114
- <script src="https://cdn.jsdelivr.net/npm/intl-tel-input@23.0.0/build/js/intlTelInput.min.js"></script>
115
- <!-- utils.js is loaded dynamically by intl-tel-input when needed, no need to load it here -->
172
+ <!-- intl-tel-input JS (defer: not needed for initial paint; used on checkout/account/login) -->
173
+ <!-- Note: utils.js is loaded automatically by intlTelInput via utilsScript option, don't load separately -->
174
+ <script src="https://cdn.jsdelivr.net/npm/intl-tel-input@23.0.0/build/js/intlTelInput.min.js" defer></script>
116
175
 
117
176
  <!-- Async Section Loader -->
118
177
  <script src="/assets/async-sections.min.js" defer></script>
119
178
  <script>
120
179
  // Initialize async section loader if sections are defined
121
180
  if (window.asyncSections && window.AsyncSectionLoader) {
122
- document.addEventListener('DOMContentLoaded', function() {
181
+ document.addEventListener('DOMContentLoaded', () => {
123
182
  AsyncSectionLoader.init(window.asyncSections);
124
183
  });
125
184
  }
@@ -132,6 +191,22 @@
132
191
 
133
192
  {% include 'snippets/cart-drawer' %}
134
193
 
194
+ {% if autoOpenCartDrawer %}
195
+ <script>
196
+ // Auto-open cart drawer for cart page preview
197
+ document.addEventListener('DOMContentLoaded', () => {
198
+ setTimeout(() => {
199
+ var drawer = document.getElementById('cart-drawer') ||
200
+ (window.__previewContainer && window.__previewContainer.querySelector('#cart-drawer'));
201
+ if (drawer) {
202
+ drawer.classList.add('active');
203
+ drawer.setAttribute('aria-hidden', 'false');
204
+ }
205
+ }, 500);
206
+ });
207
+ </script>
208
+ {% endif %}
209
+
135
210
  {% if deliveryZone.enabled %}
136
211
  {% include 'snippets/delivery-zone-modal' %}
137
212
  {% endif %}
@@ -151,7 +226,7 @@
151
226
  </svg>
152
227
  <span class="mobile-bottom-nav__label">Home</span>
153
228
  </a>
154
- <a href="/collections" class="mobile-bottom-nav__item" data-nav-item="categories" aria-label="Categories">
229
+ <a href="/categories" class="mobile-bottom-nav__item" data-nav-item="categories" aria-label="Categories">
155
230
  <svg class="mobile-bottom-nav__icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
156
231
  <rect x="3" y="3" width="7" height="7"></rect>
157
232
  <rect x="14" y="3" width="7" height="7"></rect>
@@ -188,8 +263,7 @@
188
263
  </button>
189
264
  </nav>
190
265
 
191
- <!-- Plugin hooks for body end -->
266
+ <!-- app hooks for body end -->
192
267
  {% hook 'theme_body_end' %}
193
268
  </body>
194
269
  </html>
195
-
@@ -1,57 +1,64 @@
1
- {% comment %}
2
- Content Section
3
-
4
- This section renders widgets from the "content" section in order.
5
- Widgets are rendered one below another, sorted by Position field.
6
- Supports drag-and-drop ordering in visual editor.
7
- {% endcomment %}
8
-
9
- {% assign content_widgets = widgets.content %}
10
- {% if content_widgets and content_widgets.size > 0 %}
11
- <section class="theme-section theme-section--content" data-section="content" data-section-id="{{ section.id }}">
12
- {% for widget in content_widgets %}
13
- <div class="theme-widget-wrapper"
14
- data-widget-id="{{ widget.id }}"
15
- data-widget-type="{{ widget.type }}"
16
- data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
17
- {{ widget | render_widget }}
18
- </div>
19
- {% endfor %}
20
- </section>
21
- <style>
22
- .theme-section--content .theme-widget-wrapper {
23
- display: block;
24
- margin-bottom: 2rem;
25
- }
26
- .theme-section--content .theme-widget-wrapper:last-child {
27
- margin-bottom: 0;
28
- }
29
- .widget-error {
30
- padding: 1rem;
31
- background: #fee2e2;
32
- border: 1px solid #fecaca;
33
- border-radius: 0.5rem;
34
- color: #991b1b;
35
- font-size: 0.875rem;
36
- }
37
- </style>
38
- {% else %}
39
- {% render 'sections/content-fallback' %}
40
- {% endif %}
41
-
42
- {% schema %}
43
- {
44
- "name": "Content",
45
- "settings": [
46
- {
47
- "type": "paragraph",
48
- "content": "This section displays widgets from the 'content' section. Widgets are automatically ordered by their Position field and can be reordered in the visual editor."
49
- }
50
- ],
51
- "presets": [
52
- {
53
- "name": "Content"
54
- }
55
- ]
56
- }
57
- {% endschema %}
1
+ {% comment %}
2
+ Content Section
3
+
4
+ This section renders widgets from the "content" section in order.
5
+ Widgets are rendered one below another, sorted by Position field.
6
+ Supports drag-and-drop ordering in visual editor.
7
+ {% endcomment %}
8
+
9
+ {% assign content_widgets = widgets.content %}
10
+ {% if content_widgets and content_widgets.size > 0 %}
11
+ <section class="theme-section theme-section--content" data-section="content" data-section-id="{{ section.id }}">
12
+ {% for widget in content_widgets %}
13
+ <div class="theme-widget-wrapper"
14
+ data-widget-id="{{ widget.id }}"
15
+ data-widget-type="{{ widget.type }}"
16
+ data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
17
+ {% if widget and widget.template_path %}
18
+ {% render widget.template_path, widget: widget, settings: settings, shop: shop %}
19
+ {% else %}
20
+ <div class="widget-error" data-widget-error>
21
+ <p>Widget template not found: {{ widget.template_path | default: 'unknown' }}</p>
22
+ <p>Widget type: {{ widget.type | default: 'unknown' }}</p>
23
+ </div>
24
+ {% endif %}
25
+ </div>
26
+ {% endfor %}
27
+ </section>
28
+ <style>
29
+ .theme-section--content .theme-widget-wrapper {
30
+ display: block;
31
+ margin-bottom: 2rem;
32
+ }
33
+ .theme-section--content .theme-widget-wrapper:last-child {
34
+ margin-bottom: 0;
35
+ }
36
+ .widget-error {
37
+ padding: 1rem;
38
+ background: #fee2e2;
39
+ border: 1px solid #fecaca;
40
+ border-radius: 0.5rem;
41
+ color: #991b1b;
42
+ font-size: 0.875rem;
43
+ }
44
+ </style>
45
+ {% else %}
46
+ {% render 'sections/content-fallback' %}
47
+ {% endif %}
48
+
49
+ {% schema %}
50
+ {
51
+ "name": "Content",
52
+ "settings": [
53
+ {
54
+ "type": "paragraph",
55
+ "content": "This section displays widgets from the 'content' section. Widgets are automatically ordered by their Position field and can be reordered in the visual editor."
56
+ }
57
+ ],
58
+ "presets": [
59
+ {
60
+ "name": "Content"
61
+ }
62
+ ]
63
+ }
64
+ {% endschema %}
@@ -15,7 +15,7 @@
15
15
  <h4>Shop</h4>
16
16
  <ul>
17
17
  <li><a href="/products">All Products</a></li>
18
- <li><a href="/collections">Collections</a></li>
18
+ <li><a href="/categories">Categories</a></li>
19
19
  <li><a href="/search">Search</a></li>
20
20
  </ul>
21
21
  </div>
@@ -101,12 +101,17 @@
101
101
  </div>
102
102
  {% endif %}
103
103
  <div class="site-footer__bottom">
104
- {% assign footer_text_value = settings.footer_text | default: section.settings.footer_text %}
105
- {% if footer_text_value and footer_text_value != '' %}
106
- <p>{{ footer_text_value }}</p>
107
- {% else %}
108
- <p>© {{ 'now' | date: '%Y' }} {{ shop.name }}. All rights reserved.</p>
109
- {% endif %}
104
+ <div class="site-footer__bottom-left">
105
+ {% assign footer_text_value = settings.footer_text | default: section.settings.footer_text %}
106
+ {% if footer_text_value and footer_text_value != '' %}
107
+ <p>{{ footer_text_value }}</p>
108
+ {% else %}
109
+ <p>© {{ 'now' | date: '%Y' }} {{ shop.name }}. All rights reserved.</p>
110
+ {% endif %}
111
+ <p class="footer-powered">
112
+ Powered by <a href="https://o2vend.com" class="footer-powered-link" target="_blank" rel="noopener noreferrer">O2VEND</a>
113
+ </p>
114
+ </div>
110
115
  <div class="site-footer__bottom-links">
111
116
  <a href="/pages/privacy">Privacy</a>
112
117
  <a href="/pages/terms">Terms</a>
@@ -304,10 +309,34 @@
304
309
  display: flex;
305
310
  flex-wrap: wrap;
306
311
  justify-content: space-between;
312
+ align-items: flex-start;
307
313
  gap: 1rem;
308
314
  font-size: 0.85rem;
309
315
  color: {{ settings.color_text_light }};
310
316
  }
317
+ .site-footer__bottom-left {
318
+ display: flex;
319
+ flex-direction: column;
320
+ gap: 0.5rem;
321
+ flex: 1;
322
+ min-width: 0;
323
+ }
324
+ .site-footer__bottom .footer-powered {
325
+ margin: 0;
326
+ color: {{ settings.color_text_light }};
327
+ font-size: 0.85rem;
328
+ line-height: 1.5;
329
+ display: block;
330
+ visibility: visible;
331
+ }
332
+ .site-footer__bottom .footer-powered-link {
333
+ color: {{ settings.color_text_light }};
334
+ text-decoration: underline;
335
+ transition: color 0.2s ease;
336
+ }
337
+ .site-footer__bottom .footer-powered-link:hover {
338
+ color: {{ settings.color_primary }};
339
+ }
311
340
  .site-footer__bottom-links {
312
341
  display: inline-flex;
313
342
  gap: 1rem;
@@ -323,6 +352,27 @@
323
352
  .site-footer__fallback-inner {
324
353
  grid-template-columns: minmax(0, 1fr);
325
354
  }
355
+ .site-footer__bottom {
356
+ flex-direction: column;
357
+ align-items: center;
358
+ text-align: center;
359
+ }
360
+ .site-footer__bottom-left {
361
+ width: 100%;
362
+ align-items: center;
363
+ text-align: center;
364
+ }
365
+ .site-footer__bottom .footer-powered {
366
+ display: block !important;
367
+ visibility: visible !important;
368
+ text-align: center;
369
+ margin-top: 0.5rem;
370
+ }
371
+ .site-footer__bottom-links {
372
+ justify-content: center;
373
+ width: 100%;
374
+ margin-top: 0.5rem;
375
+ }
326
376
  }
327
377
  </style>
328
378
 
@@ -27,7 +27,7 @@
27
27
 
28
28
  {%- comment -%} Debug: Log widget rendering information {%- endcomment -%}
29
29
  <script>
30
- (function() {
30
+ (() => {
31
31
  console.log('[FooterSection] Footer section rendering');
32
32
  console.log('[FooterSection] Footer widgets count:', {{ footer_widgets.size | default: 0 }});
33
33
  console.log('[FooterSection] Footer menu widget found:', {{ footer_menu_widget | default: nil | json }});
@@ -67,7 +67,7 @@
67
67
  data-widget-id="{{ footer_menu_widget.id }}"
68
68
  data-widget-type="FooterMenu"
69
69
  data-footer-menu-wrapper="true">
70
- {% render footer_menu_widget.template_path | default: 'widgets/footer-menu', widget: footer_menu_widget, settings: settings %}
70
+ {% render footer_menu_widget.template_path | default: 'widgets/footer-menu', widget: footer_menu_widget, settings: settings, shop: shop %}
71
71
  </div>
72
72
  <script>
73
73
  console.log('[FooterSection] FooterMenu widget rendered with ID: {{ footer_menu_widget.id }}');
@@ -77,7 +77,7 @@
77
77
  <div class="theme-widget-wrapper"
78
78
  data-widget-type="FooterMenu"
79
79
  data-footer-menu-wrapper="true">
80
- {% render 'widgets/footer-menu', widget: nil, settings: settings %}
80
+ {% render 'widgets/footer-menu', widget: nil, settings: settings, shop: shop %}
81
81
  </div>
82
82
  <script>
83
83
  console.warn('[FooterSection] FooterMenu widget not found in widgets, but footer menu container is rendered to auto-fetch menus');
@@ -97,7 +97,7 @@
97
97
  data-widget-type="{{ widget.type }}"
98
98
  data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
99
99
  {% if widget and widget.template_path %}
100
- {% render widget.template_path, widget: widget, settings: settings %}
100
+ {% render widget.template_path, widget: widget, settings: settings, shop: shop %}
101
101
  {% else %}
102
102
  <div class="widget-error" data-widget-error>
103
103
  <p>Widget template not found</p>
@@ -115,13 +115,18 @@
115
115
  <div class="theme-section theme-section--below-footer">
116
116
  <div class="below-footer-container">
117
117
  <div class="below-footer-content">
118
- <p class="below-footer-text">
119
- {% if section.settings.footer_text and section.settings.footer_text != '' %}
120
- {{ section.settings.footer_text }}
121
- {% else %}
122
- © {{ 'now' | date: '%Y' }} {{ shop.domain }}. All rights reserved.
123
- {% endif %}
124
- </p>
118
+ <div class="below-footer-left">
119
+ <p class="below-footer-text">
120
+ {% if section.settings.footer_text and section.settings.footer_text != '' %}
121
+ {{ section.settings.footer_text }}
122
+ {% else %}
123
+ © {{ 'now' | date: '%Y' }} {{ shop.domain }}. All rights reserved.
124
+ {% endif %}
125
+ </p>
126
+ <p class="footer-powered">
127
+ Powered by <a href="https://o2vend.com" class="footer-powered-link" target="_blank" rel="noopener noreferrer">O2VEND</a>
128
+ </p>
129
+ </div>
125
130
 
126
131
  {% if section.settings.show_social_links %}
127
132
  <div class="below-footer-social">
@@ -183,8 +188,17 @@
183
188
  .below-footer-content {
184
189
  display: flex;
185
190
  justify-content: space-between;
186
- align-items: center;
191
+ align-items: flex-start;
187
192
  gap: 2rem;
193
+ overflow: visible;
194
+ }
195
+
196
+ .below-footer-left {
197
+ display: flex;
198
+ flex-direction: column;
199
+ gap: 0.5rem;
200
+ flex: 1;
201
+ min-width: 0;
188
202
  }
189
203
 
190
204
  .below-footer-text {
@@ -192,6 +206,25 @@
192
206
  color: #9ca3af;
193
207
  }
194
208
 
209
+ .footer-powered {
210
+ margin: 0;
211
+ color: #9ca3af;
212
+ font-size: 0.875rem;
213
+ line-height: 1.5;
214
+ display: block;
215
+ visibility: visible;
216
+ }
217
+
218
+ .footer-powered-link {
219
+ color: #9ca3af;
220
+ text-decoration: underline;
221
+ transition: color 0.2s ease;
222
+ }
223
+
224
+ .footer-powered-link:hover {
225
+ color: #ffffff;
226
+ }
227
+
195
228
  .below-footer-social {
196
229
  display: flex;
197
230
  gap: 1rem;
@@ -240,6 +273,24 @@
240
273
  flex-direction: column;
241
274
  text-align: center;
242
275
  gap: 1rem;
276
+ align-items: center;
277
+ }
278
+
279
+ .below-footer-left {
280
+ align-items: center;
281
+ text-align: center;
282
+ width: 100%;
283
+ }
284
+
285
+ .below-footer-text {
286
+ text-align: center;
287
+ }
288
+
289
+ .footer-powered {
290
+ display: block !important;
291
+ visibility: visible !important;
292
+ text-align: center;
293
+ margin-top: 0.5rem;
243
294
  }
244
295
 
245
296
  .below-footer-social {