Merge Shopify changes into main - #4
Merged
Merged
Conversation
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
Committed from shop: Open Home Foundation Store Theme last edited by: Dan Long
mrdarrengriffin
approved these changes
Sep 16, 2026
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical security and correctness issues, along with moderate behavior issues, must be addressed before approval.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Updates Dawn for Open Home Foundation branding, storefront configuration, product-card color swatches, and a product price-breakdown chart.
Changes:
- Rebrands homepage, header, footer, and theme settings.
- Adjusts homepage, collection, and product presentation.
- Adds product-card color swatches and styling.
- Adds a Chart.js price-breakdown chart to product pages.
File summaries
| File | Summary and findings |
|---|---|
templates/product.json |
Adds product layout and price-breakdown chart. Moderate issues: chart values remain stale after variant changes, and Chart.js loads as a blocking external script. Critical issue: the third-party CDN dependency lacks integrity verification or a first-party asset boundary. Nit: “dependant” should be “dependent.” |
templates/index.json |
Updates homepage branding and featured product settings. |
templates/collection.json |
Adjusts collection product-card presentation. |
snippets/card-product.liquid |
Adds variant color swatches. Critical issue: color_option is unescaped in HTML contexts. Moderate issue: arbitrary option text is used as a CSS color, which can produce blank swatches. |
sections/header-group.json |
Updates header configuration. |
sections/footer-group.json |
Updates footer menus and settings. |
config/settings_data.json |
Updates store branding and theme settings. |
assets/base.css |
Adds swatch styling. Moderate issue: pointer cursors on noninteractive spans create a misleading affordance, and data-color has no consumer. |
Review details
Suppressed comments (1)
templates/product.json:118
- Loading Chart.js as a classic external script without
asyncordeferblocks parsing and delaysDOMContentLoadedon every product page. A slow or unavailable jsDelivr response can therefore delay the theme's initialization, and the currenttypeof Chartguard leaves this section blank on failure. Load the dependency non-blockingly with an explicit load/error path, or ship it as a theme asset.
"custom_liquid": "<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js\"></script>\n\n<div style=\"width: 75%; margin: 20px auto;\">\n <h3 style=\"text-align: center;\">Price breakdown</h3>\n\n <p style=\"text-align: center; font-size: 13px; color: #777; margin: 0 auto 32px; max-width: 640px;\">\n These costs represent the breakdown at the time we most recently updated the item's total price, so the percentages may change between updates. This means it's possible that the contribution to the Open Home Foundation from this purchase could be moderately higher or lower than what's shown above.\n </p>\n\n <div style=\"display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 30px;\">\n <div style=\"flex-basis: 300px; flex-grow: 1; max-width: 400px; position: relative;\">\n <canvas id=\"costBreakdownChart\"></canvas>\n </div>\n <div id=\"costBreakdownLegend\" style=\"flex-basis: 250px; flex-grow: 1;\"></div>\n </div>\n</div>\n\n<script>\n(function () {\n const totalPrice = {{ product.selected_or_first_available_variant.price | default: 0 }} / 100;\n const currencyCode = {{ cart.currency.iso_code | json }};\n const fmt = new Intl.NumberFormat({{ request.locale.iso_code | json }}, { style: 'currency', currency: currencyCode });\n\n const rows = [\n ['Manufacturing', 0.27, '#5A6675'],\n ['Warehousing & Logistics', 0.15, '#09293B'],\n ['Operational costs', 0.06, '#A2AAB6'],\n ['VAT (country dependant)', 0.22, '#7A8594'],\n ['Contribution to the Open Home Foundation', 0.30, '#18BCF2'],\n ];\n\n function init() {\n const canvas = document.getElementById('costBreakdownChart');\n if (!canvas || typeof Chart === 'undefined') return;\n\n new Chart(canvas.getContext('2d'), {\n type: 'doughnut',\n data: {\n labels: rows.map(r => r[0]),\n datasets: [{\n data: rows.map(r => totalPrice * r[1]),\n backgroundColor: rows.map(r => r[2]),\n borderWidth: 0,\n hoverOffset: 4\n }]\n },\n options: {\n responsive: true,\n cutout: '70%',\n plugins: {\n legend: { display: false },\n tooltip: { callbacks: { label: c => `${c.label}: ${fmt.format(c.raw)}` } }\n }\n }\n });\n\n document.getElementById('costBreakdownLegend').innerHTML =\n '<ul style=\"list-style:none;padding:0;margin:0;\">' +\n rows.map(([label, pct, color]) => `\n <li style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;font-size:14px;\">\n <span style=\"display:flex;align-items:center;\">\n <span style=\"width:12px;height:12px;border-radius:50%;background-color:${color};margin-right:10px;\"></span>\n ${label}\n </span>\n <span style=\"font-weight:500;\">${(pct * 100).toFixed(0)}%</span>\n </li>`).join('') +\n '</ul>';\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init);\n } else {\n init();\n }\n})();\n</script>",
- Files reviewed: 8/8 changed files
- Comments generated: 6
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+217
to
+219
| <span class="swatch-bubble" data-color="{{ color_option | downcase }}" style="background-color: {{ color_option | replace: ' ', '' | downcase }};"> | ||
| <span class="visually-hidden">{{ color_option }}</span> | ||
| </span> |
| "type": "custom-liquid", | ||
| "name": "t:sections.custom-liquid.presets.name", | ||
| "settings": { | ||
| "custom_liquid": "<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js\"></script>\n\n<div style=\"width: 75%; margin: 20px auto;\">\n <h3 style=\"text-align: center;\">Price breakdown</h3>\n\n <p style=\"text-align: center; font-size: 13px; color: #777; margin: 0 auto 32px; max-width: 640px;\">\n These costs represent the breakdown at the time we most recently updated the item's total price, so the percentages may change between updates. This means it's possible that the contribution to the Open Home Foundation from this purchase could be moderately higher or lower than what's shown above.\n </p>\n\n <div style=\"display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 30px;\">\n <div style=\"flex-basis: 300px; flex-grow: 1; max-width: 400px; position: relative;\">\n <canvas id=\"costBreakdownChart\"></canvas>\n </div>\n <div id=\"costBreakdownLegend\" style=\"flex-basis: 250px; flex-grow: 1;\"></div>\n </div>\n</div>\n\n<script>\n(function () {\n const totalPrice = {{ product.selected_or_first_available_variant.price | default: 0 }} / 100;\n const currencyCode = {{ cart.currency.iso_code | json }};\n const fmt = new Intl.NumberFormat({{ request.locale.iso_code | json }}, { style: 'currency', currency: currencyCode });\n\n const rows = [\n ['Manufacturing', 0.27, '#5A6675'],\n ['Warehousing & Logistics', 0.15, '#09293B'],\n ['Operational costs', 0.06, '#A2AAB6'],\n ['VAT (country dependant)', 0.22, '#7A8594'],\n ['Contribution to the Open Home Foundation', 0.30, '#18BCF2'],\n ];\n\n function init() {\n const canvas = document.getElementById('costBreakdownChart');\n if (!canvas || typeof Chart === 'undefined') return;\n\n new Chart(canvas.getContext('2d'), {\n type: 'doughnut',\n data: {\n labels: rows.map(r => r[0]),\n datasets: [{\n data: rows.map(r => totalPrice * r[1]),\n backgroundColor: rows.map(r => r[2]),\n borderWidth: 0,\n hoverOffset: 4\n }]\n },\n options: {\n responsive: true,\n cutout: '70%',\n plugins: {\n legend: { display: false },\n tooltip: { callbacks: { label: c => `${c.label}: ${fmt.format(c.raw)}` } }\n }\n }\n });\n\n document.getElementById('costBreakdownLegend').innerHTML =\n '<ul style=\"list-style:none;padding:0;margin:0;\">' +\n rows.map(([label, pct, color]) => `\n <li style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;font-size:14px;\">\n <span style=\"display:flex;align-items:center;\">\n <span style=\"width:12px;height:12px;border-radius:50%;background-color:${color};margin-right:10px;\"></span>\n ${label}\n </span>\n <span style=\"font-weight:500;\">${(pct * 100).toFixed(0)}%</span>\n </li>`).join('') +\n '</ul>';\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init);\n } else {\n init();\n }\n})();\n</script>", |
| height: 16px; | ||
| border-radius: 50%; | ||
| border: 1px solid rgba(0,0,0,0.15); | ||
| cursor: pointer; |
| {% if card_product.options contains key %} | ||
| <div class="card-swatches-container"> | ||
| {% for color_option in card_product.options_by_name[key].values %} | ||
| <span class="swatch-bubble" data-color="{{ color_option | downcase }}" style="background-color: {{ color_option | replace: ' ', '' | downcase }};"> |
| "type": "custom-liquid", | ||
| "name": "t:sections.custom-liquid.presets.name", | ||
| "settings": { | ||
| "custom_liquid": "<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js\"></script>\n\n<div style=\"width: 75%; margin: 20px auto;\">\n <h3 style=\"text-align: center;\">Price breakdown</h3>\n\n <p style=\"text-align: center; font-size: 13px; color: #777; margin: 0 auto 32px; max-width: 640px;\">\n These costs represent the breakdown at the time we most recently updated the item's total price, so the percentages may change between updates. This means it's possible that the contribution to the Open Home Foundation from this purchase could be moderately higher or lower than what's shown above.\n </p>\n\n <div style=\"display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 30px;\">\n <div style=\"flex-basis: 300px; flex-grow: 1; max-width: 400px; position: relative;\">\n <canvas id=\"costBreakdownChart\"></canvas>\n </div>\n <div id=\"costBreakdownLegend\" style=\"flex-basis: 250px; flex-grow: 1;\"></div>\n </div>\n</div>\n\n<script>\n(function () {\n const totalPrice = {{ product.selected_or_first_available_variant.price | default: 0 }} / 100;\n const currencyCode = {{ cart.currency.iso_code | json }};\n const fmt = new Intl.NumberFormat({{ request.locale.iso_code | json }}, { style: 'currency', currency: currencyCode });\n\n const rows = [\n ['Manufacturing', 0.27, '#5A6675'],\n ['Warehousing & Logistics', 0.15, '#09293B'],\n ['Operational costs', 0.06, '#A2AAB6'],\n ['VAT (country dependant)', 0.22, '#7A8594'],\n ['Contribution to the Open Home Foundation', 0.30, '#18BCF2'],\n ];\n\n function init() {\n const canvas = document.getElementById('costBreakdownChart');\n if (!canvas || typeof Chart === 'undefined') return;\n\n new Chart(canvas.getContext('2d'), {\n type: 'doughnut',\n data: {\n labels: rows.map(r => r[0]),\n datasets: [{\n data: rows.map(r => totalPrice * r[1]),\n backgroundColor: rows.map(r => r[2]),\n borderWidth: 0,\n hoverOffset: 4\n }]\n },\n options: {\n responsive: true,\n cutout: '70%',\n plugins: {\n legend: { display: false },\n tooltip: { callbacks: { label: c => `${c.label}: ${fmt.format(c.raw)}` } }\n }\n }\n });\n\n document.getElementById('costBreakdownLegend').innerHTML =\n '<ul style=\"list-style:none;padding:0;margin:0;\">' +\n rows.map(([label, pct, color]) => `\n <li style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;font-size:14px;\">\n <span style=\"display:flex;align-items:center;\">\n <span style=\"width:12px;height:12px;border-radius:50%;background-color:${color};margin-right:10px;\"></span>\n ${label}\n </span>\n <span style=\"font-weight:500;\">${(pct * 100).toFixed(0)}%</span>\n </li>`).join('') +\n '</ul>';\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init);\n } else {\n init();\n }\n})();\n</script>", |
| "type": "custom-liquid", | ||
| "name": "t:sections.custom-liquid.presets.name", | ||
| "settings": { | ||
| "custom_liquid": "<script src=\"https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js\"></script>\n\n<div style=\"width: 75%; margin: 20px auto;\">\n <h3 style=\"text-align: center;\">Price breakdown</h3>\n\n <p style=\"text-align: center; font-size: 13px; color: #777; margin: 0 auto 32px; max-width: 640px;\">\n These costs represent the breakdown at the time we most recently updated the item's total price, so the percentages may change between updates. This means it's possible that the contribution to the Open Home Foundation from this purchase could be moderately higher or lower than what's shown above.\n </p>\n\n <div style=\"display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 30px;\">\n <div style=\"flex-basis: 300px; flex-grow: 1; max-width: 400px; position: relative;\">\n <canvas id=\"costBreakdownChart\"></canvas>\n </div>\n <div id=\"costBreakdownLegend\" style=\"flex-basis: 250px; flex-grow: 1;\"></div>\n </div>\n</div>\n\n<script>\n(function () {\n const totalPrice = {{ product.selected_or_first_available_variant.price | default: 0 }} / 100;\n const currencyCode = {{ cart.currency.iso_code | json }};\n const fmt = new Intl.NumberFormat({{ request.locale.iso_code | json }}, { style: 'currency', currency: currencyCode });\n\n const rows = [\n ['Manufacturing', 0.27, '#5A6675'],\n ['Warehousing & Logistics', 0.15, '#09293B'],\n ['Operational costs', 0.06, '#A2AAB6'],\n ['VAT (country dependant)', 0.22, '#7A8594'],\n ['Contribution to the Open Home Foundation', 0.30, '#18BCF2'],\n ];\n\n function init() {\n const canvas = document.getElementById('costBreakdownChart');\n if (!canvas || typeof Chart === 'undefined') return;\n\n new Chart(canvas.getContext('2d'), {\n type: 'doughnut',\n data: {\n labels: rows.map(r => r[0]),\n datasets: [{\n data: rows.map(r => totalPrice * r[1]),\n backgroundColor: rows.map(r => r[2]),\n borderWidth: 0,\n hoverOffset: 4\n }]\n },\n options: {\n responsive: true,\n cutout: '70%',\n plugins: {\n legend: { display: false },\n tooltip: { callbacks: { label: c => `${c.label}: ${fmt.format(c.raw)}` } }\n }\n }\n });\n\n document.getElementById('costBreakdownLegend').innerHTML =\n '<ul style=\"list-style:none;padding:0;margin:0;\">' +\n rows.map(([label, pct, color]) => `\n <li style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;font-size:14px;\">\n <span style=\"display:flex;align-items:center;\">\n <span style=\"width:12px;height:12px;border-radius:50%;background-color:${color};margin-right:10px;\"></span>\n ${label}\n </span>\n <span style=\"font-weight:500;\">${(pct * 100).toFixed(0)}%</span>\n </li>`).join('') +\n '</ul>';\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init);\n } else {\n init();\n }\n})();\n</script>", |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Summary:
Why are these changes introduced?
Fixes #0.
What approach did you take?
Other considerations
Decision log
Visual impact on existing themes
Testing steps/scenarios
Demo links
Checklist