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
- Keep It Simple: Don't over-customize
- Performance First: Optimize assets
- Mobile-Friendly: Test on real devices
- Accessibility: Follow WCAG guidelines
- Documentation: Document all customizations
- Version Control: Use Git for tracking changes