shopify-liquid-themes
benjaminsehl/liquid-skills
Generate Shopify Liquid theme code with correct schema, headers, and patterns for sections, blocks, and snippets.
What is shopify-liquid-themes?
Generates Shopify Liquid theme code including sections, blocks, snippets, schema JSON, LiquidDoc headers, translation keys, and CSS/JS patterns. Use when creating or editing .liquid files for Shopify themes, working with schema definitions, stylesheets, JavaScript, or Shopify Liquid objects and filters.
- Generate sections, blocks, and snippets with correct {% schema %} JSON structures
- Create schema settings with proper types (checkbox, text, range, color, product picker, etc.)
- Apply LiquidDoc headers and translation key patterns (t:sections.*, t:blocks.*)
- Write Liquid syntax with correct delimiters, filters, and tags
- Handle CSS/JS patterns using {% stylesheet %} and {% javascript %} tags
- Reference Shopify Liquid objects, filters, and tags with correct syntax
How to install shopify-liquid-themes
npx skills add https://github.com/benjaminsehl/liquid-skills --skill shopify-liquid-themesHow to use shopify-liquid-themes
- 1.Install the skill via npx skills add
- 2.Reference the theme architecture to determine whether you need a section, block, or snippet
- 3.Use the schema tag reference to define settings with correct types and structure
- 4.Apply Liquid syntax rules, especially avoiding parentheses in conditions and using {% render %} instead of deprecated {% include %}
- 5.Use the filter and tag quick references to write correct Liquid code
- 6.Add translation keys following the t:sections.* and t:blocks.* pattern
Use cases
- Building a custom hero section with image, text, and button settings
- Creating a reusable product card block with color and layout options
- Writing a snippet for common button or icon patterns across sections
- Adding conditional visibility to schema settings based on other settings
- Implementing theme-wide translations and localization keys
- Shopify theme developers
- Front-end developers building custom Shopify themes
- Theme designers working with Liquid templates
- Developers maintaining or extending existing Shopify theme code
shopify-liquid-themes FAQ
Use a section for full-width customizable modules that appear in the theme editor and can contain blocks. Use a block for smaller nestable components with editor settings that live inside sections or other blocks.
No. These tags do not render Liquid—no variables, filters, or logic will be processed inside them. Pass data through data attributes or render the tags conditionally outside the stylesheet/javascript blocks.
{% include %} is deprecated. Always use {% render %}, which isolates scope and requires you to explicitly pass variables as parameters, making code more predictable and maintainable.
Use the visible_if pattern in your setting object: "visible_if": "{{ block.settings.layout == 'vertical' }}". This conditionally shows the setting in the theme editor.
No. Liquid has no ternary operator. Always use {% if condition %}value{% else %}other{% endif %} instead.
Full instructions (SKILL.md)
Source of truth, from benjaminsehl/liquid-skills.
name: shopify-liquid-themes description: "Generate Shopify Liquid theme code (sections, blocks, snippets) with correct schema JSON, LiquidDoc headers, translation keys, and CSS/JS patterns. Use when creating or editing .liquid files for Shopify themes, working with schema, doc, stylesheet, javascript tags, or Shopify Liquid objects/filters/tags."
Shopify Liquid Themes
Theme Architecture
.
├── sections/ # Full-width page modules with {% schema %} — hero, product grid, testimonials
├── blocks/ # Nestable components with {% schema %} — slides, feature items, text blocks
├── snippets/ # Reusable fragments via {% render %} — buttons, icons, image helpers
├── layout/ # Page wrappers (must include {{ content_for_header }} and {{ content_for_layout }})
├── templates/ # JSON files defining which sections appear on each page type
├── config/ # Global theme settings (settings_schema.json, settings_data.json)
├── locales/ # Translation files (en.default.json, fr.json, etc.)
└── assets/ # Static CSS, JS, images (prefer {% stylesheet %}/{% javascript %} instead)
When to use what
| Need | Use | Why |
|---|---|---|
| Full-width customizable module | Section | Has {% schema %}, appears in editor, renders blocks |
| Small nestable component with editor settings | Block | Has {% schema %}, can nest inside sections/blocks |
| Reusable logic, not editable by merchant | Snippet | No schema, rendered via {% render %}, takes params |
| Logic shared across blocks/snippets | Snippet | Blocks can't {% render %} other blocks |
Liquid Syntax
Delimiters
{{ ... }}— Output (prints a value){{- ... -}}— Output with whitespace trimming{% ... %}— Logic tag (if, for, assign) — prints nothing{%- ... -%}— Logic tag with whitespace trimming
Operators
Comparison: ==, !=, >, <, >=, <=
Logical: and, or, contains
Critical Gotchas
- No parentheses in conditions — use nested
{% if %}instead - No ternary — always use
{% if cond %}value{% else %}other{% endif %} forloops max 50 iterations — use{% paginate %}for larger arrayscontainsonly works with strings — can't check objects in arrays{% stylesheet %}/{% javascript %}don't render Liquid — no Liquid inside them- Snippets can't access outer-scope variables — pass them as render params
includeis deprecated — always use{% render 'snippet_name' %}{% liquid %}tag — multi-line logic without delimiters; useechofor output
Variables
{% assign my_var = 'value' %}
{% capture my_var %}computed {{ value }}{% endcapture %}
{% increment counter %}
{% decrement counter %}
Filter Quick Reference
Filters are chained with |. Output type of one filter feeds input of next.
Array: compact, concat, find, find_index, first, has, join, last, map, reject, reverse, size, sort, sort_natural, sum, uniq, where
String: append, capitalize, downcase, escape, handleize, lstrip, newline_to_br, prepend, remove, replace, rstrip, slice, split, strip, strip_html, truncate, truncatewords, upcase, url_decode, url_encode
Math: abs, at_least, at_most, ceil, divided_by, floor, minus, modulo, plus, round, times
Money: money, money_with_currency, money_without_currency, money_without_trailing_zeros
Color: color_brightness, color_darken, color_lighten, color_mix, color_modify, color_saturate, color_desaturate, color_to_hex, color_to_hsl, color_to_rgb
Media: image_url, image_tag, video_tag, external_video_tag, media_tag, model_viewer_tag
URL: asset_url, asset_img_url, file_url, shopify_asset_url
HTML: link_to, script_tag, stylesheet_tag, time_tag, placeholder_svg_tag
Localization: t (translate), format_address, currency_selector
Other: date, default, json, structured_data, font_face, font_url, payment_button
Full details: language filters, HTML/media filters, commerce filters
Tags Quick Reference
| Category | Tags |
|---|---|
| Theme | content_for, layout, section, sections, schema, stylesheet, javascript, style |
| Control | if, elsif, else, unless, case, when |
| Iteration | for, break, continue, cycle, tablerow, paginate |
| Variable | assign, capture, increment, decrement, echo |
| HTML | form, render, raw, comment, liquid |
| Documentation | doc |
Full details with syntax and parameters: references/tags.md
Objects Quick Reference
Global objects (available everywhere)
cart, collections, customer, localization, pages, request, routes, settings, shop, template, theme, linklists, images, blogs, articles, all_products, metaobjects, canonical_url, content_for_header, content_for_layout, page_title, page_description, handle, current_page
Page-specific objects
| Template | Objects |
|---|---|
/product | product, remote_product |
/collection | collection, current_tags |
/cart | cart |
/article | article, blog |
/blog | blog, current_tags |
/page | page |
/search | search |
/customers/* | customer, order |
Full reference: commerce objects, content objects, tier 2, tier 3
Schema Tag
Sections and blocks require {% schema %} with a valid JSON object. Sections use section.settings.*, blocks use block.settings.*.
Section schema structure
{
"name": "t:sections.hero.name",
"tag": "section",
"class": "hero-section",
"limit": 1,
"settings": [],
"max_blocks": 16,
"blocks": [{ "type": "@theme" }],
"presets": [{ "name": "t:sections.hero.name" }],
"enabled_on": { "templates": ["index"] },
"disabled_on": { "templates": ["password"] }
}
Block schema structure
{
"name": "t:blocks.slide.name",
"tag": "div",
"class": "slide",
"settings": [],
"blocks": [{ "type": "@theme" }],
"presets": [{ "name": "t:blocks.slide.name" }]
}
Setting type decision table
| Need | Setting Type | Key Fields |
|---|---|---|
| On/off toggle | checkbox | default: true/false |
| Short text | text | placeholder |
| Long text | textarea | placeholder |
Rich text (with <p>) | richtext | — |
Inline rich text (no <p>) | inline_richtext | — |
| Number input | number | placeholder |
| Slider | range | min, max, default (all required), step, unit |
| Dropdown/segmented | select | options: [{value, label}] |
| Radio buttons | radio | options: [{value, label}] |
| Text alignment | text_alignment | default: "left"/"center"/"right" |
| Color picker | color | default: "#000000" |
| Image upload | image_picker | — |
| Video upload | video | — |
| External video URL | video_url | accept: ["youtube", "vimeo"] |
| Product picker | product | — |
| Collection picker | collection | — |
| Page picker | page | — |
| Blog picker | blog | — |
| Article picker | article | — |
| URL entry | url | — |
| Menu picker | link_list | — |
| Font picker | font_picker | default (required) |
| Editor header | header | content (no id needed) |
| Editor description | paragraph | content (no id needed) |
visible_if pattern
{
"visible_if": "{{ block.settings.layout == 'vertical' }}",
"type": "select",
"id": "alignment",
"label": "t:labels.alignment",
"options": [...]
}
Conditionally shows/hides a setting in the editor based on other setting values.
Block entry types
{ "type": "@theme" }— Accept any theme block{ "type": "@app" }— Accept app blocks{ "type": "slide" }— Accept only theslideblock type
Full schema details and all 33 setting types: references/schema-and-settings.md
CSS & JavaScript
Per-component styles and scripts
Use {% stylesheet %} and {% javascript %} in sections, blocks, and snippets:
{% stylesheet %}
.my-component { display: flex; }
{% endstylesheet %}
{% javascript %}
console.log('loaded');
{% endjavascript %}
- One tag each per file — multiple
{% stylesheet %}tags will error - No Liquid inside — these tags don't process Liquid; use CSS variables or classes instead
- Only supported in
sections/,blocks/, andsnippets/
{% style %} tag (Liquid-aware CSS)
For dynamic CSS that needs Liquid (e.g., color settings that live-update in editor):
{% style %}
.section-{{ section.id }} {
background: {{ section.settings.bg_color }};
}
{% endstyle %}
CSS patterns for settings
Single CSS property — use CSS variables:
<div style="--gap: {{ block.settings.gap }}px">
Multiple CSS properties — use CSS classes as select values:
<div class="{{ block.settings.layout }}">
LiquidDoc ({% doc %})
Required for: snippets (always), blocks (when statically rendered via {% content_for 'block' %})
{% doc %}
Brief description of what this file renders.
@param {type} name - Description of required parameter
@param {type} [name] - Description of optional parameter (brackets = optional)
@example
{% render 'snippet-name', name: value %}
{% enddoc %}
Param types: string, number, boolean, image, object, array
Translations
Every user-facing string must use the t filter
<!-- Correct -->
<h2>{{ 'sections.hero.heading' | t }}</h2>
<button>{{ 'products.add_to_cart' | t }}</button>
<!-- Wrong — never hardcode strings -->
<h2>Welcome to our store</h2>
Variable interpolation
{{ 'products.price_range' | t: min: product.price_min | money, max: product.price_max | money }}
Locale file:
{
"products": {
"price_range": "From {{ min }} to {{ max }}"
}
}
Locale file structure
locales/
├── en.default.json # English translations (required)
├── en.default.schema.json # Editor setting translations (required)
├── fr.json # French translations
└── fr.schema.json # French editor translations
Key naming conventions
- Use snake_case and hierarchical keys (max 3 levels)
- Use sentence case for all text (capitalize first word only)
- Schema labels use
t:prefix:"label": "t:labels.heading" - Group by component:
sections.hero.heading,blocks.slide.title
References
- Filters: language (77), HTML/media (45), commerce (30)
- Tag reference (30 tags)
- Objects: commerce (5), content (10), tier 2 (69), tier 3 (53)
- Schema & settings reference (33 types)
- Complete examples (snippet, block, section)
Related skills
More from benjaminsehl/liquid-skills and the wider catalog.

liquid-theme-a11y
Implement WCAG 2.2 accessibility patterns in Shopify Liquid themes with semantic HTML and ARIA.

liquid-theme-standards
CSS, JavaScript, and HTML coding standards for Shopify Liquid themes with BEM, design tokens, and progressive enhancement.

agentation
Add a visual feedback toolbar to Next.js projects for real-time annotation and agent integration.

agentation-self-driving
Autonomously critique web pages by adding design annotations via the Agentation toolbar in a visible browser.

typeui-fundamentals
Universal UI/UX design principles covering visual hierarchy, interaction laws, typography foundations, and WCAG accessibility requirements. Use when making design decisions not covered by a specific design system, validating principle compliance, or resolving conflicts between aesthetics and accessibility. Design-system-agnostic and applies to every surface.

better-icons
Search and retrieve SVGs from 200+ icon libraries via Iconify CLI and MCP server.