This file provides frontend best practices and conventions for Claude Code when working on any Shopify theme project. Follow these guidelines unless the project's README or a team member explicitly overrides them.
- Follow the standard Shopify theme directory structure:
assets/,config/,layout/,sections/,snippets/,templates/,locales/. - Never create directories outside this structure without explicit justification.
- Keep
layout/theme.liquidlean — load scripts and styles via{% render %}snippets or section includes, not inline. - Use
templates/for JSON templates (Online Store 2.0). Avoid.liquidtemplates unless supporting a legacy theme.
- Prefer
{% render %}over{% include %}.renderhas a sandboxed scope;includeleaks parent variables and is deprecated. - Never use
{% include %}in new code. - Keep logic out of templates. Move conditional blocks and loops into snippets.
- Avoid deeply nested Liquid logic (>3 levels). Refactor into named snippets for readability.
- Use
liquidtag blocks ({% liquid %}) to group multiple tags without extra whitespace output. - Strip whitespace with
{%- -%}and{{- -}}in performance-sensitive areas (loops, large partials). - Always provide fallback values for variables:
{{ product.title | default: 'Untitled' }}. - Never output raw user-controlled data without appropriate filters (e.g.,
escape,strip_html).
- Every merchant-editable area must be a section or a block inside a section.
- Define schema settings (
{% schema %}) for all configurable content — never hardcode merchant-facing copy. - Use
presetsin section schemas so sections are available in the Theme Editor. - Sections must be self-contained and re-renderable in isolation (required by the section rendering API).
- Use
blocksfor repeatable elements (e.g., slides, tabs, FAQ items). Set a reasonablemax_blockslimit. - Always define a
nameandclassin the section schema for Editor discoverability. - Validate that schema
typevalues match Shopify's supported input types (text, richtext, image_picker, url, color, range, select, checkbox, radio, product, collection, etc.). - App Blocks must be declared in section schemas under
"blocks": [{ "type": "@app" }]to support app integrations cleanly.
- Themes can only read metafields — never attempt to write or update them from Liquid or client-side JS.
- Access metafields via
product.metafields.namespace.key— never construct dynamic metafield keys at runtime. - Always check for existence before outputting:
{% if product.metafields.custom.tagline != blank %}. - Metafield values have a 16 KB per-value cap. If a value appears truncated, flag it — the fix is on the data/admin side, not in the theme.
- Document every custom metafield namespace and key the theme depends on inside
README.md, so developers know what needs to be configured in the store.
- Lazy-load all images below the fold using
loading="lazy"and Shopify'simage_urlfilter with explicitwidthandheight. - Always use the
image_urlfilter with awidthparameter — never output raw CDN URLs or hardcoded sizes. - Use
srcsetfor responsive images. Shopify'simage_tagfilter generates srcset automatically. - Defer non-critical JavaScript with
deferortype="module". - Never use
document.write(). - Avoid loading third-party scripts in
<head>— useasyncor move to end of<body>. - Limit the number of section-level
<script>tags; consolidate theme JS intoassets/. - Use
preloadfor critical fonts and above-the-fold images. - Target a Lighthouse performance score ≥ 80 on mobile for all new features.
- Write vanilla JS unless the project already uses a framework. Do not introduce React, Vue, or Alpine.js without team approval.
- Use Web Components or custom elements for encapsulated interactive UI (carousels, modals, accordions). Shopify Dawn uses this pattern.
- Avoid jQuery. It is not included in modern Shopify themes.
- Namespace all global variables and custom events under a project-specific prefix (e.g.,
window.ThemeName). - Use
CustomEventfor cross-component communication. Dispatch events ondocumentand listen from any component. - Always use
addEventListener— never inlineonclickattributes. - Handle errors gracefully; wrap Fetch calls and Cart API calls in try/catch with user-visible fallback messaging.
- Use the Cart API (
/cart/add.js,/cart/update.js,/cart/change.js) for all cart mutations — never rely on full-page form submissions where a dynamic cart experience is expected. - Dispatch Shopify's standard cart events (
cart:refresh,cart:updated) after mutations so other components (mini-cart, header count, etc.) can react without tight coupling. - Always optimistically update the UI and roll back on API error with a visible message.
- Never redirect to
/checkoutdirectly from JS — usewindow.locationonly after a confirmed cart state, or use the standard checkout button.
- Use CSS custom properties (variables) for all design tokens: colors, spacing, typography, border-radius.
- Define theme color and font settings in
config/settings_schema.jsonand expose them as CSS variables inlayout/theme.liquid. - Avoid
!important. If needed, it indicates a specificity problem — fix the selector instead. - Do not use inline styles in Liquid templates except for dynamically injected CSS custom properties.
- Scope component styles using a BEM-like convention or web component selectors — avoid broad tag selectors.
- Minify production CSS. Use Shopify CLI's build pipeline or a documented build step.
- All customer-facing strings must use
{{ 'key' | t }}translation keys defined inlocales/. - Never hardcode English strings in templates — even for single-locale stores.
- Always provide
alttext for images: use the product/image alt attribute, a metafield setting, or a schema setting fallback. Never leavealtempty on meaningful images. - All interactive elements (buttons, links, modals) must be keyboard accessible and have visible focus styles.
- Use semantic HTML:
<nav>,<main>,<header>,<footer>,<article>,<section>,<button>. - Every form input must have an associated
<label>(visible or visually hidden via.visually-hidden). - Modals and drawers must trap focus and support
Escapeto close. - Target WCAG 2.1 AA compliance as a minimum baseline.
- Use Shopify CLI 3.x (
shopify theme dev,shopify theme push,shopify theme pull) for all local development. - Never push directly to a live theme. Use a development theme or a duplicate.
- Add
config/settings_data.jsonto.gitignorefor team projects unless you have a deliberate sync strategy — it contains live merchant customizations. - Always include a
.shopifyignoreto prevent pushing files that should not overwrite the remote theme.
- Use feature branches for all work. Never commit directly to
mainorproduction. Branch naming convention:feature/,fix/,chore/prefixes (e.g.,feature/product-page-tabs,fix/cart-drawer-mobile). - Keep commits small and focused — one logical change per commit. Avoid "catch-all" commits that bundle unrelated changes.
- Write commit messages in the imperative mood and reference the area of the theme:
Add sticky header behaviour,Fix quantity input on mobile cart,Refactor product form into snippet. - Open a pull request for every change, even solo projects. PRs create a record of intent and make rollbacks easier to scope.
- Require at least one review approval before merging to
mainon team projects. - Squash commits on merge to keep the main branch history linear and readable.
- Tag every release on
mainwith semantic versioning:v1.0.0,v1.1.0,v1.2.0-beta. - Never commit secrets, tokens, or credentials. Use environment variables or Shopify CLI's authenticated sessions.
- Keep the following in
.gitignore:config/settings_data.json,node_modules/,.env,.DS_Store, and any build artefact directories. - Before opening a PR, run
shopify theme checkand resolve all errors so reviewers are not blocked by avoidable issues.
- Run
shopify theme checkbefore every commit and resolve all errors. Treat warnings as errors for new code. - Validate all schema JSON — malformed schema silently breaks the Theme Editor.
- Test on real Shopify preview URLs, not just
localhost— cart, redirects, and some Liquid objects behave differently locally. - Test across: Chrome, Firefox, Safari (desktop + iOS), and Chrome Android.
- Test with a screen reader (VoiceOver on macOS/iOS, NVDA on Windows) for accessibility-critical flows.
- Test Theme Editor functionality: every section must be addable, removable, and reorderable without JS errors.
- Every section must have a comment block at the top describing its purpose and any non-obvious schema settings.
- Document all metafield namespaces and keys the theme reads in
README.md. - Add inline
{% comment %}blocks for non-obvious Liquid workarounds explaining the reason. - Maintain a
CHANGELOG.mdusing Keep a Changelog format.