@runwell/shopify-toolkit 0.21.0 → 0.24.0
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/init.js +13 -2
- package/modules/INDEX.md +3 -3
- package/modules/runwell-bundle-system/admin-metafields.json +15 -3
- package/modules/runwell-bundle-system/assets/runwell-bundle-quantity-builder.css +383 -0
- package/modules/runwell-bundle-system/assets/runwell-bundle-system.css +246 -0
- package/modules/runwell-bundle-system/assets/runwell-bundle-system.js +359 -0
- package/modules/runwell-bundle-system/module.json +18 -4
- package/modules/runwell-bundle-system/qa/v1.5-mobile-qa-checklist.md +103 -0
- package/modules/runwell-bundle-system/sections/runwell-bundle-cart-xsell.liquid +2 -1
- package/modules/runwell-bundle-system/sections/runwell-bundle-collection.liquid +20 -8
- package/modules/runwell-bundle-system/sections/runwell-bundle-pdp-banner.liquid +2 -1
- package/modules/runwell-bundle-system/sections/runwell-bundle-pdp-pairs-with.liquid +2 -1
- package/modules/runwell-bundle-system/sections/runwell-bundle-pdp.liquid +15 -1
- package/modules/runwell-bundle-system/sections/runwell-bundle-quantity-builder.liquid +318 -0
- package/modules/runwell-bundle-system/snippets/runwell-bundle-byob-picker-accordion.liquid +84 -0
- package/modules/runwell-bundle-system/snippets/runwell-bundle-byob-picker-grid.liquid +72 -0
- package/modules/runwell-bundle-system/snippets/runwell-bundle-byob-picker-radio.liquid +77 -0
- package/modules/runwell-bundle-system/snippets/runwell-bundle-byob-picker.liquid +71 -0
- package/modules/runwell-bundle-system/snippets/runwell-bundle-byob-summary.liquid +39 -0
- package/modules/runwell-bundle-system/snippets/runwell-bundle-card.liquid +15 -2
- package/modules/runwell-bundle-system/snippets/runwell-bundle-cart-xsell.liquid +85 -0
- package/modules/runwell-bundle-system/snippets/runwell-bundle-data.liquid +8 -0
- package/modules/scratch-popup/README.md +88 -0
- package/modules/scratch-popup/SPEC.md +120 -0
- package/modules/scratch-popup/assets/runwell-scratch-popup.css +315 -0
- package/modules/scratch-popup/assets/runwell-scratch-popup.js +367 -0
- package/modules/scratch-popup/module.json +128 -0
- package/modules/scratch-popup/sections/runwell-scratch-popup.liquid +184 -0
- package/package.json +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "scratch-popup",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"status": "ready",
|
|
5
|
+
"category": "conversion",
|
|
6
|
+
"description": "Mystery-discount scratch popup. Email capture + foil-card scratch-to-reveal that unlocks a unique discount code per visitor. KH-validated alternative to spin-the-wheel.",
|
|
7
|
+
"files": {
|
|
8
|
+
"sections": [
|
|
9
|
+
"sections/runwell-scratch-popup.liquid"
|
|
10
|
+
],
|
|
11
|
+
"assets": [
|
|
12
|
+
"assets/runwell-scratch-popup.js",
|
|
13
|
+
"assets/runwell-scratch-popup.css"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"depends_on": {
|
|
17
|
+
"scopes": [
|
|
18
|
+
"write_discounts",
|
|
19
|
+
"read_discounts",
|
|
20
|
+
"write_customers",
|
|
21
|
+
"read_customers"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"config": {
|
|
25
|
+
"schema": {
|
|
26
|
+
"enabled": {
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"default": false,
|
|
29
|
+
"label": "Enable scratch popup"
|
|
30
|
+
},
|
|
31
|
+
"triggers.first_visit_delay_sec": {
|
|
32
|
+
"type": "number",
|
|
33
|
+
"default": 8,
|
|
34
|
+
"label": "First-visit delay (seconds)"
|
|
35
|
+
},
|
|
36
|
+
"triggers.exit_intent": {
|
|
37
|
+
"type": "boolean",
|
|
38
|
+
"default": true,
|
|
39
|
+
"label": "Trigger on exit intent"
|
|
40
|
+
},
|
|
41
|
+
"triggers.manual_cta_selector": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"default": null,
|
|
44
|
+
"label": "Manual CTA selector (optional)"
|
|
45
|
+
},
|
|
46
|
+
"email_gate": {
|
|
47
|
+
"type": "boolean",
|
|
48
|
+
"default": true,
|
|
49
|
+
"label": "Require email before scratch"
|
|
50
|
+
},
|
|
51
|
+
"scratch_threshold_pct": {
|
|
52
|
+
"type": "number",
|
|
53
|
+
"default": 60,
|
|
54
|
+
"label": "Foil-erase percentage before auto-reveal"
|
|
55
|
+
},
|
|
56
|
+
"tiers": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"default": [
|
|
59
|
+
{ "code_prefix": "MYSTERY10", "percentage": 10, "probability": 0.60 },
|
|
60
|
+
{ "code_prefix": "MYSTERY15", "percentage": 15, "probability": 0.30 },
|
|
61
|
+
{ "code_prefix": "MYSTERY20", "percentage": 20, "probability": 0.10 }
|
|
62
|
+
],
|
|
63
|
+
"label": "Discount tiers + probability"
|
|
64
|
+
},
|
|
65
|
+
"expiry_days": {
|
|
66
|
+
"type": "number",
|
|
67
|
+
"default": 30,
|
|
68
|
+
"label": "Discount-code expiry"
|
|
69
|
+
},
|
|
70
|
+
"copy.heading": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"default": "Scratch to reveal your discount",
|
|
73
|
+
"label": "Modal heading"
|
|
74
|
+
},
|
|
75
|
+
"copy.subheading": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"default": "One scratch, one code, just for you.",
|
|
78
|
+
"label": "Modal subheading"
|
|
79
|
+
},
|
|
80
|
+
"copy.email_placeholder": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"default": "you@email.com",
|
|
83
|
+
"label": "Email field placeholder"
|
|
84
|
+
},
|
|
85
|
+
"copy.email_cta": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"default": "Continue",
|
|
88
|
+
"label": "Email CTA"
|
|
89
|
+
},
|
|
90
|
+
"copy.reveal_cta": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"default": "Shop now",
|
|
93
|
+
"label": "Post-reveal CTA"
|
|
94
|
+
},
|
|
95
|
+
"copy.post_reveal_subheading": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"default": "Code copied. Also sent to your inbox.",
|
|
98
|
+
"label": "Post-reveal subheading"
|
|
99
|
+
},
|
|
100
|
+
"design.foil_color": {
|
|
101
|
+
"type": "color",
|
|
102
|
+
"default": "#C8B89A",
|
|
103
|
+
"label": "Foil color"
|
|
104
|
+
},
|
|
105
|
+
"design.foil_texture_url": {
|
|
106
|
+
"type": "string",
|
|
107
|
+
"default": null,
|
|
108
|
+
"label": "Optional foil texture image URL"
|
|
109
|
+
},
|
|
110
|
+
"design.accent_color": {
|
|
111
|
+
"type": "color",
|
|
112
|
+
"default": "#5B7A3E",
|
|
113
|
+
"label": "Accent color"
|
|
114
|
+
},
|
|
115
|
+
"design.card_radius_px": {
|
|
116
|
+
"type": "number",
|
|
117
|
+
"default": 16,
|
|
118
|
+
"label": "Card corner radius (px)"
|
|
119
|
+
},
|
|
120
|
+
"klaviyo_list_id": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"default": null,
|
|
123
|
+
"label": "Klaviyo list ID (optional)"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"spec": "SPEC.md"
|
|
128
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Runwell scratch popup. Mystery-discount email capture with foil
|
|
3
|
+
scratch-to-reveal mechanic. KH-validated alternative to spin-the-wheel.
|
|
4
|
+
|
|
5
|
+
Discount codes are pre-created on the store via
|
|
6
|
+
`infrastructure/scripts/shopify/shopify-discount-create.sh`. Tier codes here
|
|
7
|
+
must match the codes created on the store side. Default codes:
|
|
8
|
+
MYSTERY10 (10% off, once-per-customer)
|
|
9
|
+
MYSTERY15 (15% off, once-per-customer)
|
|
10
|
+
MYSTERY20 (20% off, once-per-customer)
|
|
11
|
+
{%- endcomment -%}
|
|
12
|
+
{{ 'runwell-scratch-popup.css' | asset_url | stylesheet_tag }}
|
|
13
|
+
|
|
14
|
+
<div
|
|
15
|
+
class="runwell-scratch"
|
|
16
|
+
data-runwell-scratch
|
|
17
|
+
data-tier1-code="{{ section.settings.tier1_code }}"
|
|
18
|
+
data-tier1-pct="{{ section.settings.tier1_pct }}"
|
|
19
|
+
data-tier1-prob="{{ section.settings.tier1_prob }}"
|
|
20
|
+
data-tier2-code="{{ section.settings.tier2_code }}"
|
|
21
|
+
data-tier2-pct="{{ section.settings.tier2_pct }}"
|
|
22
|
+
data-tier2-prob="{{ section.settings.tier2_prob }}"
|
|
23
|
+
data-tier3-code="{{ section.settings.tier3_code }}"
|
|
24
|
+
data-tier3-pct="{{ section.settings.tier3_pct }}"
|
|
25
|
+
data-tier3-prob="{{ section.settings.tier3_prob }}"
|
|
26
|
+
data-trigger-delay-sec="{{ section.settings.trigger_delay_sec }}"
|
|
27
|
+
data-exit-intent="{{ section.settings.exit_intent }}"
|
|
28
|
+
data-scratch-threshold-pct="{{ section.settings.scratch_threshold_pct }}"
|
|
29
|
+
style="--runwell-scratch-foil: {{ section.settings.foil_color }}; --runwell-scratch-accent: {{ section.settings.accent_color }};"
|
|
30
|
+
aria-hidden="true"
|
|
31
|
+
role="dialog"
|
|
32
|
+
aria-labelledby="runwell-scratch-title"
|
|
33
|
+
>
|
|
34
|
+
<div class="runwell-scratch__backdrop" data-runwell-scratch-close></div>
|
|
35
|
+
|
|
36
|
+
<div class="runwell-scratch__panel">
|
|
37
|
+
<button class="runwell-scratch__close" type="button" data-runwell-scratch-close aria-label="Close">×</button>
|
|
38
|
+
|
|
39
|
+
{%- if section.settings.eyebrow != blank -%}
|
|
40
|
+
<p class="runwell-scratch__eyebrow">{{ section.settings.eyebrow }}</p>
|
|
41
|
+
{%- endif -%}
|
|
42
|
+
|
|
43
|
+
<h2 id="runwell-scratch-title" class="runwell-scratch__heading">{{ section.settings.heading }}</h2>
|
|
44
|
+
|
|
45
|
+
{%- if section.settings.subheading != blank -%}
|
|
46
|
+
<p class="runwell-scratch__subheading">{{ section.settings.subheading }}</p>
|
|
47
|
+
{%- endif -%}
|
|
48
|
+
|
|
49
|
+
{%- comment -%}
|
|
50
|
+
Flow (corrected 2026-05-11 after live-vendor research):
|
|
51
|
+
1. SCRATCH - foil card visible immediately; visitor scratches it
|
|
52
|
+
2. EMAIL - card stays as trophy; "Won X% off! Email me my code" form appears
|
|
53
|
+
3. REVEALED - code shown with Copy + Shop now (auto-applies via cart URL)
|
|
54
|
+
|
|
55
|
+
The scratch is the dopamine hook that earns the email. Showing the email
|
|
56
|
+
field first kills the mechanic. Pattern matches Knowband, Poptin,
|
|
57
|
+
AppSetup, CI Scratch, Triggerbee, Winify, Superpop.
|
|
58
|
+
{%- endcomment -%}
|
|
59
|
+
|
|
60
|
+
{%- comment -%} STEP 1 (default visible): scratch surface {%- endcomment -%}
|
|
61
|
+
<div class="runwell-scratch__step runwell-scratch__step--scratch" data-runwell-scratch-step="scratch">
|
|
62
|
+
<div class="runwell-scratch__card">
|
|
63
|
+
<div class="runwell-scratch__reveal" data-runwell-scratch-reveal>
|
|
64
|
+
<p class="runwell-scratch__reveal-eyebrow">You won</p>
|
|
65
|
+
<p class="runwell-scratch__reveal-pct" data-runwell-scratch-pct>--%</p>
|
|
66
|
+
<p class="runwell-scratch__reveal-fineprint">off your order</p>
|
|
67
|
+
</div>
|
|
68
|
+
<canvas class="runwell-scratch__canvas" data-runwell-scratch-canvas></canvas>
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
class="runwell-scratch__a11y-reveal"
|
|
72
|
+
data-runwell-scratch-a11y-reveal
|
|
73
|
+
aria-label="Reveal discount (accessibility)"
|
|
74
|
+
>
|
|
75
|
+
Or tap to reveal
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
<p class="runwell-scratch__scratch-prompt">{{ section.settings.scratch_prompt }}</p>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{%- comment -%} STEP 2: email capture (after scratch reveals the discount amount) {%- endcomment -%}
|
|
82
|
+
<div class="runwell-scratch__step runwell-scratch__step--email" data-runwell-scratch-step="email" hidden>
|
|
83
|
+
<div class="runwell-scratch__card runwell-scratch__card--trophy" data-runwell-scratch-trophy>
|
|
84
|
+
<p class="runwell-scratch__reveal-eyebrow">You won</p>
|
|
85
|
+
<p class="runwell-scratch__reveal-pct" data-runwell-scratch-pct-trophy>--%</p>
|
|
86
|
+
<p class="runwell-scratch__reveal-fineprint">off your order</p>
|
|
87
|
+
</div>
|
|
88
|
+
<p class="runwell-scratch__email-prompt">{{ section.settings.email_prompt }}</p>
|
|
89
|
+
<div class="runwell-scratch__form" data-runwell-scratch-form>
|
|
90
|
+
<input type="hidden" data-runwell-scratch-tags value="newsletter, scratch-popup">
|
|
91
|
+
<label class="visually-hidden" for="runwell-scratch-email">Email</label>
|
|
92
|
+
<input
|
|
93
|
+
id="runwell-scratch-email"
|
|
94
|
+
type="email"
|
|
95
|
+
name="contact[email]"
|
|
96
|
+
placeholder="{{ section.settings.email_placeholder }}"
|
|
97
|
+
required
|
|
98
|
+
data-runwell-scratch-email
|
|
99
|
+
>
|
|
100
|
+
<button
|
|
101
|
+
type="button"
|
|
102
|
+
class="runwell-scratch__cta runwell-scratch__cta--email"
|
|
103
|
+
data-runwell-scratch-email-submit
|
|
104
|
+
>
|
|
105
|
+
{{ section.settings.email_cta_text }}
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
<p class="runwell-scratch__fineprint">No spam. One use per customer.</p>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
{%- comment -%} STEP 3: code revealed + auto-apply CTA {%- endcomment -%}
|
|
112
|
+
<div class="runwell-scratch__step runwell-scratch__step--revealed" data-runwell-scratch-step="revealed" hidden>
|
|
113
|
+
<div class="runwell-scratch__card runwell-scratch__card--trophy">
|
|
114
|
+
<p class="runwell-scratch__reveal-eyebrow">You won</p>
|
|
115
|
+
<p class="runwell-scratch__reveal-pct" data-runwell-scratch-pct-final>--%</p>
|
|
116
|
+
<p class="runwell-scratch__reveal-fineprint">off your order</p>
|
|
117
|
+
</div>
|
|
118
|
+
<p class="runwell-scratch__revealed-heading">{{ section.settings.revealed_heading }}</p>
|
|
119
|
+
<div class="runwell-scratch__code-display">
|
|
120
|
+
<span data-runwell-scratch-code-display>------</span>
|
|
121
|
+
<button type="button" class="runwell-scratch__copy" data-runwell-scratch-copy aria-label="Copy code">Copy</button>
|
|
122
|
+
</div>
|
|
123
|
+
<a
|
|
124
|
+
href="{{ section.settings.reveal_cta_url | default: '/collections/all' }}"
|
|
125
|
+
class="runwell-scratch__cta runwell-scratch__cta--reveal"
|
|
126
|
+
data-runwell-scratch-shop
|
|
127
|
+
>
|
|
128
|
+
{{ section.settings.reveal_cta_text }}
|
|
129
|
+
</a>
|
|
130
|
+
<p class="runwell-scratch__fineprint">{{ section.settings.revealed_subheading }}</p>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<script src="{{ 'runwell-scratch-popup.js' | asset_url }}" defer="defer"></script>
|
|
136
|
+
|
|
137
|
+
{% schema %}
|
|
138
|
+
{
|
|
139
|
+
"name": "Runwell scratch popup",
|
|
140
|
+
"tag": "section",
|
|
141
|
+
"class": "section-runwell-scratch",
|
|
142
|
+
"settings": [
|
|
143
|
+
{ "type": "header", "content": "Copy" },
|
|
144
|
+
{ "type": "text", "id": "eyebrow", "label": "Eyebrow", "default": "A little surprise" },
|
|
145
|
+
{ "type": "text", "id": "heading", "label": "Heading", "default": "Scratch to reveal your discount" },
|
|
146
|
+
{ "type": "textarea", "id": "subheading", "label": "Subheading", "default": "Scratch the foil card below to see how much you saved." },
|
|
147
|
+
{ "type": "text", "id": "scratch_prompt", "label": "Scratch prompt (under the card)", "default": "Drag or tap to scratch" },
|
|
148
|
+
{ "type": "text", "id": "email_prompt", "label": "Email prompt (after scratch)", "default": "Where should we send your code?" },
|
|
149
|
+
{ "type": "text", "id": "email_placeholder", "label": "Email placeholder", "default": "you@email.com" },
|
|
150
|
+
{ "type": "text", "id": "email_cta_text", "label": "Email CTA", "default": "Email me my code" },
|
|
151
|
+
{ "type": "text", "id": "revealed_heading", "label": "Revealed heading", "default": "Here is your code." },
|
|
152
|
+
{ "type": "textarea", "id": "revealed_subheading", "label": "Revealed fineprint", "default": "Copied and sent to your inbox. One use per customer. Auto-applies at checkout." },
|
|
153
|
+
{ "type": "text", "id": "reveal_cta_text", "label": "Revealed CTA", "default": "Shop now" },
|
|
154
|
+
{ "type": "url", "id": "reveal_cta_url", "label": "Revealed CTA URL (discount auto-appends)" },
|
|
155
|
+
|
|
156
|
+
{ "type": "header", "content": "Tier 1 (most likely)" },
|
|
157
|
+
{ "type": "text", "id": "tier1_code", "label": "Code (must exist as Shopify discount)", "default": "MYSTERY10" },
|
|
158
|
+
{ "type": "range", "id": "tier1_pct", "label": "Percentage off", "min": 1, "max": 50, "step": 1, "default": 10 },
|
|
159
|
+
{ "type": "range", "id": "tier1_prob", "label": "Probability (%)", "min": 0, "max": 100, "step": 5, "default": 60 },
|
|
160
|
+
|
|
161
|
+
{ "type": "header", "content": "Tier 2 (mid)" },
|
|
162
|
+
{ "type": "text", "id": "tier2_code", "label": "Code", "default": "MYSTERY15" },
|
|
163
|
+
{ "type": "range", "id": "tier2_pct", "label": "Percentage off", "min": 1, "max": 50, "step": 1, "default": 15 },
|
|
164
|
+
{ "type": "range", "id": "tier2_prob", "label": "Probability (%)", "min": 0, "max": 100, "step": 5, "default": 30 },
|
|
165
|
+
|
|
166
|
+
{ "type": "header", "content": "Tier 3 (rare)" },
|
|
167
|
+
{ "type": "text", "id": "tier3_code", "label": "Code", "default": "MYSTERY20" },
|
|
168
|
+
{ "type": "range", "id": "tier3_pct", "label": "Percentage off", "min": 1, "max": 50, "step": 1, "default": 20 },
|
|
169
|
+
{ "type": "range", "id": "tier3_prob", "label": "Probability (%)", "min": 0, "max": 100, "step": 5, "default": 10 },
|
|
170
|
+
|
|
171
|
+
{ "type": "header", "content": "Triggers" },
|
|
172
|
+
{ "type": "range", "id": "trigger_delay_sec", "label": "First-visit delay (seconds)", "min": 0, "max": 60, "step": 1, "default": 8 },
|
|
173
|
+
{ "type": "checkbox", "id": "exit_intent", "label": "Also trigger on exit intent", "default": true },
|
|
174
|
+
{ "type": "range", "id": "scratch_threshold_pct", "label": "Foil-erase % before auto-reveal", "min": 30, "max": 90, "step": 5, "default": 60 },
|
|
175
|
+
|
|
176
|
+
{ "type": "header", "content": "Design" },
|
|
177
|
+
{ "type": "color", "id": "foil_color", "label": "Foil color", "default": "#C8B89A" },
|
|
178
|
+
{ "type": "color", "id": "accent_color", "label": "Accent color", "default": "#5B7A3E" }
|
|
179
|
+
],
|
|
180
|
+
"presets": [
|
|
181
|
+
{ "name": "Runwell scratch popup" }
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
{% endschema %}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runwell/shopify-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
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",
|