From atum-cms-ecom
Shopify Online Store 2.0 Liquid theming pattern library — JSON templates with sections and blocks, section schema (settings, blocks, presets, enabled_on), metafields UI exposure, global section groups (header/footer), Liquid filters (money, image_url, handleize, date, t for i18n), responsive images via image_url and image_tag, locales/*.json translations, settings_schema.json for theme settings, and Theme Check compliance. Use when building or maintaining Shopify OS 2.0 themes: writing sections, blocks, snippets, templates, settings schemas, or translations. Complements agent shopify-expert with copy-pasteable Liquid patterns. Differentiates from shopify-hydrogen-patterns (headless React) and wordpress-patterns (different CMS).
npx claudepluginhub arnwaldn/atum-plugins-collection --plugin atum-cms-ecomThis skill uses the workspace's default tool permissions.
Patterns Liquid pour thèmes Shopify Online Store 2.0. **Jamais** de templates `.liquid` monolithiques sur un nouveau projet — toujours JSON templates + sections modulaires.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Patterns Liquid pour thèmes Shopify Online Store 2.0. Jamais de templates .liquid monolithiques sur un nouveau projet — toujours JSON templates + sections modulaires.
theme/
├── assets/ # CSS, JS, images, fonts (servi via CDN)
├── config/
│ ├── settings_schema.json # Settings globaux (couleurs, fonts, etc.)
│ └── settings_data.json # Valeurs actuelles (géré par l'éditeur)
├── layout/
│ ├── theme.liquid # Layout principal
│ └── password.liquid # Password page
├── locales/
│ ├── en.default.json # Traductions par défaut
│ └── fr.json
├── sections/
│ ├── header.liquid
│ ├── footer.liquid
│ ├── featured-collection.liquid
│ └── header-group.json # Section group global
├── snippets/
│ ├── price.liquid
│ ├── product-card.liquid
│ └── icon-*.liquid
└── templates/
├── index.json # JSON template (composition visuelle)
├── product.json
├── collection.json
├── page.json
├── cart.json
├── 404.json
├── search.json
└── customers/ # Exceptions : toujours .liquid
├── login.liquid
└── register.liquid
{
"sections": {
"main": {
"type": "main-product",
"settings": {
"show_vendor": true,
"enable_sticky_info": true
},
"blocks": {
"title": { "type": "title", "settings": {} },
"price": { "type": "price", "settings": {} },
"variant_picker": { "type": "variant_picker", "settings": {} },
"buy_buttons": { "type": "buy_buttons", "settings": {} },
"description": { "type": "description", "settings": {} }
},
"block_order": ["title", "price", "variant_picker", "buy_buttons", "description"]
},
"featured": {
"type": "featured-collection",
"settings": {
"heading": "You may also like",
"collection": "frontpage",
"products_to_show": 4
}
}
},
"order": ["main", "featured"]
}
{% comment %} sections/hero.liquid {% endcomment %}
<section class="hero" style="background-color: {{ section.settings.bg_color }}">
<div class="hero__inner">
{% if section.settings.image %}
{{ section.settings.image | image_url: width: 2000 | image_tag: loading: 'eager', class: 'hero__image' }}
{% endif %}
<div class="hero__content">
{% for block in section.blocks %}
{% case block.type %}
{% when 'heading' %}
<h1 {{ block.shopify_attributes }}>{{ block.settings.text }}</h1>
{% when 'subheading' %}
<p {{ block.shopify_attributes }}>{{ block.settings.text }}</p>
{% when 'button' %}
<a href="{{ block.settings.link }}" class="btn" {{ block.shopify_attributes }}>
{{ block.settings.label | escape }}
</a>
{% endcase %}
{% endfor %}
</div>
</div>
</section>
{% schema %}
{
"name": "Hero",
"tag": "section",
"class": "section-hero",
"settings": [
{
"type": "image_picker",
"id": "image",
"label": "Background image"
},
{
"type": "color",
"id": "bg_color",
"label": "Background color",
"default": "#ffffff"
}
],
"blocks": [
{
"type": "heading",
"name": "Heading",
"limit": 1,
"settings": [
{ "type": "text", "id": "text", "label": "Heading", "default": "Welcome" }
]
},
{
"type": "subheading",
"name": "Subheading",
"limit": 1,
"settings": [
{ "type": "text", "id": "text", "label": "Subheading" }
]
},
{
"type": "button",
"name": "Button",
"limit": 2,
"settings": [
{ "type": "text", "id": "label", "label": "Label", "default": "Shop now" },
{ "type": "url", "id": "link", "label": "Link" }
]
}
],
"presets": [
{
"name": "Hero",
"blocks": [
{ "type": "heading" },
{ "type": "subheading" },
{ "type": "button" }
]
}
],
"enabled_on": {
"templates": ["index", "page", "collection"]
}
}
{% endschema %}
{% comment %}
snippets/product-card.liquid
Parameters:
- product (required)
- show_vendor (optional, default false)
{% endcomment %}
<a href="{{ product.url }}" class="product-card">
{% if product.featured_image %}
{{ product.featured_image | image_url: width: 600 | image_tag: loading: 'lazy', class: 'product-card__image', widths: '300,600,900', sizes: '(min-width: 750px) 25vw, 50vw' }}
{% endif %}
<h3 class="product-card__title">{{ product.title | escape }}</h3>
{% if show_vendor %}
<p class="product-card__vendor">{{ product.vendor | escape }}</p>
{% endif %}
<p class="product-card__price">
{% if product.price_varies %}
{{ 'products.price.from' | t }} {{ product.price_min | money }}
{% else %}
{{ product.price | money }}
{% endif %}
</p>
</a>
Usage dans une section :
{% for product in collection.products limit: 8 %}
{% render 'product-card', product: product, show_vendor: true %}
{% endfor %}
{
"general": {
"search": {
"placeholder": "Search"
}
},
"products": {
"price": {
"from": "From",
"regular_price": "Regular price",
"sale_price": "Sale price"
},
"buttons": {
"add_to_cart": "Add to cart",
"sold_out": "Sold out"
}
}
}
Usage :
<button>{{ 'products.buttons.add_to_cart' | t }}</button>
[
{
"name": "theme_info",
"theme_name": "Atum Base Theme",
"theme_version": "1.0.0",
"theme_author": "ATUM SAS",
"theme_documentation_url": "https://example.com/docs",
"theme_support_url": "https://example.com/support"
},
{
"name": "Colors",
"settings": [
{
"type": "header",
"content": "Primary"
},
{
"type": "color",
"id": "color_primary",
"label": "Primary color",
"default": "#0a0a0a"
},
{
"type": "color",
"id": "color_accent",
"label": "Accent color",
"default": "#ff5f1f"
}
]
},
{
"name": "Typography",
"settings": [
{
"type": "font_picker",
"id": "font_body",
"label": "Body font",
"default": "helvetica_n4"
}
]
}
]
{% comment %} Display a product metafield defined in Settings > Custom Data {% endcomment %}
{% assign care_info = product.metafields.custom.care_instructions %}
{% if care_info %}
<div class="product__care">
<h3>{{ 'products.care' | t }}</h3>
<div>{{ care_info | metafield_tag }}</div>
</div>
{% endif %}
Rappel : pour qu'un metafield soit visible dans le Theme Editor avec une UI de config, l'ajouter dans la section schema :
{
"type": "metafield",
"id": "highlighted_metafield",
"label": "Highlighted field"
}
{
"name": "Header group",
"type": "header",
"sections": {
"announcement": {
"type": "announcement-bar",
"settings": {
"text": "Free shipping over 50€"
}
},
"header": {
"type": "header",
"settings": {
"logo_position": "left"
}
}
},
"order": ["announcement", "header"]
}
Fichier : sections/header-group.json. Rendu dans layout/theme.liquid via {% sections 'header-group' %}.
| Filtre | Usage |
|---|---|
money | `{{ price |
money_without_currency | `{{ price |
image_url: width: 600 | Génère URL CDN optimisée |
image_tag | Génère <img> avec srcset/sizes |
handleize | Crée un slug URL-safe : `{{ "Hello World" |
escape | Échappe HTML (à utiliser systématiquement sur user content) |
t | Traduction : `{{ 'products.buttons.add_to_cart' |
date: '%B %Y' | `{{ article.created_at |
default | `{{ product.vendor |
pluralize | `{{ item_count }} {{ item_count |
asset_url | `{{ 'app.css' |
stylesheet_tag | `{{ 'app.css' |
script_tag | `{{ 'app.js' |
{% for %} sans limit** sur une grande collection → toujours limit: N`image_url → toujours utiliser le CDN Shopify pour le responsive<style> / <script> sans {% stylesheet %} / {% javascript %}{{ user.input }} non échappé → toujours | escape sur du contenu userlocales/*.jsonshopify.liquid deprecated filters → ex. img_url est deprecated, utiliser image_urltheme.liquid sans raison — 95% des modifs doivent passer par des sections# Installation via Shopify CLI
npm install -g @shopify/cli @shopify/theme
# Lancer le check
shopify theme check
# Auto-fix où possible
shopify theme check --auto-correct
Intégration CI (.github/workflows/theme-check.yml) :
name: Theme Check
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2
- run: gem install theme-check
- run: theme-check .