Design & UX

Why I've Been Doing Shopify CSS All Wrong (And Why That's Actually Okay)

A

Admin User

Author

Jul 18, 2026
5 min read
8 views
Why I've Been Doing Shopify CSS All Wrong (And Why That's Actually Okay)

I spent three years building Shopify themes before I really understood why I had three different ways to write CSS. I'd reach for whichever method felt convenient in the moment—throw it in a {% style %} tag if it needed a Liquid variable, use a <style> tag when I was lazy, and scattered {% stylesheet %} blocks throughout my project when I remembered performance mattered. It worked. But it wasn't intentional.

Then I inherited a sprawling custom theme built by someone who clearly had the same philosophy I did. The HTML was bloated. CSS was duplicating everywhere. The Theme Editor felt sluggish. That's when I realized the problem wasn't the methods themselves—it was that I'd never actually understood when to use each one. I just knew they existed.

This realization hit me harder than it probably should have. Here I was, a "senior" developer by title, shipping code that broke one of the fundamental rules I preach to juniors: architecture first, implementation second.

The Three Methods Aren't the Same Thing—They're Different Tools

Let me be clear on something that took me embarrassingly long to grasp: these three CSS approaches solve different problems. They're not interchangeable.

{% stylesheet %} is where your shared, reusable styles live. This is your foundation. Product cards, buttons, grids, typography utilities—anything that appears multiple times across your theme. Shopify bundles this once and caches it. When a merchant loads your theme on their store, this CSS loads once. Full stop. That's efficient.

{% style %} is for dynamic styles that respond to Theme Editor settings. When a merchant changes the heading color in the editor, they expect to see that change immediately. This tag lets Liquid variables flow directly into CSS. The trade-off? Each section instance gets its own CSS block. If you have ten product card sections on a page, you get ten copies of that CSS. It's necessary overhead for flexibility, but it's overhead nonetheless.

<style> is honest about being raw HTML. No bundling. No Liquid processing. No integration with Shopify's system. It's useful for temporary experiments or truly one-off scenarios, but treating it as a primary method is architectural laziness.

Where My Understanding Broke Down

I've been conflating "flexibility" with "good architecture" for years. Using {% style %} everywhere felt flexible. I could make anything dynamic. But flexibility without structure just creates chaos.

Here's what I realized: a production-grade theme needs all three, but each in its proper place. The moment I started thinking about responsibility instead of convenience, everything changed. Reusable styles have a responsibility to stay lean and cached. Dynamic styles have a responsibility to be efficient despite their nature. Raw HTML should barely exist in a professional theme.

The real insight isn't that you should pick one method and stick with it. It's that you should deliberately choose each method for the right reason.

A Pattern That Actually Works

Here's what I've started doing in new projects:

{% stylesheet %}
  .product-card {
    padding: 20px;
    border-radius: 12px;
    color: var(--heading-color);
  }
{% endstylesheet %}

Then in my section:

<div 
  class="product-card" 
  style="--heading-color: {{ section.settings.heading_color }};"
>
  <!-- content -->
</div>

This approach combines the best of both worlds. My CSS stays bundled and cached (fast). My styles stay dynamic (flexible). I'm not duplicating CSS for every section instance (lean HTML).

The CSS variable acts as a bridge. It's a controlled injection point. Nothing hidden. Nothing magical.

What I'd Do Differently

The original article frames this perfectly, but there's one thing I'd emphasize more strongly: this isn't just about performance metrics. Yes, bundled CSS is technically faster. But the real win is maintainability.

When you have 200 lines of {% style %} blocks scattered across 15 different sections, debugging becomes a nightmare. Finding where a color is actually defined? Impossible. Making a global change? Fragile and error-prone. When your reusable styles live in {% stylesheet %} blocks, your codebase becomes searchable. Intentional. Professional.

I'm also more skeptical about the "use CSS variables as a bridge" approach than I initially was. It's powerful, but it adds a layer of indirection. For truly merchant-facing settings, sometimes dynamic CSS in {% style %} is clearer because it's explicit. The performance hit from a few extra CSS blocks is usually worth the clarity.

The Question I'm Sitting With

How much performance optimization is actually necessary versus how much is cargo culting? I've optimized themes into illegibility before. I've also shipped bloated themes that performed fine because the actual bottleneck was API calls, not CSS.

The answer probably depends on your merchant base and their content volume. A theme serving thousands of products needs different architecture than one serving a few dozen. But I think most of us trend toward over-optimizing the wrong things.

Moving Forward

I'm committing to being more intentional about CSS architecture in every new project. Not because I need to check off a performance checklist, but because intentional code is easier to maintain, scale, and hand off.

If you're building Shopify themes, think about which method serves which responsibility. That's the real skill.


Source: This post was inspired by "Most Shopify Developers Use This Every Day. Almost Nobody Knows Why It Exists." by Dev.to. Read the original article

Share this article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

Stop Building God Apps: Why Small, Focused Tools Actually Win
Design & UX Jul 17

Stop Building God Apps: Why Small, Focused Tools Actually Win

I had a moment last week that crystallized something I've been thinking about for years. I was helping a junior developer navigate our codebase, and she asked why we had so many single-purpose internal tools instead of one unified platform. My answer took twenty minutes and revea...

Stop Treating WordPress Like a CMS and Start Building It Like Software
Design & UX Jul 16

Stop Treating WordPress Like a CMS and Start Building It Like Software

Six months ago, I convinced a client that WordPress could handle their marketplace platform. They needed user roles, custom listings, filtering, premium tiers, and payment processing. I remember sitting in that kickoff meeting thinking, "It's just WordPress, how hard could this b...