An HTML-to-Liquid converter can identify headings, paragraphs, images, links, colours, and repeated structures. It cannot know the complete theme context: whether a heading should be the page's h1, whether a repeated card belongs in a block, which image is the largest contentful element, or whether a merchant should be allowed to change a design token.
Treat generated code as a structured first draft. This guide provides a repeatable review gate and a complete small example, then shows exactly what to verify before copying a section into a published theme.
What automated conversion can and cannot decide
A converter is well suited to mechanical work:
- replacing eligible text with
section.settingsreferences; - proposing stable setting IDs;
- creating a schema array;
- mapping images to
image_pickerand destinations tourlsettings; - recognizing some repeated sibling elements as block candidates;
- removing clearly unsafe script elements and inline event handlers.
A developer still needs to decide:
- the correct content model and which choices merchants should control;
- heading order in the final page template;
- whether rich text, inline rich text, or plain text is appropriate;
- block limits, empty states, defaults, presets, and translations;
- responsive breakpoints, image sizes, loading priority, and crop behaviour;
- compatibility with the theme's CSS, snippets, colour schemes, and JavaScript;
- accessibility, performance, and security in the final storefront context.
Original HTML input
We will review this collection callout:
<section class="collection-callout">
<div class="collection-callout__content">
<p>Field notes</p>
<h2>How to choose a day pack</h2>
<p>Compare capacity, strap shape, and access before choosing a size.</p>
<a href="/blogs/guides/day-pack-fit">Read the fitting guide</a>
</div>
<img src="day-pack.jpg" alt="A day pack adjusted on a hiking jacket">
</section>
The structure is valid and semantic, but the converter cannot see where this section will be placed. During review, we keep the h2, model the body as rich text, require both link fields before rendering, and make the image responsive.
Reviewed complete Liquid output
Create sections/collection-callout.liquid:
{% style %}
#shopify-section-{{ section.id }} .collection-callout {
display: grid;
gap: 1.5rem;
align-items: center;
padding-block: 3rem;
}
#shopify-section-{{ section.id }} .collection-callout__content > :first-child {
margin-top: 0;
}
#shopify-section-{{ section.id }} .collection-callout__image {
display: block;
width: 100%;
height: auto;
border-radius: 0.75rem;
}
#shopify-section-{{ section.id }} .collection-callout__link:focus-visible {
outline: 3px solid currentColor;
outline-offset: 3px;
}
@media (min-width: 750px) {
#shopify-section-{{ section.id }} .collection-callout {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 3rem;
}
}
{% endstyle %}
<section
class="collection-callout page-width"
{% if section.settings.heading != blank %}aria-labelledby="CollectionCalloutHeading-{{ section.id }}"{% endif %}
>
<div class="collection-callout__content">
{% if section.settings.eyebrow != blank %}
<p>{{ section.settings.eyebrow | escape }}</p>
{% endif %}
{% if section.settings.heading != blank %}
<h2 id="CollectionCalloutHeading-{{ section.id }}">
{{ section.settings.heading | escape }}
</h2>
{% endif %}
{% if section.settings.body != blank %}
<div class="rte">{{ section.settings.body }}</div>
{% endif %}
{% if section.settings.link_label != blank and section.settings.link != blank %}
<a class="collection-callout__link" href="{{ section.settings.link | escape }}">
{{ section.settings.link_label | escape }}
</a>
{% endif %}
</div>
{% if section.settings.image != blank %}
{{
section.settings.image
| image_url: width: 1800
| image_tag:
class: 'collection-callout__image',
widths: '480, 720, 960, 1200, 1500, 1800',
sizes: '(min-width: 750px) 50vw, 100vw',
loading: 'lazy',
alt: section.settings.image.alt
}}
{% endif %}
</section>
{% schema %}
{
"name": "Collection callout",
"settings": [
{
"type": "text",
"id": "eyebrow",
"label": "Eyebrow",
"default": "Field notes"
},
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "How to choose a day pack"
},
{
"type": "richtext",
"id": "body",
"label": "Text",
"default": "<p>Compare capacity, strap shape, and access before choosing a size.</p>"
},
{
"type": "text",
"id": "link_label",
"label": "Link label",
"default": "Read the fitting guide"
},
{
"type": "url",
"id": "link",
"label": "Link"
},
{
"type": "image_picker",
"id": "image",
"label": "Image"
}
],
"presets": [
{
"name": "Collection callout"
}
]
}
{% endschema %}
Review gate 1: preserve valid HTML structure
Read the Liquid as if every conditional were true. Check opening and closing tags across the branches. A missing setting must not strand half an element. In this example the entire link is inside one condition, so it never renders without a destination or accessible name.
Check the content model as well. richtext can output paragraphs, so its wrapper is a div, not a p. The section references its visible heading with a unique ID derived from section.id, preventing duplicate document IDs when the section appears more than once.
Review gate 2: verify Liquid and setting IDs
List every reference beginning with section.settings and compare it with the schema. The example has exactly six IDs: eyebrow, heading, body, link_label, link, and image. Spelling and scope must match.
Generated IDs should be unique and deterministic. Re-running a converter on the same input should not produce a new random suffix that makes review noisy or breaks saved merchant data after a replacement. If two source labels normalize to the same ID, resolve the collision consistently and update the Liquid reference.
Do not add another outer element with id="shopify-section-{{ section.id }}"; Shopify supplies the section wrapper. Use that existing ID in scoped CSS selectors as the example does.
Review gate 3: parse the schema as strict JSON
Copy the schema payload into JSON.parse or another strict validator. It must contain no comments, trailing commas, single-quoted strings, or Liquid expressions. Then validate the Shopify-specific structure: recognized setting types, appropriate defaults, unique IDs, resolvable block types, and a preset when the section should appear in Add section.
The schema builder helps assemble common settings, while Shopify's settings documentation remains the source of truth for current platform rules.
Review gate 4: reject unsafe behaviour
Generated sections should not carry source script elements, inline handlers such as onclick, executable URL schemes, srcdoc, or copied third-party tracking code. Do not convert arbitrary JavaScript into a theme setting. Reintroduce behaviour only after understanding it, preferably through established theme assets and event patterns.
Escape plain text at the HTML boundary. Use platform setting types for URLs and images. Rich text is intentionally rendered as Shopify-generated HTML, but that does not make arbitrary HTML from an unknown metafield or external service trustworthy. The Liquid escaping guide covers these contexts in detail.
Review gate 5: audit accessibility in the full page
The source has an h2, but only the complete page tells you whether that level is correct. Check the page's heading outline and keep one intentional primary heading. Verify labels, focus order, focus visibility, contrast, zoom, reflow, and useful alternative text.
Do not create a button for navigation or an anchor for a non-navigation action. The example's CTA is an anchor because it goes to a guide. Its visible label explains the destination more clearly than “Learn more.”
Review gate 6: make media and CSS theme-aware
The output requests responsive image widths and describes the layout slot with sizes. Those values are estimates until you test the theme's real container. Inspect currentSrc, intrinsic dimensions, crop, loading, and layout movement on actual templates.
Scoped selectors reduce collisions but do not eliminate all theme interactions. Check whether page-width, rte, global heading margins, link styles, and CSS custom properties exist in the target theme. Remove or adapt helper classes that the source theme does not provide.
Review gate 7: test editor states, not only defaults
Default content is the easiest path. Also test:
- every setting blank;
- very long heading and link labels;
- punctuation and HTML-special characters;
- no image, a small image, and a portrait image;
- the section added twice on one page;
- theme-editor selection and live setting updates;
- narrow mobile, zoomed desktop, and a high-density screen;
- keyboard-only navigation and reduced motion;
- the storefront outside the editor iframe.
The expected default output is a two-column callout on desktop and a one-column callout on mobile. With no link, no anchor renders. With no image, the content remains complete rather than leaving a broken-image icon or empty fixed-height panel.
Common conversion failures and fixes
- Nested paragraph: change the outer
paround arichtextsetting to a flow container. - Duplicate setting ID: rename deterministically and update the matching reference.
- Invalid schema: isolate JSON syntax, then Shopify rules, rather than guessing at commas.
- Hard-coded CDN image: use
image_picker,image_url, andimage_tagwith reviewed sizes. - Empty CTA: require both destination and descriptive label.
- Unscoped CSS collision: anchor section-specific rules to Shopify's wrapper ID.
- Unsafe script copied from HTML: reject it and redesign behaviour deliberately.
- Heading level copied blindly: decide it from the final template hierarchy.
- All images eager-loaded: prioritize only verified above-the-fold media.
- Converter output replaces the whole theme: add one reviewed section file and integrate it with existing theme conventions.
Development-theme testing sequence
- Save the generated file outside the live theme and review its diff.
- Parse every schema payload as JSON.
- Run
shopify theme checkfrom the target theme. - Start a development preview with
shopify theme dev. - Add the section in the editor and exercise every setting state.
- Test the normal preview URL at mobile and desktop sizes.
- Inspect rendered DOM, accessibility tree, network-selected image, and browser console.
- Compare with nearby theme sections for spacing and design-token consistency.
- Ask another developer to review security-sensitive behaviour or unfamiliar Liquid.
- Publish only after the development version passes the checklist and theme review process.
Copyable manual verification checklist
- Source HTML is well formed and contains no unwanted scripts or handlers.
- Content is modelled as section settings or repeatable blocks appropriately.
- Rich-text output is not wrapped in another paragraph.
- Liquid tags, filters, branches, and scopes are valid.
- Setting IDs are unique, deterministic, and referenced consistently.
- Schema JSON has no comments or trailing commas.
- Presets and block references resolve.
- Plain text and attributes are escaped for their context.
- URLs, external data, and any reintroduced behaviour are reviewed.
- The complete page has a logical heading order and labelled controls.
- Keyboard focus, contrast, zoom, reflow, and reduced motion are tested.
- Images have useful alt text, intrinsic dimensions, responsive candidates, and justified loading.
- Empty, long, repeated, and missing-media states work.
- Theme Check and development-theme testing pass.
- The final
.liquidfile, including schema tags, can be copied as one unit.
Start with the HTML converter when mechanical mapping saves time, then use this checklist before treating the result as theme code. For a worked build, read How to convert HTML into a Shopify section; for repeated cards, continue with Creating repeatable Shopify blocks.
Conclusion
Conversion quality is the combination of automation and informed review. A useful generator handles repetitive mapping and rejects obvious hazards. A developer validates the content model, JSON, Liquid, theme integration, accessibility, security, responsive behaviour, and editor states. The checklist turns that responsibility into a repeatable release gate instead of an informal final glance.