Nimbu Developer Docs
Other

Theme Configuration

Theme structure, configuration files, versioning, and asset organization

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:

---
site: mysite
theme: default-theme

Advanced Example with Cloud Code Apps:

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:

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:

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:

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

Layout Organization

Default Layout

layouts/default.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:

<!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:

{% 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:

/* 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:

// 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:

@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:

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

Changelog

Maintain a CHANGELOG.md:

# 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

<!-- ✅ 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:

{% 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

<!-- ✅ 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!

On this page