Skip to main content

Assets

Assets are static files like CSS, JavaScript, images, and fonts used in your theme. This guide covers asset management, optimization, and best practices.

Asset Overview

Assets are stored in the assets/ directory:

assets/
theme.css # Main theme styles
theme.js # Main theme JavaScript
components.css # Component styles
custom.js # Custom JavaScript
logo.png # Images
fonts/
custom-font.woff2 # Font files

Asset Types

CSS Files

Stylesheets for your theme:

/* assets/theme.css */
:root {
--primary-color: #000000;
--secondary-color: #ffffff;
}

body {
font-family: 'Arial', sans-serif;
color: var(--primary-color);
}

JavaScript Files

Client-side functionality:

// assets/theme.js
document.addEventListener('DOMContentLoaded', function() {
// Theme initialization
initCart();
initSearch();
});

Images

Images used in the theme:

  • Logos
  • Icons
  • Background images
  • Product placeholders

Fonts

Custom fonts:

assets/fonts/
custom-font.woff2
custom-font.woff

Loading Assets

CSS Assets

Load stylesheets in the layout:

{{ 'theme.css' | asset_url | stylesheet_tag }}
{{ 'components.css' | asset_url | stylesheet_tag }}

JavaScript Assets

Load scripts before closing </body>:

{{ 'theme.js' | asset_url | script_tag }}
{{ 'custom.js' | asset_url | script_tag }}

Image Assets

Reference images using asset_url:

<img src="{{ 'logo.png' | asset_url }}" alt="{{ shop.name }}">

Or use the img_url filter for product/collection images:

<img src="{{ product.image | img_url: 'large' }}" alt="{{ product.name }}">

Asset URL Filter

The asset_url filter generates URLs for assets:

{{ 'theme.css' | asset_url }}
<!-- Output: /assets/theme.css?v=1234567890 -->

The version parameter (?v=...) ensures browsers load the latest version after updates.

Asset Optimization

CSS Optimization

  1. Minify CSS: Remove whitespace and comments
  2. Combine Files: Merge multiple CSS files when possible
  3. Remove Unused Styles: Clean up unused CSS
  4. Use CSS Variables: For maintainability
/* Use CSS variables */
:root {
--spacing-small: 0.5rem;
--spacing-medium: 1rem;
--spacing-large: 2rem;
}

.container {
padding: var(--spacing-medium);
}

JavaScript Optimization

  1. Minify JavaScript: Compress code
  2. Defer Loading: Load non-critical scripts asynchronously
  3. Code Splitting: Split large files
  4. Remove Unused Code: Tree-shake unused functions
{{ 'theme.js' | asset_url | script_tag }}
<!-- Add defer for non-critical scripts -->
<script src="{{ 'analytics.js' | asset_url }}" defer></script>

Image Optimization

  1. Use Appropriate Formats:

    • WebP for photos
    • SVG for icons/logos
    • PNG for transparency
    • JPEG for photos (fallback)
  2. Responsive Images: Use different sizes for different screens

<picture>
<source srcset="{{ product.image | img_url: 'large' }}" media="(min-width: 768px)">
<source srcset="{{ product.image | img_url: 'medium' }}" media="(min-width: 480px)">
<img src="{{ product.image | img_url: 'small' }}" alt="{{ product.name }}">
</picture>
  1. Lazy Loading: Load images as needed
<img 
src="{{ product.image | img_url: 'medium' }}"
alt="{{ product.name }}"
loading="lazy"
>

Asset Organization

Organize assets logically:

assets/
css/
theme.css
components.css
utilities.css
js/
theme.js
cart.js
product.js
images/
logo.png
icons/
cart.svg
search.svg
fonts/
primary-font.woff2
secondary-font.woff2

Asset Versioning

Assets are automatically versioned to prevent caching issues:

{{ 'theme.css' | asset_url }}
<!-- /assets/theme.css?v=1234567890 -->

The version changes when the file is updated, forcing browsers to load the new version.

Conditional Asset Loading

Load assets conditionally:

{% if template.name == 'product' %}
{{ 'product.js' | asset_url | script_tag }}
{% endif %}

{% if settings.enable_custom_fonts %}
{{ 'custom-fonts.css' | asset_url | stylesheet_tag }}
{% endif %}

External Assets

Link to external assets when needed:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap">

<script src="https://cdn.example.com/library.js"></script>

Asset Preloading

Preload critical assets:

<link rel="preload" href="{{ 'theme.css' | asset_url }}" as="style">
<link rel="preload" href="{{ 'theme.js' | asset_url }}" as="script">

Font Management

Loading Custom Fonts

  1. Add font files to assets/fonts/:
assets/fonts/
custom-font.woff2
custom-font.woff
  1. Define font-face in CSS:
@font-face {
font-family: 'Custom Font';
src: url('{{ "fonts/custom-font.woff2" | asset_url }}') format('woff2'),
url('{{ "fonts/custom-font.woff" | asset_url }}') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
}
  1. Use the font:
body {
font-family: 'Custom Font', sans-serif;
}

JavaScript Best Practices

DOM Ready

Always wait for DOM to be ready:

document.addEventListener('DOMContentLoaded', function() {
// Your code here
});

Event Delegation

Use event delegation for dynamic content:

document.addEventListener('click', function(e) {
if (e.target.matches('.add-to-cart')) {
// Handle add to cart
}
});

Module Pattern

Organize JavaScript in modules:

// assets/cart.js
const Cart = {
init: function() {
this.bindEvents();
},

bindEvents: function() {
// Event handlers
},

update: function() {
// Update cart
}
};

Cart.init();

CSS Best Practices

Use BEM Methodology

.product-card { }
.product-card__image { }
.product-card__title { }
.product-card__price { }
.product-card--featured { }

Mobile-First Approach

/* Mobile styles (default) */
.container {
padding: 1rem;
}

/* Tablet and up */
@media (min-width: 768px) {
.container {
padding: 2rem;
}
}

/* Desktop */
@media (min-width: 1024px) {
.container {
padding: 3rem;
}
}

CSS Variables

Use CSS variables for theming:

:root {
--primary-color: #000000;
--secondary-color: #ffffff;
--font-size-base: 16px;
--spacing-unit: 8px;
}

.button {
background-color: var(--primary-color);
color: var(--secondary-color);
font-size: var(--font-size-base);
padding: calc(var(--spacing-unit) * 2);
}

Asset Management in Editor

Assets can be managed through the O2VEND Theme Editor:

  1. Browse assets in file explorer
  2. Edit CSS/JS files directly
  3. Upload new images
  4. Delete unused assets

Asset Optimization Guide

CSS Optimization

  1. Minify CSS:
/* Before: 2KB */
.button { background-color: #000; color: #fff; padding: 10px; }

/* After minified: 1KB */
.button{background:#000;color:#fff;padding:10px}
  1. Remove Unused CSS: Use tools like PurgeCSS to remove unused styles

  2. Combine CSS Files:

<!-- Instead of multiple files -->
{{ 'theme.css' | asset_url | stylesheet_tag }}
{{ 'components.css' | asset_url | stylesheet_tag }}
{{ 'utilities.css' | asset_url | stylesheet_tag }}

<!-- Combine into one -->
{{ 'theme.min.css' | asset_url | stylesheet_tag }}

JavaScript Optimization

  1. Minify JavaScript:
// Before
function addToCart(productId) {
console.log('Adding product to cart:', productId);
// ... code
}

// After minified
function addToCart(a){console.log('Adding product to cart:',a);...}
  1. Code Splitting:
// Load critical JS immediately
{{ 'theme.js' | asset_url | script_tag }}

// Load non-critical JS asynchronously
<script defer src="{{ 'analytics.js' | asset_url }}"></script>
  1. Remove Console Logs in Production:
// Development
console.log('Debug info');

// Production - remove or use conditional
if (process.env.NODE_ENV === 'development') {
console.log('Debug info');
}

Image Optimization

  1. Use Appropriate Formats:
  • WebP: Modern browsers, best compression
  • JPEG: Photos, complex images
  • PNG: Graphics with transparency
  • SVG: Icons, simple graphics
  1. Responsive Images:
<picture>
<source media="(min-width: 1200px)"
srcset="{{ image | img_url: '1920x' }}.webp"
type="image/webp">
<source media="(min-width: 1200px)"
srcset="{{ image | img_url: '1920x' }}.jpg">
<source media="(min-width: 768px)"
srcset="{{ image | img_url: '1200x' }}.webp"
type="image/webp">
<img src="{{ image | img_url: '768x' }}"
alt="{{ product.name }}"
loading="lazy">
</picture>
  1. Image Compression:
  • Use tools like ImageOptim, TinyPNG
  • Target: 70-80% quality for JPEG
  • Use lossless compression for PNG

Asset Loading Strategies

Critical CSS Inline

<head>
<style>
/* Critical CSS inline */
body { margin: 0; font-family: system-ui; }
.header { position: fixed; top: 0; }
</style>
{{ 'theme.css' | asset_url | stylesheet_tag }}
</head>

Preload Critical Assets

<head>
<link rel="preload" href="{{ 'theme.css' | asset_url }}" as="style">
<link rel="preload" href="{{ 'logo.png' | asset_url }}" as="image">
<link rel="preload" href="{{ 'theme.js' | asset_url }}" as="script">
</head>

Defer Non-Critical JavaScript

<body>
<!-- Page content -->

<!-- Defer non-critical scripts -->
<script defer src="{{ 'analytics.js' | asset_url }}"></script>
<script defer src="{{ 'chat-widget.js' | asset_url }}"></script>
</body>

Lazy Load Images

<img src="{{ image | img_url: 'small' }}" 
data-src="{{ image | img_url: 'large' }}"
loading="lazy"
alt="{{ product.name }}"
class="lazy-image">

Asset Versioning Examples

Query String Versioning

{{ 'theme.css?v=1.0.0' | asset_url | stylesheet_tag }}
{{ 'theme.js?v=1.0.0' | asset_url | script_tag }}

Filename Versioning

{{ 'theme-1.0.0.css' | asset_url | stylesheet_tag }}
{{ 'theme-1.0.0.js' | asset_url | script_tag }}

Hash-Based Versioning

{% assign css_hash = 'theme.css' | asset_url | split: '/' | last | split: '?' | first | md5 %}
{{ 'theme.css' | asset_url | append: '?v=' | append: css_hash | stylesheet_tag }}

Theme Settings Versioning

{% assign theme_version = settings.theme_version | default: '1.0.0' %}
{{ 'theme.css' | asset_url | append: '?v=' | append: theme_version | stylesheet_tag }}

CDN Integration Guide

Using CDN for Assets

{% assign cdn_url = 'https://cdn.example.com/themes' %}
{% assign theme_name = tenant.theme %}

<link rel="stylesheet"
href="{{ cdn_url }}/{{ theme_name }}/theme.css">
<script src="{{ cdn_url }}/{{ theme_name }}/theme.js"></script>

Fallback to Local Assets

<script>
// Try CDN first, fallback to local
const cdnUrl = 'https://cdn.example.com';
const localUrl = '{{ 'theme.js' | asset_url }}';

const script = document.createElement('script');
script.src = cdnUrl + '/theme.js';
script.onerror = function() {
// Fallback to local
script.src = localUrl;
};
document.head.appendChild(script);
</script>

CDN with Cache Busting

{% assign cache_buster = 'now' | date: '%s' %}
<link rel="stylesheet"
href="{{ cdn_url }}/theme.css?v={{ cache_buster }}">

Asset Performance Metrics

Target Metrics

  • First Contentful Paint (FCP): < 1.8s
  • Largest Contentful Paint (LCP): < 2.5s
  • Time to Interactive (TTI): < 3.8s
  • Total Blocking Time (TBT): < 200ms
  • Cumulative Layout Shift (CLS): < 0.1

Measuring Asset Performance

// Measure asset load time
performance.getEntriesByType('resource').forEach(entry => {
if (entry.name.includes('theme.css') || entry.name.includes('theme.js')) {
console.log(`${entry.name}: ${entry.duration}ms`);
}
});

Asset Size Targets

  • CSS: < 50KB (gzipped)
  • JavaScript: < 100KB (gzipped)
  • Images:
    • Hero images: < 200KB
    • Product images: < 100KB
    • Icons: < 10KB
  • Fonts: < 50KB per font file

Performance Tips

  1. Minify Assets: Compress CSS and JavaScript
  2. Combine Files: Reduce HTTP requests
  3. Optimize Images: Compress and use appropriate formats
  4. Lazy Load: Load non-critical assets asynchronously
  5. Cache Assets: Use proper cache headers
  6. CDN: Use CDN for static assets if available
  7. Preload Critical Assets: Use <link rel="preload">
  8. Inline Critical CSS: Reduce render-blocking
  9. Defer JavaScript: Load scripts after page render
  10. Use Modern Formats: WebP for images, ES6+ for JS

Asset Checklist

  • All CSS files minified
  • JavaScript files optimized
  • Images compressed and optimized
  • Fonts properly loaded
  • Assets organized logically
  • Unused assets removed
  • Asset URLs use asset_url filter
  • Critical assets preloaded
  • Non-critical assets lazy-loaded
  • CDN configured (if applicable)
  • Asset versioning implemented
  • Performance metrics within targets

Next Steps