Skip to content

Theme Configuration

Learn how to structure, configure, and organize your Nimbu themes for maintainability and performance.

Theme Directory Structure

theme-name/
├── nimbu.yml                     # Toolbelt configuration (site, theme, apps)
├── nimbu.js                      # Build configuration (webpack, CDN)
├── layouts/
│   ├── default.liquid           # Default layout
│   ├── checkout.liquid          # Checkout layout
│   └── account.liquid           # Customer account layout
├── templates/
│   ├── index.liquid             # Homepage
│   ├── page.liquid              # Page template
│   ├── article.liquid           # Article template
│   ├── product.liquid           # Product template
│   └── collection.liquid        # Collection template
├── snippets/
│   ├── header.liquid            # Header component
│   ├── footer.liquid            # Footer component
│   ├── product-card.liquid      # Product card component
│   └── navigation.liquid        # Navigation component
├── stylesheets/
│   ├── theme.css                # Main stylesheet
│   └── print.css                # Print stylesheet
├── javascripts/
│   ├── theme.js                 # Main JavaScript
│   └── vendor.js                # Third-party libraries
├── images/
│   ├── logo.png
│   └── placeholder.jpg
├── fonts/
│   ├── custom-font.woff2        # Custom web fonts
│   └── custom-font.woff
├── src/                         # Source files (if using build tools)
│   ├── index.ts
│   └── index.css
└── README.md                     # Theme documentation

Configuration Files

nimbu.yml

The nimbu.yml file configures the Nimbu toolbelt for your theme.

Basic Example:

yaml
---
site: mysite
theme: default-theme

Advanced Example with Cloud Code Apps:

yaml
theme: default-theme
site: mysite
apps:
  - name: production
    id: 84acd4b8941e722e34e1c6b36bd40137
    dir: code/dist
    glob: '*.js'
  - name: staging
    id: 7b7d14488c5d7eea0ff108c59c020882
    dir: code/dist
    glob: '*.js'
    host: api.nimbu.be

Configuration Options:

  • site (required) - Site subdomain
  • theme (required) - Theme name
  • apps (optional) - Cloud code app configurations
    • name - Environment name (e.g., production, staging)
    • id - Cloud code app ID
    • dir - Directory containing compiled code
    • glob - File pattern to upload
    • host - API host (optional, defaults to api.nimbu.io)

nimbu.js

The nimbu.js file configures the Nimbu build toolbelt for webpack compilation and asset management.

Basic Example:

javascript
process.env.GENERATE_SOURCEMAP = 'false';

module.exports = {
  REACT: false,
  CDN_ROOT: '../', // Put the root url of the CDN here
  WEBPACK_ENTRY: {
    app: ['./src/index.ts', './src/index.css'],
  },
};

Advanced Example with Environment Configuration:

javascript
const initNimbuEnv = require('./nimbu_env');
const execa = require('execa');

// Git-based versioning
const gitHash = execa.sync('git', ['rev-parse', '--short', 'HEAD']).stdout;
const gitNumCommits = Number(execa.sync('git', ['rev-list', 'HEAD', '--count']).stdout);
const gitDirty = execa.sync('git', ['status', '-s', '-uall']).stdout.length > 0;
process.env.__VERSION__ = `${gitHash}-${gitNumCommits}-${gitDirty}-${new Date().getTime()}`;

process.env.GENERATE_SOURCEMAP = 'true';

module.exports = initNimbuEnv().then(() => {
  console.log(`Connecting to: ${process.env.NIMBU_ENV}`);

  return {
    REACT: true,
    BUGSNAG_API_KEY: 'your-bugsnag-key',
    GOOGLE_MAPS_KEY: 'your-google-maps-key',
    GOOGLE_ANALYTICS_KEY:
      process.env.NIMBU_ENV === 'production' ? 'UA-PROD' : 'UA-STAGING',
    CDN_ROOT:
      process.env.NIMBU_ENV === 'production'
        ? 'https://cdn.nimbu.io/s/xxx/themes/xxx'
        : 'https://cdn.nimbu.io/s/xxx/themes/xxx',
    SVG_LOADER_INCLUDE: [/src\/assets\/svg-icons\//],
    SVG_LOADER_OPTIONS: {
      plugins: [{ removeViewBox: false }],
    },
    NIMBU_SITE: process.env.NIMBU_ENV === 'staging' ? 'mysite-staging' : 'mysite',
    WEBPACK_ENTRY: {
      app: ['./src/app.scss', './src/app.js'],
      admin: ['./src/admin.scss', './src/admin.js'],
    },
    MEMORY_LIMIT: 4096,
  };
});

Configuration Options:

  • REACT - Enable React support (boolean)
  • CDN_ROOT - CDN URL for assets (string)
  • WEBPACK_ENTRY - Webpack entry points (object)
  • BUGSNAG_API_KEY - Bugsnag error tracking key
  • GOOGLE_MAPS_KEY - Google Maps API key
  • GOOGLE_ANALYTICS_KEY - Google Analytics tracking ID
  • SVG_LOADER_INCLUDE - Paths for SVG loader (array)
  • SVG_LOADER_OPTIONS - SVGO plugin options (object)
  • NIMBU_SITE - Site subdomain for environment
  • MEMORY_LIMIT - Node memory limit in MB (number)

Async Configuration:

The nimbu.js file can return a Promise for async environment setup:

javascript
module.exports = initNimbuEnv().then(() => {
  return {
    // ... configuration based on process.env.NIMBU_ENV
  };
});

Layout Organization

Default Layout

layouts/default.liquid:

liquid
<!DOCTYPE html>
<html lang="{{ locale }}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>{{ page.title }} - {{ site.name }}</title>

  {% if page.meta_description %}
    <meta name="description" content="{{ page.meta_description }}">
  {% else %}
    <meta name="description" content="{{ site.meta_description }}">
  {% endif %}

  <!-- Stylesheets -->
  {{ 'theme.css' | asset_url | stylesheet_tag }}

  <!-- Canonical URL -->
  <link rel="canonical" href="{{ request.url }}">

  <!-- Language alternates -->
  {% for translation in page.translations %}
    <link rel="alternate" hreflang="{{ translation.locale }}" href="{{ translation.url }}">
  {% endfor %}
</head>
<body class="template-{{ template }}">
  {% include 'header' %}

  <main id="main-content" class="main-content">
    {{ content_for_body }}
  </main>

  {% include 'footer' %}

  <!-- JavaScript -->
  {{ 'theme.js' | asset_url | script_tag }}
</body>
</html>

Specialized Layouts

layouts/checkout.liquid - Minimal layout for checkout:

liquid
<!DOCTYPE html>
<html lang="{{ locale }}">
<head>
  <title>Checkout - {{ site.name }}</title>
  {{ 'checkout.css' | asset_url | stylesheet_tag }}
</head>
<body class="checkout-page">
  <header class="checkout-header">
    <a href="/">{{ site.name }}</a>
  </header>

  {{ content_for_body }}

  {{ 'checkout.js' | asset_url | script_tag }}
</body>
</html>

Template Organization

Naming Conventions

  • index.liquid - Homepage
  • page.liquid - Generic pages
  • article.liquid - Blog articles
  • product.liquid - Product pages
  • collection.liquid - Product collections
  • customer.liquid - Customer account
  • 404.liquid - Not found page

Template Routing

Templates are automatically routed:

URLTemplate
/index.liquid
/aboutpage.liquid
/blog/article-slugarticle.liquid
/products/product-slugproduct.liquid
/collections/collection-slugcollection.liquid

Snippet Organization

Component Naming

snippets/
├── components/
│   ├── button.liquid
│   ├── card.liquid
│   └── modal.liquid
├── sections/
│   ├── hero.liquid
│   ├── features.liquid
│   └── testimonials.liquid
├── partials/
│   ├── header.liquid
│   ├── footer.liquid
│   └── navigation.liquid
└── utils/
    ├── social-share.liquid
    └── breadcrumbs.liquid

Snippet Conventions

snippets/product-card.liquid:

liquid
{% comment %}
  Product Card Component

  Parameters:
  - product: Product object (required)
  - show_vendor: Boolean (optional, default: false)
  - image_size: String (optional, default: '400px')
{% endcomment %}

<div class="product-card">
  <a href="{{ product.url }}" class="product-link">
    {% if product.images.first %}
      <img
        src="{{ product.images.first.url | filter, width: image_size | default: '400px' }}"
        alt="{{ product.name }}"
        loading="lazy">
    {% endif %}
  </a>

  <h3 class="product-title">
    <a href="{{ product.url }}">{{ product.name }}</a>
  </h3>

  {% if show_vendor and product.vendor %}
    <p class="product-vendor">{{ product.vendor }}</p>
  {% endif %}

  <p class="product-price">
    {{ product.price | money_with_currency }}
  </p>
</div>

Asset Organization

CSS Architecture

stylesheets/
├── base/
│   ├── reset.css
│   ├── typography.css
│   └── variables.css
├── components/
│   ├── buttons.css
│   ├── forms.css
│   └── cards.css
├── layouts/
│   ├── header.css
│   ├── footer.css
│   └── grid.css
├── pages/
│   ├── homepage.css
│   ├── product.css
│   └── checkout.css
└── theme.css             # Main import file

stylesheets/theme.css:

css
/* Base */
@import "base/reset.css";
@import "base/variables.css";
@import "base/typography.css";

/* Components */
@import "components/buttons.css";
@import "components/forms.css";
@import "components/cards.css";

/* Layouts */
@import "layouts/header.css";
@import "layouts/footer.css";
@import "layouts/grid.css";

/* Pages */
@import "pages/homepage.css";
@import "pages/product.css";

JavaScript Organization

javascripts/
├── modules/
│   ├── cart.js
│   ├── product.js
│   └── search.js
├── utils/
│   ├── helpers.js
│   └── ajax.js
├── vendor/
│   ├── swiper.js
│   └── alpine.js
└── theme.js             # Main entry point

javascripts/theme.js:

javascript
// Utilities
import { debounce, throttle } from './utils/helpers.js';
import { fetchJSON } from './utils/ajax.js';

// Modules
import './modules/cart.js';
import './modules/product.js';
import './modules/search.js';

// Initialize
document.addEventListener('DOMContentLoaded', function() {
  console.log('Theme initialized');
});

Images Organization

images/
├── logo.png
├── [email protected]
├── favicon.ico
├── placeholder.jpg
└── icons/
    ├── cart.svg
    ├── search.svg
    └── user.svg

Fonts Organization

fonts/
├── custom-font-regular.woff2
├── custom-font-regular.woff
├── custom-font-bold.woff2
└── custom-font-bold.woff

Using custom fonts in CSS:

css
@font-face {
  font-family: 'Custom Font';
  src: url('/fonts/custom-font-regular.woff2') format('woff2'),
       url('/fonts/custom-font-regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Custom Font';
  src: url('/fonts/custom-font-bold.woff2') format('woff2'),
       url('/fonts/custom-font-bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: 'Custom Font', sans-serif;
}

Version Management

Versioning Strategy

Use semantic versioning (MAJOR.MINOR.PATCH):

  • MAJOR: Breaking changes
  • MINOR: New features (backward compatible)
  • PATCH: Bug fixes

Track versions in your package.json or README.md:

package.json:

json
{
  "name": "theme-name",
  "version": "2.1.3"
}

Changelog

Maintain a CHANGELOG.md:

markdown
# Changelog

## [2.1.3] - 2025-10-07

### Fixed
- Product image lazy loading on mobile
- Cart total calculation with discounts

## [2.1.0] - 2025-09-15

### Added
- Quick view modal for products
- Wishlist functionality
- AJAX cart updates

### Changed
- Improved mobile navigation
- Updated checkout flow

## [2.0.0] - 2025-08-01

### Breaking Changes
- New template structure
- Updated Liquid syntax

### Added
- Multi-currency support
- Advanced filtering

Best Practices

1. Modular Organization

liquid
<!-- ✅ Good: Modular components -->
{% include 'components/product-card', product: product %}
{% include 'sections/hero' %}

<!-- ❌ Bad: Monolithic templates -->
<div class="product-card">
  <!-- Hundreds of lines of code -->
</div>

2. Consistent Naming

<!-- ✅ Good: Clear naming -->
snippets/product-card.liquid
snippets/collection-filters.liquid
snippets/checkout-summary.liquid

<!-- ❌ Bad: Unclear naming -->
snippets/comp1.liquid
snippets/helper.liquid

3. Documentation

Add comments to complex logic:

liquid
{% comment %}
  Calculate dynamic pricing based on quantity tiers:
  - 1-10 items: regular price
  - 11-50 items: 10% discount
  - 51+ items: 20% discount
{% endcomment %}

{% if cart.items.size > 50 %}
  {% assign discount = 0.20 %}
{% elsif cart.items.size > 10 %}
  {% assign discount = 0.10 %}
{% else %}
  {% assign discount = 0 %}
{% endif %}

4. Asset Optimization

liquid
<!-- ✅ Good: Optimized assets -->
{{ 'theme.min.css' | asset_url | stylesheet_tag }}
{{ 'theme.min.js' | asset_url | script_tag, defer: true }}

<!-- ⚠️ Development only -->
{{ 'theme.css' | asset_url | stylesheet_tag }}
{{ 'theme.js' | asset_url | script_tag }}

Next Steps

Build well-organized, maintainable Nimbu themes!