@runwell/shopify-toolkit 0.1.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.
Files changed (76) hide show
  1. package/README.md +87 -0
  2. package/bin/runwell-shopify +98 -0
  3. package/lib/add.js +66 -0
  4. package/lib/config-loader.js +47 -0
  5. package/lib/doctor.js +66 -0
  6. package/lib/list.js +31 -0
  7. package/lib/remove.js +37 -0
  8. package/lib/sync.js +107 -0
  9. package/lib/template.js +57 -0
  10. package/lib/validate.js +23 -0
  11. package/modules/comparison-table/README.md +17 -0
  12. package/modules/comparison-table/module.json +50 -0
  13. package/modules/comparison-table/sections/runwell-comparison-table.liquid +157 -0
  14. package/modules/delivery-estimate/README.md +17 -0
  15. package/modules/delivery-estimate/module.json +14 -0
  16. package/modules/delivery-estimate/snippets/runwell-delivery-estimate.liquid +39 -0
  17. package/modules/editorial-block/README.md +17 -0
  18. package/modules/editorial-block/module.json +40 -0
  19. package/modules/editorial-block/sections/runwell-editorial-block.liquid +155 -0
  20. package/modules/editorial-hero/README.md +17 -0
  21. package/modules/editorial-hero/module.json +61 -0
  22. package/modules/editorial-hero/sections/runwell-video-hero.liquid +151 -0
  23. package/modules/exit-intent/README.md +18 -0
  24. package/modules/exit-intent/assets/runwell-exit-intent.js +54 -0
  25. package/modules/exit-intent/module.json +33 -0
  26. package/modules/exit-intent/sections/runwell-exit-intent.liquid +48 -0
  27. package/modules/faq/README.md +17 -0
  28. package/modules/faq/module.json +35 -0
  29. package/modules/faq/sections/runwell-faq.liquid +66 -0
  30. package/modules/how-it-works/README.md +17 -0
  31. package/modules/how-it-works/module.json +57 -0
  32. package/modules/how-it-works/sections/runwell-how-it-works.liquid +99 -0
  33. package/modules/inventory-urgency/README.md +17 -0
  34. package/modules/inventory-urgency/module.json +14 -0
  35. package/modules/inventory-urgency/snippets/runwell-inventory-urgency.liquid +19 -0
  36. package/modules/pdp-ingredients/README.md +17 -0
  37. package/modules/pdp-ingredients/module.json +40 -0
  38. package/modules/pdp-ingredients/sections/runwell-ingredients.liquid +139 -0
  39. package/modules/pdp-journal-link/README.md +17 -0
  40. package/modules/pdp-journal-link/module.json +53 -0
  41. package/modules/pdp-journal-link/sections/runwell-pdp-journal.liquid +124 -0
  42. package/modules/pdp-trust-checks/README.md +17 -0
  43. package/modules/pdp-trust-checks/module.json +49 -0
  44. package/modules/pdp-trust-checks/sections/runwell-pdp-trust.liquid +141 -0
  45. package/modules/post-purchase-upsell/README.md +52 -0
  46. package/modules/post-purchase-upsell/admin/discount-setup.md +25 -0
  47. package/modules/post-purchase-upsell/admin/order-status-paste.html +31 -0
  48. package/modules/post-purchase-upsell/assets/runwell-thank-you.css +119 -0
  49. package/modules/post-purchase-upsell/assets/runwell-thank-you.js +162 -0
  50. package/modules/post-purchase-upsell/module.json +44 -0
  51. package/modules/press-bar/README.md +17 -0
  52. package/modules/press-bar/module.json +30 -0
  53. package/modules/press-bar/sections/runwell-press-bar.liquid +119 -0
  54. package/modules/recently-viewed/README.md +18 -0
  55. package/modules/recently-viewed/assets/runwell-recently-viewed.js +57 -0
  56. package/modules/recently-viewed/module.json +33 -0
  57. package/modules/recently-viewed/sections/runwell-recently-viewed.liquid +38 -0
  58. package/modules/reviews/README.md +17 -0
  59. package/modules/reviews/module.json +20 -0
  60. package/modules/reviews/sections/runwell-pdp-reviews.liquid +93 -0
  61. package/modules/risk-reversal/README.md +17 -0
  62. package/modules/risk-reversal/module.json +49 -0
  63. package/modules/risk-reversal/sections/runwell-risk-reversal.liquid +94 -0
  64. package/modules/shipping-bar/README.md +17 -0
  65. package/modules/shipping-bar/module.json +45 -0
  66. package/modules/shipping-bar/sections/runwell-shipping-bar.liquid +95 -0
  67. package/modules/sticky-atc/README.md +17 -0
  68. package/modules/sticky-atc/module.json +14 -0
  69. package/modules/sticky-atc/sections/runwell-pdp-sticky.liquid +78 -0
  70. package/modules/testimonials/README.md +17 -0
  71. package/modules/testimonials/module.json +35 -0
  72. package/modules/testimonials/sections/runwell-testimonials.liquid +87 -0
  73. package/modules/trust-badges/README.md +17 -0
  74. package/modules/trust-badges/module.json +25 -0
  75. package/modules/trust-badges/sections/runwell-trust-badges.liquid +93 -0
  76. package/package.json +45 -0
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "comparison-table",
3
+ "version": "0.1.0",
4
+ "category": "pdp",
5
+ "description": "Lushi comparison table module migrated from Lushi.",
6
+ "files": {
7
+ "sections": [
8
+ "sections/runwell-comparison-table.liquid"
9
+ ]
10
+ },
11
+ "config": {
12
+ "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"
37
+ },
38
+ "background_color": {
39
+ "type": "string",
40
+ "default": "#F5F0EE",
41
+ "label": "Background color"
42
+ },
43
+ "text_color": {
44
+ "type": "string",
45
+ "default": "#0B3D38",
46
+ "label": "Text color"
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,157 @@
1
+ {%- comment -%}
2
+ Lushi product comparison table. Drops on PDP (or any template) to
3
+ compare attributes across Lushi products or against a competitor.
4
+ Editorial style, not a feature dump. Per CRO §5.2 (decision-aid for
5
+ high-trust niches).
6
+ {%- endcomment -%}
7
+
8
+ {%- if section.blocks.size > 0 -%}
9
+ <section
10
+ class="runwell-comparison"
11
+ style="background: {{ section.settings.background_color }}; color: {{ section.settings.text_color }};"
12
+ >
13
+ <div style="max-width: 1200px; margin: 0 auto; padding: clamp(3rem, 6vw, 5rem) 6vw;">
14
+ {%- if section.settings.eyebrow != blank -%}
15
+ <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;">
16
+ {{ section.settings.eyebrow }}
17
+ </div>
18
+ {%- endif -%}
19
+
20
+ {%- if section.settings.heading != blank -%}
21
+ <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 0 1rem 0; max-width: 22ch;">
22
+ {{ section.settings.heading }}
23
+ </h2>
24
+ {%- endif -%}
25
+
26
+ {%- if section.settings.lede != blank -%}
27
+ <p style="font-family: var(--font-body-family); font-size: 1rem; line-height: 1.65; max-width: 60ch; opacity: 0.85; margin: 0 0 2rem 0;">
28
+ {{ section.settings.lede }}
29
+ </p>
30
+ {%- endif -%}
31
+
32
+ <div style="overflow-x: auto;">
33
+ <table style="width: 100%; border-collapse: collapse; min-width: 600px; font-family: var(--font-body-family); font-size: 0.95rem;">
34
+ <thead>
35
+ <tr>
36
+ <th style="text-align: left; padding: 0.8rem 1rem 0.8rem 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.row_label_header | default: 'What you get' }}
38
+ </th>
39
+ {%- for block in section.blocks -%}
40
+ <th style="text-align: left; padding: 0.8rem 1rem; vertical-align: bottom; border-bottom: 2px solid currentColor;">
41
+ <div style="font-family: var(--font-heading-family); font-style: italic; font-weight: 400; font-size: 1.15rem; line-height: 1.2; margin-bottom: 0.2rem;">
42
+ {{ block.settings.column_title }}
43
+ </div>
44
+ {%- if block.settings.column_subtitle != blank -%}
45
+ <div style="font-family: var(--font-body-family); font-size: 0.78rem; opacity: 0.7; font-weight: 400; letter-spacing: normal; text-transform: none;">
46
+ {{ block.settings.column_subtitle }}
47
+ </div>
48
+ {%- endif -%}
49
+ </th>
50
+ {%- endfor -%}
51
+ </tr>
52
+ </thead>
53
+ <tbody>
54
+ {%- assign rows = section.settings.rows | newline_to_br | split: '<br />' -%}
55
+ {%- for row in rows -%}
56
+ {%- assign parts = row | split: '|' -%}
57
+ {%- if parts.size > 0 and parts[0] != blank -%}
58
+ <tr>
59
+ <td style="padding: 0.8rem 1rem 0.8rem 0; vertical-align: top; border-bottom: 1px solid rgba(11,61,56,0.12); font-weight: 700;">
60
+ {{ parts[0] | strip }}
61
+ </td>
62
+ {%- for block in section.blocks -%}
63
+ {%- assign idx = forloop.index -%}
64
+ {%- assign cell = parts[idx] | default: '' | strip -%}
65
+ <td style="padding: 0.8rem 1rem; vertical-align: top; border-bottom: 1px solid rgba(11,61,56,0.12); opacity: 0.92;">
66
+ {%- if cell == 'yes' or cell == '✓' -%}
67
+ <span style="color: var(--runwell-rain-forrest); font-weight: 700;">✓</span>
68
+ {%- elsif cell == 'no' -%}
69
+ <span style="opacity: 0.4;">no</span>
70
+ {%- else -%}
71
+ {{ cell }}
72
+ {%- endif -%}
73
+ </td>
74
+ {%- endfor -%}
75
+ </tr>
76
+ {%- endif -%}
77
+ {%- endfor -%}
78
+ </tbody>
79
+ </table>
80
+ </div>
81
+ </div>
82
+ </section>
83
+ {%- endif -%}
84
+
85
+ {% schema %}
86
+ {
87
+ "name": "Lushi comparison table",
88
+ "tag": "section",
89
+ "class": "section-runwell-comparison",
90
+ "settings": [
91
+ {
92
+ "type": "text",
93
+ "id": "eyebrow",
94
+ "label": "Eyebrow",
95
+ "default": "Compared"
96
+ },
97
+ {
98
+ "type": "text",
99
+ "id": "heading",
100
+ "label": "Heading",
101
+ "default": "How it stacks up."
102
+ },
103
+ {
104
+ "type": "textarea",
105
+ "id": "lede",
106
+ "label": "Lede",
107
+ "default": "We do not stock everything; here is what makes this one earn the spot."
108
+ },
109
+ {
110
+ "type": "text",
111
+ "id": "row_label_header",
112
+ "label": "Row label header",
113
+ "default": "What you get"
114
+ },
115
+ {
116
+ "type": "textarea",
117
+ "id": "rows",
118
+ "label": "Rows",
119
+ "info": "One row per line. Format: Row name | Col1 cell | Col2 cell | ... Use yes for checked, no for missing.",
120
+ "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"
121
+ },
122
+ {
123
+ "type": "color",
124
+ "id": "background_color",
125
+ "label": "Background color",
126
+ "default": "#F5F0EE"
127
+ },
128
+ {
129
+ "type": "color",
130
+ "id": "text_color",
131
+ "label": "Text color",
132
+ "default": "#0B3D38"
133
+ }
134
+ ],
135
+ "blocks": [
136
+ {
137
+ "type": "column",
138
+ "name": "Column",
139
+ "settings": [
140
+ { "type": "text", "id": "column_title", "label": "Column title", "default": "Lushi pick" },
141
+ { "type": "text", "id": "column_subtitle", "label": "Column subtitle" }
142
+ ]
143
+ }
144
+ ],
145
+ "max_blocks": 4,
146
+ "presets": [
147
+ {
148
+ "name": "Lushi comparison table",
149
+ "blocks": [
150
+ { "type": "column", "settings": { "column_title": "Lushi pick", "column_subtitle": "This product" } },
151
+ { "type": "column", "settings": { "column_title": "Drugstore", "column_subtitle": "Average mass-market option" } },
152
+ { "type": "column", "settings": { "column_title": "Hype brand", "column_subtitle": "Trendy DTC competitor" } }
153
+ ]
154
+ }
155
+ ]
156
+ }
157
+ {% endschema %}
@@ -0,0 +1,17 @@
1
+ # delivery-estimate
2
+
3
+ delivery-estimate. Migrated from Lushi (`snippets/lushi-delivery-estimate.liquid`) into the Runwell Shopify Toolkit.
4
+
5
+ Category: `pdp`
6
+
7
+ ## Files
8
+
9
+ - `snippets/runwell-delivery-estimate.liquid`
10
+
11
+ ## Config
12
+
13
+ See `module.json` config schema. Defaults match the original Lushi defaults; override per client in `runwell.config.json`.
14
+
15
+ ## Lineage
16
+
17
+ This module was extracted from the Lushi build (Capital V) and generalised. The original lives at `lushi-shopify/snippets/lushi-delivery-estimate.liquid`. The toolkit version uses `runwell-` class prefixes and pulls brand vars from the client config.
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "delivery-estimate",
3
+ "version": "0.1.0",
4
+ "category": "pdp",
5
+ "description": "delivery-estimate module migrated from Lushi.",
6
+ "files": {
7
+ "snippets": [
8
+ "snippets/runwell-delivery-estimate.liquid"
9
+ ]
10
+ },
11
+ "config": {
12
+ "schema": {}
13
+ }
14
+ }
@@ -0,0 +1,39 @@
1
+ {%- comment -%}
2
+ Lushi delivery estimate. Renders a placeholder span filled by JS with
3
+ a date range based on cutoff time and standard transit. Native
4
+ replacement for the Estimated Delivery Date widget in Vitals etc.
5
+ {%- endcomment -%}
6
+
7
+ <p class="runwell-delivery" role="status">
8
+ <span aria-hidden="true">&rarr;</span>
9
+ Order today for delivery between
10
+ <strong data-runwell-delivery="from">soon</strong>
11
+ and
12
+ <strong data-runwell-delivery="to">soon</strong>.
13
+ </p>
14
+
15
+ <script>
16
+ (function () {
17
+ var fromEl = document.querySelector('[data-runwell-delivery="from"]');
18
+ var toEl = document.querySelector('[data-runwell-delivery="to"]');
19
+ if (!fromEl || !toEl) return;
20
+ var now = new Date();
21
+ var cutoffHour = 14;
22
+ var ordersToday = (now.getUTCHours() - 5) < cutoffHour;
23
+ function addBusinessDays(date, days) {
24
+ var d = new Date(date);
25
+ while (days > 0) {
26
+ d.setDate(d.getDate() + 1);
27
+ var dow = d.getDay();
28
+ if (dow !== 0 && dow !== 6) days--;
29
+ }
30
+ return d;
31
+ }
32
+ var shipDay = ordersToday ? new Date(now) : addBusinessDays(now, 1);
33
+ var fromDate = addBusinessDays(shipDay, 3);
34
+ var toDate = addBusinessDays(shipDay, 7);
35
+ var fmt = { month: 'short', day: 'numeric' };
36
+ fromEl.textContent = fromDate.toLocaleDateString('en-US', fmt);
37
+ toEl.textContent = toDate.toLocaleDateString('en-US', fmt);
38
+ })();
39
+ </script>
@@ -0,0 +1,17 @@
1
+ # editorial-block
2
+
3
+ Lushi editorial block. Migrated from Lushi (`sections/lushi-editorial-block.liquid`) into the Runwell Shopify Toolkit.
4
+
5
+ Category: `storefront`
6
+
7
+ ## Files
8
+
9
+ - `sections/runwell-editorial-block.liquid`
10
+
11
+ ## Config
12
+
13
+ See `module.json` config schema. Defaults match the original Lushi defaults; override per client in `runwell.config.json`.
14
+
15
+ ## Lineage
16
+
17
+ This module was extracted from the Lushi build (Capital V) and generalised. The original lives at `lushi-shopify/sections/lushi-editorial-block.liquid`. The toolkit version uses `runwell-` class prefixes and pulls brand vars from the client config.
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "editorial-block",
3
+ "version": "0.1.0",
4
+ "category": "storefront",
5
+ "description": "Lushi editorial block module migrated from Lushi.",
6
+ "files": {
7
+ "sections": [
8
+ "sections/runwell-editorial-block.liquid"
9
+ ]
10
+ },
11
+ "config": {
12
+ "schema": {
13
+ "eyebrow": {
14
+ "type": "string",
15
+ "default": "Our approach",
16
+ "label": "Eyebrow"
17
+ },
18
+ "heading": {
19
+ "type": "string",
20
+ "default": "Wellness, made simple.",
21
+ "label": "Heading"
22
+ },
23
+ "lede": {
24
+ "type": "string",
25
+ "default": "Curated supplements, skincare, and rituals from the brands we trust most. Editorial-first, science-aware, beautifully made.",
26
+ "label": "Lede"
27
+ },
28
+ "background_color": {
29
+ "type": "string",
30
+ "default": "#EDE6D8",
31
+ "label": "Background color"
32
+ },
33
+ "text_color": {
34
+ "type": "string",
35
+ "default": "#0B3D38",
36
+ "label": "Text color"
37
+ }
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,155 @@
1
+ {%- comment -%}
2
+ Lushi v2 editorial section: eyebrow + serif italic heading + lede + body.
3
+ Used for the homepage section flow (Approach, Press, Partnering equivalents).
4
+ {%- endcomment -%}
5
+
6
+ <section
7
+ class="runwell-editorial-section"
8
+ style="background: {{ section.settings.background_color | default: '#EDE6D8' }}; color: {{ section.settings.text_color | default: '#0B3D38' }};"
9
+ >
10
+ {%- if section.settings.eyebrow != blank -%}
11
+ <div class="runwell-editorial-section__eyebrow">{{ section.settings.eyebrow }}</div>
12
+ {%- endif -%}
13
+
14
+ {%- if section.settings.heading != blank -%}
15
+ <h2 class="runwell-editorial-section__heading" style="color: {{ section.settings.text_color | default: '#0B3D38' }};">
16
+ {{ section.settings.heading }}
17
+ </h2>
18
+ {%- endif -%}
19
+
20
+ {%- if section.settings.lede != blank -%}
21
+ <p class="runwell-editorial-section__lede" style="color: {{ section.settings.text_color | default: '#0B3D38' }};">
22
+ {{ section.settings.lede }}
23
+ </p>
24
+ {%- endif -%}
25
+
26
+ {%- if section.blocks.size > 0 -%}
27
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 2rem; margin-top: 2rem;">
28
+ {%- for block in section.blocks -%}
29
+ <div {{ block.shopify_attributes }}>
30
+ {%- if block.settings.image != blank -%}
31
+ <img src="{{ block.settings.image | image_url: width: 800 }}"
32
+ alt="{{ block.settings.title | escape }}"
33
+ width="800"
34
+ height="1000"
35
+ loading="lazy"
36
+ style="width: 100%; height: auto; border-radius: 8px; margin-bottom: 1rem; aspect-ratio: 4/5; object-fit: cover;">
37
+ {%- elsif block.settings.asset_filename != blank -%}
38
+ <img src="{{ block.settings.asset_filename | asset_url }}"
39
+ alt="{{ block.settings.title | escape }}"
40
+ width="800"
41
+ height="1000"
42
+ loading="lazy"
43
+ style="width: 100%; height: auto; border-radius: 8px; margin-bottom: 1rem; aspect-ratio: 4/5; object-fit: cover;">
44
+ {%- endif -%}
45
+ {%- if block.settings.title != blank -%}
46
+ <h3 style="font-family: var(--font-heading-family); font-style: normal; font-weight: 400; font-size: 1.6rem; margin: 0 0 0.5rem 0; line-height: 1.2;">
47
+ {{ block.settings.title }}
48
+ </h3>
49
+ {%- endif -%}
50
+ {%- if block.settings.body != blank -%}
51
+ <p style="font-size: 0.95rem; line-height: 1.6; opacity: 0.85; margin: 0;">
52
+ {{ block.settings.body }}
53
+ </p>
54
+ {%- endif -%}
55
+ {%- if block.settings.link_label != blank -%}
56
+ <a href="{{ block.settings.link_url | default: '#' }}"
57
+ style="display: inline-block; margin-top: 0.8rem; font-weight: 700; font-size: 0.9rem; text-decoration: underline; text-underline-offset: 4px; text-decoration-color: var(--runwell-blue);">
58
+ {{ block.settings.link_label }} &rarr;
59
+ </a>
60
+ {%- endif -%}
61
+ </div>
62
+ {%- endfor -%}
63
+ </div>
64
+ {%- endif -%}
65
+ </section>
66
+
67
+ {% schema %}
68
+ {
69
+ "name": "Lushi editorial block",
70
+ "tag": "section",
71
+ "class": "section-runwell-editorial",
72
+ "settings": [
73
+ {
74
+ "type": "text",
75
+ "id": "eyebrow",
76
+ "label": "Eyebrow",
77
+ "default": "Our approach"
78
+ },
79
+ {
80
+ "type": "text",
81
+ "id": "heading",
82
+ "label": "Heading",
83
+ "default": "Wellness, made simple."
84
+ },
85
+ {
86
+ "type": "textarea",
87
+ "id": "lede",
88
+ "label": "Lede",
89
+ "default": "Curated supplements, skincare, and rituals from the brands we trust most. Editorial-first, science-aware, beautifully made."
90
+ },
91
+ {
92
+ "type": "color",
93
+ "id": "background_color",
94
+ "label": "Background color",
95
+ "default": "#EDE6D8"
96
+ },
97
+ {
98
+ "type": "color",
99
+ "id": "text_color",
100
+ "label": "Text color",
101
+ "default": "#0B3D38"
102
+ }
103
+ ],
104
+ "blocks": [
105
+ {
106
+ "type": "card",
107
+ "name": "Card",
108
+ "settings": [
109
+ {
110
+ "type": "image_picker",
111
+ "id": "image",
112
+ "label": "Image (uploaded)",
113
+ "info": "Optional. Falls through to bundled asset filename below."
114
+ },
115
+ {
116
+ "type": "text",
117
+ "id": "asset_filename",
118
+ "label": "Bundled asset filename",
119
+ "info": "e.g. runwell-about-1.jpg (theme asset)."
120
+ },
121
+ {
122
+ "type": "text",
123
+ "id": "title",
124
+ "label": "Title"
125
+ },
126
+ {
127
+ "type": "textarea",
128
+ "id": "body",
129
+ "label": "Body"
130
+ },
131
+ {
132
+ "type": "text",
133
+ "id": "link_label",
134
+ "label": "Link label"
135
+ },
136
+ {
137
+ "type": "url",
138
+ "id": "link_url",
139
+ "label": "Link URL"
140
+ }
141
+ ]
142
+ }
143
+ ],
144
+ "presets": [
145
+ {
146
+ "name": "Lushi editorial block",
147
+ "blocks": [
148
+ { "type": "card" },
149
+ { "type": "card" },
150
+ { "type": "card" }
151
+ ]
152
+ }
153
+ ]
154
+ }
155
+ {% endschema %}
@@ -0,0 +1,17 @@
1
+ # editorial-hero
2
+
3
+ Lushi video hero. Migrated from Lushi (`sections/lushi-video-hero.liquid`) into the Runwell Shopify Toolkit.
4
+
5
+ Category: `storefront`
6
+
7
+ ## Files
8
+
9
+ - `sections/runwell-video-hero.liquid`
10
+
11
+ ## Config
12
+
13
+ See `module.json` config schema. Defaults match the original Lushi defaults; override per client in `runwell.config.json`.
14
+
15
+ ## Lineage
16
+
17
+ This module was extracted from the Lushi build (Capital V) and generalised. The original lives at `lushi-shopify/sections/lushi-video-hero.liquid`. The toolkit version uses `runwell-` class prefixes and pulls brand vars from the client config.
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "editorial-hero",
3
+ "version": "0.1.0",
4
+ "category": "storefront",
5
+ "description": "Lushi video hero module migrated from Lushi.",
6
+ "files": {
7
+ "sections": [
8
+ "sections/runwell-video-hero.liquid"
9
+ ]
10
+ },
11
+ "config": {
12
+ "schema": {
13
+ "eyebrow": {
14
+ "type": "string",
15
+ "default": "Wellness, curated",
16
+ "label": "Eyebrow text"
17
+ },
18
+ "heading": {
19
+ "type": "string",
20
+ "default": "Glow from within.",
21
+ "label": "Headline"
22
+ },
23
+ "subheading": {
24
+ "type": "string",
25
+ "default": "Lushi is a modern wellness brand offering curated supplements, skincare, and rituals for everyday health.",
26
+ "label": "Subheading"
27
+ },
28
+ "video_url": {
29
+ "type": "string",
30
+ "label": "Background video URL (mp4)"
31
+ },
32
+ "poster_image": {
33
+ "type": "string",
34
+ "label": "Poster image (and fallback)"
35
+ },
36
+ "min_height": {
37
+ "type": "number",
38
+ "default": 100,
39
+ "label": "Hero min-height (vh)"
40
+ },
41
+ "button_label_primary": {
42
+ "type": "string",
43
+ "default": "Shop wellness",
44
+ "label": "Primary button label"
45
+ },
46
+ "button_link_primary": {
47
+ "type": "string",
48
+ "label": "Primary button link"
49
+ },
50
+ "button_label_secondary": {
51
+ "type": "string",
52
+ "default": "Read the journal",
53
+ "label": "Secondary button label"
54
+ },
55
+ "button_link_secondary": {
56
+ "type": "string",
57
+ "label": "Secondary button link"
58
+ }
59
+ }
60
+ }
61
+ }