Skip to main content

Customize a Theme

Learn how to customize existing Shoplazza themes to meet merchant needs.

Overview

Theme customization allows you to modify existing themes to:

  • Match merchant brand identity
  • Add custom functionality
  • Optimize for specific use cases
  • Improve performance

Getting Started

Download Existing Theme

shoplazza theme pull

This downloads the active theme from your store.

Explore Theme Structure

Familiarize yourself with the theme's organization:

theme/
├── assets/ # CSS, JS, images
├── config/ # Settings and configuration
├── layout/ # Base templates
├── locales/ # Translations
├── sections/ # Modular content blocks
├── snippets/ # Reusable code
└── templates/ # Page templates

Common Customizations

1. Changing Colors and Fonts

Edit config/settings_schema.json:

{
"name": "Colors",
"settings": [
{
"type": "color",
"id": "color_primary",
"label": "Primary Color",
"default": "#000000"
},
{
"type": "color",
"id": "color_secondary",
"label": "Secondary Color",
"default": "#666666"
}
]
}

Apply in CSS:

:root {
--color-primary: {{ settings.color_primary }};
--color-secondary: {{ settings.color_secondary }};
}

2. Customizing Header

Edit sections/header.liquid to add custom navigation:

<header class="site-header" style="background-color: {{ section.settings.header_bg }};">
<div class="header-wrapper">
<!-- Logo -->
<div class="site-logo">
{% if section.settings.logo %}
<a href="/">
<img src="{{ section.settings.logo | img_url: '200x' }}"
alt="{{ shop.name }}">
</a>
{% else %}
<a href="/">{{ shop.name }}</a>
{% endif %}
</div>

<!-- Custom Menu -->
<nav class="main-nav">
<ul>
{% for link in linklists[section.settings.menu].links %}
<li class="{% if link.active %}active{% endif %}">
<a href="{{ link.url }}">{{ link.title }}</a>

{% if link.links.size > 0 %}
<ul class="dropdown">
{% for child_link in link.links %}
<li>
<a href="{{ child_link.url }}">{{ child_link.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>

<!-- Header Actions -->
<div class="header-actions">
{% if shop.customer_accounts_enabled %}
<a href="/account">
{% if customer %}
{{ customer.first_name }}
{% else %}
Login
{% endif %}
</a>
{% endif %}

<a href="/cart" class="cart-link">
Cart ({{ cart.item_count }})
</a>
</div>
</div>
</header>

{% schema %}
{
"name": "Header",
"settings": [
{
"type": "image_picker",
"id": "logo",
"label": "Logo Image"
},
{
"type": "color",
"id": "header_bg",
"label": "Background Color",
"default": "#ffffff"
},
{
"type": "link_list",
"id": "menu",
"label": "Menu",
"default": "main-menu"
}
]
}
{% endschema %}

3. Adding Custom Product Features

Create a custom product template templates/product.custom.liquid:

<div class="product-page custom-layout">
<div class="product-media">
<!-- Main Image -->
<div class="product-image-main">
<img src="{{ product.featured_image | img_url: '1000x' }}"
alt="{{ product.title }}"
id="main-product-image">
</div>

<!-- Thumbnail Gallery -->
<div class="product-thumbnails">
{% for image in product.images %}
<img src="{{ image | img_url: '100x' }}"
alt="{{ product.title }}"
onclick="changeMainImage('{{ image | img_url: '1000x' }}')">
{% endfor %}
</div>
</div>

<div class="product-details">
<h1>{{ product.title }}</h1>

<!-- Custom Reviews Display -->
{% if product.metafields.reviews.rating %}
<div class="product-rating">
{% render 'star-rating', rating: product.metafields.reviews.rating %}
<span>({{ product.metafields.reviews.count }} reviews)</span>
</div>
{% endif %}

<div class="product-price">
<span class="price">{{ product.selected_or_first_available_variant.price | money }}</span>
{% if product.selected_or_first_available_variant.compare_at_price > product.selected_or_first_available_variant.price %}
<span class="compare-price">
{{ product.selected_or_first_available_variant.compare_at_price | money }}
</span>
<span class="sale-badge">Sale</span>
{% endif %}
</div>

<!-- Custom Variant Selector -->
{% unless product.has_only_default_variant %}
<div class="product-variants">
{% for option in product.options_with_values %}
<div class="variant-option">
<label>{{ option.name }}</label>
<div class="variant-values">
{% for value in option.values %}
<button type="button"
class="variant-button"
data-option="{{ option.position }}"
data-value="{{ value }}">
{{ value }}
</button>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endunless %}

<!-- Add to Cart -->
{% form 'product', product %}
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">

<div class="quantity-selector">
<label>Quantity</label>
<input type="number" name="quantity" value="1" min="1">
</div>

<button type="submit"
class="add-to-cart-btn"
{% unless product.selected_or_first_available_variant.available %}disabled{% endunless %}>
{% if product.selected_or_first_available_variant.available %}
Add to Cart
{% else %}
Sold Out
{% endif %}
</button>
{% endform %}

<!-- Product Description -->
<div class="product-description">
{{ product.description }}
</div>

<!-- Custom Product Info Tabs -->
<div class="product-tabs">
<div class="tab-buttons">
<button class="tab-btn active" data-tab="details">Details</button>
<button class="tab-btn" data-tab="shipping">Shipping</button>
<button class="tab-btn" data-tab="reviews">Reviews</button>
</div>

<div class="tab-content active" id="details">
{{ product.metafields.custom.details }}
</div>

<div class="tab-content" id="shipping">
{{ product.metafields.custom.shipping_info }}
</div>

<div class="tab-content" id="reviews">
{% render 'product-reviews', product: product %}
</div>
</div>
</div>
</div>

<script>
function changeMainImage(imageUrl) {
document.getElementById('main-product-image').src = imageUrl;
}

// Tab functionality
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', function() {
const tabId = this.dataset.tab;

// Remove active class from all
document.querySelectorAll('.tab-btn, .tab-content').forEach(el => {
el.classList.remove('active');
});

// Add active class to clicked tab and content
this.classList.add('active');
document.getElementById(tabId).classList.add('active');
});
});
</script>

4. Adding Custom Sections

Create a custom announcement bar sections/announcement-bar.liquid:

{% if section.settings.show_announcement %}
<div class="announcement-bar"
style="background-color: {{ section.settings.bg_color }};
color: {{ section.settings.text_color }};">
<div class="announcement-content">
{{ section.settings.message }}

{% if section.settings.link %}
<a href="{{ section.settings.link }}" class="announcement-link">
{{ section.settings.link_text }}
</a>
{% endif %}
</div>
</div>
{% endif %}

{% schema %}
{
"name": "Announcement Bar",
"settings": [
{
"type": "checkbox",
"id": "show_announcement",
"label": "Show announcement",
"default": true
},
{
"type": "text",
"id": "message",
"label": "Message",
"default": "Welcome to our store!"
},
{
"type": "url",
"id": "link",
"label": "Link"
},
{
"type": "text",
"id": "link_text",
"label": "Link Text",
"default": "Shop Now"
},
{
"type": "color",
"id": "bg_color",
"label": "Background Color",
"default": "#000000"
},
{
"type": "color",
"id": "text_color",
"label": "Text Color",
"default": "#ffffff"
}
],
"presets": [
{
"name": "Announcement Bar"
}
]
}
{% endschema %}

5. Custom JavaScript Functionality

Add to assets/custom.js:

// Quick View Functionality
class QuickView {
constructor() {
this.init();
}

init() {
document.querySelectorAll('[data-quick-view]').forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
const productHandle = btn.dataset.productHandle;
this.showQuickView(productHandle);
});
});
}

async showQuickView(handle) {
const response = await fetch(`/products/${handle}?view=quick-view`);
const html = await response.text();

const modal = document.createElement('div');
modal.className = 'quick-view-modal';
modal.innerHTML = html;

document.body.appendChild(modal);
document.body.style.overflow = 'hidden';

// Close button
modal.querySelector('.close-modal').addEventListener('click', () => {
this.closeQuickView();
});
}

closeQuickView() {
document.querySelector('.quick-view-modal').remove();
document.body.style.overflow = '';
}
}

// Initialize
new QuickView();

// Sticky Header
window.addEventListener('scroll', () => {
const header = document.querySelector('.site-header');
if (window.scrollY > 100) {
header.classList.add('sticky');
} else {
header.classList.remove('sticky');
}
});

// Product Image Zoom
document.querySelectorAll('.product-image-main img').forEach(img => {
img.addEventListener('click', function() {
this.classList.toggle('zoomed');
});
});

Testing Customizations

Local Testing

shoplazza theme serve

Test Checklist

  • ✅ Responsive design on all devices
  • ✅ Cross-browser compatibility
  • ✅ Performance (load times, image optimization)
  • ✅ Accessibility (keyboard navigation, screen readers)
  • ✅ Checkout flow
  • ✅ Error scenarios

Deploying Changes

Create Theme Backup

shoplazza theme pull --save

Push Changes

shoplazza theme push

Publish Theme

Preview changes before publishing, then activate in admin.

Best Practices

  1. Keep It Simple: Don't over-customize
  2. Performance First: Optimize assets
  3. Mobile-Friendly: Test on real devices
  4. Accessibility: Follow WCAG guidelines
  5. Documentation: Document all customizations
  6. Version Control: Use Git for tracking changes

Next Steps