@runwell/shopify-toolkit 0.3.0 → 0.4.1

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.
package/lib/template.js CHANGED
@@ -21,7 +21,8 @@ export function interpolate(source, vars) {
21
21
  let working = source;
22
22
 
23
23
  // Second pass: substitute {{config.X}} and {{brand.Y}} and {{client.Z}}
24
- working = working.replace(/\{\{\s*([a-zA-Z][a-zA-Z0-9_.]*)\s*\}\}/g, (match, key) => {
24
+ // Keys can include hyphens (e.g. brand.rain-forrest) and dots for nesting.
25
+ working = working.replace(/\{\{\s*([a-zA-Z][a-zA-Z0-9_.\-]*)\s*\}\}/g, (match, key) => {
25
26
  // If the key starts with a Liquid keyword, leave it alone (it is Shopify Liquid)
26
27
  const liquidKeywords = new Set([
27
28
  'product', 'collection', 'cart', 'customer', 'shop', 'request',
@@ -0,0 +1,288 @@
1
+ # Runwell Shopify Toolkit: module index
2
+
3
+ Auto-generated by `scripts/generate-index.mjs`. Do not edit by hand.
4
+ Skills (shopify-storefront, shopify-cro, shopify-cli-ops, shopify-admin-browser) grep this file before writing custom Liquid.
5
+
6
+ Total modules: 31.
7
+
8
+ ## By category
9
+
10
+ - **catalog**: bundle-builder, quantity-breaks, subscriptions
11
+ - **conversion**: cart-cross-sell, cart-freeship-progress, cart-usps, exit-intent, gift-with-purchase, post-purchase-upsell, quick-view, risk-reversal, shipping-bar, sticky-atc
12
+ - **customer**: loyalty-tiers, wishlist
13
+ - **pdp**: comparison-table, delivery-estimate, inventory-urgency, pdp-ingredients, pdp-journal-link, pdp-trust-checks, recently-viewed
14
+ - **social-proof**: press-bar, product-badges, reviews, testimonials, trust-badges
15
+ - **storefront**: editorial-block, editorial-hero, faq, how-it-works
16
+
17
+ ## Modules
18
+
19
+ | Name | Category | Replaces | Files | Config keys | Variants | Admin steps |
20
+ |---|---|---|---|---|---|---|
21
+ | `bundle-builder` | catalog | BYOB-style bundle apps | sections:1 assets:1 | heading, sale_prefix, show_rating, rating_score, rating_count, show_trust_badges, trust_1, trust_2, trust_3, fomo_mode, fomo_cycle_days, fomo_stock_count | (none) | (none) |
22
+ | `cart-cross-sell` | conversion | Rebuy / OneClickUpsell pre-purchase upsell | snippets:1 | eyebrow, cta_label | (none) | (none) |
23
+ | `cart-freeship-progress` | conversion | Bold Free Shipping Manager and similar app features | snippets:1 | threshold_cents, away_text, unlocked_message | (none) | (none) |
24
+ | `cart-usps` | conversion | (native build) | snippets:1 | icon, usp_1, usp_2, usp_3 | (none) | (none) |
25
+ | `comparison-table` | pdp | app-driven comparison widgets | | eyebrow, heading, lede, background_color, text_color | feature-columns, feature-rows | (none) |
26
+ | `delivery-estimate` | pdp | (native build) | snippets:1 | (none) | (none) | (none) |
27
+ | `editorial-block` | storefront | (native build) | sections:1 | eyebrow, heading, lede, background_color, text_color | (none) | (none) |
28
+ | `editorial-hero` | storefront | (native build) | sections:1 | eyebrow, heading, subheading, video_url, poster_image, min_height, button_label_primary, button_link_primary, button_label_secondary, button_link_secondary | (none) | (none) |
29
+ | `exit-intent` | conversion | (native build) | sections:1 assets:1 | eyebrow, heading, lede | (none) | (none) |
30
+ | `faq` | storefront | (native build) | sections:1 | eyebrow, heading, background_color, text_color | (none) | (none) |
31
+ | `gift-with-purchase` | conversion | (native build) | snippets:1 assets:1 | threshold_cents, gift_handle, unlocked_message, locked_message_suffix | (none) | create-gift-product + create-gift-discount |
32
+ | `how-it-works` | storefront | (native build) | sections:1 | eyebrow, heading, lede, image, asset_filename, background_color, text_color, cta_label, cta_link | (none) | (none) |
33
+ | `inventory-urgency` | pdp | (native build) | snippets:1 | (none) | (none) | (none) |
34
+ | `loyalty-tiers` | customer | (native build) | sections:1 | tier_0_name, tier_1_name, tier_1_threshold, tier_1_perk_pct, tier_2_name, tier_2_threshold, tier_2_perk_pct | (none) | create-tier-tags-and-flows + create-tier-discounts + add-tier-card-to-account |
35
+ | `pdp-ingredients` | pdp | (native build) | sections:1 | eyebrow, heading, lede, background_color, text_color | (none) | (none) |
36
+ | `pdp-journal-link` | pdp | (native build) | sections:1 | eyebrow, article, background_color, text_color, fallback_title, fallback_body, fallback_link_label, fallback_link_url | (none) | (none) |
37
+ | `pdp-trust-checks` | pdp | (native build) | sections:1 | eyebrow, heading, lede, background_color, text_color, link_label, link_url | (none) | (none) |
38
+ | `post-purchase-upsell` | conversion | Reconvert / OneClickUpsell / AfterSell display layer | assets:2 | discount_code, discount_label, max_items, heading, eyebrow, lede, cross_category_logic | (none) | create-discount + paste-bootstrap |
39
+ | `press-bar` | social-proof | (native build) | sections:1 | eyebrow, background_color, text_color | (none) | (none) |
40
+ | `product-badges` | social-proof | (native build) | snippets:1 | label_best_seller, label_new, label_editor_pick | (none) | (none) |
41
+ | `quantity-breaks` | catalog | (native build) | snippets:1 | eyebrow, fineprint | (none) | define-breaks-metafield + set-product-breaks + create-discount-function |
42
+ | `quick-view` | conversion | (native build) | snippets:2 assets:1 | (none) | (none) | (none) |
43
+ | `recently-viewed` | pdp | (native build) | sections:1 assets:1 | eyebrow, heading, background_color | (none) | (none) |
44
+ | `reviews` | social-proof | (native build) | sections:1 | heading | (none) | (none) |
45
+ | `risk-reversal` | conversion | (native build) | sections:1 | icon, heading, body, link_label, link_url, background_color, text_color | (none) | (none) |
46
+ | `shipping-bar` | conversion | (native build) | sections:1 | threshold_cents, message_below, message_qualified, message_default, background_color, text_color | (none) | (none) |
47
+ | `sticky-atc` | conversion | Sticky Add To Cart Booster Pro and similar | sections:1 assets:1 | (none) | (none) | (none) |
48
+ | `subscriptions` | catalog | Recharge / Bold Subscriptions / Appstle for the storefront display layer; subscription management still uses Shopify's native customer account | snippets:1 | one_time_label, subscribe_label, fineprint | (none) | install-shopify-subscriptions + create-selling-plan-group + enable-customer-account-tab |
49
+ | `testimonials` | social-proof | (native build) | sections:1 | eyebrow, heading, background_color, text_color | (none) | (none) |
50
+ | `trust-badges` | social-proof | (native build) | sections:1 | background_color, text_color | (none) | (none) |
51
+ | `wishlist` | customer | Wishlist Plus, Wishlist King, Globo Wishlist | sections:1 snippets:1 assets:1 templates:1 | max_items | (none) | create-wishlist-page |
52
+
53
+ ## Descriptions
54
+
55
+ ### bundle-builder
56
+
57
+ - Category: catalog
58
+ - Replaces: BYOB-style bundle apps
59
+ - What: Build-your-own-bundle PDP section.
60
+ - Files: sections:1 assets:1
61
+ - Config: heading, sale_prefix, show_rating, rating_score, rating_count, show_trust_badges, trust_1, trust_2, trust_3, fomo_mode, fomo_cycle_days, fomo_stock_count
62
+
63
+ ### cart-cross-sell
64
+
65
+ - Category: conversion
66
+ - Replaces: Rebuy / OneClickUpsell pre-purchase upsell
67
+ - What: Cart drawer cross-sell card.
68
+ - Files: snippets:1
69
+ - Config: eyebrow, cta_label
70
+
71
+ ### cart-freeship-progress
72
+
73
+ - Category: conversion
74
+ - Replaces: Bold Free Shipping Manager and similar app features
75
+ - What: Cart drawer free-shipping progress bar.
76
+ - Files: snippets:1
77
+ - Config: threshold_cents, away_text, unlocked_message
78
+
79
+ ### cart-usps
80
+
81
+ - Category: conversion
82
+ - What: Three brand promises listed at the bottom of the cart drawer.
83
+ - Files: snippets:1
84
+ - Config: icon, usp_1, usp_2, usp_3
85
+
86
+ ### comparison-table
87
+
88
+ - Category: pdp
89
+ - Replaces: app-driven comparison widgets
90
+ - What: Comparison table section.
91
+ - Config: eyebrow, heading, lede, background_color, text_color
92
+ - Variants: feature-columns, feature-rows
93
+
94
+ ### delivery-estimate
95
+
96
+ - Category: pdp
97
+ - What: delivery-estimate module migrated from Lushi.
98
+ - Files: snippets:1
99
+
100
+ ### editorial-block
101
+
102
+ - Category: storefront
103
+ - What: Lushi editorial block module migrated from Lushi.
104
+ - Files: sections:1
105
+ - Config: eyebrow, heading, lede, background_color, text_color
106
+
107
+ ### editorial-hero
108
+
109
+ - Category: storefront
110
+ - What: Lushi video hero module migrated from Lushi.
111
+ - Files: sections:1
112
+ - Config: eyebrow, heading, subheading, video_url, poster_image, min_height, button_label_primary, button_link_primary, button_label_secondary, button_link_secondary
113
+
114
+ ### exit-intent
115
+
116
+ - Category: conversion
117
+ - What: Lushi exit popup module migrated from Lushi.
118
+ - Files: sections:1 assets:1
119
+ - Config: eyebrow, heading, lede
120
+
121
+ ### faq
122
+
123
+ - Category: storefront
124
+ - What: Lushi FAQ module migrated from Lushi.
125
+ - Files: sections:1
126
+ - Config: eyebrow, heading, background_color, text_color
127
+
128
+ ### gift-with-purchase
129
+
130
+ - Category: conversion
131
+ - What: Auto-add a gift product to the cart when total clears a threshold.
132
+ - Files: snippets:1 assets:1
133
+ - Config: threshold_cents, gift_handle, unlocked_message, locked_message_suffix
134
+ - Admin steps: create-gift-product + create-gift-discount
135
+
136
+ ### how-it-works
137
+
138
+ - Category: storefront
139
+ - What: Lushi how it works module migrated from Lushi.
140
+ - Files: sections:1
141
+ - Config: eyebrow, heading, lede, image, asset_filename, background_color, text_color, cta_label, cta_link
142
+
143
+ ### inventory-urgency
144
+
145
+ - Category: pdp
146
+ - What: inventory-urgency module migrated from Lushi.
147
+ - Files: snippets:1
148
+
149
+ ### loyalty-tiers
150
+
151
+ - Category: customer
152
+ - What: Lifetime-spend tier card on the customer account page.
153
+ - Files: sections:1
154
+ - Config: tier_0_name, tier_1_name, tier_1_threshold, tier_1_perk_pct, tier_2_name, tier_2_threshold, tier_2_perk_pct
155
+ - Admin steps: create-tier-tags-and-flows + create-tier-discounts + add-tier-card-to-account
156
+
157
+ ### pdp-ingredients
158
+
159
+ - Category: pdp
160
+ - What: Lushi ingredients module migrated from Lushi.
161
+ - Files: sections:1
162
+ - Config: eyebrow, heading, lede, background_color, text_color
163
+
164
+ ### pdp-journal-link
165
+
166
+ - Category: pdp
167
+ - What: Lushi PDP journal link module migrated from Lushi.
168
+ - Files: sections:1
169
+ - Config: eyebrow, article, background_color, text_color, fallback_title, fallback_body, fallback_link_label, fallback_link_url
170
+
171
+ ### pdp-trust-checks
172
+
173
+ - Category: pdp
174
+ - What: Lushi PDP trust module migrated from Lushi.
175
+ - Files: sections:1
176
+ - Config: eyebrow, heading, lede, background_color, text_color, link_label, link_url
177
+
178
+ ### post-purchase-upsell
179
+
180
+ - Category: conversion
181
+ - Replaces: Reconvert / OneClickUpsell / AfterSell display layer
182
+ - What: Order status page upsell with auto-applied discount on click-through.
183
+ - Files: assets:2
184
+ - Config: discount_code, discount_label, max_items, heading, eyebrow, lede, cross_category_logic
185
+ - Admin steps: create-discount + paste-bootstrap
186
+
187
+ ### press-bar
188
+
189
+ - Category: social-proof
190
+ - What: Lushi press bar module migrated from Lushi.
191
+ - Files: sections:1
192
+ - Config: eyebrow, background_color, text_color
193
+
194
+ ### product-badges
195
+
196
+ - Category: social-proof
197
+ - What: Tag-driven badges on product cards (Best seller, New, Editor's pick).
198
+ - Files: snippets:1
199
+ - Config: label_best_seller, label_new, label_editor_pick
200
+
201
+ ### quantity-breaks
202
+
203
+ - Category: catalog
204
+ - What: Tiered quantity-discount display on the PDP.
205
+ - Files: snippets:1
206
+ - Config: eyebrow, fineprint
207
+ - Admin steps: define-breaks-metafield + set-product-breaks + create-discount-function
208
+
209
+ ### quick-view
210
+
211
+ - Category: conversion
212
+ - What: Modal product quick-view triggered from product cards.
213
+ - Files: snippets:2 assets:1
214
+
215
+ ### recently-viewed
216
+
217
+ - Category: pdp
218
+ - What: Lushi recently viewed module migrated from Lushi.
219
+ - Files: sections:1 assets:1
220
+ - Config: eyebrow, heading, background_color
221
+
222
+ ### reviews
223
+
224
+ - Category: social-proof
225
+ - What: Lushi PDP reviews module migrated from Lushi.
226
+ - Files: sections:1
227
+ - Config: heading
228
+
229
+ ### risk-reversal
230
+
231
+ - Category: conversion
232
+ - What: Lushi risk reversal module migrated from Lushi.
233
+ - Files: sections:1
234
+ - Config: icon, heading, body, link_label, link_url, background_color, text_color
235
+
236
+ ### shipping-bar
237
+
238
+ - Category: conversion
239
+ - What: Lushi shipping bar module migrated from Lushi.
240
+ - Files: sections:1
241
+ - Config: threshold_cents, message_below, message_qualified, message_default, background_color, text_color
242
+
243
+ ### sticky-atc
244
+
245
+ - Category: conversion
246
+ - Replaces: Sticky Add To Cart Booster Pro and similar
247
+ - What: Sticky add-to-cart bar that slides up from the bottom on PDP after the buy area scrolls out of view.
248
+ - Files: sections:1 assets:1
249
+
250
+ ### subscriptions
251
+
252
+ - Category: catalog
253
+ - Replaces: Recharge / Bold Subscriptions / Appstle for the storefront display layer; subscription management still uses Shopify's native customer account
254
+ - What: Brand-styled subscribe-and-save picker that wraps Shopify's native Selling Plan API.
255
+ - Files: snippets:1
256
+ - Config: one_time_label, subscribe_label, fineprint
257
+ - Admin steps: install-shopify-subscriptions + create-selling-plan-group + enable-customer-account-tab
258
+
259
+ ### testimonials
260
+
261
+ - Category: social-proof
262
+ - What: Lushi testimonials module migrated from Lushi.
263
+ - Files: sections:1
264
+ - Config: eyebrow, heading, background_color, text_color
265
+
266
+ ### trust-badges
267
+
268
+ - Category: social-proof
269
+ - What: Lushi trust badges module migrated from Lushi.
270
+ - Files: sections:1
271
+ - Config: background_color, text_color
272
+
273
+ ### wishlist
274
+
275
+ - Category: customer
276
+ - Replaces: Wishlist Plus, Wishlist King, Globo Wishlist
277
+ - What: Native wishlist using localStorage.
278
+ - Files: sections:1 snippets:1 assets:1 templates:1
279
+ - Config: max_items
280
+ - Admin steps: create-wishlist-page
281
+
282
+ ## How a skill should use this
283
+
284
+ 1. Grep this file for keywords from the user request (e.g. "free shipping", "exit popup", "bundle").
285
+ 2. If a row matches, run `node ~/Documents/Code/runwell-shopify-toolkit/bin/runwell-shopify add <module-name>` from the client theme directory.
286
+ 3. Run `runwell-shopify sync` to copy files into the theme.
287
+ 4. If the module has admin steps, hand them to shopify-admin-browser to drive merchant clicks.
288
+ 5. Push: `shopify theme push` from the client theme directory.
@@ -1,50 +1,39 @@
1
1
  {
2
2
  "name": "comparison-table",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "category": "pdp",
5
- "description": "Lushi comparison table module migrated from Lushi.",
6
- "files": {
7
- "sections": [
8
- "sections/runwell-comparison-table.liquid"
9
- ]
10
- },
5
+ "description": "Comparison table section. Two visual treatments: feature-columns (default) renders columns as Theme Editor blocks with a single textarea of pipe-delimited rows; feature-rows renders one Theme Editor block per comparison row in a 'us vs them' shape with two fixed brand columns. Replaces app-driven comparison widgets.",
6
+ "default_variant": "feature-columns",
11
7
  "config": {
12
8
  "schema": {
13
- "eyebrow": {
14
- "type": "string",
15
- "default": "Compared",
16
- "label": "Eyebrow"
17
- },
18
- "heading": {
19
- "type": "string",
20
- "default": "How it stacks up.",
21
- "label": "Heading"
22
- },
23
- "lede": {
24
- "type": "string",
25
- "default": "We do not stock everything; here is what makes this one earn the spot.",
26
- "label": "Lede"
27
- },
28
- "row_label_header": {
29
- "type": "string",
30
- "default": "What you get",
31
- "label": "Row label header"
32
- },
33
- "rows": {
34
- "type": "string",
35
- "default": "Bioavailable form|yes|no|no\nDose matches research|yes|no|no\nThird-party tested|yes|yes|no\nClean ingredient deck|yes|no|no\nTransparent sourcing|yes|no|yes",
36
- "label": "Rows"
9
+ "eyebrow": { "type": "string", "default": "Compared", "label": "Eyebrow" },
10
+ "heading": { "type": "string", "default": "How it stacks up.", "label": "Heading" },
11
+ "lede": { "type": "string", "default": "We do not stock everything; here is what makes this one earn the spot.", "label": "Lede" },
12
+ "background_color": { "type": "string", "default": "#F5F0EE", "label": "Background color" },
13
+ "text_color": { "type": "string", "default": "#0B3D38", "label": "Text color" }
14
+ }
15
+ },
16
+ "variants": {
17
+ "feature-columns": {
18
+ "description": "Theme Editor blocks are columns (e.g. Lushi pick / Drugstore / Hype brand). Rows come from a textarea (pipe-delimited). Best when comparing many products across the same attributes.",
19
+ "files": {
20
+ "sections": ["variants/feature-columns/sections/runwell-comparison-table.liquid"]
37
21
  },
38
- "background_color": {
39
- "type": "string",
40
- "default": "#F5F0EE",
41
- "label": "Background color"
22
+ "config_extra": {
23
+ "row_label_header": { "type": "string", "default": "What you get", "label": "Row label header" },
24
+ "rows": { "type": "string", "default": "Bioavailable form|yes|no|no\nDose matches research|yes|no|no\nThird-party tested|yes|yes|no\nClean ingredient deck|yes|no|no\nTransparent sourcing|yes|no|yes", "label": "Rows (pipe-delimited)" }
25
+ }
26
+ },
27
+ "feature-rows": {
28
+ "description": "Theme Editor blocks are rows (one per feature). Two fixed columns: your brand vs one competitor. Best for 'us vs them' positioning on PDP or landing pages.",
29
+ "files": {
30
+ "sections": ["variants/feature-rows/sections/runwell-comparison-table.liquid"]
42
31
  },
43
- "text_color": {
44
- "type": "string",
45
- "default": "#0B3D38",
46
- "label": "Text color"
32
+ "config_extra": {
33
+ "feature_label": { "type": "string", "default": "Feature", "label": "Feature column label" },
34
+ "brand_name": { "type": "string", "default": "Us", "label": "Your brand name" },
35
+ "competitor_name": { "type": "string", "default": "Traditional", "label": "Competitor name" }
47
36
  }
48
37
  }
49
38
  }
50
- }
39
+ }
@@ -0,0 +1,143 @@
1
+ {%- comment -%}
2
+ Comparison table, feature-rows variant. Each Theme Editor block is one
3
+ comparison row (feature + ours + theirs). Use when the brand wants a
4
+ "us vs them" pattern with a small fixed competitor set.
5
+ Original shape ported from Lusha (guabrasha-store/sections/comparison-table.liquid).
6
+ {%- endcomment -%}
7
+
8
+ <section
9
+ class="runwell-comparison runwell-comparison--feature-rows"
10
+ style="background: {{ section.settings.background_color }}; color: {{ section.settings.text_color }};"
11
+ >
12
+ <div style="max-width: 1200px; margin: 0 auto; padding: clamp(3rem, 6vw, 5rem) 6vw;">
13
+ {%- if section.settings.eyebrow != blank -%}
14
+ <div style="font-family: var(--font-body-family); font-size: 0.78rem; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase; opacity: 0.65; margin-bottom: 0.6rem; text-align: center;">
15
+ {{ section.settings.eyebrow }}
16
+ </div>
17
+ {%- endif -%}
18
+
19
+ {%- if section.settings.heading != blank -%}
20
+ <h2 style="font-family: var(--font-heading-family); font-style: italic; font-weight: 400; font-size: clamp(1.8rem, 3.2vw, 2.6rem); line-height: 1.1; margin: 0 auto 1rem; max-width: 24ch; text-align: center;">
21
+ {{ section.settings.heading }}
22
+ </h2>
23
+ {%- endif -%}
24
+
25
+ {%- if section.settings.lede != blank -%}
26
+ <p style="font-family: var(--font-body-family); font-size: 1rem; line-height: 1.65; max-width: 60ch; opacity: 0.85; margin: 0 auto 2rem; text-align: center;">
27
+ {{ section.settings.lede }}
28
+ </p>
29
+ {%- endif -%}
30
+
31
+ {%- if section.blocks.size > 0 -%}
32
+ <div style="overflow-x: auto;">
33
+ <table style="width: 100%; border-collapse: collapse; min-width: 520px; font-family: var(--font-body-family); font-size: 0.95rem;">
34
+ <thead>
35
+ <tr>
36
+ <th style="text-align: left; padding: 0.9rem 1rem 0.9rem 0; font-family: var(--font-body-family); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; opacity: 0.6; vertical-align: bottom; border-bottom: 2px solid currentColor;">
37
+ {{ section.settings.feature_label }}
38
+ </th>
39
+ <th style="text-align: center; padding: 0.9rem 1rem; vertical-align: bottom; border-bottom: 2px solid currentColor;">
40
+ <div style="font-family: var(--font-heading-family); font-style: italic; font-weight: 400; font-size: 1.15rem; line-height: 1.2;">
41
+ {{ section.settings.brand_name }}
42
+ </div>
43
+ </th>
44
+ <th style="text-align: center; padding: 0.9rem 1rem; vertical-align: bottom; border-bottom: 2px solid currentColor;">
45
+ <div style="font-family: var(--font-heading-family); font-style: italic; font-weight: 400; font-size: 1.15rem; line-height: 1.2; opacity: 0.7;">
46
+ {{ section.settings.competitor_name }}
47
+ </div>
48
+ </th>
49
+ </tr>
50
+ </thead>
51
+ <tbody>
52
+ {%- for block in section.blocks -%}
53
+ <tr {{ block.shopify_attributes }}>
54
+ <td style="padding: 0.9rem 1rem 0.9rem 0; vertical-align: top; border-bottom: 1px solid rgba(0,0,0,0.08); font-weight: 600;">
55
+ {{ block.settings.feature }}
56
+ </td>
57
+ <td style="padding: 0.9rem 1rem; vertical-align: top; border-bottom: 1px solid rgba(0,0,0,0.08); text-align: center;">
58
+ {%- if block.settings.ours == 'yes' -%}
59
+ <span style="display: inline-block; min-width: 1.6rem; font-weight: 700; color: var(--runwell-rain-forrest, currentColor);">&check;</span>
60
+ {%- elsif block.settings.ours == 'no' -%}
61
+ <span style="display: inline-block; min-width: 1.6rem; opacity: 0.4;">&times;</span>
62
+ {%- else -%}
63
+ <span style="opacity: 0.92;">{{ block.settings.ours_text }}</span>
64
+ {%- endif -%}
65
+ </td>
66
+ <td style="padding: 0.9rem 1rem; vertical-align: top; border-bottom: 1px solid rgba(0,0,0,0.08); text-align: center;">
67
+ {%- if block.settings.theirs == 'yes' -%}
68
+ <span style="display: inline-block; min-width: 1.6rem; font-weight: 700; opacity: 0.6;">&check;</span>
69
+ {%- elsif block.settings.theirs == 'no' -%}
70
+ <span style="display: inline-block; min-width: 1.6rem; opacity: 0.4;">&times;</span>
71
+ {%- else -%}
72
+ <span style="opacity: 0.7;">{{ block.settings.theirs_text }}</span>
73
+ {%- endif -%}
74
+ </td>
75
+ </tr>
76
+ {%- endfor -%}
77
+ </tbody>
78
+ </table>
79
+ </div>
80
+ {%- endif -%}
81
+ </div>
82
+ </section>
83
+
84
+ {% schema %}
85
+ {
86
+ "name": "Runwell comparison (rows)",
87
+ "tag": "section",
88
+ "class": "section-runwell-comparison-rows",
89
+ "settings": [
90
+ { "type": "text", "id": "eyebrow", "label": "Eyebrow", "default": "Compared" },
91
+ { "type": "text", "id": "heading", "label": "Heading", "default": "How it stacks up." },
92
+ { "type": "textarea", "id": "lede", "label": "Lede" },
93
+ { "type": "text", "id": "feature_label", "label": "Feature column label", "default": "Feature" },
94
+ { "type": "text", "id": "brand_name", "label": "Your brand name", "default": "Us" },
95
+ { "type": "text", "id": "competitor_name", "label": "Competitor name", "default": "Traditional" },
96
+ { "type": "color", "id": "background_color", "label": "Background", "default": "#F5F0EE" },
97
+ { "type": "color", "id": "text_color", "label": "Text", "default": "#0B3D38" }
98
+ ],
99
+ "blocks": [
100
+ {
101
+ "type": "row",
102
+ "name": "Comparison row",
103
+ "settings": [
104
+ { "type": "text", "id": "feature", "label": "Feature" },
105
+ {
106
+ "type": "select",
107
+ "id": "ours",
108
+ "label": "Your brand",
109
+ "options": [
110
+ { "value": "yes", "label": "Yes" },
111
+ { "value": "no", "label": "No" },
112
+ { "value": "custom", "label": "Custom text" }
113
+ ],
114
+ "default": "yes"
115
+ },
116
+ { "type": "text", "id": "ours_text", "label": "Custom text (yours)" },
117
+ {
118
+ "type": "select",
119
+ "id": "theirs",
120
+ "label": "Competitor",
121
+ "options": [
122
+ { "value": "yes", "label": "Yes" },
123
+ { "value": "no", "label": "No" },
124
+ { "value": "custom", "label": "Custom text" }
125
+ ],
126
+ "default": "no"
127
+ },
128
+ { "type": "text", "id": "theirs_text", "label": "Custom text (theirs)" }
129
+ ]
130
+ }
131
+ ],
132
+ "presets": [
133
+ {
134
+ "name": "Runwell comparison (rows)",
135
+ "blocks": [
136
+ { "type": "row", "settings": { "feature": "Bioavailable form", "ours": "yes", "theirs": "no" } },
137
+ { "type": "row", "settings": { "feature": "Third-party tested", "ours": "yes", "theirs": "no" } },
138
+ { "type": "row", "settings": { "feature": "Transparent sourcing", "ours": "yes", "theirs": "no" } }
139
+ ]
140
+ }
141
+ ]
142
+ }
143
+ {% endschema %}
@@ -0,0 +1,117 @@
1
+ /* Runwell sticky add-to-cart bar.
2
+ Slides up from bottom on scroll past the buy area; hides when the
3
+ main buy button returns to view (driven by IntersectionObserver in
4
+ the section template).
5
+ */
6
+
7
+ .runwell-sticky-atc {
8
+ position: fixed;
9
+ inset: auto 0 0 0;
10
+ z-index: 60;
11
+ background: #FFFFFF;
12
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
13
+ box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
14
+ transform: translateY(100%);
15
+ transition: transform 0.25s ease, opacity 0.25s ease;
16
+ opacity: 0;
17
+ visibility: hidden;
18
+ }
19
+
20
+ .runwell-sticky-atc[aria-hidden='false'] {
21
+ transform: translateY(0);
22
+ opacity: 1;
23
+ visibility: visible;
24
+ }
25
+
26
+ .runwell-sticky-atc__inner {
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: space-between;
30
+ gap: 1rem;
31
+ max-width: 1200px;
32
+ margin: 0 auto;
33
+ padding: 0.75rem 1.25rem;
34
+ }
35
+
36
+ .runwell-sticky-atc__product {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 0.75rem;
40
+ min-width: 0;
41
+ }
42
+
43
+ .runwell-sticky-atc__thumb {
44
+ width: 40px;
45
+ height: 40px;
46
+ border-radius: 4px;
47
+ object-fit: cover;
48
+ flex-shrink: 0;
49
+ }
50
+
51
+ .runwell-sticky-atc__meta {
52
+ display: flex;
53
+ flex-direction: column;
54
+ min-width: 0;
55
+ }
56
+
57
+ .runwell-sticky-atc__title {
58
+ font-family: var(--font-heading-family, serif);
59
+ font-style: italic;
60
+ font-size: 0.95rem;
61
+ font-weight: 400;
62
+ line-height: 1.2;
63
+ color: var(--runwell-primary, #1A1A1A);
64
+ white-space: nowrap;
65
+ overflow: hidden;
66
+ text-overflow: ellipsis;
67
+ }
68
+
69
+ .runwell-sticky-atc__price {
70
+ font-family: var(--font-body-family, sans-serif);
71
+ font-size: 0.85rem;
72
+ font-weight: 500;
73
+ color: var(--runwell-primary, #1A1A1A);
74
+ opacity: 0.8;
75
+ }
76
+
77
+ .runwell-sticky-atc__form {
78
+ flex-shrink: 0;
79
+ margin: 0;
80
+ }
81
+
82
+ .runwell-sticky-atc__cta {
83
+ background: var(--runwell-primary, #1A1A1A);
84
+ color: #FFFFFF;
85
+ border: none;
86
+ border-radius: 0;
87
+ padding: 0.75rem 1.5rem;
88
+ font-family: var(--font-body-family, sans-serif);
89
+ font-size: 0.9rem;
90
+ font-weight: 600;
91
+ letter-spacing: 0.04em;
92
+ text-transform: uppercase;
93
+ cursor: pointer;
94
+ transition: opacity 0.15s ease;
95
+ }
96
+
97
+ .runwell-sticky-atc__cta:hover {
98
+ opacity: 0.85;
99
+ }
100
+
101
+ .runwell-sticky-atc__cta:disabled {
102
+ opacity: 0.5;
103
+ cursor: not-allowed;
104
+ }
105
+
106
+ @media (max-width: 749px) {
107
+ .runwell-sticky-atc__inner {
108
+ padding: 0.6rem 0.9rem;
109
+ }
110
+ .runwell-sticky-atc__title {
111
+ font-size: 0.85rem;
112
+ }
113
+ .runwell-sticky-atc__cta {
114
+ padding: 0.6rem 1.1rem;
115
+ font-size: 0.8rem;
116
+ }
117
+ }
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "sticky-atc",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "category": "conversion",
5
- "description": "Lushi sticky ATC module migrated from Lushi.",
5
+ "description": "Sticky add-to-cart bar that slides up from the bottom on PDP after the buy area scrolls out of view. Native, no app. Replaces Sticky Add To Cart Booster Pro and similar.",
6
6
  "files": {
7
7
  "sections": [
8
8
  "sections/runwell-pdp-sticky.liquid"
9
+ ],
10
+ "assets": [
11
+ "assets/runwell-sticky-atc.css"
9
12
  ]
10
13
  },
11
14
  "config": {
12
15
  "schema": {}
13
16
  }
14
- }
17
+ }
@@ -5,6 +5,7 @@
5
5
  {%- endcomment -%}
6
6
 
7
7
  {%- if template == 'product' and product != blank -%}
8
+ {{ 'runwell-sticky-atc.css' | asset_url | stylesheet_tag }}
8
9
  <div class="runwell-sticky-atc" data-runwell-sticky aria-hidden="true">
9
10
  <div class="runwell-sticky-atc__inner">
10
11
  <div class="runwell-sticky-atc__product">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runwell/shopify-toolkit",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Reusable Shopify theme modules from Runwell. Replaces typically app-driven features (reviews, wishlist, urgency, FAQ, post-purchase upsell, exit popups, free-ship progress, sticky ATC, testimonials, badges, bundles) with native Liquid + JS + CSS that ship across multiple client themes via a config-driven sync CLI.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",