From 80a2b55f45d5fb68ac563cb87285a2e2a885e03c Mon Sep 17 00:00:00 2001 From: Guilherme Carreiro Date: Thu, 1 Oct 2026 12:04:36 +0200 Subject: [PATCH] Port rc-v2.0.0 with updated block composition syntax --- .gitignore | 3 + AGENTS.md | 157 +++++++++++++ README.md | 356 ++++++++++++++++------------- assets/base.css | 213 +++++++++++++++++ assets/critical.css | 13 +- assets/liquid-tips.js | 42 ++++ blocks/container.liquid | 52 +++++ {sections => blocks}/footer.liquid | 29 +-- blocks/group.liquid | 105 --------- {sections => blocks}/header.liquid | 43 +--- blocks/hello-world.liquid | 60 +++++ blocks/liquid-tips.liquid | 51 +++++ blocks/text.liquid | 59 ----- layout/password.liquid | 5 +- layout/theme.liquid | 13 +- locales/en.default.json | 19 ++ locales/en.default.schema.json | 17 +- sections/404.liquid | 23 -- sections/article.liquid | 68 ------ sections/blog.liquid | 35 --- sections/cart.liquid | 37 --- sections/collection.liquid | 47 ---- sections/collections.liquid | 83 ------- sections/custom-section.liquid | 60 ----- sections/footer-group.json | 25 -- sections/header-group.json | 22 -- sections/hello-world.liquid | 144 ------------ sections/page.liquid | 17 -- sections/password.liquid | 35 --- sections/product.liquid | 52 ----- sections/search.liquid | 70 ------ snippets/image.liquid | 15 -- templates/404.json | 20 -- templates/404.liquid | 11 + templates/article.json | 20 -- templates/article.liquid | 55 +++++ templates/blog.json | 20 -- templates/blog.liquid | 23 ++ templates/cart.json | 20 -- templates/cart.liquid | 25 ++ templates/collection.json | 20 -- templates/collection.liquid | 28 +++ templates/gift_card.liquid | 14 +- templates/index.json | 20 -- templates/index.liquid | 5 + templates/list-collections.json | 20 -- templates/list-collections.liquid | 27 +++ templates/page.json | 20 -- templates/page.liquid | 5 + templates/password.json | 21 -- templates/password.liquid | 25 ++ templates/product.json | 20 -- templates/product.liquid | 37 +++ templates/search.json | 20 -- templates/search.liquid | 46 ++++ 55 files changed, 1127 insertions(+), 1365 deletions(-) create mode 100644 AGENTS.md create mode 100644 assets/base.css create mode 100644 assets/liquid-tips.js create mode 100644 blocks/container.liquid rename {sections => blocks}/footer.liquid (64%) delete mode 100644 blocks/group.liquid rename {sections => blocks}/header.liquid (56%) create mode 100644 blocks/hello-world.liquid create mode 100644 blocks/liquid-tips.liquid delete mode 100644 blocks/text.liquid delete mode 100644 sections/404.liquid delete mode 100644 sections/article.liquid delete mode 100644 sections/blog.liquid delete mode 100644 sections/cart.liquid delete mode 100644 sections/collection.liquid delete mode 100644 sections/collections.liquid delete mode 100644 sections/custom-section.liquid delete mode 100644 sections/footer-group.json delete mode 100644 sections/header-group.json delete mode 100644 sections/hello-world.liquid delete mode 100644 sections/page.liquid delete mode 100644 sections/password.liquid delete mode 100644 sections/product.liquid delete mode 100644 sections/search.liquid delete mode 100644 templates/404.json create mode 100644 templates/404.liquid delete mode 100644 templates/article.json create mode 100644 templates/article.liquid delete mode 100644 templates/blog.json create mode 100644 templates/blog.liquid delete mode 100644 templates/cart.json create mode 100644 templates/cart.liquid delete mode 100644 templates/collection.json create mode 100644 templates/collection.liquid delete mode 100644 templates/index.json create mode 100644 templates/index.liquid delete mode 100644 templates/list-collections.json create mode 100644 templates/list-collections.liquid delete mode 100644 templates/page.json create mode 100644 templates/page.liquid delete mode 100644 templates/password.json create mode 100644 templates/password.liquid delete mode 100644 templates/product.json create mode 100644 templates/product.liquid delete mode 100644 templates/search.json create mode 100644 templates/search.liquid diff --git a/.gitignore b/.gitignore index a638c160a..b63fb4551 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ node_modules/ ## Release files release *.zip + +## Local listings +listings/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..720cab8c4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,157 @@ +# Skeleton Theme Agent Guide + +A minimal Shopify theme that defines page structure directly in Liquid. This +file lists the repository conventions that aren't obvious from an individual +file. The code is the source of truth. + +## Non-negotiables when editing this theme + +- **No sections:** no `sections/` folder, no `{% section %}`/`{% sections %}` + tags, no JSON templates, no schema `presets`. +- **No Liquid-embedded assets:** no `{% stylesheet %}`, no `{% javascript %}`. + All CSS and JavaScript live in `assets/`. +- **Direct Liquid templates:** templates render page content from blocks, + snippets, and inline markup. Don't introduce a section for markup that is + used by only one page. +- **Template-owned containers:** each `templates/*.liquid` file is the + composition root and wraps its page content in one or more `container` + blocks — one per vertical slice. `layout/theme.liquid` wraps only the + `header` and `footer` blocks in their own `container` blocks and renders + `content_for_layout` in a plain `
`; `layout/password.liquid` renders + `content_for_layout` in a plain `
`, and its template owns the + container. The exception is `gift_card.liquid` (`{% layout none %}`): it + manages its own document structure. +- **Whitespace matters:** include whitespace between an HTML tag name and a + following Liquid delimiter (`
  • content_for_layout + {% block 'container' %} (footer) +layout/password.liquid →
    content_for_layout +templates/*.liquid → {% block 'container' %} → blocks / snippets / inline HTML +``` + +Neither layout wraps `content_for_layout` in a `container` block; each renders +it in a plain `
    `. In `theme.liquid` the `header` and `footer` blocks each +get their own `container` block. Every template is the composition root and +wraps its page content in one or more `container` blocks — a template may hold +any number of containers, one per vertical slice. + +## The block tag + +```liquid +{% block 'name', named_parameter: value %} + Body content +{% endblock %} +``` + +The tag works like `{% render %}`, but renders `blocks/name.liquid`. Every +named parameter is available as a plain variable inside the block. If its name +matches a setting declared in the block's schema, it also sets +`block.settings.`. A parameter with no matching schema setting is only a +variable. + +For example, the container schema declares `alignment`, but not `tag`: + +```liquid +{% block 'container', alignment: 'center', tag: 'div' %} + {{ page.content }} +{% endblock %} +``` + +Inside the container, both `alignment` and `block.settings.alignment` return +`center`; `tag` returns `div` and does not create `block.settings.tag`. Continue +to use `block.settings.` for schema-backed controls in block implementations. +`class` is an ordinary parameter with no special platform behavior. + +`{% doc %}` documents parameters; it does not declare, validate, or bind them. +A parameter documented only in LiquidDoc is read as a plain variable and does +not become a schema setting. Use `{% schema %}` for merchant-editable controls. +Inline literal arrays are supported in `{% block %}` arguments, as shown by +`tips` in `templates/index.liquid`; `{% render %}` and `{% partial %}` do not +accept inline literal arrays. + +The content between `{% block %}` and `{% endblock %}` is available inside the +block as `{{ content }}`. Always include the closing `{% endblock %}` tag, +even when the call has no body content. The body is rendered in the caller's +scope; it cannot read the callee's settings or local assignments. Prefer body +content for display-only text and markup instead of adding `title`, `body`, or +`heading` parameters. Add parameters when the block needs data or must change +how it renders. + +Executable `{% block %}` tags are allowed only in `layout/` and `templates/`. +Keep child calls in the caller-owned body, never in block or snippet +implementations. LiquidDoc examples may show block calls, but must be authored +in a layout or template when used. + +## The partial tag + +```liquid +{% partial 'name' %}...{% endpartial %} +``` + +Partials name inline regions of server-rendered HTML. JavaScript can request a +region by name and replace the matching region in the DOM. The name in the +Liquid template and the name in JavaScript must match. + +The `liquid-tips` block is the theme's canonical partial-refresh example: its +tip sentence lives in a `{% partial 'liquid-tip' %}` region, and +`assets/liquid-tips.js` calls `partials.refresh("liquid-tip")` to swap in a +fresh server-rendered tip. Import `partials` from +`@shopify/partial-rendering`. Use `refresh()` to fetch and apply regions from +the current page URL, or `fetch()` followed by `apply()` when you need control +over the request URL, method, body, or when the update appears. Fetch related +regions together so one response keeps them synchronized. Build request URLs +from the current page URL or Liquid `routes.*` to preserve locale and market +routing. + +`apply()` preserves focus, text selection, form values, and scroll position. +It also preserves input, textarea, and select values when returned markup +changes them: explicitly update server-adjusted controls after applying the +partial (for example, a cart quantity corrected by inventory validation). +Cancel stale requests with an `AbortSignal`, use `aria-busy` while loading, +announce meaningful results in a live region, and restore transient DOM state +such as open disclosures. Read URL state from `window.location.search` so +shared links and browser navigation produce the same result. + +The `{% partial %}` tag renders on the storefront only when +`shop.features.agentic_editor_enabled?` is on and the page is served by +StorefrontRenderer; otherwise the storefront raises `Unknown tag 'partial'`. + +## Blocks + +Every block must: + +- Start with a `{% doc %}` header with typed params. +- Include a `{% schema %}` tag without `presets`. +- Document each named parameter that the block reads, such as `tag` or `class`. +- Document `content` in LiquidDoc and indicate whether it is required or + optional: `@param {string} content` or `@param {string} [content]`. For + self-contained blocks, describe that callers must leave the body empty. +- Render `{{ content }}` where caller-supplied body content belongs. + Self-contained blocks may omit the outlet and use an empty caller body. +- Keep executable child block calls in layouts or templates. +- Keep `{{ block.shopify_attributes }}` on the root element so the theme editor + can identify the block. + +Skeleton keeps `.theme-check.yml` as a pristine +`extends: theme-check:recommended` with **zero overrides**. Fix Theme Check +errors in the Liquid instead of adding configuration exceptions. + +Current blocks: `container`, `hello-world`, `header`, `footer`, +`liquid-tips`. + +## Theme map + +``` +blocks/ container, hello-world, header, footer, liquid-tips +templates/ *.liquid page structure (no JSON templates) +layout/ theme.liquid document shell: header/footer container blocks +
    +snippets/ internal utilities (css-variables, image, meta-tags) +assets/ CSS, JavaScript, and other static assets +``` diff --git a/README.md b/README.md index eaf59b2c4..df0f28194 100644 --- a/README.md +++ b/README.md @@ -1,160 +1,196 @@ -

    -
    - 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. +

    +
    + logo +
    + Shopify Skeleton Theme +

    + +A minimal Shopify theme built on block-first composition. Templates compose each +page directly from blocks, snippets, and inline markup — no sections, no JSON +templates. It's designed to stay lean and to be edited by coding agents as +readily as by people. + +

    + 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 # CSS, JavaScript, and other static assets +├── blocks # Reusable, customizable UI components +├── config # Global theme settings and customization options +├── layout # Top-level page wrappers +├── locales # Translation files for theme internationalization +├── snippets # Reusable Liquid code or HTML fragments +└── templates # Liquid composition roots, one per page type +``` + +To learn more, refer to the [theme architecture documentation](https://shopify.dev/docs/storefronts/themes/architecture). + +## Block-first composition + +Every page is composed from blocks. The composition flows in one direction: + +``` +templates/*.liquid → {% block 'container' %} → blocks / snippets / inline markup +``` + +### Templates + +[Templates](https://shopify.dev/docs/storefronts/themes/architecture/templates#template-types) +control what's rendered on each type of page. In this theme they are Liquid +files (`templates/*.liquid`), not JSON. Each template is a composition root: +it wraps its page content in one or more `container` blocks — one per vertical +slice — and composes blocks, snippets, and inline markup inside them. The layout +renders `content_for_layout` in a plain `
    ` and reserves the `container` +block for the header and footer only. + +For example, `templates/index.liquid` wraps the `hello-world` block in a +container: + +```liquid +{% block 'container' %} + {% block 'hello-world' %} + {% block 'liquid-tips', tips: ['hello_world.liquid_tips_1', 'hello_world.liquid_tips_2', 'hello_world.liquid_tips_3'] %}{% endblock %} + {% endblock %} +{% endblock %} +``` + +### Blocks + +[Blocks](https://shopify.dev/docs/storefronts/themes/architecture/blocks) are +the theme's building units. Each block is a single file in `blocks/`, opens with +a `{% doc %}` header describing its parameters, and ends with a `{% schema %}` +(no `presets`). A block renders caller-supplied content through +`{{ content }}` and keeps `{{ block.shopify_attributes }}` on its root +element for theme-editor support. Self-contained blocks can omit the content +outlet and accept an empty body. + +Executable `{% block %}` calls belong only in `layout/` and `templates/`. +Nested calls stay in the caller-owned body, rather than in block or snippet +implementations. The body renders in the caller's scope before the block +implementation; it cannot read that block's settings or local assignments. + +Pass all parameters as plain named arguments: + +```liquid +{% block 'container', alignment: 'center', tag: 'div' %} + {{ page.content }} +{% endblock %} +``` + +Every argument is a plain variable inside the block. Since the container +schema declares `alignment`, that argument also sets +`block.settings.alignment`: both reads return `center`. The `tag` argument +has no matching schema setting, so it is only the variable `tag`. `class` +is likewise an ordinary parameter with no special platform behavior. + +LiquidDoc documents parameters; it does not declare, validate, or bind them. +Use schema settings for merchant-editable controls, and document whether body +content is required (`@param {string} content`) or optional +(`@param {string} [content]`). Prefer body content for display-only text and +markup; use parameters for data or choices that affect how a block renders. +Inline literal arrays, such as the `tips` list above, are supported by the +block tag; render and partial tags do not accept inline literal arrays. + +The `container` block owns a page region's outer layout element. Each template +wraps its content in one or more `container` blocks, and `layout/theme.liquid` +wraps the `header` and `footer` blocks in their own containers while rendering +`content_for_layout` in a plain `
    `. `blocks/hello-world.liquid` is the +theme's starter demo block. + +## Non-negotiables + +This theme deliberately excludes the section-based model. When editing it: + +- No `sections/` directory, and no `{% section %}` / `{% sections %}` tags. +- No JSON templates and no schema `presets`. +- No Liquid-embedded assets: keep all CSS and JavaScript in `assets/` rather + `{% stylesheet %}` / `{% javascript %}` blocks. +- Compose pages from blocks and inline markup, not single-use page sections. +- Invoke blocks only in layouts and templates; render caller bodies with + `{{ content }}` inside block implementations. + +[`AGENTS.md`](./AGENTS.md) is the source of truth for the theme's dialect and the +full set of rules coding agents follow. + +## CSS and JavaScript + +All theme CSS and JavaScript live in [`assets/`](./assets/), rather than being +embedded in Liquid. This keeps blocks focused on markup without requiring +assets to live in a single file. + +## Partial updates + +Partials mark named regions of server-rendered HTML that JavaScript can update +without a full page reload. Wrap only the content that changes. In this theme, +`blocks/liquid-tips.liquid` wraps the tip sentence in +`{% partial 'liquid-tip' %}...{% endpartial %}`, and +`assets/liquid-tips.js` updates it with: + +```js +import { partials } from '@shopify/partial-rendering'; + +await partials.refresh('liquid-tip'); +``` + +The Liquid region name and JavaScript target must match. `refresh()` fetches +and applies updates from the current page URL. Use `fetch()` followed by +`apply()` for control over the request or when the update appears, and fetch +related regions together. Build URLs from the current page URL or Liquid +`routes.*` so requests preserve locale and market routing. + +`apply()` preserves focus, text selection, form values, and scroll position. +If the server corrects a form value, such as a cart quantity, explicitly update +the control after applying the partial; the returned markup alone does not +replace its preserved value. Cancel stale requests, provide loading feedback +and accessible announcements, and restore transient state such as open +disclosures. Read URL state from `window.location.search` for shared links +and browser navigation. + +The partial tag currently requires `shop.features.agentic_editor_enabled?` +and StorefrontRenderer; otherwise the storefront raises +`Unknown tag 'partial'`. + +## 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/assets/base.css b/assets/base.css new file mode 100644 index 000000000..eacbd3767 --- /dev/null +++ b/assets/base.css @@ -0,0 +1,213 @@ +/** Hello world block */ +.welcome { + display: grid; + grid-template-columns: var(--content-grid); + background-color: #f6f6f7; + padding: 72px 0; +} + +.welcome-content { + grid-column: 2; + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + width: 100%; + padding: 0 24px; +} + +.welcome-description { + max-width: 80ch; + line-height: 1.4; + margin-top: 1.5rem; +} + +.icon { + width: 300px; +} + +.highlights { + display: grid; + gap: 2rem; + grid-template-columns: repeat(4, 1fr); + margin-top: 50px; +} + +@media (max-width: 1100px) { + .highlights { + grid-template-columns: 1fr; + } +} + +.highlight { + display: flex; + flex-direction: column; + height: 100%; + padding: 24px; + border-radius: 8px; + background-color: #eef3ff; + color: rgb(92, 95, 98); + line-height: 1.4; +} + +.highlight > * + * { + margin-top: 1rem; +} + +.highlight h3 { + font-size: 1rem; + color: rgb(32, 34, 35); +} + +.highlight-description { + flex: 1 1; +} + +/** + * Flashes a liquid-tip after the "show another tip" button swaps in fresh + * content through partial rendering. The brief highlight pulse draws the + * eye to the new tip so the change is noticeable without a page reload. + */ +@keyframes highlight-flash { + from { + background-color: #c9d9ff; + } + to { + background-color: transparent; + } +} + +.highlight-description--flash { + animation: highlight-flash 0.6s ease-out; + border-radius: 4px; +} + +.highlight a, +.welcome-content a { + display: flex; + width: fit-content; + background-color: rgb(250, 251, 251); + box-shadow: rgba(0, 0, 0, 0.2) 0px -3px 0px 0px inset, rgba(255, 255, 255, 0.9) 0px 2px 0px 0px inset; + border: 1px solid rgb(140, 145, 150); + border-radius: 4px; + color: rgb(92, 95, 98); + padding: 3px 10px 5px; + text-decoration: none; + cursor: pointer; +} + +/** Text block */ +.text { + text-align: var(--text-align); +} +.text--title { + font-size: 2rem; + font-weight: 700; +} +.text--subtitle { + font-size: 1.5rem; +} + +/** Collection page */ +.collection-products { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(500px, 1fr)); +} + +/** Collections list page */ +.collections { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(min(var(--collection-card-size), 100%), 1fr)); + gap: var(--grid-gap); +} +.collections--compact { + --collection-card-size: 160px; +} +.collections--full { + --collection-card-size: 280px; +} +.collection-card { + display: flex; + flex-direction: column; + width: 100%; +} + +/** Search page */ +.search-results { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); +} +.search-results .prev, +.search-results .page, +.search-results .next { + grid-column: 1 / -1; +} + +/** Header */ +header { + height: 5rem; + display: flex; + align-items: center; + justify-content: space-between; +} +header a { + position: relative; + text-decoration: none; + color: var(--color-foreground); + display: flex; + align-items: center; + justify-content: center; +} +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; +} + +/** Footer */ +footer { + display: flex; + justify-content: space-between; + margin-top: 2rem; +} +footer a { + text-decoration: none; + color: var(--color-foreground); +} +footer .footer__links, +footer .footer__payment { + display: flex; + gap: 1rem; +} + +/** Image snippet */ +.image { + display: block; + position: relative; + overflow: hidden; + width: 100%; + height: auto; +} + +.image > img { + width: 100%; + height: auto; +} + +/** Gift card template */ +.template-gift-card main { + text-align: center; +} + +.template-gift-card main img { + display: unset; +} diff --git a/assets/critical.css b/assets/critical.css index cdb1ae1aa..ba657787c 100644 --- a/assets/critical.css +++ b/assets/critical.css @@ -78,20 +78,20 @@ body { color: var(--color-foreground); } -/** Section layout utilities */ +/** Block container 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. + * sides of the container 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 { +.block-container { --content-width: min( calc(var(--page-width) - var(--page-margin) * 2), calc(100% - var(--page-margin) * 2) @@ -103,15 +103,16 @@ body { position: relative; grid-template-columns: var(--content-grid); display: grid; + text-align: var(--text-align, left); width: 100%; } /* Child elements, by default, are constrained to the central column of the grid. */ -.shopify-section > * { +.block-container > * { grid-column: 2; } /* Child elements that use the full-width utility class span the entire viewport. */ -.shopify-section > .full-width { +.block-container > .full-width { grid-column: 1 / -1; } diff --git a/assets/liquid-tips.js b/assets/liquid-tips.js new file mode 100644 index 000000000..1fbca70fa --- /dev/null +++ b/assets/liquid-tips.js @@ -0,0 +1,42 @@ +/* + * Refreshes the liquid-tips partial with a fresh server-rendered tip. + * Temporary import path until SFR ships the partial runtime. + */ +import { partials } from "@shopify/partial-rendering"; + +const control = document.querySelector("[data-refresh-tip]"); +const card = control?.closest(".highlight"); + +/* + * Briefly flashes the tip so the swapped-in content is noticeable. + * Partial rendering replaces the tip node during the refresh, so the + * fresh element must be re-queried once the swap resolves rather than + * captured beforehand — a reference taken earlier points at the stale, + * detached node. + */ +function flashTip() { + const tip = card?.querySelector(".highlight-description"); + if (!tip) { + return; + } + + tip.classList.add("highlight-description--flash"); + tip.addEventListener( + "animationend", + () => tip.classList.remove("highlight-description--flash"), + { once: true }, + ); +} + +async function refresh() { + control.setAttribute("aria-disabled", "true"); + + try { + await partials.refresh("liquid-tip"); + flashTip(); + } finally { + control.removeAttribute("aria-disabled"); + } +} + +control?.addEventListener("click", refresh); diff --git a/blocks/container.liquid b/blocks/container.liquid new file mode 100644 index 000000000..983f175a1 --- /dev/null +++ b/blocks/container.liquid @@ -0,0 +1,52 @@ +{% doc %} + Wraps composed page content in a semantic HTML element. Every template + composes its page through a container block: the block owns the outer + layout element and renders the composed body via {{ content }}. + + @param {string} [tag] - HTML element for the wrapper. Default: 'section'. + + @example Default section wrapper + {% block 'container' %} +

    {{ page.title }}

    + {{ page.content }} + {% endblock %} + + @example Override the wrapper element + {% block 'container', tag: 'div' %} + ... + {% endblock %} +{% enddoc %} + +{%- assign tag = tag | default: 'section' -%} + +<{{ tag }} class="block-container" style="--text-align: {{ block.settings.alignment }};" {{ block.shopify_attributes }}> + {{ content }} + + +{% schema %} +{ + "name": "t:general.container", + "settings": [ + { + "type": "select", + "id": "alignment", + "label": "t:labels.alignment", + "options": [ + { + "value": "left", + "label": "t:options.alignment.left" + }, + { + "value": "center", + "label": "t:options.alignment.center" + }, + { + "value": "right", + "label": "t:options.alignment.right" + } + ], + "default": "left" + } + ] +} +{% endschema %} diff --git a/sections/footer.liquid b/blocks/footer.liquid similarity index 64% rename from sections/footer.liquid rename to blocks/footer.liquid index 66e6f0978..309f43a36 100644 --- a/sections/footer.liquid +++ b/blocks/footer.liquid @@ -1,4 +1,10 @@ -