@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,85 @@
1
+ {% comment %}
2
+ Product Rating Display Snippet
3
+
4
+ Displays product rating with stars and review count.
5
+ Usage: {% render 'snippets/rating', product: product %}
6
+ {% endcomment %}
7
+
8
+ {% liquid
9
+ assign product = product | default: null
10
+ assign rating = rating | default: product.rating | default: product.averageRating | default: 0
11
+ assign review_count = review_count | default: product.reviewCount | default: product.reviewsCount | default: 0
12
+ assign show_count = show_count | default: true
13
+ assign max_rating = max_rating | default: 5
14
+
15
+ if rating == null or rating == empty
16
+ assign rating = 0
17
+ endif
18
+
19
+ assign rating_float = rating | times: 1.0
20
+ assign full_stars = rating_float | floor
21
+ assign has_half_star = rating_float | modulo: 1 | times: 10 | round | divided_by: 10.0
22
+ assign empty_stars = max_rating | minus: full_stars
23
+ if has_half_star > 0
24
+ assign empty_stars = empty_stars | minus: 1
25
+ endif
26
+ %}
27
+
28
+ <div class="rating" data-rating="{{ rating }}" aria-label="Rating: {{ rating }} out of {{ max_rating }}">
29
+ <div class="rating-stars" role="img" aria-label="{{ rating }} out of {{ max_rating }} stars">
30
+ {% for i in (1..full_stars) %}
31
+ <span class="rating-star rating-star-full" aria-hidden="true">★</span>
32
+ {% endfor %}
33
+
34
+ {% if has_half_star > 0 %}
35
+ <span class="rating-star rating-star-half" aria-hidden="true">★</span>
36
+ {% endif %}
37
+
38
+ {% for i in (1..empty_stars) %}
39
+ <span class="rating-star rating-star-empty" aria-hidden="true">☆</span>
40
+ {% endfor %}
41
+ </div>
42
+
43
+ {% if show_count and review_count > 0 %}
44
+ <span class="rating-count" data-rating-count>
45
+ ({{ review_count }} {{ 'product.reviews' | t | default: 'review' | pluralize: review_count }})
46
+ </span>
47
+ {% endif %}
48
+ </div>
49
+
50
+ <style>
51
+ .rating {
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 0.5rem;
55
+ }
56
+
57
+ .rating-stars {
58
+ display: inline-flex;
59
+ gap: 0.125rem;
60
+ font-size: 1rem;
61
+ line-height: 1;
62
+ }
63
+
64
+ .rating-star {
65
+ color: #fbbf24;
66
+ font-size: 1rem;
67
+ }
68
+
69
+ .rating-star-empty {
70
+ color: #e5e7eb;
71
+ }
72
+
73
+ .rating-star-half {
74
+ background: linear-gradient(90deg, #fbbf24 50%, #e5e7eb 50%);
75
+ -webkit-background-clip: text;
76
+ -webkit-text-fill-color: transparent;
77
+ background-clip: text;
78
+ }
79
+
80
+ .rating-count {
81
+ font-size: 0.875rem;
82
+ color: {{ settings.color_text_light }};
83
+ }
84
+ </style>
85
+
@@ -0,0 +1,114 @@
1
+ {% comment %}
2
+ Skeleton Loader for Collection/Category Grid
3
+ Parameters:
4
+ - count: Number of skeleton cards to display (default: 6)
5
+ {% endcomment %}
6
+
7
+ {% assign skeletonCount = count | default: 6 %}
8
+
9
+ <div class="collections-grid skeleton-grid">
10
+ {% if skeletonCount >= 1 %}
11
+ <div class="collection-card skeleton-card">
12
+ <div class="collection-image-container skeleton-image">
13
+ <div class="skeleton-shimmer"></div>
14
+ </div>
15
+ <div class="collection-content">
16
+ <div class="skeleton-text skeleton-title"></div>
17
+ <div class="skeleton-text skeleton-description"></div>
18
+ <div class="skeleton-text skeleton-meta"></div>
19
+ </div>
20
+ </div>
21
+ {% endif %}
22
+ {% if skeletonCount >= 2 %}
23
+ <div class="collection-card skeleton-card">
24
+ <div class="collection-image-container skeleton-image">
25
+ <div class="skeleton-shimmer"></div>
26
+ </div>
27
+ <div class="collection-content">
28
+ <div class="skeleton-text skeleton-title"></div>
29
+ <div class="skeleton-text skeleton-description"></div>
30
+ <div class="skeleton-text skeleton-meta"></div>
31
+ </div>
32
+ </div>
33
+ {% endif %}
34
+ {% if skeletonCount >= 3 %}
35
+ <div class="collection-card skeleton-card">
36
+ <div class="collection-image-container skeleton-image">
37
+ <div class="skeleton-shimmer"></div>
38
+ </div>
39
+ <div class="collection-content">
40
+ <div class="skeleton-text skeleton-title"></div>
41
+ <div class="skeleton-text skeleton-description"></div>
42
+ <div class="skeleton-text skeleton-meta"></div>
43
+ </div>
44
+ </div>
45
+ {% endif %}
46
+ {% if skeletonCount >= 4 %}
47
+ <div class="collection-card skeleton-card">
48
+ <div class="collection-image-container skeleton-image">
49
+ <div class="skeleton-shimmer"></div>
50
+ </div>
51
+ <div class="collection-content">
52
+ <div class="skeleton-text skeleton-title"></div>
53
+ <div class="skeleton-text skeleton-description"></div>
54
+ <div class="skeleton-text skeleton-meta"></div>
55
+ </div>
56
+ </div>
57
+ {% endif %}
58
+ {% if skeletonCount >= 5 %}
59
+ <div class="collection-card skeleton-card">
60
+ <div class="collection-image-container skeleton-image">
61
+ <div class="skeleton-shimmer"></div>
62
+ </div>
63
+ <div class="collection-content">
64
+ <div class="skeleton-text skeleton-title"></div>
65
+ <div class="skeleton-text skeleton-description"></div>
66
+ <div class="skeleton-text skeleton-meta"></div>
67
+ </div>
68
+ </div>
69
+ {% endif %}
70
+ {% if skeletonCount >= 6 %}
71
+ <div class="collection-card skeleton-card">
72
+ <div class="collection-image-container skeleton-image">
73
+ <div class="skeleton-shimmer"></div>
74
+ </div>
75
+ <div class="collection-content">
76
+ <div class="skeleton-text skeleton-title"></div>
77
+ <div class="skeleton-text skeleton-description"></div>
78
+ <div class="skeleton-text skeleton-meta"></div>
79
+ </div>
80
+ </div>
81
+ {% endif %}
82
+ </div>
83
+
84
+ <style>
85
+ .skeleton-card .collection-image-container {
86
+ background: #f0f0f0;
87
+ position: relative;
88
+ overflow: hidden;
89
+ aspect-ratio: 1;
90
+ border-radius: 12px;
91
+ }
92
+
93
+ .skeleton-card .collection-content {
94
+ padding: 1rem 0;
95
+ }
96
+
97
+ .skeleton-card .skeleton-title {
98
+ width: 80%;
99
+ height: 20px;
100
+ margin-bottom: 8px;
101
+ }
102
+
103
+ .skeleton-card .skeleton-description {
104
+ width: 100%;
105
+ height: 14px;
106
+ margin-bottom: 6px;
107
+ }
108
+
109
+ .skeleton-card .skeleton-meta {
110
+ width: 50%;
111
+ height: 12px;
112
+ }
113
+ </style>
114
+
@@ -0,0 +1,124 @@
1
+ {% comment %}
2
+ Skeleton Loader for Product Card
3
+ Shows animated placeholder while product data loads
4
+ {% endcomment %}
5
+
6
+ <div class="product-card skeleton-card">
7
+ <div class="product-card-image skeleton-image">
8
+ <div class="skeleton-shimmer"></div>
9
+ </div>
10
+ <div class="product-card-content">
11
+ <div class="skeleton-text skeleton-vendor"></div>
12
+ <div class="skeleton-text skeleton-title"></div>
13
+ <div class="skeleton-text skeleton-price"></div>
14
+ <div class="skeleton-button"></div>
15
+ </div>
16
+ </div>
17
+
18
+ <style>
19
+ .skeleton-card {
20
+ pointer-events: none;
21
+ }
22
+
23
+ .skeleton-image {
24
+ background: #f0f0f0;
25
+ position: relative;
26
+ overflow: hidden;
27
+ border-radius: 12px;
28
+ aspect-ratio: 1;
29
+ }
30
+
31
+ .skeleton-shimmer {
32
+ position: absolute;
33
+ top: 0;
34
+ left: -100%;
35
+ height: 100%;
36
+ width: 100%;
37
+ background: linear-gradient(
38
+ 90deg,
39
+ transparent 0%,
40
+ rgba(255, 255, 255, 0.6) 50%,
41
+ transparent 100%
42
+ );
43
+ animation: shimmer 2s infinite;
44
+ }
45
+
46
+ @keyframes shimmer {
47
+ 0% {
48
+ left: -100%;
49
+ }
50
+ 100% {
51
+ left: 100%;
52
+ }
53
+ }
54
+
55
+ .skeleton-text {
56
+ height: 16px;
57
+ background: #f0f0f0;
58
+ border-radius: 4px;
59
+ position: relative;
60
+ overflow: hidden;
61
+ margin-bottom: 8px;
62
+ }
63
+
64
+ .skeleton-text::after {
65
+ content: '';
66
+ position: absolute;
67
+ top: 0;
68
+ left: -100%;
69
+ height: 100%;
70
+ width: 100%;
71
+ background: linear-gradient(
72
+ 90deg,
73
+ transparent 0%,
74
+ rgba(255, 255, 255, 0.6) 50%,
75
+ transparent 100%
76
+ );
77
+ animation: shimmer 2s infinite;
78
+ }
79
+
80
+ .skeleton-vendor {
81
+ width: 60%;
82
+ height: 12px;
83
+ margin-bottom: 6px;
84
+ }
85
+
86
+ .skeleton-title {
87
+ width: 90%;
88
+ height: 18px;
89
+ margin-bottom: 8px;
90
+ }
91
+
92
+ .skeleton-price {
93
+ width: 50%;
94
+ height: 20px;
95
+ margin-bottom: 12px;
96
+ }
97
+
98
+ .skeleton-button {
99
+ width: 100%;
100
+ height: 44px;
101
+ background: #f0f0f0;
102
+ border-radius: 8px;
103
+ position: relative;
104
+ overflow: hidden;
105
+ margin-top: 8px;
106
+ }
107
+
108
+ .skeleton-button::after {
109
+ content: '';
110
+ position: absolute;
111
+ top: 0;
112
+ left: -100%;
113
+ height: 100%;
114
+ width: 100%;
115
+ background: linear-gradient(
116
+ 90deg,
117
+ transparent 0%,
118
+ rgba(255, 255, 255, 0.6) 50%,
119
+ transparent 100%
120
+ );
121
+ animation: shimmer 2s infinite;
122
+ }
123
+ </style>
124
+
@@ -0,0 +1,34 @@
1
+ {% comment %}
2
+ Skeleton Loader for Product Grid
3
+ Parameters:
4
+ - count: Number of skeleton cards to display (default: 4)
5
+ {% endcomment %}
6
+
7
+ {% assign skeletonCount = count | default: 4 %}
8
+
9
+ <div class="products-grid skeleton-grid">
10
+ {% if skeletonCount >= 1 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
11
+ {% if skeletonCount >= 2 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
12
+ {% if skeletonCount >= 3 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
13
+ {% if skeletonCount >= 4 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
14
+ {% if skeletonCount >= 5 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
15
+ {% if skeletonCount >= 6 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
16
+ {% if skeletonCount >= 7 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
17
+ {% if skeletonCount >= 8 %}{% include 'snippets/skeleton-product-card' %}{% endif %}
18
+ </div>
19
+
20
+ <style>
21
+ .skeleton-grid {
22
+ animation: fadeIn 0.3s ease-in;
23
+ }
24
+
25
+ @keyframes fadeIn {
26
+ from {
27
+ opacity: 0;
28
+ }
29
+ to {
30
+ opacity: 1;
31
+ }
32
+ }
33
+ </style>
34
+
@@ -0,0 +1,185 @@
1
+ {% comment %}
2
+ Social Sharing Snippet
3
+
4
+ Displays social sharing buttons for products, articles, etc.
5
+ Usage: {% render 'snippets/social-sharing', url: product.url, title: product.title %}
6
+ {% endcomment %}
7
+
8
+ {% liquid
9
+ assign url = url | default: request.url
10
+ assign title = title | default: page.title | default: product.title | default: shop.name
11
+ assign description = description | default: page.description | default: product.description | default: shop.description
12
+ assign image = image | default: product.image | default: page.image
13
+
14
+ # Encode URL for sharing
15
+ assign encoded_url = url | url_encode
16
+ assign encoded_title = title | url_encode
17
+ assign encoded_description = description | truncate: 200 | url_encode
18
+ %}
19
+
20
+ <div class="social-sharing" data-social-sharing>
21
+ <span class="social-sharing-label">{{ 'general.social.share' | t | default: 'Share' }}</span>
22
+ <div class="social-sharing-buttons">
23
+ <a href="https://www.facebook.com/sharer/sharer.php?u={{ encoded_url }}"
24
+ class="social-sharing-button social-sharing-button--facebook"
25
+ target="_blank"
26
+ rel="noopener noreferrer"
27
+ aria-label="Share on Facebook">
28
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
29
+ <path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
30
+ </svg>
31
+ <span>Facebook</span>
32
+ </a>
33
+
34
+ <a href="https://twitter.com/intent/tweet?url={{ encoded_url }}&text={{ encoded_title }}"
35
+ class="social-sharing-button social-sharing-button--twitter"
36
+ target="_blank"
37
+ rel="noopener noreferrer"
38
+ aria-label="Share on Twitter">
39
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
40
+ <path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
41
+ </svg>
42
+ <span>Twitter</span>
43
+ </a>
44
+
45
+ <a href="https://pinterest.com/pin/create/button/?url={{ encoded_url }}&description={{ encoded_title }}"
46
+ class="social-sharing-button social-sharing-button--pinterest"
47
+ target="_blank"
48
+ rel="noopener noreferrer"
49
+ aria-label="Share on Pinterest">
50
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
51
+ <path d="M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.402.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.746-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24.009 12.017 24.009c6.624 0 11.99-5.367 11.99-11.988C24.007 5.367 18.641.001 12.017.001z"/>
52
+ </svg>
53
+ <span>Pinterest</span>
54
+ </a>
55
+
56
+ <a href="mailto:?subject={{ encoded_title }}&body={{ encoded_url }}"
57
+ class="social-sharing-button social-sharing-button--email"
58
+ aria-label="Share via Email">
59
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
60
+ <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
61
+ <polyline points="22,6 12,13 2,6"></polyline>
62
+ </svg>
63
+ <span>Email</span>
64
+ </a>
65
+
66
+ <button class="social-sharing-button social-sharing-button--copy"
67
+ data-copy-url="{{ url }}"
68
+ aria-label="Copy link">
69
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
70
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
71
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
72
+ </svg>
73
+ <span>Copy Link</span>
74
+ </button>
75
+ </div>
76
+ </div>
77
+
78
+ <style>
79
+ .social-sharing {
80
+ display: flex;
81
+ align-items: center;
82
+ gap: 1rem;
83
+ flex-wrap: wrap;
84
+ }
85
+
86
+ .social-sharing-label {
87
+ font-size: 0.875rem;
88
+ font-weight: 500;
89
+ color: {{ settings.color_text }};
90
+ }
91
+
92
+ .social-sharing-buttons {
93
+ display: flex;
94
+ gap: 0.5rem;
95
+ flex-wrap: wrap;
96
+ }
97
+
98
+ .social-sharing-button {
99
+ display: inline-flex;
100
+ align-items: center;
101
+ gap: 0.5rem;
102
+ padding: 0.5rem 1rem;
103
+ border: 1px solid #e5e7eb;
104
+ background: white;
105
+ color: {{ settings.color_text }};
106
+ text-decoration: none;
107
+ border-radius: 0.375rem;
108
+ font-size: 0.875rem;
109
+ transition: all 0.2s ease;
110
+ cursor: pointer;
111
+ }
112
+
113
+ .social-sharing-button:hover {
114
+ background: #f9fafb;
115
+ border-color: {{ settings.color_primary }};
116
+ color: {{ settings.color_primary }};
117
+ }
118
+
119
+ .social-sharing-button--facebook:hover {
120
+ background: #1877f2;
121
+ border-color: #1877f2;
122
+ color: white;
123
+ }
124
+
125
+ .social-sharing-button--twitter:hover {
126
+ background: #1da1f2;
127
+ border-color: #1da1f2;
128
+ color: white;
129
+ }
130
+
131
+ .social-sharing-button--pinterest:hover {
132
+ background: #bd081c;
133
+ border-color: #bd081c;
134
+ color: white;
135
+ }
136
+
137
+ .social-sharing-button--email:hover {
138
+ background: {{ settings.color_primary }};
139
+ border-color: {{ settings.color_primary }};
140
+ color: white;
141
+ }
142
+
143
+ .social-sharing-button svg {
144
+ flex-shrink: 0;
145
+ }
146
+
147
+ @media (max-width: 480px) {
148
+ .social-sharing {
149
+ flex-direction: column;
150
+ align-items: flex-start;
151
+ }
152
+
153
+ .social-sharing-button span {
154
+ display: none;
155
+ }
156
+
157
+ .social-sharing-button {
158
+ padding: 0.5rem;
159
+ width: 40px;
160
+ height: 40px;
161
+ justify-content: center;
162
+ }
163
+ }
164
+ </style>
165
+
166
+ <script>
167
+ document.addEventListener('DOMContentLoaded', function() {
168
+ const copyButton = document.querySelector('[data-copy-url]');
169
+ if (copyButton) {
170
+ copyButton.addEventListener('click', function() {
171
+ const url = this.dataset.copyUrl;
172
+ navigator.clipboard.writeText(url).then(() => {
173
+ const originalText = this.querySelector('span').textContent;
174
+ this.querySelector('span').textContent = 'Copied!';
175
+ setTimeout(() => {
176
+ this.querySelector('span').textContent = originalText;
177
+ }, 2000);
178
+ }).catch(err => {
179
+ console.error('Failed to copy:', err);
180
+ });
181
+ });
182
+ }
183
+ });
184
+ </script>
185
+