agents/skills/shopify-liquid-patterns/SKILL.md
Common Liquid code patterns for Shopify theme development. Use when writing Liquid templates, handling translations, product displays, or theme customizations.
npx skillsauth add carterdea/dots shopify-liquid-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
{% comment %} Use locale files for hardcoded text {% endcomment %}
{{ 'products.benefits.lifetime_guarantee' | t }}
{% comment %} With default fallback {% endcomment %}
{{ 'products.benefits.lifetime_description' | t: default: 'Fallback text' }}
{% comment %} Dynamic key translation {% endcomment %}
{% assign key = 'products.' | append: product.type | append: '.title' %}
{{ key | t }}
<div class="product-card" data-product-id="{{ product.id }}">
{% if product.featured_image %}
<img
src="{{ product.featured_image | image_url: width: 400 }}"
alt="{{ product.featured_image.alt | escape }}"
loading="lazy"
width="400"
height="{{ 400 | divided_by: product.featured_image.aspect_ratio | round }}"
>
{% endif %}
<h3>{{ product.title }}</h3>
{% if product.compare_at_price > product.price %}
<span class="price--sale">{{ product.price | money }}</span>
<span class="price--compare">{{ product.compare_at_price | money }}</span>
{% else %}
<span class="price">{{ product.price | money }}</span>
{% endif %}
</div>
{% for option in product.options_with_values %}
<div class="option-selector">
<label for="option-{{ forloop.index }}">{{ option.name }}</label>
<select id="option-{{ forloop.index }}" name="options[{{ option.name }}]">
{% for value in option.values %}
<option
value="{{ value }}"
{% if option.selected_value == value %}selected{% endif %}
>
{{ value }}
</option>
{% endfor %}
</select>
</div>
{% endfor %}
{% for option in product.options_with_values %}
<fieldset class="option">
<legend>{{ option.name }}</legend>
{% for option_value in option.values %}
<input
type="radio"
id="option-{{ option.position }}-{{ forloop.index0 }}"
name="{{ option.name }}"
value="{{ option_value | escape }}"
{% if option_value.selected %}checked{% endif %}
{% unless option_value.available %}disabled{% endunless %}
>
<label for="option-{{ option.position }}-{{ forloop.index0 }}">
{{ option_value }}
</label>
{% endfor %}
</fieldset>
{% endfor %}
{% comment %} Use image_tag filter for automatic responsive images {% endcomment %}
{{ product.featured_image | image_url: width: 2000 | image_tag: loading: 'lazy' }}
{% comment %} Fixed-size image with retina support (1x/2x) {% endcomment %}
{% assign image = product.featured_image %}
<img
src="{{ image | image_url: width: 600 }}"
srcset="{{ image | image_url: width: 600 }} 1x,
{{ image | image_url: width: 1200 }} 2x"
alt="{{ image.alt | escape }}"
loading="lazy"
width="600"
height="{{ 600 | divided_by: image.aspect_ratio | round }}"
>
{% comment %} Responsive srcset with retina support {% endcomment %}
{% assign image = product.featured_image %}
<img
src="{{ image | image_url: width: 600 }}"
srcset="{{ image | image_url: width: 300 }} 300w,
{{ image | image_url: width: 600 }} 600w,
{{ image | image_url: width: 900 }} 900w,
{{ image | image_url: width: 1200 }} 1200w,
{{ image | image_url: width: 1800 }} 1800w"
sizes="(max-width: 600px) 300px, (max-width: 900px) 600px, 900px"
alt="{{ image.alt | escape }}"
loading="lazy"
>
{% paginate collection.products by 12 %}
<div class="product-grid">
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
</div>
{% if paginate.pages > 1 %}
{{ paginate | default_pagination }}
{% endif %}
{% endpaginate %}
{% comment %} Product metafield {% endcomment %}
{% if product.metafields.custom.badge_type %}
<span class="badge-type">{{ product.metafields.custom.badge_type.value }}</span>
{% endif %}
{% comment %} Shop metafield {% endcomment %}
{{ shop.metafields.global.announcement | metafield_tag }}
{% comment %} Check for specific template {% endcomment %}
{% if template.name == 'product' %}
{% endif %}
{% comment %} Check customer status {% endcomment %}
{% if customer %}
{{ 'account.welcome' | t: name: customer.first_name }}
{% else %}
{{ 'account.login_prompt' | t }}
{% endif %}
{% form 'product', product %}
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
<div class="quantity">
<label for="quantity">{{ 'products.quantity' | t }}</label>
<input type="number" id="quantity" name="quantity" value="1" min="1">
</div>
<button
type="submit"
{% unless product.available %}disabled{% endunless %}
>
{% if product.available %}
{{ 'products.add_to_cart' | t }}
{% else %}
{{ 'products.sold_out' | t }}
{% endif %}
</button>
{% endform %}
{% schema %}
{
"name": "Custom Section",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "Section Heading"
},
{
"type": "image_picker",
"id": "image",
"label": "Image"
}
],
"presets": [
{
"name": "Custom Section"
}
]
}
{% endschema %}
development
Add net-new product, workflow, platform, or developer-experience features as small vertical slices. Use this skill whenever the user asks to build a new feature, add a new page/route/API/workflow/job/eval/operator path, enrich an existing feature with a new user-visible capability, or plan feature architecture before coding. This skill maps the files to change or create, defines the authoritative contract, specifies tests, and gives a QA plan before treating the feature as done.
development
Verify a developer's finished Trello ticket on a non-Shopify web app and render a verdict. Dogfood the posted preview (desktop + mobile) against the card's acceptance criteria, then PASS it (approve the PR, move to Ready for Release) or FAIL it (request changes, attach repro, reassign the dev, move to Development). Read-only: never implements, commits, or opens a PR. Use when asked to 'QA this card', 'test before release', or 'sign off on this ticket'. Shopify themes use shopify-trello-qa; building a ticket uses trello-delivery.
development
Verify a developer's finished Shopify theme ticket and render a verdict. Dogfood the posted preview theme and Customizer (desktop + mobile) against the card's acceptance criteria and Figma, then PASS it (approve the PR, move to Ready for Release) or FAIL it (request changes, attach repro, reassign the dev, move to Development). Read-only: never implements, commits, deploys, or opens a PR. Use when asked to 'QA this Shopify card', 'verify the Ready for Testing card', or 'sign off on this theme ticket'. Non-Shopify apps use trello-qa; building a ticket uses shopify-trello-delivery.
development
Survey any codebase as a senior advisor and produce prioritized, self-contained implementation plans for OTHER models/agents to execute. Strictly read-only on source code — never implements, fixes, or refactors anything itself. Use when asked to audit a codebase, find improvement opportunities (bugs, security, performance, test coverage, tech debt, migrations, DX), suggest features or where to take the project next (roadmap, product direction), or generate handoff plans for another agent to implement.