# Theme assets and styling

Source: https://support.storeconnect.com/articles/theme-assets-and-styling · Last modified 21 August 2026

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

<img src="{{ 'logo.png' | asset_url }}" alt="Logo">
<link rel="icon" href="{{ 'favicon.ico' | asset_url }}">
```


### 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 -%}
<head>
  {% require "styles/theme.css" %}
  {{ theme_supplement_stylesheet }}
  {% if current_store.global_css != blank %}
    <style>{{ current_store.global_css }}</style>
  {% endif %}

  {% require "scripts/configure.js" %}
  {% require "scripts/metadata.js" %}
  {% require "scripts/theme.js" %}
  {{ theme_supplement_javascript }}
  {% if current_store.global_javascript != blank %}
    <script>{{ current_store.global_javascript }}</script>
  {% endif %}
</head>
```


### 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 %}
  <style>{{ current_store.global_css }}</style>
{% endif %}

{% if current_store.global_javascript != blank %}
  <script>{{ current_store.global_javascript }}</script>
{% 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](#base-cssjs-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](https://res.cloudinary.com/hzkr6fi81/image/upload/v1725790512/knowledge/themes/new-css-supplement-asset_drn2vq.png)

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](#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

<div class="container mx-auto px-4">
  <h1 class="text-3xl font-bold mb-6">{{ current_product.name }}</h1>

  <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
    <div>
      <img src="{{ current_product.image.huge_url }}"
           alt="{{ current_product.image.alt_text | default: current_product.name }}"
           class="w-full rounded-lg"
           fetchpriority="high">
    </div>
  </div>
</div>
```


### 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.

---

## Follow StoreConnect

- [Email Newsletter](https://getstoreconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://getstoreconnect.com/partners)
- [News](https://getstoreconnect.com/articles/news)
- [Events](https://getstoreconnect.com/articles/events)
- [Feature Comparison](https://getstoreconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://getstoreconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [AI agents](https://support.storeconnect.com/ai)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

---

StoreConnect Support — https://support.storeconnect.com/articles/theme-assets-and-styling