Skip to main content

Theme Development Best Practices

Recommended practices for developing O2VEND themes.

Code Organization

File Structure

Organize files logically:

theme/
layout/
theme.liquid
templates/
index.liquid
product.liquid
sections/
header.liquid
footer.liquid
snippets/
product-card.liquid
assets/
theme.css
theme.js
config/
settings_schema.json

Naming Conventions

  • Use lowercase with hyphens for file names: product-card.liquid
  • Use descriptive names: hero-banner.liquid not hb.liquid
  • Group related files: product-*.liquid for product-related snippets

Template Best Practices

1. Use Layouts

Always use the layout system:

{% layout 'theme' %}
<!-- Template content -->

2. Error Handling

Always handle missing data:

{% if product %}
{{ product.name }}
{% else %}
<p>Product not found</p>
{% endif %}

3. Escape User Input

Always escape user-generated content:

{{ user_content | escape }}
{{ product.name | escape }}

4. Use Default Values

Provide fallbacks:

{{ product.name | default: 'Untitled Product' }}
{{ settings.color | default: '#000000' }}

Performance Best Practices

1. Minimize Loops

Avoid deep nesting:

{% for collection in collections limit: 5 %}
{% render 'collection-grid', collection: collection %}
{% endfor %}

2. Cache Values

Cache expensive operations:

{% assign product_count = collection.products.size %}
{% assign has_discount = product.compare_at_price > product.price %}

3. Lazy Load Images

<img src="{{ image | img_url: 'small' }}" 
data-src="{{ image | img_url: 'large' }}"
loading="lazy">

4. Optimize Assets

  • Minify CSS and JavaScript
  • Combine files when possible
  • Use appropriate image formats
  • Compress images

Widget Best Practices

1. Provide Fallbacks

{% if widgets.content.size > 0 %}
{% for widget in widgets.content %}
{{ widget | render_widget }}
{% endfor %}
{% else %}
<!-- Fallback content -->
{% endif %}

2. Validate Settings

{% if widget.settings.show_title %}
<h2>{{ widget.settings.title | default: 'Default Title' }}</h2>
{% endif %}

3. Limit Widget Count

{% assign limited_widgets = widgets.content | limit: 10 %}

Snippet Best Practices

1. Make Snippets Reusable

{% comment %}
Snippet: button
Parameters: text, url, style
{% endcomment %}
<a href="{{ url }}" class="btn btn-{{ style | default: 'primary' }}">
{{ text }}
</a>

2. Document Parameters

Always document snippet parameters in comments.

3. Keep Snippets Focused

Each snippet should do one thing well.

Section Best Practices

1. Use Settings

Make sections configurable:

<section style="background-color: {{ section.settings.bg_color }}">
<h2>{{ section.settings.title }}</h2>
</section>

2. Support Widgets

Allow widgets in sections:

{% for widget in widgets.content %}
{{ widget | render_widget }}
{% endfor %}

Security Best Practices

1. Escape Output

{{ user_input | escape }}
{{ product.name | escape }}

2. Validate Input

{% if product and product.id %}
<!-- Use product -->
{% endif %}

3. Use Secure Asset Loading

{{ 'script.js' | asset_url | script_tag }}

Accessibility Best Practices

1. Semantic HTML

<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
</header>

2. Alt Text for Images

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

3. ARIA Labels

<button aria-label="Add to cart">
Add to Cart
</button>

CSS Variables Best Practices (Updated - January 2025)

Use CSS Variables Instead of Hardcoded Values

Always use CSS custom properties (CSS variables) from the root :root selector instead of hardcoded values:

Avoid:

.widget {
padding: 16px;
border-radius: 8px;
color: #333333;
background: #ffffff;
}

Use:

.widget {
padding: var(--space-4);
border-radius: var(--border-radius-medium);
color: var(--color-text);
background: var(--color-background);
}

Available CSS Variables

Color Variables

  • --color-primary, --color-secondary, --color-accent
  • --color-background, --color-surface, --color-text
  • --color-border, --color-error, --color-success

Spacing Variables

  • --space-1, --space-2, --space-4, --space-6, --space-8, etc.
  • --theme-spacing-sm, --theme-spacing-md, --theme-spacing-lg

Typography Variables

  • --font-primary, --font-display
  • --text-base, --text-sm, --text-lg, --text-xl, etc.
  • --leading-tight, --leading-normal, --leading-relaxed

Border Radius Variables

  • --border-radius-small, --border-radius-medium, --border-radius-large

Shadow Variables

  • --shadow-sm, --shadow-md, --shadow-lg, --shadow-xl

Transition Variables

  • --transition-fast, --transition, --transition-slow
  • --duration-* and --ease-* variables

Benefits of CSS Variables

  1. Centralized Theming: All values defined in root CSS
  2. Runtime Customization: Values can be changed via theme configuration
  3. Consistency: Ensures consistent styling across themes
  4. Maintainability: Easier to update theme-wide values

Using Theme Settings

Values from settings_data.json are automatically mapped to CSS variables:

:root {
--color-primary: {{ settings.color_primary | default: '#000000' }};
--color-accent: {{ settings.color_accent | default: '#007bff' }};
}

Use these in your CSS:

.button {
background-color: var(--color-primary);
border-color: var(--color-accent);
}

Fallback Values

Always provide fallback values when using CSS variables:

.widget {
/* With fallback */
padding: var(--space-4, 16px);
color: var(--color-text, #333333);
}