Building a Custom Theme

A theme in SmallBlock CMS controls your site’s appearance — its typography, colors, spacing, and the overall structure of each page. In this tutorial, you’ll create a custom theme from scratch, define a reusable color system, and override templates to give your site a distinct look and identity.

This tutorial builds on concepts from earlier lessons and is ideal if you want to personalize a project or develop themes for reuse across multiple sites.

Before You Begin

You should have:

  • Completed the Build Your First Site tutorial.

  • A basic understanding of HTML and CSS.

  • A running development server.

  • Access to your project’s file structure.

How Themes Work in SmallBlock

A theme contains:

  • Templates — HTML structure.

  • Static assets — CSS, JavaScript, images.

  • Metadata — theme name, version, color variables.

SmallBlock loads the selected theme at startup, injecting theme-defined colors and assets automatically into the template environment.

The theme resolution flow looks like this:

digraph G {
    rankdir=LR;
    node [shape=box, style="rounded,filled", fillcolor="#eef3f8"];
    A [label="settings.ini\n(site.theme = prussian_dark)"];
    B [label="Theme Loader"];
    C [label="Load Templates\n& Static Assets"];
    D [label="Render Page with Theme"];
    A -> B -> C -> D;
}

Step 1 — Create the Theme Directory

Themes live under the themes/ directory in your project root.

cd mysite
mkdir -p themes/prussian_dark
cd themes/prussian_dark

A typical theme may look like this:

prussian_dark/
├── templates/
├── static/
│   ├── css/
│   └── images/
└── theme.ini

Create the folders:

mkdir -p templates
mkdir -p static/css
mkdir -p static/images

Step 2 — Define Theme Metadata

Create theme.ini:

[theme]
name = Prussian Dark
description = A dark, minimal theme with Prussian blue highlights.
author = Your Name
version = 1.0

[colors]
background = #0f1c2c
surface = #13273d
accent = #23445a
text_primary = #e6edf3
text_muted = #9baec8

These values become available in templates and can be referenced in CSS using the theme loader or by manually defining CSS variables.

Step 3 — Create the Base Template

Inside templates/, create base.html:

<!DOCTYPE html>
<html lang="{{ language or 'en' }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ page.title }} — {{ site_name }}</title>
    <link rel="stylesheet" href="{{ static('css/style.css') }}">
</head>
<body>
    <header class="site-header">
        <h1><a href="/">{{ site_name }}</a></h1>

        <nav class="main-nav">
            {% for item in nav %}
                <a href="{{ item.url }}">{{ item.title }}</a>
            {% endfor %}
        </nav>
    </header>

    <main class="content">
        {{ content }}
    </main>

    <footer class="site-footer">
        <p>© {{ current_year }} {{ site_name }}</p>
    </footer>
</body>
</html>

This is the foundation for all pages in your theme.

Step 4 — Add Styles

Create static/css/style.css:

:root {
    --bg: #0f1c2c;
    --surface: #13273d;
    --accent: #23445a;
    --text: #e6edf3;
    --muted: #9baec8;
}

body {
    background-color: var(--bg);
    color: var(--text);
    font-family: system-ui, sans-serif;
    margin: 0;
    line-height: 1.6;
}

a {
    color: var(--muted);
    text-decoration: none;
}

a:hover {
    color: var(--text);
}

.site-header,
.site-footer {
    background-color: var(--surface);
    padding: 1em;
    border-bottom: 2px solid var(--accent);
}

.main-nav a {
    margin-right: 1em;
}

.content {
    padding: 2rem;
}

At this point, your theme should already start to resemble a cohesive dark UI.

Step 5 — Register and Activate the Theme

Open config/settings.ini and set:

[site]
theme = prussian_dark

Restart the development server:

smallblock runserver

Visit your site — the new Prussian Dark theme should now be active.

Step 6 — Override CMS Templates

To customize specific page types or components, you can override templates from SmallBlock’s internal defaults.

For example, to override the default page view:

touch templates/page.html

Add custom markup:

{% extends "base.html" %}

{% block content %}
    <article class="page">
        <h2>{{ page.title }}</h2>
        <div class="page-body">
            {{ page.body_html|safe }}
        </div>
    </article>
{% endblock %}

Or override navigation, sidebars, blocks, or any template your project uses.

Step 7 — Add Custom Assets (Optional)

You can include images, icons, or theme-specific JavaScript:

<script src="{{ static('js/theme.js') }}"></script>

SmallBlock automatically resolves static paths based on the active theme.

Step 8 — Verify the Theme

Check that:

  • The base template loads successfully

  • CSS is applied and colors match your theme.ini palette

  • Navigation links resolve correctly

  • Overridden templates render as expected

  • Language context (if multilingual) still works with your layout

Use browser dev tools to confirm your assets load from:

/themes/prussian_dark/static/...

Troubleshooting

Theme not applying?

  • Ensure theme = prussian_dark is set under [site].

  • Restart the server — themes load at startup.

  • Check directory name and spelling.

Styles missing?

  • Confirm CSS is placed in static/css/style.css.

  • Verify template includes the CSS link:

    <link rel="stylesheet" href="{{ static('css/style.css') }}">

Overridden template not used?

  • Ensure filenames match the CMS’s internal template names.

  • Clear any template caching (restart server).

Next Steps

Continue exploring advanced customization:

  • Extend the Toolbar — integrate theme styling with editor UI elements.

  • Using Template Tags — create data-driven components for your theme.

  • Concepts: Templates — understand how templates flow through the system.