How to Add Custom CSS to a Shopify Section

Three ways to style a Shopify section — a {% stylesheet %} block, an assets file, and theme color settings — scoped so it never leaks into the rest of the theme.

5 min readUpdated

Part of our guide to Shopify Custom Sections: The Complete Guide.

Styling a section is where most theme breakage starts. A rule written a little too broadly — button { ... }, .container { ... } — leaks out of your section and quietly changes buttons and containers on every other page. This guide covers the three places you can put a section's CSS, how to scope it so it stays contained, and how to lean on the theme's own color settings instead of hard-coding values.

It assumes you already have a section file. If you don't, start with how to create a custom section in Shopify and come back — the section referenced below is the feature-banner.liquid from that guide.

Option 1 — A {% stylesheet %} block in the section file

The simplest place for a section's CSS is a {% stylesheet %} tag inside the section's own .liquid file. Shopify pulls the contents of every section's stylesheet block into a single, minified theme stylesheet, and only loads it when the section is on the page. Everything stays in one file, so the markup and its styles travel together.

{% stylesheet %}
.feature-banner {
  padding: 3rem 1.5rem;
  background: #f4f4f2;
}
.feature-banner__heading {
  font-size: 1.75rem;
  margin: 0 0 0.5rem;
}
{% endstylesheet %}

Two rules keep this out of trouble:

  • Scope every selector to the section's own class. Prefix with .feature-banner (or a BEM-style .feature-banner__heading) so nothing matches elements outside the section.
  • Liquid does not run inside {% stylesheet %}. The block is treated as static CSS, so you cannot write {{ section.settings.color }} in it. For merchant-set values, use the inline-style approach in Option 3.

This is the right default for most sections: self-contained, only loaded when needed, and impossible to forget when you copy the file elsewhere.

Option 2 — A file in assets/

When the CSS is long, shared across several sections, or you simply prefer separate files, put it in the theme's assets/ folder and link it from the section.

Create assets/feature-banner.css, then load it at the top of the section file:

{{ 'feature-banner.css' | asset_url | stylesheet_tag }}

<section class="feature-banner">
  ...
</section>

asset_url resolves the file to its full CDN path, and stylesheet_tag wraps it in a proper <link> element. Because the tag sits inside the section, the stylesheet is requested wherever the section renders.

The trade-off versus Option 1: an assets/ file is a separate HTTP request rather than part of the bundled theme stylesheet, and it is a second file you have to remember to carry over when you move the section. For a large, reusable stylesheet that is a fair price; for a small section it usually is not.

Option 3 — Merchant-controlled colors from the schema

Hard-coding #008060 means every store that uses your section gets that exact green, and a merchant can never change it. To make color a setting, add a color (or color_scheme) field to the schema and apply it with an inline style where Liquid does run — the markup.

Add the setting:

{% schema %}
{
  "name": "Feature banner",
  "settings": [
    {
      "type": "color",
      "id": "bg_color",
      "label": "Background color",
      "default": "#f4f4f2"
    }
  ]
}
{% endschema %}

Then apply it in the markup with a scoped inline style. Wrapping it in a {% style %} block keyed to the section's unique id keeps the rule tied to this one instance:

{% style %}
  #shopify-section-{{ section.id }} .feature-banner {
    background: {{ section.settings.bg_color }};
  }
{% endstyle %}

<section class="feature-banner">
  ...
</section>

section.id is unique per placement, and Shopify wraps every section in a #shopify-section-{{ section.id }} element automatically — so this rule can never match another section on the page, even a second copy of the same one.

Better still: reuse the theme's color scheme

The most maintainable option is not to introduce a new color at all, but to inherit the theme's. Online Store 2.0 themes expose a color_scheme setting type and CSS custom properties (Dawn uses rgb(var(--color-foreground)), rgb(var(--color-background)), and similar). Reference those and your section re-brands itself whenever the merchant changes the theme's colors:

{% stylesheet %}
.feature-banner {
  color: rgb(var(--color-foreground));
  background: rgb(var(--color-background));
}
{% endstylesheet %}

The exact variable names vary by theme, so check the theme's config/settings_data.json or its base stylesheet before relying on them.

The one thing that actually breaks themes

Almost every "my section broke the whole store" problem traces back to an unscoped selector. Styling a bare tag or a generic class name — h2 { ... }, .button, .grid — matches those elements everywhere, not just inside your section. Keep three habits and it stays contained:

  • Give the section one root class (.feature-banner) and scope every rule under it.
  • For per-instance rules, key them to #shopify-section-{{ section.id }}.
  • Never style an element type or a theme utility class directly.

Keeping the CSS through a theme update

Whichever option you choose, the CSS lives in your theme files — a {% stylesheet %} block, an assets/ file, or a schema setting inside the section. When you update or switch themes, Shopify publishes a fresh copy of the theme and those files are not carried over, so the styling goes with the section it belonged to. The disciplined way to handle that is in update your theme without losing customizations; the structural way is to render the section — and its CSS — from an app instead, so a theme update never touches it.

If a section is one you rely on long-term, SectionGuard renders it and its styles from the app rather than your theme, so both survive every theme update and swap while keeping the same in-editor color settings merchants expect.