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
Ship sections on Dawn
Register sections on the correct JSON templates.
Open guide →Maintainable FAQ sections
Content architecture when Q&A must stay editable.
Open guide →Product-page disclosure accordions
PDP disclosure UX—not FAQ clones.
Open guide →Featured production guides
Curriculum hub for the rewritten implementation guides.
Open guide →Editorial review
Reviewed by the HTML to Liquid Converter team


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.