Skip to content
Log in

Theme assets and styling

On this page

Theme assets — CSS, JavaScript, fonts, and images — live in the resources/ directory of your theme and are served to the browser via the asset pipeline.

Asset directory structure

```

resources/ ├── src/ │ ├── scripts/ │ │ ├── theme/ # Core theme JavaScript │ │ │ └── liquid-components.js # Component reloader │ │ └── packs/ # Additional script bundles │ ├── styles/ │ │ ├── theme/ # Core theme CSS │ │ └── packs/ # Additional style bundles │ └── files/ # Static files (images, fonts, etc.) ├── build/ # Build configuration ├── dist/ # Compiled output (fingerprinted filenames) │ └── manifest.json # Maps logical names to fingerprinted filenames └── package.json ```

Loading assets

The {% require %} tag

Use {% require %} to load CSS and JavaScript files. It ensures each asset is loaded only once, even if required from multiple templates:

```liquid

{% require “styles/theme.css” %} {% require “scripts/theme.js” %} {% require “scripts/bundle.js” %} {% require “scripts/sort.js” %} ```

The {% require %} tag is typically used in: - The layout’s <head> section for global assets - Page templates for page-specific assets - Snippets for component-specific assets

The asset_url filter

Use {{ "filename" | asset_url }} to get the URL for a static asset:

```liquid

Logo

```

Asset resolution

When you call {% require "styles/theme.css" %}, the platform:

  1. Looks up dist/styles/theme.css in the custom theme’s manifest.json (if one exists).
  2. Falls back to the base theme’s manifest.json.
  3. Resolves the fingerprinted filename (for example styles/theme.LBH765JF.css).
  4. Renders a <link> or <script> tag with the resolved URL.

The {{ "filename" | asset_url }} filter resolves static files through the same manifest system.

Including assets in the layout

```liquid

{%- comment -%} layouts/theme.liquid — head section {%- endcomment -%}

{% require "styles/theme.css" %} {{ theme_supplement_stylesheet }} {% if current_store.global_css != blank %} {% endif %} {% require "scripts/configure.js" %} {% require "scripts/metadata.js" %} {% require "scripts/theme.js" %} {{ theme_supplement_javascript }} {% if current_store.global_javascript != blank %} {% endif %}

```

Page-specific assets

Load assets only on the pages that need them:

```liquid

{%- comment -%} pages/product.liquid — load bundle JS only on product pages {%- endcomment -%} {% if current_product.is_bundle? %} {% require “scripts/bundle-v2.js” %} {% require “scripts/quantity-picker.js” %} {% endif %} ```

Store-level custom CSS and JavaScript

Stores can have custom CSS and JavaScript configured in the CMS. Include them in your layout:

```liquid

{% if current_store.global_css != blank %}

{% endif %}

{% if current_store.global_javascript != blank %} {% endif %} ```

Custom CSS and JavaScript loading

Custom themes inject their own CSS and JavaScript through two mechanisms:

1. Theme supplement files — Upload theme-supplement.css and theme-supplement.js as theme assets. These are automatically injected via the layout’s {{ theme_supplement_stylesheet }} and {{ theme_supplement_javascript }} variables. This is how most client themes add custom styling.

2. The {% require %} tag — Load assets from resources/dist/. Custom themes can provide their own resources/dist/manifest.json to override or extend the base theme’s assets.

:::note The base CSS/JS provided by the platform is a reference implementation — not required. See Base CSS/JS is optional below for details. :::

Example: overriding the site font with a supplement

Add a theme asset with the key theme-supplement.css containing:

```css

body { font-family: “Comic Sans MS”; } ```

New theme asset with key theme-supplement.css containing a Comic Sans font override

Preview the site. The new asset has overridden the site font, and the rest of the built-in theme still applies.

Translations

Translation strings are stored in translations/en.default.json and accessed with the t filter:

```liquid

{{ “products.show.add_to_cart” | t }} {{ “checkout.title” | t }} ```

Translation file structure:

```json

{ “products”: { “show”: { “add_to_cart”: “Add to Cart”, “discontinued”: “This product has been discontinued” } }, “checkout”: { “title”: “Checkout” }, “cart”: { “header”: “Shopping Cart” } } ```

Theme variables

theme_variables provides key-value configuration for the theme. These are stored as theme variable records in StoreConnect. The built-in theme provides defaults (for example products.per_page: 12) that custom theme variables override.

```liquid

{% assign per_page = theme_variables[“products.per_page”] %} {% assign show_compare = theme_variables[“products.comparisons”] == true %} {% assign image_ratio = theme_variables[“images.ratio”] %} ```

Customizing the base theme with CSS variables

The quickest way to rebrand the base theme is to override its CSS custom properties — variables the base CSS exposes for colors, fonts, and spacing on :root. There are two places to set those overrides:

  • Store global CSS (recommended for per-store branding) — set the :root overrides on the store’s custom_styles via the update_store agent tool. This needs no theme file. custom_styles is the same field surfaced in Liquid as current_store.global_css, which the layout renders inside a <style> block (see Including assets in the layout above), so the overrides load on top of the base theme automatically.
  • theme-supplement.css (theme-author / build-time route) — bake the same overrides into the theme’s supplement stylesheet, as shown below.

The base CSS exposes these variables on :root:

Color variables

```css

:root { –sc-color-primary: hsl(212, 100%, 50%); /* Buttons, links, highlights / –sc-color-secondary: hsl(0, 0%, 15%); / Secondary actions / –sc-color-error: hsl(0, 100%, 45%); / Error states / –sc-color-sale: hsl(0, 100%, 45%); / Sale prices */ } ```

Each color also exposes HSL channels for flexible manipulation: --sc-color-primary-h, --sc-color-primary-s, --sc-color-primary-l.

Font variables

```css

:root { –sc-font-family: -apple-system, BlinkMacSystemFont, sans-serif; –sc-font-base: 16px; –sc-font-medium: 18px; –sc-font-large: 20px; –sc-font-xlarge: 24px; } ```

Spacing variables

```css

:root { –sc-spacing-small: 10px; –sc-spacing-base: 20px; –sc-spacing-large: 30px; –sc-spacing-xlarge: 40px; } ```

Overriding variables

For per-store branding, set these overrides on the store’s custom_styles via update_store — no theme file required:

```css

:root { –sc-color-primary: hsl(142, 71%, 45%); –sc-color-secondary: hsl(142, 71%, 25%); –sc-font-family: “Inter”, sans-serif; –sc-spacing-base: 24px; } ```

The layout renders current_store.global_css (the Liquid name for custom_styles) inside a <style> block, so these overrides apply on top of the base theme automatically.

Theme authors can instead bake the same overrides into theme-supplement.css to ship a rebrand inside the theme itself:

```css

/* theme-supplement.css — minimal rebrand */ :root { –sc-color-primary: hsl(142, 71%, 45%); –sc-color-secondary: hsl(142, 71%, 25%); –sc-font-family: “Inter”, sans-serif; –sc-spacing-base: 24px; } ```

Either approach gives you the full base theme layout and components with your brand’s colors, fonts, and spacing, with no build step required.

Built-in utility classes

The base theme CSS includes responsive utility classes:

Responsive grid

Class Mobile Medium+
sc-one-to-two-column 1 col 2 col
sc-one-to-three-column 1 col 3 col
sc-one-to-four-column 1 col 4 col
sc-two-to-four-column 2 col 4 col
sc-two-to-five-column 2 col → 5 col scales up

Responsive visibility

  • sc-hide-up-to-small — hidden below 576px
  • sc-hide-up-to-medium — hidden below 768px
  • sc-hide-medium-and-up — shown only on mobile
  • sc-hide-large-and-up — shown on mobile and tablet only

Layout utilities

  • sc-container — centered content container (max-width 1700px)
  • sc-container-skinny — narrow container
  • sc-flex-col — flex column layout
  • SC-Grid, SC-Grid_main, SC-Grid_sidebar — grid system

Breakpoints

Name Min-width
small 576px
medium 768px
large 992px
xlarge 1400px
huge 1700px

Base CSS/JS is optional

The sc- prefixed CSS classes, responsive grid utilities, and JavaScript modules described above are all part of the built-in base theme — a reference implementation. They are loaded by the base theme’s {% require "styles/theme.css" %} and {% require "scripts/theme.js" %} calls.

Custom themes are not required to use any of this. You can: - Replace the entire CSS with your own framework (Tailwind, Bootstrap, and so on) - Use a completely custom build system - Write vanilla CSS with no framework at all

The only things a layout must include are the system variables ({{ csrf_meta_tags }}, {{ body_content }}, {{ theme_bar }}, and so on) and the {{ theme_supplement_stylesheet }} / {{ theme_supplement_javascript }} outputs. Everything else — including the {% require %} calls for base CSS/JS — is your choice.

:::tip Keep {% require "scripts/theme.js" %} in your layout unless you are also replacing the component reloader (liquid-components.js) that powers async component updates. :::

Using modern CSS frameworks

Tailwind CSS

  1. Create a CSS entry point (src/theme.css):

    css @import "tailwindcss";

  2. Build to a single CSS file:

    bash npx @tailwindcss/cli -i src/theme.css -o dist/theme-supplement.css --minify

  3. Upload dist/theme-supplement.css as a theme asset with the key theme-supplement.css. It is automatically loaded via {{ theme_supplement_stylesheet }}.

Using Tailwind in Liquid templates:

```liquid

{{ current_product.name }}

{{ current_product.image.alt_text | default: current_product.name }}

```

Bootstrap

Build Bootstrap CSS locally and upload as a theme supplement:

```bash

npm install -D bootstrap sass npx sass src/theme.scss dist/theme-supplement.css –style=compressed ```

Vanilla CSS

For lightweight themes, vanilla CSS with custom properties requires no build step:

```css

/* theme-supplement.css */ :root { –color-primary: #2563eb; –container-max: 1200px; –gap: clamp(1rem, 3vw, 2rem); }

.container { max-width: var(–container-max); margin: 0 auto; padding: 0 var(–gap); } ```

Framework comparison

Approach Build step Best for
Tailwind CSS Yes (CLI) Rapid development, utility-first design
Bootstrap Optional Teams familiar with Bootstrap
Vanilla CSS None Simple themes, maximum control
Base theme CSS None (built-in) Extending the existing theme

Tips for any framework

  • Always output {{ theme_supplement_stylesheet }} in your layout — this is how your custom CSS gets loaded.
  • You can omit {% require "styles/theme.css" %} if replacing the base CSS entirely.
  • Keep {% require "scripts/theme.js" %} unless you are also replacing the base JavaScript (component reloader, and so on).
  • Use {{ "filename" | asset_url }} for static files (fonts, images) uploaded as theme assets.

Was this article helpful?

Was this article helpful?