{"title":"Theme assets and styling","slug":"theme-assets-and-styling","url":"https://support.storeconnect.com/articles/theme-assets-and-styling","url_markdown":"https://support.storeconnect.com/articles/theme-assets-and-styling.md","subtitle":null,"summary":"Use the require tag to load CSS and JavaScript, the asset_url filter to reference static files, and supplement or replacement theme assets for custom styling: asset loading, the built-in CSS framework, theme variables, CSS custom properties, and modern CSS frameworks.","type":"Developer_Documentation","video_url":"","keywords":"theme assets, require tag, asset_url, theme-supplement.css, theme.css, theme.js, CSS framework, CSS variables, Tailwind, Bootstrap, esbuild, asset resolution, resources directory","last_modified":"2026-08-21T07:12:35+0000","body_markdown":"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.\n\n## Asset directory structure\n\n```\n\nresources/\n├── src/\n│   ├── scripts/\n│   │   ├── theme/          # Core theme JavaScript\n│   │   │   └── liquid-components.js  # Component reloader\n│   │   └── packs/          # Additional script bundles\n│   ├── styles/\n│   │   ├── theme/          # Core theme CSS\n│   │   └── packs/          # Additional style bundles\n│   └── files/              # Static files (images, fonts, etc.)\n├── build/                  # Build configuration\n├── dist/                   # Compiled output (fingerprinted filenames)\n│   └── manifest.json       # Maps logical names to fingerprinted filenames\n└── package.json\n```\n\n## Loading assets\n\n### The `{% require %}` tag\n\nUse `{% require %}` to load CSS and JavaScript files. It ensures each asset is loaded only once, even if required from multiple templates:\n\n\n```liquid\n\n{% require \"styles/theme.css\" %}\n{% require \"scripts/theme.js\" %}\n{% require \"scripts/bundle.js\" %}\n{% require \"scripts/sort.js\" %}\n```\n\n\nThe `{% require %}` tag is typically used in:\n- The layout's `\u003chead\u003e` section for global assets\n- Page templates for page-specific assets\n- Snippets for component-specific assets\n\n### The `asset_url` filter\n\nUse `{{ \"filename\" | asset_url }}` to get the URL for a static asset:\n\n\n```liquid\n\n\u003cimg src=\"{{ 'logo.png' | asset_url }}\" alt=\"Logo\"\u003e\n\u003clink rel=\"icon\" href=\"{{ 'favicon.ico' | asset_url }}\"\u003e\n```\n\n\n### Asset resolution\n\nWhen you call `{% require \"styles/theme.css\" %}`, the platform:\n\n1. Looks up `dist/styles/theme.css` in the custom theme's `manifest.json` (if one exists).\n2. Falls back to the base theme's `manifest.json`.\n3. Resolves the fingerprinted filename (for example `styles/theme.LBH765JF.css`).\n4. Renders a `\u003clink\u003e` or `\u003cscript\u003e` tag with the resolved URL.\n\nThe `{{ \"filename\" | asset_url }}` filter resolves static files through the same manifest system.\n\n## Including assets in the layout\n\n\n```liquid\n\n{%- comment -%} layouts/theme.liquid — head section {%- endcomment -%}\n\u003chead\u003e\n  {% require \"styles/theme.css\" %}\n  {{ theme_supplement_stylesheet }}\n  {% if current_store.global_css != blank %}\n    \u003cstyle\u003e{{ current_store.global_css }}\u003c/style\u003e\n  {% endif %}\n\n  {% require \"scripts/configure.js\" %}\n  {% require \"scripts/metadata.js\" %}\n  {% require \"scripts/theme.js\" %}\n  {{ theme_supplement_javascript }}\n  {% if current_store.global_javascript != blank %}\n    \u003cscript\u003e{{ current_store.global_javascript }}\u003c/script\u003e\n  {% endif %}\n\u003c/head\u003e\n```\n\n\n### Page-specific assets\n\nLoad assets only on the pages that need them:\n\n\n```liquid\n\n{%- comment -%} pages/product.liquid — load bundle JS only on product pages {%- endcomment -%}\n{% if current_product.is_bundle? %}\n  {% require \"scripts/bundle-v2.js\" %}\n  {% require \"scripts/quantity-picker.js\" %}\n{% endif %}\n```\n\n\n### Store-level custom CSS and JavaScript\n\nStores can have custom CSS and JavaScript configured in the CMS. Include them in your layout:\n\n\n```liquid\n\n{% if current_store.global_css != blank %}\n  \u003cstyle\u003e{{ current_store.global_css }}\u003c/style\u003e\n{% endif %}\n\n{% if current_store.global_javascript != blank %}\n  \u003cscript\u003e{{ current_store.global_javascript }}\u003c/script\u003e\n{% endif %}\n```\n\n\n## Custom CSS and JavaScript loading\n\nCustom themes inject their own CSS and JavaScript through two mechanisms:\n\n**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.\n\n**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.\n\n:::note\nThe 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.\n:::\n\n### Example: overriding the site font with a supplement\n\nAdd a theme asset with the key `theme-supplement.css` containing:\n\n```css\n\nbody {\n  font-family: \"Comic Sans MS\";\n}\n```\n\n![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)\n\nPreview the site. The new asset has overridden the site font, and the rest of the built-in theme still applies.\n\n## Translations\n\nTranslation strings are stored in `translations/en.default.json` and accessed with the `t` filter:\n\n\n```liquid\n\n{{ \"products.show.add_to_cart\" | t }}\n{{ \"checkout.title\" | t }}\n```\n\n\nTranslation file structure:\n\n```json\n\n{\n  \"products\": {\n    \"show\": {\n      \"add_to_cart\": \"Add to Cart\",\n      \"discontinued\": \"This product has been discontinued\"\n    }\n  },\n  \"checkout\": {\n    \"title\": \"Checkout\"\n  },\n  \"cart\": {\n    \"header\": \"Shopping Cart\"\n  }\n}\n```\n\n## Theme variables\n\n`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.\n\n\n```liquid\n\n{% assign per_page = theme_variables[\"products.per_page\"] %}\n{% assign show_compare = theme_variables[\"products.comparisons\"] == true %}\n{% assign image_ratio = theme_variables[\"images.ratio\"] %}\n```\n\n\n## Customizing the base theme with CSS variables\n\nThe 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:\n\n- **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 `\u003cstyle\u003e` block (see [Including assets in the layout](#including-assets-in-the-layout) above), so the overrides load on top of the base theme automatically.\n- **`theme-supplement.css` (theme-author / build-time route)** — bake the same overrides into the theme's supplement stylesheet, as shown below.\n\nThe base CSS exposes these variables on `:root`:\n\n### Color variables\n\n```css\n\n:root {\n  --sc-color-primary: hsl(212, 100%, 50%);    /* Buttons, links, highlights */\n  --sc-color-secondary: hsl(0, 0%, 15%);       /* Secondary actions */\n  --sc-color-error: hsl(0, 100%, 45%);         /* Error states */\n  --sc-color-sale: hsl(0, 100%, 45%);          /* Sale prices */\n}\n```\n\nEach color also exposes HSL channels for flexible manipulation: `--sc-color-primary-h`, `--sc-color-primary-s`, `--sc-color-primary-l`.\n\n### Font variables\n\n```css\n\n:root {\n  --sc-font-family: -apple-system, BlinkMacSystemFont, sans-serif;\n  --sc-font-base: 16px;\n  --sc-font-medium: 18px;\n  --sc-font-large: 20px;\n  --sc-font-xlarge: 24px;\n}\n```\n\n### Spacing variables\n\n```css\n\n:root {\n  --sc-spacing-small: 10px;\n  --sc-spacing-base: 20px;\n  --sc-spacing-large: 30px;\n  --sc-spacing-xlarge: 40px;\n}\n```\n\n### Overriding variables\n\nFor per-store branding, set these overrides on the store's `custom_styles` via `update_store` — no theme file required:\n\n```css\n\n:root {\n  --sc-color-primary: hsl(142, 71%, 45%);\n  --sc-color-secondary: hsl(142, 71%, 25%);\n  --sc-font-family: \"Inter\", sans-serif;\n  --sc-spacing-base: 24px;\n}\n```\n\nThe layout renders `current_store.global_css` (the Liquid name for `custom_styles`) inside a `\u003cstyle\u003e` block, so these overrides apply on top of the base theme automatically.\n\nTheme authors can instead bake the same overrides into `theme-supplement.css` to ship a rebrand inside the theme itself:\n\n```css\n\n/* theme-supplement.css — minimal rebrand */\n:root {\n  --sc-color-primary: hsl(142, 71%, 45%);\n  --sc-color-secondary: hsl(142, 71%, 25%);\n  --sc-font-family: \"Inter\", sans-serif;\n  --sc-spacing-base: 24px;\n}\n```\n\nEither approach gives you the full base theme layout and components with your brand's colors, fonts, and spacing, with no build step required.\n\n## Built-in utility classes\n\nThe base theme CSS includes responsive utility classes:\n\n### Responsive grid\n\n| Class | Mobile | Medium+ |\n|-------|--------|---------|\n| `sc-one-to-two-column` | 1 col | 2 col |\n| `sc-one-to-three-column` | 1 col | 3 col |\n| `sc-one-to-four-column` | 1 col | 4 col |\n| `sc-two-to-four-column` | 2 col | 4 col |\n| `sc-two-to-five-column` | 2 col → 5 col | scales up |\n\n### Responsive visibility\n\n- `sc-hide-up-to-small` — hidden below 576px\n- `sc-hide-up-to-medium` — hidden below 768px\n- `sc-hide-medium-and-up` — shown only on mobile\n- `sc-hide-large-and-up` — shown on mobile and tablet only\n\n### Layout utilities\n\n- `sc-container` — centered content container (max-width 1700px)\n- `sc-container-skinny` — narrow container\n- `sc-flex-col` — flex column layout\n- `SC-Grid`, `SC-Grid_main`, `SC-Grid_sidebar` — grid system\n\n### Breakpoints\n\n| Name | Min-width |\n|------|-----------|\n| `small` | 576px |\n| `medium` | 768px |\n| `large` | 992px |\n| `xlarge` | 1400px |\n| `huge` | 1700px |\n\n## Base CSS/JS is optional\n\nThe `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.\n\n**Custom themes are not required to use any of this.** You can:\n- Replace the entire CSS with your own framework (Tailwind, Bootstrap, and so on)\n- Use a completely custom build system\n- Write vanilla CSS with no framework at all\n\nThe 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.\n\n:::tip\nKeep `{% require \"scripts/theme.js\" %}` in your layout unless you are also replacing the component reloader (`liquid-components.js`) that powers async component updates.\n:::\n\n## Using modern CSS frameworks\n\n### Tailwind CSS\n\n1. Create a CSS entry point (`src/theme.css`):\n\n   ```css\n   @import \"tailwindcss\";\n   ```\n\n2. Build to a single CSS file:\n\n   ```bash\n   npx @tailwindcss/cli -i src/theme.css -o dist/theme-supplement.css --minify\n   ```\n\n3. Upload `dist/theme-supplement.css` as a theme asset with the key `theme-supplement.css`. It is automatically loaded via `{{ theme_supplement_stylesheet }}`.\n\nUsing Tailwind in Liquid templates:\n\n\n```liquid\n\n\u003cdiv class=\"container mx-auto px-4\"\u003e\n  \u003ch1 class=\"text-3xl font-bold mb-6\"\u003e{{ current_product.name }}\u003c/h1\u003e\n\n  \u003cdiv class=\"grid grid-cols-1 md:grid-cols-2 gap-8\"\u003e\n    \u003cdiv\u003e\n      \u003cimg src=\"{{ current_product.image.huge_url }}\"\n           alt=\"{{ current_product.image.alt_text | default: current_product.name }}\"\n           class=\"w-full rounded-lg\"\n           fetchpriority=\"high\"\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n\n### Bootstrap\n\nBuild Bootstrap CSS locally and upload as a theme supplement:\n\n```bash\n\nnpm install -D bootstrap sass\nnpx sass src/theme.scss dist/theme-supplement.css --style=compressed\n```\n\n### Vanilla CSS\n\nFor lightweight themes, vanilla CSS with custom properties requires no build step:\n\n```css\n\n/* theme-supplement.css */\n:root {\n  --color-primary: #2563eb;\n  --container-max: 1200px;\n  --gap: clamp(1rem, 3vw, 2rem);\n}\n\n.container {\n  max-width: var(--container-max);\n  margin: 0 auto;\n  padding: 0 var(--gap);\n}\n```\n\n### Framework comparison\n\n| Approach | Build step | Best for |\n|----------|-----------|----------|\n| Tailwind CSS | Yes (CLI) | Rapid development, utility-first design |\n| Bootstrap | Optional | Teams familiar with Bootstrap |\n| Vanilla CSS | None | Simple themes, maximum control |\n| Base theme CSS | None (built-in) | Extending the existing theme |\n\n### Tips for any framework\n\n- Always output `{{ theme_supplement_stylesheet }}` in your layout — this is how your custom CSS gets loaded.\n- You can omit `{% require \"styles/theme.css\" %}` if replacing the base CSS entirely.\n- Keep `{% require \"scripts/theme.js\" %}` unless you are also replacing the base JavaScript (component reloader, and so on).\n- Use `{{ \"filename\" | asset_url }}` for static files (fonts, images) uploaded as theme assets."}