Learning examples · not a catalog

Shopify learning examples

Each example is a lesson: what the markup looked like before, what production Liquid requires after, which decision you are making, which mistakes teams repeat, and why merchants feel the failure first.

Learning examples · Updated 2026-08-03

How to read these examples

These are not drop-in theme files and not a directory of converter templates. Read them as short production post-mortems. The through-line is isolation: when CSS, JavaScript, or hardcoded copy is not owned by the section, merchants cannot edit safely.

The primary Shopify concept on this page is the scoped CSS contract—wrapping section styles with #shopify-section-{{ section.id }}. Every example either teaches that contract directly or shows a related binding decision that keeps editor changes local.

Before

The static or fragile pattern we see in client handoffs.

After

The production shape that survives theme editor use.

Decision

The one implementation choice the lesson forces you to make.

Mistake

The shortcut that looks fine in a preview and fails after launch.

Why

Who pays for the failure—usually the merchant, then your weekend.

Worked example

Example 1 — Scope CSS to the section, not the theme

Primary lesson for this hub: the section.id CSS contract.

Before

.hero-title {
  font-size: 48px;
  color: #111;
}

.hero-title a {
  text-decoration: none;
}

After

#shopify-section-{{ section.id }} .hero-title {
  font-size: {{ section.settings.title_size }}px;
  color: {{ section.settings.text_color }};
}

#shopify-section-{{ section.id }} .hero-title a {
  text-decoration: none;
}

Implementation decision

Decide whether styles belong to this section instance or to the whole theme. If a merchant should change type size on one homepage band without rewriting collection pages, the selector must include section.id.

Mistakes we still see

  • Shipping unscoped .hero-title rules that restyle Dawn’s product title and blog headings.
  • Scoping some rules but leaving hover/focus styles global.
  • Putting critical layout CSS in theme.css.liquid “temporarily” and never moving it.

Why it matters

Merchants edit settings expecting a local change. Unscoped CSS turns one heading tweak into a storewide visual regression—and they blame the last developer who touched the section.

Worked example

Example 2 — Bind plain text settings with escape

Supports isolation: merchant copy stays inside the section output contract.

Before

<h2 class="hero-title">Summer sale starts Friday</h2>
<p class="hero-sub">Free shipping over $50</p>

After

<h2 class="hero-title">{{ section.settings.heading | escape }}</h2>
{% if section.settings.subheading != blank %}
  <p class="hero-sub">{{ section.settings.subheading | escape }}</p>
{% endif %}

Implementation decision

Choose text for short headings and subheads; choose richtext only when merchants need bold/links. Then filter plain text with escape so theme editor input cannot break markup.

Mistakes we still see

  • Leaving marketing copy hardcoded after “we will schema it later.”
  • Using richtext for a one-line heading and fighting unwanted paragraph tags.
  • Omitting blank checks so empty settings still reserve awkward whitespace.

Why it matters

When copy is hardcoded, every campaign refresh needs a deploy. When escape is missing, a pasted quote or angle bracket can fracture the section HTML in the editor preview.

Worked example

Example 3 — Guard empty image settings

Merchants often save before uploading art—guards keep unrelated templates stable.

Before

<img
  src="{{ section.settings.image | image_url: width: 1600 }}"
  alt=""
  width="1600"
  height="900"
>

After

{% if section.settings.image != blank %}
  {{
    section.settings.image
    | image_url: width: 1600
    | image_tag:
      loading: 'lazy',
      widths: '400, 800, 1200, 1600',
      sizes: '(min-width: 990px) 1600px, 100vw',
      alt: section.settings.image.alt
    | escape
  }}
{% endif %}

Implementation decision

Treat image_picker as optional until the merchant uploads. Decide whether a missing image hides the media, shows a placeholder setting, or disables the whole band via a checkbox.

Mistakes we still see

  • Calling image_url on a blank setting and shipping broken image icons on launch day.
  • Hardcoding alt text that ignores the file’s alt in Files.
  • Using deprecated img_url filters on new OS 2.0 work.

Why it matters

Content teams save drafts mid-upload. Without blank guards, incomplete editor state becomes a production broken-image story on mobile—often on templates that were never part of the campaign brief.

Worked example

Example 4 — Ship a preset or the section stays invisible

Isolation is useless if merchants cannot add the section instance.

Before

{% schema %}
{
  "name": "Campaign hero",
  "settings": [
    { "type": "text", "id": "heading", "label": "Heading" }
  ]
}
{% endschema %}

After

{% schema %}
{
  "name": "Campaign hero",
  "settings": [
    { "type": "text", "id": "heading", "label": "Heading", "default": "Summer sale" }
  ],
  "presets": [
    { "name": "Campaign hero" }
  ]
}
{% endschema %}

Implementation decision

Every merchant-facing section needs at least one preset. Decide the default name merchants will recognize in Add section—not an internal codename.

Mistakes we still see

  • Forgetting presets so the file exists in sections/ but never appears in the editor.
  • Preset names like hero_v3 that confuse brand teams.
  • Seeding defaults that fight the brand (wrong tone, placeholder Latin).

Why it matters

Merchants cannot edit what they cannot add. A missing preset turns a finished Liquid file into a private developer artifact and forces another deploy just to place the band.

Worked example

Example 5 — Isolate interactive scripts per section.id

Complements scoped CSS: behavior must be instance-local too.

Before

document.querySelectorAll('.faq-item button').forEach((btn) => {
  btn.addEventListener('click', () => {
    btn.parentElement.classList.toggle('is-open');
  });
});

After

{% javascript %}
class FaqSection extends HTMLElement {
  connectedCallback() {
    this.querySelectorAll('[data-faq-trigger]').forEach((btn) => {
      btn.addEventListener('click', () => {
        const expanded = btn.getAttribute('aria-expanded') === 'true';
        btn.setAttribute('aria-expanded', String(!expanded));
      });
    });
  }
}
customElements.define('faq-section-{{ section.id }}', FaqSection);
{% endjavascript %}

<faq-section-{{ section.id }} class="faq">
  <!-- triggers + panels -->
</faq-section-{{ section.id }}>

Implementation decision

If two instances of the same section can appear on one template, JavaScript must key off section.id (or a unique custom element name). Global querySelectorAll is not a section API.

Mistakes we still see

  • Binding once on DOMContentLoaded and breaking when the editor re-renders the section.
  • Toggling class names without aria-expanded for keyboard users.
  • Copying FAQ accordion JS onto product disclosures without reading the PDP disclosure guide.

Why it matters

Merchants duplicate sections on landing pages. Global listeners double-fire, open the wrong panel, or stop working after the first editor save—exactly when the brand is trying to self-serve.

The through-line: local edits, local consequences

Across these examples the merchant task is the same: change a setting, image, or block row and expect only this section instance to change. The Shopify object that enforces that expectation for styles is #shopify-section-{{ section.id }}. Settings bindings, blank guards, presets, and script isolation are the supporting decisions that keep the contract honest.

When you review a pull request, ask whether a merchant edit can escape this section. If yes, the example failed—even if the desktop preview looked perfect.

Go deeper after the examples

Editorial review

Reviewed by the HTML to Liquid Converter team

Dhruv Goyani, Shopify Developer at HTML to Liquid Converter

Dhruv Goyani

3 years web design + 3 years Shopify development experience

LinkedIn profile →
Nishad Kikani, Lead Shopify Developer at HTML to Liquid Converter

Nishad Kikani

2 years web design + 6 years Shopify development experience

LinkedIn profile →

Content is reviewed by the HTML to Liquid Converter Shopify development team before publication. Technical accuracy is validated against current Shopify Online Store 2.0 conventions and active client theme work.

Questions or corrections? Contact us.

Supporting utility

Practice after you understand the decisions

When you can explain why section.id scoping, escape filters, blank guards, and presets belong in a production section, the converter workspace can help you draft markup faster. It does not replace the judgment in these examples.