---
description: Liquid syntax standards — render tag, valid tags/filters, formatting
globs:
  - "**/*.liquid"
alwaysApply: false
---

# Liquid Syntax Standards

## Render tag: assign before passing computed values

**Do not use filters or `append` inside a `render` tag's parameter values.** Assign the computed string to a variable first, then pass that variable.

```liquid
{%- assign props = 'data-section-id="' | append: section.id | append: '"' -%}
{% render 'a--button', properties: props %}
```

**Wrong:** `render 'snippet', properties: 'data-id="' | append: section.id | append: '"'` — inline append in a render parameter is invalid/unreliable. Always assign, then pass.

## Valid Tags with Parameters

**Control Flow:**

- `if condition` / `endif` - Conditional logic
- `unless condition` / `endunless` - Negative conditional
- `case variable` / `when value` / `endcase` - Switch statement
- `for item in array` / `endfor` - Loop with optional `limit:`, `offset:`

**Variable Assignment:**

- `assign variable = value` - Create variable
- `capture variable` / `endcapture` - Capture output
- `increment variable` - Add 1 to counter
- `decrement variable` - Subtract 1 from counter

**Template Inclusion:**

- `render 'snippet-name'` - Include snippet
- `render 'snippet-name', param: value` - With parameters
- `section 'section-name'` - Include section

**Forms:**

- `form 'cart'` / `endform` - Cart form
- `form 'product'` / `endform` - Product form
- `form 'customer_login'` / `endform` - Login form

**Other:**

- `paginate collection.products by 12` / `endpaginate` - Paginate results
- `liquid` / `endliquid` - Multiline Liquid block
- `comment` / `endcomment` - Block comments
- `raw` / `endraw` - Output without processing

## Valid Filters

**Array Filters:**

- `compact` - Remove nil values: `array | compact`
- `concat` - Join arrays: `array | concat: array`
- `find` - Find object: `array | find: property, value`
- `where` - Filter objects: `array | where: property, value`
- `map` - Extract property: `array | map: property`
- `sort` - Sort array: `array | sort`
- `reverse` - Reverse order: `array | reverse`
- `first` - First item: `array | first`
- `last` - Last item: `array | last`
- `size` - Count items: `array | size`

**String Filters:**

- `escape` - HTML escape: `string | escape`
- `truncate` - Limit length: `string | truncate: 150`
- `handleize` - URL handle: `string | handleize`
- `replace` - Replace text: `string | replace: 'old', 'new'`
- `split` - Split string: `string | split: 'delimiter'`
- `upcase` - Uppercase: `string | upcase`
- `downcase` - Lowercase: `string | downcase`
- `capitalize` - Capitalize: `string | capitalize`

**Money Filters:**

- `money` - Format price: `price | money`
- `money_with_currency` - With symbol: `price | money_with_currency`
- `money_without_currency` - No symbol: `price | money_without_currency`

**Media Filters:**

- `image_url` - Responsive image: `image | image_url: width: 800`
- `image_tag` - Complete img tag: `image | image_tag`
- `asset_url` - Theme asset: `'style.css' | asset_url`

## Syntax Rules

- Use `{% liquid %}` for multiline code blocks
- Use `{% # comment %}` for inline comments
- Never invent new filters, tags, or objects
- Follow proper tag closing order (last opened, first closed)
- Use object dot notation: `product.title` not `product['title']`
- Respect object scope and availability

## Render Tag Formatting Rules

### Within Liquid Blocks
When using `render` within a `{% liquid %}` block, all parameters must be on the same line:

**✅ Do this (single line within liquid block):**
```liquid
{% liquid
  render 'm--electric-slider', id: 'slider-1', feed: content, per_row_mobile: 1, per_row_tablet: 2, per_row_desktop: 2, gap: 'gap-x-4', nav: false
%}
```

**❌ Don't do this (multiline within liquid block):**
```liquid
{% liquid
  render 'm--electric-slider', 
    id: 'slider-1',
    feed: content,
    per_row_mobile: 1,
    per_row_tablet: 2,
    per_row_desktop: 2
%}
```

### Standalone Render Tags
When using `render` as a standalone tag (outside of `{% liquid %}` blocks), you can and should use multiline formatting for readability:

**✅ Do this (multiline standalone render):**
```liquid
{% render 'm--electric-slider',
  id: 'slider-1',
  feed: content,
  per_row_mobile: 1,
  per_row_tablet: 2,
  per_row_desktop: 2,
  gap: 'gap-x-4',
  nav: false
%}
```

**✅ Also acceptable (single line standalone render):**
```liquid
{% render 'm--electric-slider', id: 'slider-1', feed: content, per_row_mobile: 1 %}
```

## Inline Variables Pattern

For props that are relatively straightforward, prefer to inline the liquid instead of declaring extra variables. In smaller components it doesn't make a big difference, but in bigger ones it helps not having to scroll up and down to know what is being applied where.

**✅ Do this (inline approach):**

```liquid
<div
  class='component component--{{ settings.style_modifier }}'
  style='
    color: {{ settings.text_color }};
    {% if settings.show_border %}
      border: 1px solid {{ settings.border_color }};
    {% endif %}
  '
>
  <h2>{{ 'sections.component.title' | t }}</h2>

  {{ content | truncate: settings.max_length | default: 200 }}

  <a
    href='{{ link_url }}'
    class='link--{{ settings.link_style | default: 'primary' }}'
  >
    {{ 'general.read_more' | t }}
  </a>
</div>
```

**❌ Don't do this (variable declaration approach):**

```liquid
{% liquid
  assign component_class = 'component component--' | append: settings.style_modifier
  assign text_color = settings.text_color
  assign truncate_length = settings.max_length | default: 200
  assign link_class = 'link--' | append: settings.link_style | default: 'primary'
%}

{% capture component_style %}
  color: {{ text_color }};
  {% if settings.show_border %}
    border: 1px solid {{ settings.border_color }};
  {% endif %}
{% endcapture %}

<div
  class='{{ component_class }}'
  style='{{ component_style }}'
>
  <h2>{{ 'sections.component.title' | t }}</h2>

  {{ content | truncate: truncate_length }}

  <a
    href='{{ link_url }}'
    class='{{ link_class }}'
  >
    {{ 'general.read_more' | t }}
  </a>
</div>
```

**Exceptions:**

- When Liquid filter parameters require string values and complex logic cannot be inlined
- When the same complex calculation is used multiple times
- When the logic is extremely complex and would harm readability
- When you need to build a string incrementally with conditional parts

**Benefits:**

- Easier to understand what's being applied where
- No need to scroll up and down to find variable definitions
- Reduces cognitive load in larger components
- Makes the code more maintainable

- Easier to understand what's being applied where
- No need to scroll up and down to find variable definitions
- Reduces cognitive load in larger components
- Makes the code more maintainable