Initial commit
This commit is contained in:
498
skills/shopify/references/themes.md
Normal file
498
skills/shopify/references/themes.md
Normal file
@@ -0,0 +1,498 @@
|
||||
# Themes Reference
|
||||
|
||||
Guide for developing Shopify themes with Liquid templating.
|
||||
|
||||
## Liquid Templating
|
||||
|
||||
### Syntax Basics
|
||||
|
||||
**Objects (Output):**
|
||||
```liquid
|
||||
{{ product.title }}
|
||||
{{ product.price | money }}
|
||||
{{ customer.email }}
|
||||
```
|
||||
|
||||
**Tags (Logic):**
|
||||
```liquid
|
||||
{% if product.available %}
|
||||
<button>Add to Cart</button>
|
||||
{% else %}
|
||||
<p>Sold Out</p>
|
||||
{% endif %}
|
||||
|
||||
{% for product in collection.products %}
|
||||
{{ product.title }}
|
||||
{% endfor %}
|
||||
|
||||
{% case product.type %}
|
||||
{% when 'Clothing' %}
|
||||
<span>Apparel</span>
|
||||
{% when 'Shoes' %}
|
||||
<span>Footwear</span>
|
||||
{% else %}
|
||||
<span>Other</span>
|
||||
{% endcase %}
|
||||
```
|
||||
|
||||
**Filters (Transform):**
|
||||
```liquid
|
||||
{{ product.title | upcase }}
|
||||
{{ product.price | money }}
|
||||
{{ product.description | strip_html | truncate: 100 }}
|
||||
{{ product.image | img_url: 'medium' }}
|
||||
{{ 'now' | date: '%B %d, %Y' }}
|
||||
```
|
||||
|
||||
### Common Objects
|
||||
|
||||
**Product:**
|
||||
```liquid
|
||||
{{ product.id }}
|
||||
{{ product.title }}
|
||||
{{ product.handle }}
|
||||
{{ product.description }}
|
||||
{{ product.price }}
|
||||
{{ product.compare_at_price }}
|
||||
{{ product.available }}
|
||||
{{ product.type }}
|
||||
{{ product.vendor }}
|
||||
{{ product.tags }}
|
||||
{{ product.images }}
|
||||
{{ product.variants }}
|
||||
{{ product.featured_image }}
|
||||
{{ product.url }}
|
||||
```
|
||||
|
||||
**Collection:**
|
||||
```liquid
|
||||
{{ collection.title }}
|
||||
{{ collection.handle }}
|
||||
{{ collection.description }}
|
||||
{{ collection.products }}
|
||||
{{ collection.products_count }}
|
||||
{{ collection.image }}
|
||||
{{ collection.url }}
|
||||
```
|
||||
|
||||
**Cart:**
|
||||
```liquid
|
||||
{{ cart.item_count }}
|
||||
{{ cart.total_price }}
|
||||
{{ cart.items }}
|
||||
{{ cart.note }}
|
||||
{{ cart.attributes }}
|
||||
```
|
||||
|
||||
**Customer:**
|
||||
```liquid
|
||||
{{ customer.email }}
|
||||
{{ customer.first_name }}
|
||||
{{ customer.last_name }}
|
||||
{{ customer.orders_count }}
|
||||
{{ customer.total_spent }}
|
||||
{{ customer.addresses }}
|
||||
{{ customer.default_address }}
|
||||
```
|
||||
|
||||
**Shop:**
|
||||
```liquid
|
||||
{{ shop.name }}
|
||||
{{ shop.email }}
|
||||
{{ shop.domain }}
|
||||
{{ shop.currency }}
|
||||
{{ shop.money_format }}
|
||||
{{ shop.enabled_payment_types }}
|
||||
```
|
||||
|
||||
### Common Filters
|
||||
|
||||
**String:**
|
||||
- `upcase`, `downcase`, `capitalize`
|
||||
- `strip_html`, `strip_newlines`
|
||||
- `truncate: 100`, `truncatewords: 20`
|
||||
- `replace: 'old', 'new'`
|
||||
|
||||
**Number:**
|
||||
- `money` - Format currency
|
||||
- `round`, `ceil`, `floor`
|
||||
- `times`, `divided_by`, `plus`, `minus`
|
||||
|
||||
**Array:**
|
||||
- `join: ', '`
|
||||
- `first`, `last`
|
||||
- `size`
|
||||
- `map: 'property'`
|
||||
- `where: 'property', 'value'`
|
||||
|
||||
**URL:**
|
||||
- `img_url: 'size'` - Image URL
|
||||
- `url_for_type`, `url_for_vendor`
|
||||
- `link_to`, `link_to_type`
|
||||
|
||||
**Date:**
|
||||
- `date: '%B %d, %Y'`
|
||||
|
||||
## Theme Architecture
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
theme/
|
||||
├── assets/ # CSS, JS, images
|
||||
├── config/ # Theme settings
|
||||
│ ├── settings_schema.json
|
||||
│ └── settings_data.json
|
||||
├── layout/ # Base templates
|
||||
│ └── theme.liquid
|
||||
├── locales/ # Translations
|
||||
│ └── en.default.json
|
||||
├── sections/ # Reusable blocks
|
||||
│ ├── header.liquid
|
||||
│ ├── footer.liquid
|
||||
│ └── product-grid.liquid
|
||||
├── snippets/ # Small components
|
||||
│ ├── product-card.liquid
|
||||
│ └── icon.liquid
|
||||
└── templates/ # Page templates
|
||||
├── index.json
|
||||
├── product.json
|
||||
├── collection.json
|
||||
└── cart.liquid
|
||||
```
|
||||
|
||||
### Layout
|
||||
|
||||
Base template wrapping all pages (`layout/theme.liquid`):
|
||||
|
||||
```liquid
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ request.locale.iso_code }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{{ page_title }}</title>
|
||||
|
||||
{{ content_for_header }}
|
||||
|
||||
<link rel="stylesheet" href="{{ 'theme.css' | asset_url }}">
|
||||
</head>
|
||||
<body>
|
||||
{% section 'header' %}
|
||||
|
||||
<main>
|
||||
{{ content_for_layout }}
|
||||
</main>
|
||||
|
||||
{% section 'footer' %}
|
||||
|
||||
<script src="{{ 'theme.js' | asset_url }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Templates
|
||||
|
||||
Page-specific structures (`templates/product.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"sections": {
|
||||
"main": {
|
||||
"type": "product-template",
|
||||
"settings": {
|
||||
"show_vendor": true,
|
||||
"show_quantity_selector": true
|
||||
}
|
||||
},
|
||||
"recommendations": {
|
||||
"type": "product-recommendations"
|
||||
}
|
||||
},
|
||||
"order": ["main", "recommendations"]
|
||||
}
|
||||
```
|
||||
|
||||
Legacy format (`templates/product.liquid`):
|
||||
```liquid
|
||||
<div class="product">
|
||||
<div class="product-images">
|
||||
<img src="{{ product.featured_image | img_url: 'large' }}" alt="{{ product.title }}">
|
||||
</div>
|
||||
|
||||
<div class="product-details">
|
||||
<h1>{{ product.title }}</h1>
|
||||
<p class="price">{{ product.price | money }}</p>
|
||||
|
||||
{% form 'product', product %}
|
||||
<select name="id">
|
||||
{% for variant in product.variants %}
|
||||
<option value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<button type="submit">Add to Cart</button>
|
||||
{% endform %}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Sections
|
||||
|
||||
Reusable content blocks (`sections/product-grid.liquid`):
|
||||
|
||||
```liquid
|
||||
<div class="product-grid">
|
||||
{% for product in section.settings.collection.products %}
|
||||
<div class="product-card">
|
||||
<a href="{{ product.url }}">
|
||||
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
|
||||
<h3>{{ product.title }}</h3>
|
||||
<p>{{ product.price | money }}</p>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% schema %}
|
||||
{
|
||||
"name": "Product Grid",
|
||||
"settings": [
|
||||
{
|
||||
"type": "collection",
|
||||
"id": "collection",
|
||||
"label": "Collection"
|
||||
},
|
||||
{
|
||||
"type": "range",
|
||||
"id": "products_per_row",
|
||||
"min": 2,
|
||||
"max": 5,
|
||||
"step": 1,
|
||||
"default": 4,
|
||||
"label": "Products per row"
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"name": "Product Grid"
|
||||
}
|
||||
]
|
||||
}
|
||||
{% endschema %}
|
||||
```
|
||||
|
||||
### Snippets
|
||||
|
||||
Small reusable components (`snippets/product-card.liquid`):
|
||||
|
||||
```liquid
|
||||
<div class="product-card">
|
||||
<a href="{{ product.url }}">
|
||||
{% if product.featured_image %}
|
||||
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
|
||||
{% endif %}
|
||||
<h3>{{ product.title }}</h3>
|
||||
<p class="price">{{ product.price | money }}</p>
|
||||
{% if product.compare_at_price > product.price %}
|
||||
<p class="sale-price">{{ product.compare_at_price | money }}</p>
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
Include snippet:
|
||||
```liquid
|
||||
{% render 'product-card', product: product %}
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# Initialize new theme
|
||||
shopify theme init
|
||||
|
||||
# Choose Dawn (reference theme) or blank
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
# Start local server
|
||||
shopify theme dev
|
||||
|
||||
# Preview at http://localhost:9292
|
||||
# Changes auto-sync to development theme
|
||||
```
|
||||
|
||||
### Pull Theme
|
||||
|
||||
```bash
|
||||
# Pull live theme
|
||||
shopify theme pull --live
|
||||
|
||||
# Pull specific theme
|
||||
shopify theme pull --theme=123456789
|
||||
|
||||
# Pull only templates
|
||||
shopify theme pull --only=templates
|
||||
```
|
||||
|
||||
### Push Theme
|
||||
|
||||
```bash
|
||||
# Push to development theme
|
||||
shopify theme push --development
|
||||
|
||||
# Create new unpublished theme
|
||||
shopify theme push --unpublished
|
||||
|
||||
# Push specific files
|
||||
shopify theme push --only=sections,snippets
|
||||
```
|
||||
|
||||
### Theme Check
|
||||
|
||||
Lint theme code:
|
||||
```bash
|
||||
shopify theme check
|
||||
shopify theme check --auto-correct
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Product Form with Variants
|
||||
|
||||
```liquid
|
||||
{% form 'product', product %}
|
||||
{% unless product.has_only_default_variant %}
|
||||
{% for option in product.options_with_values %}
|
||||
<div class="product-option">
|
||||
<label>{{ option.name }}</label>
|
||||
<select name="options[{{ option.name }}]">
|
||||
{% for value in option.values %}
|
||||
<option value="{{ value }}">{{ value }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endunless %}
|
||||
|
||||
<input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
|
||||
<input type="number" name="quantity" value="1" min="1">
|
||||
|
||||
<button type="submit" {% unless product.available %}disabled{% endunless %}>
|
||||
{% if product.available %}Add to Cart{% else %}Sold Out{% endif %}
|
||||
</button>
|
||||
{% endform %}
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
```liquid
|
||||
{% paginate collection.products by 12 %}
|
||||
{% for product in collection.products %}
|
||||
{% render 'product-card', product: product %}
|
||||
{% endfor %}
|
||||
|
||||
{% if paginate.pages > 1 %}
|
||||
<div class="pagination">
|
||||
{% if paginate.previous %}
|
||||
<a href="{{ paginate.previous.url }}">Previous</a>
|
||||
{% endif %}
|
||||
|
||||
{% for part in paginate.parts %}
|
||||
{% if part.is_link %}
|
||||
<a href="{{ part.url }}">{{ part.title }}</a>
|
||||
{% else %}
|
||||
<span class="current">{{ part.title }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if paginate.next %}
|
||||
<a href="{{ paginate.next.url }}">Next</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endpaginate %}
|
||||
```
|
||||
|
||||
### Cart AJAX
|
||||
|
||||
```javascript
|
||||
// Add to cart
|
||||
fetch('/cart/add.js', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: variantId,
|
||||
quantity: 1
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(item => console.log('Added:', item));
|
||||
|
||||
// Get cart
|
||||
fetch('/cart.js')
|
||||
.then(res => res.json())
|
||||
.then(cart => console.log('Cart:', cart));
|
||||
|
||||
// Update cart
|
||||
fetch('/cart/change.js', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: lineItemKey,
|
||||
quantity: 2
|
||||
})
|
||||
})
|
||||
.then(res => res.json());
|
||||
```
|
||||
|
||||
## Metafields in Themes
|
||||
|
||||
Access custom data:
|
||||
|
||||
```liquid
|
||||
{{ product.metafields.custom.care_instructions }}
|
||||
{{ product.metafields.custom.material.value }}
|
||||
|
||||
{% if product.metafields.custom.featured %}
|
||||
<span class="badge">Featured</span>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
**Performance:**
|
||||
- Optimize images (use appropriate sizes)
|
||||
- Minimize Liquid logic complexity
|
||||
- Use lazy loading for images
|
||||
- Defer non-critical JavaScript
|
||||
|
||||
**Accessibility:**
|
||||
- Use semantic HTML
|
||||
- Include alt text for images
|
||||
- Support keyboard navigation
|
||||
- Ensure sufficient color contrast
|
||||
|
||||
**SEO:**
|
||||
- Use descriptive page titles
|
||||
- Include meta descriptions
|
||||
- Structure content with headings
|
||||
- Implement schema markup
|
||||
|
||||
**Code Quality:**
|
||||
- Follow Shopify theme guidelines
|
||||
- Use consistent naming conventions
|
||||
- Comment complex logic
|
||||
- Keep sections focused and reusable
|
||||
|
||||
## Resources
|
||||
|
||||
- Theme Development: https://shopify.dev/docs/themes
|
||||
- Liquid Reference: https://shopify.dev/docs/api/liquid
|
||||
- Dawn Theme: https://github.com/Shopify/dawn
|
||||
- Theme Check: https://shopify.dev/docs/themes/tools/theme-check
|
||||
Reference in New Issue
Block a user