A lot of the answers we all give (mine included) are “paste this code and it works.” And it does — until you want to change the value. Then you’re back in the code editor every single time. Here’s how to take the exact same fix one step further, so you set it once and adjust it forever from the theme editor — no code re-runs.
I’ll use one example that comes up constantly: adding an image overlay (a dark/tinted layer so text stays readable over a photo) to a section that doesn’t have that control natively. The built-in Image banner HAS an opacity slider — multicolumn, collection and custom image sections usually don’t. The same idea applies to spacing, font sizes, button colours, show/hide badges, and more.
LEVEL 1 — Custom CSS (fastest, but frozen and global)
The usual answer: drop CSS into your theme code — theme.liquid or the theme’s main stylesheet:
.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 to change it you edit code again. Fine for a one-off, painful to maintain.
LEVEL 2 — Custom Liquid block (scoped, but still hardcoded)
Add a Custom Liquid block and scope the style to that 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 %}
Better — no theme-file edits, and it only affects this block. But the Custom Liquid box 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, so the value becomes an editor slider
This is the Online Store 2.0 way: put the value in the {% schema %} as a range setting, and it turns into a slider in the editor. 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 %}
Three things that make it show up and work in the editor:
- “presets” is what makes the section appear under Add section — leave it out and it never shows up (the #1 “why can’t I see it” cause).
- Every entry under “settings” becomes a control in the section panel: range = slider, color = colour picker, text/richtext = text fields.
- range returns a number 0–100, so divide by 100 for CSS opacity: overlay_opacity | divided_by: 100.0 (use 100.0, not 100, or Liquid floors it to 0).
- Scoping the CSS to #Overlay-{{ section.id }} means you can add the section ten times with ten different overlays — no collisions.
Now the merchant drags “Overlay opacity” in the editor and never touches code again. (Advanced: the same schema works as a theme block in /blocks/ — add “presets” there too, and drop it into any section that has “blocks”: [{ “type”: “@theme” }] plus {% content_for ‘blocks’ %}, referencing block.settings.…)
The takeaway: whenever you’re about to paste a fixed value into a theme — a spacing, a font size, a colour, an opacity, a show/hide toggle — ask “should this be a setting instead?” If it’ll ever change, the few extra minutes to make it a range/checkbox/color save both you and the merchant every future edit.
Happy to help adapt this to your specific theme or section — just paste what you’re working with.