---
name: Shopify Section Patterns
description: Best practices for creating Shopify 2.0 sections with inline CSS and JavaScript
---
# Shopify Section Patterns
## Section Anatomy
A complete Shopify section consists of:
1. **Liquid markup** - HTML structure with dynamic content
2. **Inline CSS** - `{% stylesheet %}` tag (optional)
3. **Inline JavaScript** - `{% javascript %}` tag (optional)
4. **Schema** - `{% schema %}` JSON configuration
## Complete Section Template
```liquid
{%- comment -%}
Section: [Name]
Description: [Brief description]
Usage: Add via Theme Editor
{%- endcomment -%}
{%- liquid
assign heading = section.settings.heading | default: 'Default Heading'
assign bg_color = section.settings.bg_color
assign text_color = section.settings.text_color
-%}
{%- if heading != blank -%}
{{ heading | escape }}
{%- endif -%}
{%- comment -%} Section content {%- endcomment -%}
{% stylesheet %}
#section-{{ section.id }} {
padding: {{ section.settings.padding_top }}px 0 {{ section.settings.padding_bottom }}px;
background-color: {{ bg_color }};
color: {{ text_color }};
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
@media (min-width: 768px) {
/* Tablet styles */
}
@media (min-width: 1024px) {
/* Desktop styles */
}
{% endstylesheet %}
{% javascript %}
(function() {
'use strict';
function initSection(sectionId) {
const section = document.querySelector('[data-section-id="' + sectionId + '"]');
if (!section) return;
// Your JavaScript here
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
initSection('{{ section.id }}');
});
// Re-initialize when section reloads in theme editor
document.addEventListener('shopify:section:load', function(event) {
if (event.detail.sectionId === '{{ section.id }}') {
initSection('{{ section.id }}');
}
});
})();
{% endjavascript %}
{% schema %}
{
"name": "Section Name",
"tag": "section",
"class": "section-wrapper",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "Default Heading"
},
{
"type": "color",
"id": "bg_color",
"label": "Background Color",
"default": "#ffffff"
},
{
"type": "color",
"id": "text_color",
"label": "Text Color",
"default": "#000000"
},
{
"type": "range",
"id": "padding_top",
"label": "Top Spacing",
"min": 0,
"max": 100,
"step": 4,
"unit": "px",
"default": 40
},
{
"type": "range",
"id": "padding_bottom",
"label": "Bottom Spacing",
"min": 0,
"max": 100,
"step": 4,
"unit": "px",
"default": 40
}
],
"presets": [
{
"name": "Section Name"
}
]
}
{% endschema %}
```
## When to Use Inline CSS/JS
### Use Inline `{% stylesheet %}` When:
- CSS needs Liquid variables: `{{ section.settings.color }}`
- Dynamic styles based on merchant settings
- Section-specific styles that don't need external file
### Use Inline `{% javascript %}` When:
- JS needs Liquid settings: `{{ section.settings.enable_feature }}`
- Section-specific functionality
- Need access to `{{ section.id }}` or other Liquid values
### Skip Inline Tags When:
- Styles/scripts are static and reusable
- Better to use theme.css or theme.js
- No Liquid variables needed
## Common Section Patterns
### 1. Product Grid Section
```liquid
{%- liquid
assign collection = section.settings.collection
assign products_count = section.settings.products_count
assign columns = section.settings.columns_desktop
-%}
{%- for product in collection.products limit: products_count -%}
{%- endfor -%}
{% stylesheet %}
.product-grid {
display: grid;
gap: 1.5rem;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.product-grid {
grid-template-columns: repeat({{ columns }}, 1fr);
}
}
{% endstylesheet %}
```
### 2. Hero Banner Section
```liquid
{%- liquid
assign image = section.settings.image
assign heading = section.settings.heading
assign text = section.settings.text
assign button_label = section.settings.button_label
assign button_link = section.settings.button_link
-%}
{%- if image -%}
{%- endif -%}
{%- if heading != blank -%}
{{ heading | escape }}
{%- endif -%}
{%- if text != blank -%}
{{ text }}
{%- endif -%}
{%- if button_label != blank and button_link != blank -%}
{{ button_label | escape }}
{%- endif -%}
{% stylesheet %}
.hero-banner {
position: relative;
min-height: 500px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.hero-banner__image {
position: absolute;
inset: 0;
z-index: -1;
}
.hero-banner__image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.hero-banner__content {
padding: 2rem;
background: rgba(255, 255, 255, 0.9);
border-radius: 8px;
}
.button {
display: inline-block;
padding: 1rem 2rem;
background: #000;
color: #fff;
text-decoration: none;
border-radius: 4px;
margin-top: 1rem;
}
{% endstylesheet %}
```
### 3. Testimonials Section with Blocks
```liquid
{%- for block in section.blocks -%}
{%- if block.settings.quote != blank -%}
{{ block.settings.quote }}
{%- endif -%}
{%- if block.settings.author != blank -%}
{{ block.settings.author | escape }}
{%- endif -%}
{%- endfor -%}
{% schema %}
{
"name": "Testimonials",
"blocks": [
{
"type": "testimonial",
"name": "Testimonial",
"settings": [
{
"type": "textarea",
"id": "quote",
"label": "Quote"
},
{
"type": "text",
"id": "author",
"label": "Author"
}
]
}
],
"presets": [
{
"name": "Testimonials",
"blocks": [
{
"type": "testimonial"
}
]
}
]
}
{% endschema %}
```
## Shopify Section Events
Handle theme editor events:
```javascript
{% javascript %}
(function() {
'use strict';
function initSection(sectionId) {
const section = document.querySelector('[data-section-id="' + sectionId + '"]');
if (!section) return;
console.log('Section initialized:', sectionId);
}
function cleanupSection(sectionId) {
// Clean up event listeners, timers, etc.
console.log('Section cleaned up:', sectionId);
}
// Load event - section added or page loaded
document.addEventListener('shopify:section:load', function(event) {
initSection(event.detail.sectionId);
});
// Unload event - section removed
document.addEventListener('shopify:section:unload', function(event) {
cleanupSection(event.detail.sectionId);
});
// Select event - section selected in theme editor
document.addEventListener('shopify:section:select', function(event) {
console.log('Section selected:', event.detail.sectionId);
});
// Deselect event - section deselected in theme editor
document.addEventListener('shopify:section:deselect', function(event) {
console.log('Section deselected:', event.detail.sectionId);
});
// Block select event - block selected in theme editor
document.addEventListener('shopify:block:select', function(event) {
console.log('Block selected:', event.detail.blockId);
});
// Block deselect event - block deselected in theme editor
document.addEventListener('shopify:block:deselect', function(event) {
console.log('Block deselected:', event.detail.blockId);
});
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
initSection('{{ section.id }}');
});
})();
{% endjavascript %}
```
## Best Practices
### 1. Use Section ID for Unique Styling
```liquid
{% stylesheet %}
#section-{{ section.id }} {
/* Section-specific styles using Liquid variables */
background: {{ section.settings.bg_color }};
}
.my-section {
/* General styles without Liquid variables */
padding: 2rem 0;
}
{% endstylesheet %}
```
### 2. Provide Sensible Defaults
```liquid
{%- liquid
assign heading = section.settings.heading | default: 'Default Heading'
assign columns = section.settings.columns | default: 3
assign show_prices = section.settings.show_prices | default: true
-%}
```
### 3. Handle Empty States
```liquid
{%- if collection.products.size > 0 -%}
{%- else -%}
No products available in this collection.
{%- endif -%}
```
### 4. Mobile-First Responsive
```css
/* Mobile first - base styles */
.grid {
grid-template-columns: 1fr;
}
/* Tablet and up */
@media (min-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
```
### 5. Use data- Attributes for JavaScript
```liquid
{% javascript %}
const slider = document.querySelector('.slider');
const autoplay = slider.dataset.autoplay === 'true';
const speed = parseInt(slider.dataset.speed, 10);
{% endjavascript %}
```
## Common Patterns
### Conditional Classes
```liquid
```
### Dynamic Grid Columns
```liquid
{% stylesheet %}
.product-grid {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat({{ section.settings.columns_mobile }}, 1fr);
}
@media (min-width: 1024px) {
.product-grid {
grid-template-columns: repeat({{ section.settings.columns_desktop }}, 1fr);
}
}
{% endstylesheet %}
```
### Loading States
```javascript
{% javascript %}
function addToCart(button) {
button.classList.add('loading');
button.disabled = true;
// Add to cart logic...
button.classList.remove('loading');
button.disabled = false;
}
{% endjavascript %}
```
Create sections that are flexible, performant, and easy for merchants to customize through the theme editor.