shopatch
← Blog

Stop pasting one-off code: build Shopify customizations your team can actually edit

Short answer: A “paste this snippet” fix works — right up until you want to change the value, at which point you are back in the code editor every single time. Building the same change as a theme-editor setting takes a few extra minutes and hands you (or your client) a slider you can adjust forever, no developer required. That is the difference between a throwaway hack and a customization you actually own.

If you have ever searched the Shopify forums for a small tweak — a bit more spacing, a darker image overlay, a different button colour — you know the pattern. The top answer is almost always “add this CSS to your theme.” It works. You paste, you save, it looks right. Then a month later you want the overlay slightly lighter, and you are back in theme.liquid hunting for the line. At Shopatch we build these the other way round, so here is the approach — with real code you can use today.

Why do “paste this code” fixes cost you later?

A pasted snippet costs you every future edit, because it hardcodes a value you will almost certainly want to change — and each change means reopening the code editor. Storefront tweaks are rarely one-off: spacing gets revisited when the design shifts, an overlay gets re-tuned for a new hero image, a button colour changes with a seasonal campaign. Across a busy store and a small team, that convenience quietly becomes a maintenance tax — usually paid by whoever is least comfortable in the code.

There is a better default, and it is built into every modern Shopify theme (Online Store 2.0, Horizon included): put the value behind a setting, and it appears in the theme editor as a control anyone can use.

How do you turn a hardcoded fix into an editable theme setting?

You turn a hardcoded fix into an editable setting by defining the value in a section or block’s {% schema %}, which Shopify then renders as a control in the theme editor. Below is one common request — an adjustable image overlay (a dark layer that keeps text readable over a photo) — taken through three levels, from frozen CSS to a live slider. The built-in Image banner already has this slider; multicolumn, collection and custom image sections usually do not, which is exactly why the request shows up in the forums constantly.

Level 1 — Custom CSS (fast, but frozen and global)

Level 1 is plain CSS pasted into your theme code — theme.liquid or the theme’s main stylesheet. It works instantly:

.multicolumn-card__image-wrapper { position: relative; }
.multicolumn-card__image-wrapper::after {
  content: ""; position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.4);
}

It works — but it hits every matching element on the site, the 0.4 is hardcoded, and changing it means editing code again. Fine for a genuine one-off, painful to maintain.

Level 2 — Custom Liquid, scoped but still hardcoded

Level 2 moves the same CSS into a Custom Liquid block, scoped to one instance with its id so it stops leaking onto every card:

<div class="overlay-{{ block.id }}">
  <!-- your image / content -->
</div>
{% style %}
  .overlay-{{ block.id }} { position: relative; }
  .overlay-{{ block.id }}::after {
    content: ""; position: absolute; inset: 0;
    background: rgba(0, 0, 0, 0.4);
  }
{% endstyle %}

This is better — no theme-file edits, and it only affects this block. But a Custom Liquid block has no settings of its own, so 0.4 is still frozen in code. This is where most “paste this” solutions stop.

Level 3 — A section with its own settings (the editor slider)

Level 3 puts the value in a section’s {% schema %} as a range setting, which Shopify turns into a slider in the theme editor — the Online Store 2.0 way. Create a section file at sections/image-with-overlay.liquid (Edit code → Sections → Add a new section):

{% style %}
  #Overlay-{{ section.id }} { position: relative; display: grid; place-items: center;
    min-height: {{ section.settings.min_height }}px; overflow: hidden; }
  #Overlay-{{ section.id }} .overlay-media { position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: cover; }
  #Overlay-{{ section.id }}::after { content: ""; position: absolute; inset: 0;
    background: {{ section.settings.overlay_color }};
    opacity: {{ section.settings.overlay_opacity | divided_by: 100.0 }}; }
  #Overlay-{{ section.id }} .overlay-content { position: relative; z-index: 1;
    color: {{ section.settings.text_color }}; text-align: center; padding: 2rem; }
{% endstyle %}

<div id="Overlay-{{ section.id }}">
  {% if section.settings.image %}
    {{ section.settings.image | image_url: width: 2000 | image_tag:
       class: 'overlay-media', loading: 'lazy', widths: '600,1000,1500,2000' }}
  {% endif %}
  <div class="overlay-content">
    {% if section.settings.heading != blank %}<h2>{{ section.settings.heading }}</h2>{% endif %}
    {{ section.settings.text }}
  </div>
</div>

{% schema %}
{
  "name": "Image with overlay",
  "settings": [
    { "type": "image_picker", "id": "image", "label": "Image" },
    {
      "type": "range",
      "id": "overlay_opacity",
      "min": 0, "max": 100, "step": 5,
      "unit": "%",
      "label": "Overlay opacity",
      "default": 40
    },
    { "type": "color", "id": "overlay_color", "label": "Overlay colour", "default": "#000000" },
    { "type": "color", "id": "text_color",   "label": "Text colour",    "default": "#ffffff" },
    {
      "type": "range",
      "id": "min_height",
      "min": 200, "max": 800, "step": 20,
      "unit": "px",
      "label": "Section height",
      "default": 400
    },
    { "type": "text", "id": "heading", "label": "Heading", "default": "Your heading" },
    { "type": "richtext", "id": "text", "label": "Text" }
  ],
  "presets": [{ "name": "Image with overlay" }]
}
{% endschema %}

Now the merchant drags Overlay opacity in the editor and never touches code again.

Why isn’t my custom section showing up in the theme editor?

A custom section almost always fails to appear because its {% schema %} is missing a presets key — that key is what makes a section selectable under Add section. Two more details make the settings behave as editor controls:

  • Every entry under "settings" becomes a control in the section panel: range is a slider, color is a colour picker, text/richtext are text fields, checkbox is a toggle.
  • Scope the CSS to #Overlay-{{ section.id }} so you can add the section ten times with ten different overlays and nothing collides.

One small gotcha: a range returns a number from 0 to 100, so divide by 100 for CSS opacity — | divided_by: 100.0. Use 100.0 with the decimal, or Liquid does integer maths and floors your opacity to 0.

(Advanced: the same schema works as a theme block in /blocks/. Add "presets" there too, and drop it into any section that declares "blocks": [{ "type": "@theme" }] and renders {% content_for 'blocks' %}, then reference block.settings….)

Which Shopify customizations should be settings instead of code?

Any customization whose value you might change again should be a setting rather than hardcoded code. The most common examples on the Shopify forums are spacing between sections, mobile-versus-desktop font sizes, button and hover colours, hiding a sold-out badge, and a sticky header — each is a fixed value that maps cleanly onto a range, checkbox or color control. Swap the hardcoded number for a range, the on/off for a checkbox, the colour for a color, and the customization stops being a developer errand and becomes a control on the page.

When is pasting a code snippet still the right call?

Pasting a snippet is the right call when the value will genuinely never change — a global reset or a permanent brand fix. The rule of thumb: if you can imagine anyone ever wanting to change it, spend the extra few minutes to make it an editable setting. It pays for itself the first time you would have opened the code editor and now do not have to.

Will a Shopify theme update delete my customizations?

Yes — a Shopify theme update can delete customizations, because updating a theme installs a fresh copy of the theme rather than merging your changes. Any code you edited directly inside the theme’s core files — its sections, snippets and assets — does not carry over when you publish the new version. On a heavily customized theme, this is the single most common way weeks of work quietly disappear.

Four habits keep that from hurting:

  • Keep custom code in its own files. New sections, blocks and snippets you add (like the image-with-overlay section above) are self-contained, so you copy them into the new theme version in minutes instead of re-diffing edits scattered through the theme’s core files.
  • Capture as much as possible as settings, not code. Values stored as theme settings live in the JSON templates and config, which are far more portable than inline code edits — another reason the slider approach here pays off.
  • Put the theme under version control. Connect it to Git via the Shopify CLI or the GitHub integration, so every change is tracked and diffable, and migrating to a new version becomes “apply these known changes” instead of guesswork.
  • Never update the live theme directly. Duplicate it, apply the new version on the copy, migrate and test your customizations there, then publish.

The through-line is the same as the rest of this post: the more your customizations live as settings and self-contained files rather than scattered code edits, the less a theme update can take from you.

Want this built into your store?

This is exactly the kind of work we do at Shopatch — customizations your team can actually run, not fragile snippets that lock you back into a developer for every tweak. If your store is stitched together from “paste this code” fixes, we can turn the ones that matter into clean, editable settings that survive theme updates. Get in touch and tell us what you keep having to change.