diff --git a/rfcs/084-shopify-integration-strategies/README.md b/rfcs/084-shopify-integration-strategies/README.md new file mode 100644 index 00000000..db6c82eb --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/README.md @@ -0,0 +1,302 @@ +# Shopify Integration Approaches for Wellcome Collection + +**Research Focus:** Evaluating different approaches for integrating Shopify with wellcomecollection.org + +## Summary + +This research outlines five approaches for integrating Shopify with the Wellcome Collection website, ranging from simple embedded solutions to fully headless implementations. + +## Current Technical Context + +The Wellcome Collection website is built as a Next.js monorepo with: +- React-based frontend (Next.js) +- Styled-components for styling +- Prismic CMS for content management +- Yarn workspaces architecture +- Strong emphasis on performance optimisation and accessibility + +## Initial Requirements +At first the only things to be sold via the shop will be exclusive Wellcome Collection ranges (wellcome collection books, wellcome collection toys, and exhibition ranges). This may change in the future. + +## Integration Approaches + +### Option 1: Shopify Buy Button + +**Description:** Embeds Shopify products directly into existing pages using JavaScript widgets. + +**Implementation:** +- Add Shopify-generated code snippets to relevant pages +- Products display in pop-up or redirect to Shopify checkout +- Minimal development effort required + +**Advantages:** +- Quick implementation (hours to days) +- Low cost +- No complex integration required +- Shopify handles all checkout and payment processing +- Suitable for testing e-commerce viability + +**Disadvantages:** +- Limited design customisation +- Checkout redirects to Shopify domain breaking brand continuity +- Inconsistent user experience +- Limited control over customer journey +- Not suitable for substantial product catalogues + +**Best For:** Testing e-commerce concept, limited product offerings, content-heavy sites with occasional sales + +**Sources:** +- [Shopify Buy Button](https://www.shopify.com/buy-button) +- [Buy Button FAQ](https://help.shopify.com/en/manual/online-sales-channels/buy-button/faq) + +--- + +### Option 2: Embedded Shopify Storefront + +**Description:** Creates a separate Shopify store accessible via subdomain or integrated section whilst maintaining primary site navigation. + +**Implementation:** +- Standard Shopify theme on `shop.wellcomecollection.org` or `/shop` path +- Create custom Liquid theme to match brand +- Link from main site to shop + +**Advantages:** +- Full Shopify feature set available +- Extensive app ecosystem +- Proven reliability for e-commerce operations +- Professional checkout experience +- Comprehensive analytics and reporting +- Easy to hand over management to a different team if needed +- There's only one place to search (no confusion around whether you're searching the shop or the main site) + + +**Disadvantages:** +- Inconsistent user experience between main site and shop (although this is a common pattern in the sector and unlikely to cause significant issues) +- Duplicate navigation/header/footer/component maintenance +- Users perceive being taken away from main site +- SEO considerations for separate domain/path +- Theme customisation limited by Liquid templating + +**Best For:** Organisations prioritising speed to market, those without significant development resources, when complete feature parity with Shopify ecosystem is required + + + +There is a skeleton Liquid theme available [here](./skeleton-theme) showing how products and categories could be grouped and displayed, with a simple cart and checkout flow (video above). We would have to write seperate css to style a theme created in this way, but we should be able to import and use the [existing design system](https://github.com/wellcometrust/wellcome-design-system/tree/main) for spacing, typography, grid, and colour variables to help ensure consistency at least. + +Probably the biggest advantage of this approach is that it would be the easiest to hand over to a different team to manage in the future, as it would be a more standard standalone Shopify implementation and as such wouldn't need to be unpicked from our existing codebase. + +--- + +### Option 3: Headless Commerce with Shopify Storefront API + +**Description:** Uses Shopify as backend commerce engine whilst building custom frontend using Storefront API (GraphQL). + +**Implementation:** +- Shopify manages products, inventory, orders, customers +- Custom React components in Next.js fetch data via the Storefront API +- Checkout can be embedded or redirect to Shopify +- Full control over frontend presentation + +**Technical Architecture:** +``` +Next.js Frontend (wellcomecollection.org) + ↓ + Storefront API (GraphQL) + ↓ +Shopify Backend (products, orders, inventory) + ↓ + Shopify Checkout +``` + +**Key Implementation Details:** +- GraphQL API with no request-count rate limits +- Versioned API updated quarterly +- Public and private access tokens via Headless channel + +**Advantages:** +- Complete design control and brand consistency +- Seamless user experience within existing site +- Leverage existing Next.js expertise +- Optimised performance (fetch only required data) +- Scalable architecture +- Aligns with existing strategies (composable, API-first) +- Lends itself to iteration + +**Disadvantages:** +- Higher development cost +- Requires Next.js knowledge +- Ongoing maintenance responsibility +- Checkout still redirects to Shopify domain +- More complex than out-of-box solutions +- Potentially complicates site-search and user expectations + +Robert put forward a relatively major concern with this approach: + +> I'm concerned the recommended approach will add a maintenance overhead that won't be sustainable, and might cause issues in the future if we want to resource changes to the shop outside of the experience team. Adding the shop also adds a slew of product concerns that will come with requirements and requests for changes - if we couple these to our development process tightly I think we risk issues with delivery + +If we were to go with this approach, we should ensure: +1. We have full control of product direction +2. The experience team will be resourced indefinitely to take on the extra maintenance burden of the new product area +3. We should consider whether it belongs as its own service rather than being built directly into the content app + +It is still unknown if we want to allow for user accounts, or always use guest checkout. Maintaining user accounts ourselves would add a significant amount of complexity and if they were required, we might want to consider using Shopify's customer accounts functionality, which might add some friction to the ux but would be a lot easier to implement and maintain than building our own. + +**Development Approach:** +1. Set up Shopify Headless channel and generate API tokens +2. Configure environment variables in Next.js +3. Create GraphQL queries for products, collections, cart +4. Build React components for product display, cart, checkout(?) flow +5. Implement state management for cart functionality +6. Integrate with existing design system/component library +7. Add analytics and tracking + +**Best For:** Organisations with development resources, those requiring brand consistency, sites with existing sophisticated frontend, when user experience is paramount + +**Sources:** +- [Shopify Headless Commerce](https://www.shopify.com/plus/solutions/headless-commerce) +- [Building with the Storefront API](https://shopify.dev/docs/storefronts/headless/building-with-the-storefront-api) +- [Storefront API Reference](https://shopify.dev/docs/api/storefront/latest) +- [Building Ecommerce Sites with Next.js and Shopify](https://vercel.com/kb/guide/building-ecommerce-sites-with-next-js-and-shopify) +- [Good and Bad of Headless Commerce with Shopify](https://www.plytix.com/blog/headless-commerce-with-shopify) +- [Headless Commerce vs Traditional Commerce](https://www.shopify.com/enterprise/blog/headless-commerce-vs-traditional-commerce) + +--- + +### Option 4: Hydrogen Framework (Shopify's React/Remix Framework) + +**Description:** Shopify's official React-based framework specifically built for headless commerce. + +**Implementation:** +- Use Hydrogen as frontend framework instead of custom Next.js integration +- Pre-built commerce components and hooks +- Oxygen hosting for global deployment (free with Shopify) +- Optimised for Shopify's commerce patterns + +**Advantages:** +- Accelerated development with pre-built components +- Built-in Shopify integrations and best practices +- Optimised for performance at global scale +- Regular updates from Shopify +- Shopify-specific tooling and conventions +- Free global hosting with Oxygen + +**Disadvantages:** +- Introduces different framework from existing Next.js +- Would require site migration or dual-framework approach +- Less flexibility for non-commerce features +- Smaller community compared to Next.js +- Oxygen deployment limitations (one storefront per store, limited logging) +- Learning curve for team familiar with Next.js +- Not suitable if requiring extensive customisation beyond e-commerce + +**Best For:** New projects, shops with minimal non-commerce content, teams starting fresh without existing frontend constraints + +**Sources:** +- [Hydrogen: Shopify's headless commerce framework](https://hydrogen.shopify.dev/) +- [Shopify Headless Commerce: A Complete Guide](https://litextension.com/blog/shopify-headless/) + +--- + +### Option 5: Shopify Plus with Custom Checkout Extensibility + +**Description:** Shopify Plus plan with extensive checkout customisation using Checkout UI Extensions. + +**Implementation:** +- Shopify Plus subscription (£££) +- Custom checkout steps using UI Extensions API +- Full Shopify feature set with enhanced customisation +- Can maintain brand experience through checkout + +**Technical Details:** +- Checkout UI Extensions +- Custom functionality for product offers, fields, loyalty programmes +- Extensions available for information, shipping, payment, order summary steps + +**Advantages:** +- Branded checkout experience on Shopify domain +- Access to Plus-only features (scripts, automation, wholesale) +- Dedicated support +- Can customise checkout with UI extensions + +**Disadvantages:** +- Significant ongoing cost +- May be over-specification for smaller operations + +**Best For:** Large-scale operations, high-volume sales, organisations requiring extensive Shopify app ecosystem, wholesale capabilities + +**Sources:** +- [Checkout UI Extensions](https://shopify.dev/docs/api/checkout-ui-extensions/latest) + +--- + +## Key Decision Criteria + +### Technical Considerations +- How easy is it to back out/hand over? + - Option 2 would be relatively easy to hand over, as it is a standalone Shopify implementation + - Option 3 would be harder to hand over, as it would be tightly integrated with our existing codebase and would require significant unpicking to hand over to a different team. + +**Existing Architecture:** +- Current Next.js implementation/(and expertise) favours headless approach +- Styled-components design system can extend to commerce components +- TypeScript throughout supports type-safe API integration + +### User Experience Priorities + +**Brand Consistency:** +- Requires cohesive UX/UI +- Headless approaches provide seamless integration +- Checkout on Shopify domain is probably acceptable – it is a familiar/trustworthy user flow. Fully customising checkout ourselves (including managing credit card information, shipping, and taxes etc.) feels like it would probably be building in a wealth of future pain + +**Customer Journey:** +- Content-to-commerce flow should be natural +- Integration with existing site navigation and search + +### Implementation Roadmap for option 3 + +#### Phase 1: Foundation +- Set up Shopify backend +- Configure Headless channel and API access +- Create product catalogue structure +- Design commerce component architecture + +#### Phase 2: Core Features +- Product listing pages +- Product detail pages +- Shopping cart functionality +- Checkout flow (redirect to Shopify) +- Basic search and filtering + +#### Phase 3: Enhanced Experience +- Content-product associations in Prismic +- Advanced search and recommendations +- Analytics integration + +#### Phase 4: Optimisation +- Performance tuning +- A/B testing +- User feedback incorporation +- Additional features based on usage patterns + + +## Technical Implementation Notes + +- It isn’t obvious how to redirect back to wc.org after checkout clicking the ‘Continue shopping’ page, with several unanswered questions from community boards [1](https://community.shopify.com/t/change-continue-shopping-url-on-thank-you-page-headless/403380), [2](https://community.shopify.com/t/change-continue-shopping-url-on-thank-you-page-headless/403380), [3](https://community.shopify.dev/t/headless-shopify-continue-shopping-button-redirects-to-wrong-domain-after-checkout/18950), [4](https://community.shopify.com/t/headless-redirect-after-checkout/397344/3), [5](https://www.reddit.com/r/shopify/comments/1iz2fa0/headless_redirect_after_checkout/), but I made a minimal Liquid theme that just redirects client-side and this seems to work fine. +- I haven’t looked much into [integration with existing Prismic content](https://prismic.io/docs/fields/integration#sync-products-from-a-shopify-store) beyond knowing that it exists. +- I think/presume additional cookies should be added to the functional/required list in order for cart/checkout functionality to work. +- I haven't looked at any Shopify-specific analytics/tracking options. + +## Proof of Concept +The [shopify branch](https://github.com/wellcomecollection/wellcomecollection.org/compare/shopify) of the wc.org monorepo contains a minimal proof of concept implementation of Option 3 (Headless Commerce with Storefront API). It demonstrates fetching products and variants from Shopify and displaying them on a Next.js page, with a cart and checkout flow. + + + +**Sources:** +- [GraphQL Storefront API](https://shopify.dev/docs/api/storefront/latest) +- [GraphiQL explorer](https://shopify.dev/docs/api/usage/api-exploration/admin-graphiql-explorer) +- [Getting Started with GraphQL](https://www.shopify.com/partners/blog/getting-started-with-graphql) +- [Working with Shopify Storefront API](https://medium.com/@sandeeppangeni17/working-with-shopify-storefront-api-graphql-javascript-e02fb89eb682) + +--- diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/.shopifyignore b/rfcs/084-shopify-integration-strategies/skeleton-theme/.shopifyignore new file mode 100644 index 00000000..59a11a13 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/.shopifyignore @@ -0,0 +1,13 @@ +# This file contains a list of files that will be excluded from Shopify CLI +# operations such as push, pull, dev, etc. +# +# Examples: +# +# Ignore a specific file: +# templates/product.json +# +# Ignore templates with a wildcard pattern: +# templates/*.json +# +# Ignore templates and sections with a regular expression: +# /(templates|sections)/.*\.json/ diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/.theme-check.yml b/rfcs/084-shopify-integration-strategies/skeleton-theme/.theme-check.yml new file mode 100644 index 00000000..f9576a6c --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/.theme-check.yml @@ -0,0 +1 @@ +extends: theme-check:recommended diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/LICENSE.md b/rfcs/084-shopify-integration-strategies/skeleton-theme/LICENSE.md new file mode 100644 index 00000000..611b31d0 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/LICENSE.md @@ -0,0 +1,9 @@ +Copyright (c) 2018-present Shopify Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, sell and/or create derivative works of the Software or any part thereof, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The rights granted above may only be exercised to develop themes that integrate or interoperate with Shopify software or services, and, if applicable, to distribute, offer for sale or otherwise make available any such themes via the Shopify Theme Store. All other uses of the Software are strictly prohibited. + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/README.md b/rfcs/084-shopify-integration-strategies/skeleton-theme/README.md new file mode 100644 index 00000000..eaf59b2c --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/README.md @@ -0,0 +1,160 @@ +

+
+ logo +
+ Shopify Skeleton Theme +

+ +A minimal, carefully structured Shopify theme designed to help you quickly get started. Designed with modularity, maintainability, and Shopify's best practices in mind. + +

+ License + CI +

+ +## Getting started + +### Prerequisites + +Before starting, ensure you have the latest Shopify CLI installed: + +- [Shopify CLI](https://shopify.dev/docs/api/shopify-cli) – helps you download, upload, preview themes, and streamline your workflows + +If you use VS Code: + +- [Shopify Liquid VS Code Extension](https://shopify.dev/docs/storefronts/themes/tools/shopify-liquid-vscode) – provides syntax highlighting, linting, inline documentation, and auto-completion specifically designed for Liquid templates + +### Clone + +Clone this repository using Git or Shopify CLI: + +```bash +git clone git@github.com:Shopify/skeleton-theme.git +# or +shopify theme init +``` + +### Preview + +Preview this theme using Shopify CLI: + +```bash +shopify theme dev +``` + +## Theme architecture + +```bash +. +├── assets # Stores static assets (CSS, JS, images, fonts, etc.) +├── blocks # Reusable, nestable, customizable UI components +├── config # Global theme settings and customization options +├── layout # Top-level wrappers for pages (layout templates) +├── locales # Translation files for theme internationalization +├── sections # Modular full-width page components +├── snippets # Reusable Liquid code or HTML fragments +└── templates # Templates combining sections to define page structures +``` + +To learn more, refer to the [theme architecture documentation](https://shopify.dev/docs/storefronts/themes/architecture). + +### Templates + +[Templates](https://shopify.dev/docs/storefronts/themes/architecture/templates#template-types) control what's rendered on each type of page in a theme. + +The Skeleton Theme scaffolds [JSON templates](https://shopify.dev/docs/storefronts/themes/architecture/templates/json-templates) to make it easy for merchants to customize their store. + +None of the template types are required, and not all of them are included in the Skeleton Theme. Refer to the [template types reference](https://shopify.dev/docs/storefronts/themes/architecture/templates#template-types) for a full list. + +### Sections + +[Sections](https://shopify.dev/docs/storefronts/themes/architecture/sections) are Liquid files that allow you to create reusable modules of content that can be customized by merchants. They can also include blocks which allow merchants to add, remove, and reorder content within a section. + +Sections are made customizable by including a `{% schema %}` in the body. For more information, refer to the [section schema documentation](https://shopify.dev/docs/storefronts/themes/architecture/sections/section-schema). + +### Blocks + +[Blocks](https://shopify.dev/docs/storefronts/themes/architecture/blocks) let developers create flexible layouts by breaking down sections into smaller, reusable pieces of Liquid. Each block has its own set of settings, and can be added, removed, and reordered within a section. + +Blocks are made customizable by including a `{% schema %}` in the body. For more information, refer to the [block schema documentation](https://shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/schema). + +## Schemas + +When developing components defined by schema settings, we recommend these guidelines to simplify your code: + +- **Single property settings**: For settings that correspond to a single CSS property, use CSS variables: + + ```liquid +
+ ... +
+ + {% stylesheet %} + .collection { + gap: var(--gap); + } + {% endstylesheet %} + + {% schema %} + { + "settings": [{ + "type": "range", + "label": "gap", + "id": "gap", + "min": 0, + "max": 100, + "unit": "px", + "default": 0, + }] + } + {% endschema %} + ``` + +- **Multiple property settings**: For settings that control multiple CSS properties, use CSS classes: + + ```liquid +
+ ... +
+ + {% stylesheet %} + .collection--full-width { + /* multiple styles */ + } + .collection--narrow { + /* multiple styles */ + } + {% endstylesheet %} + + {% schema %} + { + "settings": [{ + "type": "select", + "id": "layout", + "label": "layout", + "values": [ + { "value": "collection--full-width", "label": "t:options.full" }, + { "value": "collection--narrow", "label": "t:options.narrow" } + ] + }] + } + {% endschema %} + ``` + +## CSS & JavaScript + +For CSS and JavaScript, we recommend using the [`{% stylesheet %}`](https://shopify.dev/docs/api/liquid/tags#stylesheet) and [`{% javascript %}`](https://shopify.dev/docs/api/liquid/tags/javascript) tags. They can be included multiple times, but the code will only appear once. + +### `critical.css` + +The Skeleton Theme explicitly separates essential CSS necessary for every page into a dedicated `critical.css` file. + +## Contributing + +We're excited for your contributions to the Skeleton Theme! This repository aims to remain as lean, lightweight, and fundamental as possible, and we kindly ask your contributions to align with this intention. + +Visit our [CONTRIBUTING.md](./CONTRIBUTING.md) for a detailed overview of our process, guidelines, and recommendations. + +## License + +Skeleton Theme is open-sourced under the [MIT](./LICENSE.md) License. diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/critical.css b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/critical.css new file mode 100644 index 00000000..d1429158 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/critical.css @@ -0,0 +1,192 @@ +/** Critical CSS for the theme. This file is included on every page. */ + +/* Reset styles inspired by https://www.joshwcomeau.com/css/custom-css-reset/ */ +* { + box-sizing: border-box; + margin: 0; +} + +body { + display: flex; + flex-direction: column; + margin: 0; + min-height: 100svh; +} + +html:has(dialog[scroll-lock][open], details[scroll-lock][open]) { + overflow: hidden; +} + +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; + height: auto; +} + +input, +textarea, +select { + font: inherit; + border-radius: var(--style-border-radius-inputs); +} + +select { + background-color: var(--color-background); + color: currentcolor; +} + +dialog { + background-color: var(--color-background); + color: var(--color-foreground); +} + +p { + text-wrap: pretty; +} +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +p:empty { + display: none; +} + +:is(p, h1, h2, h3, h4, h5, h6):first-child, +:empty:first-child + :where(p, h1, h2, h3, h4, h5, h6) { + margin-block-start: 0; +} + +:is(p, h1, h2, h3, h4, h5, h6):last-child, +:where(p, h1, h2, h3, h4, h5, h6) + :has(+ :empty:last-child) { + margin-block-end: 0; +} + +/** Theme styles below */ +body { + font-family: var(--font-body--family); + background-color: var(--color-background); + color: var(--color-foreground); +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-heading--family); +} + +/** Section layout utilities */ + +/** + * Setup a grid that enables both full-width and constrained layouts + * depending on the class of the child elements. + * + * By default, a minimum content margin is set on the left and right + * sides of the section and the content is centered in the viewport to + * not exceed the maximum page width. + * + * When a child element is given the `full-width` class, it will span + * the entire viewport. + */ +.shopify-section { + --content-width: min( + calc(var(--page-width) - var(--page-margin) * 2), + calc(100% - var(--page-margin) * 2) + ); + --content-margin: minmax(var(--page-margin), 1fr); + --content-grid: var(--content-margin) var(--content-width) var(--content-margin); + + /* This is required to make elements work as background images */ + position: relative; + grid-template-columns: var(--content-grid); + display: grid; + width: 100%; +} + +/* Child elements, by default, are constrained to the central column of the grid. */ +.shopify-section > * { + grid-column: 2; +} + +/* Child elements that use the full-width utility class span the entire viewport. */ +.shopify-section > .full-width { + grid-column: 1 / -1; +} + +.shopify-section-group-header-group { + border-bottom: 1px solid #ccc; + margin-bottom: 2rem; +} + +/** Card grid layout */ +.highlights { + display: grid; + gap: 2rem; + grid-template-columns: repeat(3, 1fr); + margin-top: 2rem; +} + +@media (max-width: 1100px) { + .highlights { + grid-template-columns: 1fr; + } +} + +.highlight { + display: flex; + flex-direction: column; + height: 100%; + border-radius: 8px; + background-color: #edece4; + color: rgb(92, 95, 98); + line-height: 1.4; + text-decoration: none; + overflow: hidden; +} + +.highlight > * { + padding-inline: 24px; +} + +.highlight > *:first-child { + padding-top: 24px; +} + +.highlight > *:last-child { + padding-bottom: 24px; +} + +.highlight > * + * { + margin-top: 1rem; +} + +.highlight > .image { + padding-inline: 0; + padding-top: 0; +} + +.highlight h3 { + font-size: 1rem; + color: rgb(32, 34, 35); +} + +.highlight-description { + flex: 1 1; +} + +.section-heading::before { + content: ''; + width: 64px; + height: 18px; + background: #ffce3c; + display: block; + margin-bottom: 1rem; + +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/icon-account.svg b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/icon-account.svg new file mode 100644 index 00000000..ed2687e4 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/icon-account.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/icon-cart.svg b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/icon-cart.svg new file mode 100644 index 00000000..0a8d0a9b --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/icon-cart.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/logo-wellcome-collection.svg b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/logo-wellcome-collection.svg new file mode 100644 index 00000000..4bb6f1fa --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/logo-wellcome-collection.svg @@ -0,0 +1,22 @@ + + Wellcome Collection + + + + + + + + + + + + + + + + + + + + diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/shoppy-x-ray.svg b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/shoppy-x-ray.svg new file mode 100644 index 00000000..6937b3c0 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/assets/shoppy-x-ray.svg @@ -0,0 +1 @@ + diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/blocks/group.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/blocks/group.liquid new file mode 100644 index 00000000..baea31c5 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/blocks/group.liquid @@ -0,0 +1,105 @@ +{% doc %} + Renders a group of blocks with configurable layout direction, gap and + alignment. + + All settings apply to only one dimension to reduce configuration complexity. + + This component is a wrapper concerned only with rendering its children in + the specified layout direction with appropriate padding and alignment. + + @example + {% content_for 'block', type: 'group', id: 'group' %} +{% enddoc %} + +
+ {% content_for 'blocks' %} +
+ +{% stylesheet %} + .group { + display: flex; + flex-wrap: nowrap; + overflow: hidden; + width: 100%; + } + + .group--horizontal { + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 0 var(--padding); + } + + .group--vertical { + flex-direction: column; + align-items: var(--alignment); + padding: var(--padding) 0; + } +{% endstylesheet %} + +{% schema %} +{ + "name": "t:general.group", + "blocks": [{ "type": "@theme" }], + "settings": [ + { + "type": "select", + "id": "layout_direction", + "label": "t:labels.layout_direction", + "default": "group--vertical", + "options": [ + { "value": "group--horizontal", "label": "t:options.direction.horizontal" }, + { "value": "group--vertical", "label": "t:options.direction.vertical" } + ] + }, + { + "visible_if": "{{ block.settings.layout_direction == 'group--vertical' }}", + "type": "select", + "id": "alignment", + "label": "t:labels.alignment", + "default": "flex-start", + "options": [ + { "value": "flex-start", "label": "t:options.alignment.left" }, + { "value": "center", "label": "t:options.alignment.center" }, + { "value": "flex-end", "label": "t:options.alignment.right" } + ] + }, + { + "type": "range", + "id": "padding", + "label": "t:labels.padding", + "default": 0, + "min": 0, + "max": 200, + "step": 2, + "unit": "px" + } + ], + "presets": [ + { + "name": "t:general.column", + "category": "t:general.layout", + "settings": { + "layout_direction": "group--vertical", + "alignment": "flex-start", + "padding": 0 + } + }, + { + "name": "t:general.row", + "category": "t:general.layout", + "settings": { + "layout_direction": "group--horizontal", + "padding": 0 + } + } + ] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/blocks/text.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/blocks/text.liquid new file mode 100644 index 00000000..b985c389 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/blocks/text.liquid @@ -0,0 +1,59 @@ +{% doc %} + Renders a text block. + + @example + {% content_for 'block', type: 'text', id: 'text' %} +{% enddoc %} + +
+ {{ block.settings.text }} +
+ +{% stylesheet %} + .text { + text-align: var(--text-align); + } + .text--title { + font-size: 2rem; + font-weight: 700; + } + .text--subtitle { + font-size: 1.5rem; + } +{% endstylesheet %} + +{% schema %} +{ + "name": "t:general.text", + "settings": [ + { + "type": "text", + "id": "text", + "label": "t:labels.text", + "default": "Text" + }, + { + "type": "select", + "id": "text_style", + "label": "t:labels.text_style", + "options": [ + { "value": "text--title", "label": "t:options.text_style.title" }, + { "value": "text--subtitle", "label": "t:options.text_style.subtitle" }, + { "value": "text--normal", "label": "t:options.text_style.normal" } + ], + "default": "text--title" + }, + { + "type": "text_alignment", + "id": "alignment", + "label": "t:labels.alignment", + "default": "left" + } + ], + "presets": [{ "name": "t:general.text" }] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/config/settings_data.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/config/settings_data.json new file mode 100644 index 00000000..512f358e --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/config/settings_data.json @@ -0,0 +1,12 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "current": {} +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/config/settings_schema.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/config/settings_schema.json new file mode 100644 index 00000000..8d3b532d --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/config/settings_schema.json @@ -0,0 +1,83 @@ +[ + { + "name": "theme_info", + "theme_name": "Skeleton", + "theme_version": "0.1.0", + "theme_author": "Shopify", + "theme_documentation_url": "https://help.shopify.com/manual/online-store/themes", + "theme_support_url": "https://support.shopify.com/" + }, + { + "name": "t:general.typography", + "settings": [ + { + "type": "header", + "content": "t:general.fonts" + }, + { + "type": "font_picker", + "id": "type_primary_font", + "default": "work_sans_n4", + "label": "t:general.primary" + } + ] + }, + { + "name": "t:general.layout", + "settings": [ + { + "type": "select", + "id": "max_page_width", + "label": "t:labels.page_width", + "options": [ + { + "value": "90rem", + "label": "t:options.page_width.narrow" + }, + { + "value": "110rem", + "label": "t:options.page_width.wide" + } + ], + "default": "90rem" + }, + { + "type": "range", + "id": "min_page_margin", + "min": 10, + "max": 100, + "step": 1, + "unit": "px", + "label": "t:labels.page_margin", + "default": 20 + } + ] + }, + { + "name": "t:general.colors", + "settings": [ + { + "type": "color", + "id": "background_color", + "default": "#FFFFFF", + "label": "t:labels.background" + }, + { + "type": "color", + "id": "foreground_color", + "default": "#333333", + "label": "t:labels.foreground" + }, + { + "type": "range", + "id": "input_corner_radius", + "min": 0, + "max": 10, + "step": 1, + "unit": "px", + "label": "t:labels.input_corner_radius", + "default": 4 + } + ] + } +] diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/layout/password.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/layout/password.liquid new file mode 100644 index 00000000..350ec0bd --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/layout/password.liquid @@ -0,0 +1,19 @@ + + + + {% # Inlined CSS Variables %} + {% render 'css-variables' %} + + {% # Load and preload the critical CSS %} + {{ 'critical.css' | asset_url | stylesheet_tag: preload: true }} + + {% # Social, title, etc. %} + {% render 'meta-tags' %} + + {{ content_for_header }} + + + + {{ content_for_layout }} + + diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/layout/theme.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/layout/theme.liquid new file mode 100644 index 00000000..8123ab78 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/layout/theme.liquid @@ -0,0 +1,23 @@ + + + + {% # Inlined CSS Variables %} + {% render 'css-variables' %} + + {% # Load and preload the critical CSS %} + {{ 'critical.css' | asset_url | stylesheet_tag: preload: true }} + + {% # Social, title, etc. %} + {% render 'meta-tags' %} + + {{ content_for_header }} + + + + {% sections 'header-group' %} + + {{ content_for_layout }} + + {% sections 'footer-group' %} + + diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/locales/en.default.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/locales/en.default.json new file mode 100644 index 00000000..3a43570d --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/locales/en.default.json @@ -0,0 +1,52 @@ +{ + "404": { + "title": "404", + "not_found": "Page not found.", + "back_to_shopping": "Back to shopping" + }, + "blog": { + "article_comments": "Comments", + "article_metadata_html": "{{ date }} by {{ author }}", + "comment_form_body": "Comment", + "comment_form_email": "Email", + "comment_form_name": "Name", + "comment_form_submit": "Post", + "comment_form_title": "Add a comment" + }, + "cart": { + "checkout": "Checkout", + "title": "Cart", + "update": "Update", + "remove": "Remove" + }, + "customers": { + "login": { + "email": "Email", + "password": "Password", + "submit": "Sign in", + "title": "Login" + } + }, + "collections": { + "title": "Collections" + }, + "gift_card": { + "add_to_apple_wallet": "Add to Apple Wallet", + "card": "Gift card", + "expired": "This gift card has expired", + "expires_on": "Expires on {{ expires_on }}", + "use_at_checkout": "Use this gift card at checkout" + }, + "password": { + "title": "This shop is private", + "password": "Password", + "enter": "Enter" + }, + "search": { + "title": "Search", + "placeholder": "Search articles, pages, or products", + "submit": "Search", + "no_results_html": "No results found for {{ terms }}", + "results_for_html": "{{ count }} results found for {{ terms }}" + } +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/locales/en.default.schema.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/locales/en.default.schema.json new file mode 100644 index 00000000..a2fa8680 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/locales/en.default.schema.json @@ -0,0 +1,76 @@ +{ + "general": { + "404": "Not found", + "article": "Article", + "block": "Block", + "blog": "Blog", + "cart": "Cart", + "collection": "Collection", + "collections": "Collections", + "collection_card": "Collection card", + "collections_grid": "Collections grid", + "colors": "Colors", + "column": "Column", + "content": "Content", + "custom_section": "Custom section", + "fonts": "Fonts", + "footer": "Footer", + "group": "Group", + "header": "Header", + "layout": "Layout", + "main": "Main", + "page": "Page", + "password": "Password", + "primary": "Primary", + "product": "Product", + "row": "Row", + "search": "Search", + "secondary": "Secondary", + "section": "Section", + "settings": "Settings", + "sidebar": "Sidebar", + "text": "Text", + "theme": "Theme", + "typography": "Typography" + }, + "labels": { + "alignment": "Alignment", + "background": "Background", + "foreground": "Foreground", + "grid_gap": "Grid spacing", + "grid_item_width": "Grid item width", + "input_corner_radius": "Input corner radius", + "layout_direction": "Layout direction", + "menu": "Menu", + "padding": "Padding", + "page_margin": "Page margin", + "page_width": "Page width", + "show_payment_icons": "Show payment icons", + "text_style": "Text style", + "text": "Text" + }, + "options": { + "alignment": { + "left": "Left", + "center": "Center", + "right": "Right" + }, + "direction": { + "horizontal": "Horizontal", + "vertical": "Vertical" + }, + "page_width": { + "narrow": "Narrow", + "wide": "Wide" + }, + "size": { + "large": "Large", + "small": "Small" + }, + "text_style": { + "title": "Title", + "subtitle": "Subtitle", + "normal": "Normal text" + }, + } +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/404.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/404.liquid new file mode 100644 index 00000000..076cfc91 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/404.liquid @@ -0,0 +1,23 @@ +{% comment %} + This section is used in the 404 template to render page shown when customers + enter an invalid store URL. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/404 +{% endcomment %} + +

{{ '404.title' | t }}

+ +

+ {{ '404.not_found' | t }} +

+ + + {{ '404.back_to_shopping' | t }} + + +{% schema %} +{ + "name": "t:general.404", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/article.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/article.liquid new file mode 100644 index 00000000..146acd1e --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/article.liquid @@ -0,0 +1,68 @@ +{% comment %} + This section is used in the article template to render the article page, which + displays the full content of the blog post and can optionally include a + comments section for customers (not shown in this example). + + https://shopify.dev/docs/storefronts/themes/architecture/templates/article +{% endcomment %} + +{% if article.image %} + {{ article.image | image_url: width: 1000 | image_tag }} +{% endif %} + +

{{ article.title }}

+ +{% assign date = article.published_at | time_tag: format: 'date' %} +

{{ 'blog.article_metadata_html' | t: date: date, author: article.author }}

+ +{{ article.content }} + +{% if blog.comments_enabled? %} +

{{ 'blog.article_comments' | t }}

+ +
+ {% paginate article.comments by 10 %} + {% for comment in article.comments %} +
+

{{ comment.author }}

+

+ {{- comment.created_at | time_tag: format: 'date' -}} +

+

{{ comment.content }}

+
+ {% endfor %} + + {{ paginate | default_pagination: anchor: 'comments' }} + {% endpaginate %} +
+ + {% form 'new_comment', article %} +

{{ 'blog.comment_form_title' | t }}

+ + {{ form.errors | default_errors }} + +
+ + +
+ +
+ + +
+ +
+ + +
+ + + {% endform %} +{% endif %} + +{% schema %} +{ + "name": "t:general.article", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/blog.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/blog.liquid new file mode 100644 index 00000000..2dba086c --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/blog.liquid @@ -0,0 +1,37 @@ +{% comment %} + This section is used in the blog template to render the blog page listing all + articles within a blog. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/blog +{% endcomment %} + +

{{ blog.title }}

+ +
+ {% paginate blog.articles by 5 %} + {% for article in blog.articles %} + + {% if article.image %} + {% render 'image', class: 'highlight__image', image: article.image, width: 600 %} + {% endif %} +

{{ article.title }}

+ {% assign date = article.published_at | time_tag: format: 'date' %} +

{{ 'blog.article_metadata_html' | t: date: date, author: article.author }}

+ {% if article.excerpt != blank %} +

{{ article.excerpt | strip_html | truncatewords: 15 }}

+ {% endif %} +
+ {% endfor %} + + {%- if paginate.pages > 1 -%} + {{- paginate | default_pagination -}} + {%- endif -%} + {% endpaginate %} +
+ +{% schema %} +{ + "name": "t:general.blog", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/cart.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/cart.liquid new file mode 100644 index 00000000..6d7e4984 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/cart.liquid @@ -0,0 +1,37 @@ +{% comment %} + This section is used in the cart template to render /cart page with an + overview of the items in customer's cart. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/cart +{% endcomment %} + +

{{ 'cart.title' | t }}

+ +
+ + {% for item in cart.items %} + + + + + + {% endfor %} +
+ {% render 'image', image: item.image, url: item.url %} + +

{{ item.product.title }}

+ {{ 'cart.remove' | t | link_to: item.url_to_remove }} +
+ + +
+ + +
+ +{% schema %} +{ + "name": "t:general.cart", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/collection.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/collection.liquid new file mode 100644 index 00000000..6a1effbe --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/collection.liquid @@ -0,0 +1,37 @@ +{% comment %} + This section is used in the collection template to render collection page + listing all products within a collection. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/collection +{% endcomment %} + +

{{ collection.title }}

+ +
+ {% paginate collection.products by 20 %} + {% for product in collection.products %} + + {% if product.featured_image %} + {% render 'image', + class: 'highlight__image', + image: product.featured_image, + width: 400, + height: 400, + crop: 'center' + %} + {% endif %} +

{{ product.title | escape }}

+

{{ product.price | money }}

+
+ {% endfor %} + + {{ paginate | default_pagination }} + {% endpaginate %} +
+ +{% schema %} +{ + "name": "t:general.collection", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/collections.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/collections.liquid new file mode 100644 index 00000000..1d0c9123 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/collections.liquid @@ -0,0 +1,38 @@ +{% comment %} + This section is used in the list-collections template to render a list of + collections. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/collection +{% endcomment %} + +

{{ 'collections.title' | t }}

+ +
+ {% for collection in collections %} + + {% if collection.featured_image %} + {% render 'image', + class: 'highlight__image', + image: collection.featured_image, + width: 600, + height: 600, + crop: 'center' + %} + {% endif %} + +

{{ collection.title }}

+ + {% if collection.description %} +

{{ collection.description | strip_html | truncatewords: 15 }}

+ {% endif %} +
+ {% endfor %} +
+ +{% schema %} +{ + "name": "t:general.collections_grid", + "settings": [], + "presets": [{ "name": "t:general.collections_grid" }] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/custom-section.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/custom-section.liquid new file mode 100644 index 00000000..529d497b --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/custom-section.liquid @@ -0,0 +1,60 @@ +
+ {% if section.settings.background_image %} +
+ {{ section.settings.background_image | image_url: width: 2000 | image_tag }} +
+ {% endif %} + +
+ {% content_for 'blocks' %} +
+
+ +{% stylesheet %} + .custom-section { + position: relative; + overflow: hidden; + width: 100%; + } + .custom-section__background { + position: absolute; + width: 100%; + height: 100%; + z-index: -1; + overflow: hidden; + } + .custom-section__background img { + position: absolute; + width: 100%; + height: auto; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + .custom-section__content { + display: grid; + grid-template-columns: var(--content-grid); + } + .custom-section__content > * { + grid-column: 2; + } +{% endstylesheet %} + +{% schema %} +{ + "name": "t:general.custom_section", + "blocks": [{ "type": "@theme" }], + "settings": [ + { + "type": "image_picker", + "id": "background_image", + "label": "t:labels.background" + } + ], + "presets": [ + { + "name": "t:general.custom_section" + } + ] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/footer-group.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/footer-group.json new file mode 100644 index 00000000..6560feb3 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/footer-group.json @@ -0,0 +1,25 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "type": "footer", + "name": "t:general.footer", + "sections": { + "footer": { + "type": "footer", + "settings": { + "menu": "", + "show_payment_icons": true + } + } + }, + "order": [ + "footer" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/footer.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/footer.liquid new file mode 100644 index 00000000..419c0a29 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/footer.liquid @@ -0,0 +1,68 @@ + + +{% stylesheet %} + footer { + background-color: var(--color-foreground); + color: var(--color-background); + padding-block: 2rem; + margin-top: 2rem; + } + footer .footer__inner { + display: flex; + justify-content: space-between; + max-width: calc(var(--page-width) - var(--page-margin) * 2); + margin-inline: auto; + padding-inline: var(--page-margin); + width: 100%; + } + footer a { + text-decoration: none; + color: var(--color-background); + } + footer .footer__links, + footer .footer__payment { + display: flex; + gap: 1rem; + } +{% endstylesheet %} + +{% schema %} +{ + "name": "t:general.footer", + "settings": [ + { + "type": "link_list", + "id": "menu", + "label": "t:labels.menu" + }, + { + "type": "checkbox", + "id": "show_payment_icons", + "label": "t:labels.show_payment_icons", + "default": true + } + ] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/header-group.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/header-group.json new file mode 100644 index 00000000..25ed9071 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/header-group.json @@ -0,0 +1,22 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "type": "header", + "name": "t:general.header", + "sections": { + "header": { + "type": "header", + "settings": {} + } + }, + "order": [ + "header" + ], +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/header.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/header.liquid new file mode 100644 index 00000000..80f1b2cf --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/header.liquid @@ -0,0 +1,70 @@ +
+ + +
+ {% for link in section.settings.menu.links %} + {{ link.title | link_to: link.url }} + {% endfor %} +
+ +
+ {% if shop.customer_accounts_enabled %} + {{ 'icon-account.svg' | inline_asset_content | link_to: routes.account_url }} + {% endif %} + + + {% if cart.item_count > 0 %} + {{ cart.item_count }} + {% endif %} + + {{ 'icon-cart.svg' | inline_asset_content }} + +
+
+ +{% stylesheet %} + header { + height: 5rem; + display: flex; + align-items: center; + justify-content: space-between; + } + header a { + position: relative; + text-decoration: none; + color: var(--color-foreground); + } + header .header__logo svg { + width: 128px; + height: 42px; + } + header a sup { + position: absolute; + left: 100%; + overflow: hidden; + max-width: var(--page-margin); + } + header svg { + width: 2rem; + } + header .header__menu, + header .header__icons { + display: flex; + gap: 1rem; + } +{% endstylesheet %} + +{% schema %} +{ + "name": "t:general.header", + "settings": [ + { + "type": "link_list", + "id": "menu", + "label": "t:labels.menu" + } + ] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/hello-world.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/hello-world.liquid new file mode 100644 index 00000000..e518a897 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/hello-world.liquid @@ -0,0 +1,157 @@ +{% comment %} + Welcome to Shopify theme development! +{% endcomment %} + +

{{ section.settings.heading }}

+ +{% assign featured = section.settings.featured_collection | default: collections.first %} +{% if featured %} + + {% if featured.featured_image %} +
+ {% render 'image', + image: featured.featured_image, + width: 1200, + height: 800, + crop: 'center' + %} +
+ {% endif %} +
+ {% if section.settings.featured_label != blank %} + {{ section.settings.featured_label }} + {% endif %} +

{{ featured.title }}

+ {% if featured.description != blank %} +

{{ featured.description | strip_html | truncatewords: 30 }}

+ {% endif %} +
+
+{% endif %} + +

Stuff in the shop

+ +
+ {% for collection in collections %} + {% if collection.handle == featured.handle %} + {% continue %} + {% endif %} + + {% if collection.featured_image %} + {% render 'image', + class: 'highlight__image', + image: collection.featured_image, + width: 600, + height: 600, + crop: 'center' + %} + {% endif %} + +

{{ collection.title }}

+ + {% if collection.description %} +

{{ collection.description | strip_html | truncatewords: 15 }}

+ {% endif %} +
+ {% endfor %} +
+ +{% stylesheet %} + .hero-heading { + font-size: clamp(2rem, 4vw, 2.5rem); + line-height: 1.15; + max-width: 30ch; + margin-bottom: 2rem; + } + + .hero-card { + display: grid; + grid-template-columns: 3fr 2fr; + text-decoration: none; + color: inherit; + + border-radius: 8px; + margin-bottom: 2rem; + } + + @media (max-width: 768px) { + .hero-card { + grid-template-columns: 1fr; + } + } + + .hero-card__image { + min-height: 400px; + } + + .hero-card__image .image, + .hero-card__image .image img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .hero-card__content { + display: flex; + flex-direction: column; + + gap: 1rem; + padding: 2.5rem; + background-color: #323232; + color: #fff; + transform: translateY(22px); + } + + .hero-card__label { + display: inline-block; + width: fit-content; + padding: 0.2em 0.6em; + background-color: #ffce3c; + color: #121212; + font-weight: 700; + font-size: 0.875rem; + transform: translate(-40px, -62px); + } + + .hero-card__title { + font-size: clamp(1.5rem, 3vw, 2rem); + line-height: 1.2; + color: #fff; + } + + .hero-card__content p { + line-height: 1.5; + color: rgba(255, 255, 255, 0.85); + } +{% endstylesheet %} + +{% schema %} +{ + "name": "Hello World", + "settings": [ + { + "type": "text", + "id": "heading", + "label": "Heading", + "default": "A shop exploring health, human experience, and cuddly viruses" + }, + { + "type": "collection", + "id": "featured_collection", + "label": "New" + }, + { + "type": "text", + "id": "featured_label", + "label": "Featured label", + "default": "New" + } + ], + "presets": [ + { + "name": "Hello World Template", + "category": "Demo" + } + ] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/page.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/page.liquid new file mode 100644 index 00000000..1a3fea12 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/page.liquid @@ -0,0 +1,17 @@ +{% comment %} + This section is used in the page template to render store pages like About us + or Contact us. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/page +{% endcomment %} + +

{{ page.title }}

+ +{{ page.content }} + +{% schema %} +{ + "name": "t:general.page", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/password.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/password.liquid new file mode 100644 index 00000000..6f227218 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/password.liquid @@ -0,0 +1,35 @@ +{% comment %} + This section is used in the password template to render landing page shown + when password protection is applied to a store. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/password +{% endcomment %} + +

{{ 'password.title' | t }}

+ +{% if shop.password_message %} +

{{ shop.password_message }}

+{% endif %} + +{% form 'storefront_password' %} + {% if form.errors %} + {{ form.errors | default_errors }} + {% endif %} + + + + + + +{% endform %} + +{% schema %} +{ + "name": "t:general.password", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/product.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/product.liquid new file mode 100644 index 00000000..9664dfda --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/product.liquid @@ -0,0 +1,52 @@ +{% comment %} + This section is used in the product template to render product page with + media, content, and add-to-cart form. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/product +{% endcomment %} + +
+ {% for image in product.images %} + {% render 'image', class: 'product-image', image: image %} + {% endfor %} +
+ +
+

{{ product.title }}

+

{{ product.price | money }}

+

{{ product.description }}

+
+ +
+ {% form 'product', product %} + {% assign current_variant = product.selected_or_first_available_variant %} + + + + + + + {{ form | payment_button }} + {% endform %} +
+ +{% schema %} +{ + "name": "t:general.product", + "settings": [], + "disabled_on": { + "groups": ["header", "footer"] + } +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/search.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/search.liquid new file mode 100644 index 00000000..898a1b06 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/sections/search.liquid @@ -0,0 +1,53 @@ +{% comment %} + This section is used in the search template to render search results for + products, articles, and pages. + + https://shopify.dev/docs/storefronts/themes/architecture/templates/search +{% endcomment %} + +

{{ 'search.title' | t }}

+ +
+ + +
+ +{% if search.performed %} + {% if search.results_count == 0 %} +

{{ 'search.no_results_html' | t: terms: search.terms }}

+ {% else %} +

{{ 'search.results_for_html' | t: terms: search.terms, count: search.results_count }}

+ +
+ {% paginate search.results by 20 %} + {% for result in search.results %} + + {% assign featured_image = result.featured_image | default: result.image %} + {% if featured_image %} + {% render 'image', class: 'highlight__image', image: featured_image, width: 400 %} + {% endif %} +

{{ result.title }}

+ {% if result.price %} +

{{ result.price | money_with_currency }}

+ {% endif %} +
+ {% endfor %} + + {{ paginate | default_pagination }} + {% endpaginate %} +
+ {% endif %} +{% endif %} + +{% schema %} +{ + "name": "t:general.search", + "settings": [] +} +{% endschema %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/css-variables.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/css-variables.liquid new file mode 100644 index 00000000..763d341e --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/css-variables.liquid @@ -0,0 +1,35 @@ +{% style %} + @font-face { + font-family: 'Wellcome Bold'; + src: local('Wellcome Bold'), local('WellcomeBold'); + font-weight: 700; + font-style: normal; + font-display: swap; + } + + @font-face { + font-family: 'Inter'; + src: local('Inter'); + font-weight: 100 900; + font-style: normal; + font-display: swap; + } + + @font-face { + font-family: 'Inter'; + src: local('Inter Italic'), local('Inter-Italic'); + font-weight: 100 900; + font-style: italic; + font-display: swap; + } + + :root { + --font-heading--family: 'Wellcome Bold', sans-serif; + --font-body--family: 'Inter', sans-serif; + --page-width: {{ settings.max_page_width }}; + --page-margin: {{ settings.min_page_margin }}px; + --color-background: {{ settings.background_color }}; + --color-foreground: {{ settings.foreground_color }}; + --style-border-radius-inputs: {{ settings.input_corner_radius }}px; + } +{% endstyle %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/image.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/image.liquid new file mode 100644 index 00000000..9ea580a8 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/image.liquid @@ -0,0 +1,64 @@ +{% doc %} + Renders a responsive image that might be wrapped in a link. + + When `width`, `height` and `crop` are provided, the image will be rendered + with a fixed aspect ratio. + + Serves as an example of how to use the `image_url` filter and `image_tag` filter + as well as how you can use LiquidDoc to document your code. + + @param {image} image - The image to be rendered + @param {string} [url] - An optional destination URL for the image + @param {string} [class] - Optional class to be added to the image wrapper + @param {number} [width] - The highest resolution width of the image to be rendered + @param {number} [height] - The highest resolution height of the image to be rendered + @param {string} [crop] - The crop position of the image + + @example + {% render 'image', image: product.featured_image %} + {% render 'image', image: product.featured_image, url: product.url %} + {% render 'image', + class: 'product__image', + image: product.featured_image, + url: product.url, + width: 1200, + height: 800, + crop: 'center', + %} +{% enddoc %} + +{% liquid + unless height + assign width = width | default: image.width + endunless + + if url + assign wrapper = 'a' + else + assign wrapper = 'div' + endif +%} + +<{{ wrapper }} + class="image {{ class }}" + {% if url %} + href="{{ url }}" + {% endif %} +> + {{ image | image_url: width: width, height: height, crop: crop | image_tag }} + + +{% stylesheet %} + .image { + display: block; + position: relative; + overflow: hidden; + width: 100%; + height: auto; + } + + .image > img { + width: 100%; + height: auto; + } +{% endstylesheet %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/meta-tags.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/meta-tags.liquid new file mode 100644 index 00000000..67299c04 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/snippets/meta-tags.liquid @@ -0,0 +1,105 @@ + + + + +{%- liquid + assign og_title = page_title | default: shop.name + assign og_url = canonical_url | default: request.origin + assign og_type = 'website' + assign og_description = page_description | default: shop.description | default: shop.name + + if request.page_type == 'product' + assign og_type = 'product' + elsif request.page_type == 'article' + assign og_type = 'article' + elsif request.page_type == 'password' + assign og_url = request.origin + endif +%} + + + + + + + +{%- if page_image -%} + + + + +{%- endif -%} + +{%- if request.page_type == 'product' -%} + + + + +{%- endif -%} + + + + + + + {{ page_title }} + {%- if current_tags %} – tagged "{{ current_tags | join: ', ' }}"{% endif -%} + {%- if current_page != 1 %} – Page {{ current_page }}{% endif -%} + {%- unless page_title contains shop.name %} – {{ shop.name }}{% endunless -%} + + + + +{% if page_description %} + +{% endif %} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/404.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/404.json new file mode 100644 index 00000000..fc12fc78 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/404.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ + { + "sections": { + "main": { + "type": "404", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/article.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/article.json new file mode 100644 index 00000000..d3bc0dfd --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/article.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "article", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/blog.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/blog.json new file mode 100644 index 00000000..09705d70 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/blog.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "blog", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/cart.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/cart.json new file mode 100644 index 00000000..78c2add2 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/cart.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "cart", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/collection.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/collection.json new file mode 100644 index 00000000..7592ac07 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/collection.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "collection", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/gift_card.liquid b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/gift_card.liquid new file mode 100644 index 00000000..db5d8289 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/gift_card.liquid @@ -0,0 +1,73 @@ +{% layout none %} + + + + + {% # Inlined CSS Variables %} + {% render 'css-variables' %} + + {% # Load and preload the critical CSS %} + {{ 'critical.css' | asset_url | stylesheet_tag: preload: true }} + + {% # Social, title, etc. %} + {% render 'meta-tags' %} + + {% style %} + main { + text-align: center; + } + main img { + display: unset; + } + {% endstyle %} + + {{ content_for_header }} + + + +
+
+

{{ gift_card.balance | money }}

+ + {% if gift_card.enabled == false or gift_card.expired %} +

{{ 'gift_card.expired' | t }}

+ {% endif %} + + {% if gift_card.expires_on %} + {% assign expires_on = gift_card.expires_on | date: '%B %e, %Y' %} +

+ {{ 'gift_card.expires_on' | t: expires_on: expires_on }} +

+ {% endif %} + +

+ {% if settings.logo %} + {{ settings.logo | image_url: width: 300 | image_tag: alt: shop.name }} + {% else %} + {{ 'gift_card.card' | t }} + {% endif %} +

+ +

{{ shop.name }}

+

{{ 'gift_card.use_at_checkout' | t }}

+

{{ gift_card.code | format_code }}

+ + {% if gift_card.pass_url %} + + {{ 'gift_card.add_to_apple_wallet' | t }} + + {% endif %} +
+
+ + diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/index.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/index.json new file mode 100644 index 00000000..b03e3a86 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/index.json @@ -0,0 +1,22 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "hello-world", + "settings": { + "featured_collection": "featured" + } + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/list-collections.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/list-collections.json new file mode 100644 index 00000000..30984287 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/list-collections.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "collections", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/page.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/page.json new file mode 100644 index 00000000..36d23000 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/page.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "page", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/password.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/password.json new file mode 100644 index 00000000..030b7259 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/password.json @@ -0,0 +1,21 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "layout": "password", + "sections": { + "main": { + "type": "password", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/product.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/product.json new file mode 100644 index 00000000..8329ddc6 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/product.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "product", + "settings": {} + } + }, + "order": [ + "main" + ] +} diff --git a/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/search.json b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/search.json new file mode 100644 index 00000000..8c540c54 --- /dev/null +++ b/rfcs/084-shopify-integration-strategies/skeleton-theme/templates/search.json @@ -0,0 +1,20 @@ +/* + * ------------------------------------------------------------ + * IMPORTANT: The contents of this file are auto-generated. + * + * This file may be updated by the Shopify admin theme editor + * or related systems. Please exercise caution as any changes + * made to this file may be overwritten. + * ------------------------------------------------------------ + */ +{ + "sections": { + "main": { + "type": "search", + "settings": {} + } + }, + "order": [ + "main" + ] +}