Shopify / Shopify/liquid

Improve readability of slotted content in snippets

Open
#1,966 2 comments 14 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Feature Request
Dominant language
Ruby
Stars
11.9k
Forks
1.5k
Avg merge
17h 55m
Merged PRs (30d)
3

Description

👋🏻

I like building compositions from primitives, e.g. a simple text and image section can be quickly thrown together right now in Liquid like:

{% capture content %}
  {% render 'element.text', as:'h2', variant: 'heading-xl', slot: 'Welcome to composable Liquid theme components' %}
  {% render 'element.text', variant: 'rte', slot: '<p>You can compose consistent, high-quality storefronts using well-engineered Liquid components</p>'  %}
{% endcapture %}

{% capture content_stack %}
  {% render 'layout.stack', gap: 'md', slot: content %}
{% endcapture %}

{% capture columns %}
  {% render 'element.image' %}
  {{ content_stack }}
{% endcapture %}

{% capture grid %}
  {% render 'layout.grid', columns_xs: 1, columns_md: 2, gap: 'lg', slot: columns %}
{% endcapture %}

{% render 'layout.section', slot: grid %}

This approach works, but I'm sure we can all agree the syntax is 💩

It would be great if snippets came with a default slot tag, so I could instead write something like this:

{% render 'layout.section' %}
  {% render 'layout.grid', columns_xs: 1, columns_md: 2, gap: 'lg' %}
    {% render 'element.image', img: section.settings.image %}
    {% render 'layout.stack', gap: 'md' %}
      {% render 'element.text', as:'h2', variant: 'heading-xl' %}
        Welcome to composable theme components
      {% endrender %}
      {% render 'element.text', variant: 'rte' %}
        <p>You can compose consistent, high-quality storefronts using well-composed Liquid components</p>
        <a href="#">Click here to learn more</a>
      {% endrender %}
    {% endrender %}
  {% endrender %}  
{% endrender %}

where layout.stack.liquid:

<div class="flex flex-col">{{ children }}</div>

and element.text.liquid:

<{{ as }}>{{ children }}</{{ as }}>

However, from my conversations with @karreiro and @charlespwd, adding an {% endrender %} tag would be a complex and potentially breaking change. We already have other way's of rendering snippets, ie. {% include %}, so what's one more way, right?

Following in suit with {%- ... -%} and {% # ... %} symbol identifiers that have already been introduced, I propose a new tag that emulates an existing open and close pattern most devs are already familiar with,
{%< ... >%}...{%</ ... >%}:

{%< 'layout.section' >%}
  {%< 'layout.grid', columns_xs: 1, columns_md: 2, gap: 'lg' >%}
    {%< 'element.image', img: section.settings.image >%}
    {%< 'layout.stack', gap: 'md' >%}
      {%< 'element.text', as:'h2', variant: 'heading-xl' >%}
        Welcome to composable theme components
      {%</ 'element.text' >%}
      {%< 'element.text', variant: 'rte' >%}
        <p>You can compose consistent, high-quality storefronts using well-composed Liquid components</p>
      {%</ 'element.text' >%}
      {%< 'element.button', text: 'Learn more' >%}
    {%</ 'layout.stack' >%}
  {%</ 'layout.grid' >%}  
{%</ 'layout.section' >%}

I'm not at all married to this specific proposal, but at least let's use it to kickstart some creative thinking?

Why don't I just use slots made possible via theme blocks and {% content_for "block" %}?

Short version -- IMO the theme editor shouldn't be a requirement to make slotting + code-reuse possible in Liquid. It should remain a progressive enhancement that selectively exposes points of configurability in the editor.

  1. I want to build sections and deal with the theme editor layer later. Why do I need to wrangle a ton of JSON configs to just render something on the page. That's a ton of extra work that I don't need for a first prototype. Let me deal with breaking things into blocks later.

  2. I want to have full control when I'm crafting my theme editor experience. I don't want to expose layout.section, layout.grid, and layout.stack as blocks -- I don't think my customers using the editor will care much about those and they'll just add complexity where it's not needed. I'd rather just add a few settings at the section level.

sections/image-with-text.liquid

{%< 'layout.section', padding: section.setting.padding >%}
  {%< 'layout.grid', columns_xs: 1, columns_md: 2, gap: 'lg' >%}
    {% content_for: 'block', type: 'image' %}
    {%< 'layout.stack', gap: 'md' >%}
      {% content_for: 'blocks' %}
    {%</ 'layout.stack' >%}
  {%</ 'layout.grid' >%}  
{%</ 'layout.section' >%}

blocks/image.liquid

{%< 'element.image', img: block.settings.image >%}

blocks/text.liquid

{%< 'element.text', as: block.settings.element, variant: block.settings.variant >%}
  {{ block.settings.text }}
{%</ 'element.text' >%}

blocks/button.liquid

{%< 'element.button', text: block.settings.text >%}

Related issues:

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the related issues 1854, 1829, and 1565, then compare the proposed nested snippet syntax with the existing render, include, and content_for examples in this issue. Done means the project has agreed on a concrete slotting syntax and its compatibility and implementation scope, rather than only a collection of proposals.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.