Create your first MintyFlask theme in 10 minutes

Theme Creation Quick Start

What You Need to Know

A MintyFlask theme is a collection of templates and styles that define your site's appearance. Themes build on top of mixins (like blog, portfolio) to create a complete website.

Three key parts of a theme:

  1. Templates - Page layouts and structure (Jinja2)
  2. CSS - Visual styling (Tailwind + custom CSS)
  3. Configuration - Theme metadata (YAML)

Most themes start by using existing mixins and adding custom styling.

Your First Theme in 3 Steps

Step 1: Create Theme Directory

themes/
└── my-blog/                 # Your theme name
    ├── config/
       └── theme.yaml       # Theme configuration
    ├── templates/
       └── pages/          # Your page templates
           └── homepage.html
    └── static/
        └── css/
            └── src/
                └── input.css # CSS entry point

Step 2: Configure Your Theme

# themes/my-blog/config/theme.yaml

name: "My Blog"
version: "1.0.0"
parent: "_core"
description: "A personal blog theme"
author: "Your Name"

dependencies:
  templates:
    - "_core/layouts/base.html"
  css:
    - "_core/foundations/typography.css"
  js:
    - "_core/components/theme-switcher.js"

features:
  - blog-posts
  - categories
  - tags

Step 3: Create Your CSS Entry Point

/* themes/my-blog/static/css/src/input.css */

/* Import Tailwind */
@import "tailwindcss";

/* Define dark mode variant */
@custom-variant dark (&:where(.dark, .dark *));

/* Declare sources for Tailwind */
@source "../../../../../themes/_core/templates";
@source "../../../../../mixins/blog/templates";
@source "../../../templates";

/* CSS Layers */
@layer base {
  /* Import core foundations */
  @import "../../../../../themes/_core/static/css/foundations/index.css";
}

@layer components {
  /* Import core components */
  @import "../../../../../themes/_core/static/css/components/index.css";

  /* Import blog mixin */
  @import "../../../../../mixins/blog/static/css/blog.css";
}

@layer theme {
  /* Your custom theme styles */
  :root {
    /* Override design tokens */
    --color-primary: oklch(60% 0.15 250);
    --color-secondary: oklch(65% 0.12 180);
  }
}

That's it! You now have a functioning theme that uses the blog mixin.

Creating Your Homepage

{# themes/my-blog/templates/pages/homepage.html #}
{% extends "layouts/blog-home.html" %}

{# Customise the hero section #}
{% set hero_title = "Welcome to My Blog" %}
{% set hero_subtitle = "Thoughts on development and design" %}
{% set hero_cta_text = "Read Articles" %}

{# Configure recent posts section #}
{% set recent_posts_title = "Latest Posts" %}
{% set recent_posts_limit = 6 %}

Flask route:

@blog_bp.route('/')
def homepage():
    posts = get_recent_posts(limit=10)
    return render_template('pages/homepage.html', posts=posts)

Customising Mixin Styles

Add your custom styles in the theme layer:

/* themes/my-blog/static/css/src/input.css */

@layer theme {
  /* Override blog post card */
  .blog-post-card {
    border-radius: 0; /* Square corners */
    border-width: 2px;
  }

  /* Custom hero styling */
  .hero-banner {
    background: linear-gradient(
      135deg,
      var(--color-primary),
      var(--color-secondary)
    );
    padding: 4rem 2rem;
    text-align: centre;
  }
}

Common Theme Patterns

Pattern 1: Using a Mixin Layout

{# themes/my-blog/templates/pages/category.html #}
{% extends "layouts/post-list.html" %}

{# Configure the layout #}
{% set page_title = "Category: " + category %}
{% set pagination_endpoint = 'blog_bp.category_view' %}
{% set pagination_params = {'category': category} %}

Pattern 2: Creating a Custom Page

{# themes/my-blog/templates/pages/about.html #}
{% extends resolve_base_template('base.html') %}

{% block content %}
<article class="prose prose-lg mx-auto py-12">
  <h1>About Me</h1>
  <p>I'm a developer who loves writing about code...</p>
</article>
{% endblock %}

Pattern 3: Custom Component

{# themes/my-blog/templates/pages/landing.html #}
{% extends resolve_base_template('base.html') %}

{% block content %}
<div class="hero-banner">
  <h1 class="text-4xl font-bold mb-4">Welcome</h1>
  <p class="text-xl mb-8">Start your journey here</p>
  <a href="/blog" class="btn-base btn-intent-primary btn-size-lg">
    Get Started
  </a>
</div>

<div class="container mx-auto py-12">
  <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
    {% for feature in features %}
    <div class="card-base card-intent-feature">
      <div class="card-body text-centre">
        <div class="text-4xl mb-4">{{ feature.icon }}</div>
        <h3 class="card-title">{{ feature.title }}</h3>
        <p class="card-description">{{ feature.description }}</p>
      </div>
    </div>
    {% endfor %}
  </div>
</div>
{% endblock %}

Design Tokens

Customise your theme's appearance through tokens:

@layer theme {
  :root {
    /* Colours */
    --color-primary: oklch(60% 0.15 250);
    --color-secondary: oklch(65% 0.12 180);

    /* Spacing (make everything more spacious) */
    --spacing-md: 1.25rem;
    --spacing-lg: 2rem;
    --spacing-xl: 3rem;

    /* Typography (larger text) */
    --text-base: 1.125rem;
    --text-lg: 1.25rem;
    --text-xl: 1.5rem;

    /* Border radius (rounder corners) */
    --radius-md: 0.5rem;
    --radius-lg: 0.75rem;
  }
}

Directory Structure

Complete theme structure:

themes/my-blog/
├── config/
│   └── theme.yaml              # Theme metadata
├── templates/
│   ├── pages/                  # Your page templates
│   │   ├── homepage.html
│   │   ├── about.html
│   │   └── contact.html
│   └── layouts/                # Custom layouts (optional)
│       └── custom-layout.html
└── static/
    ├── css/
    │   ├── src/
    │   │   └── input.css       # CSS entry point
    │   └── dist/
    │       └── output.css      # Built CSS
    └── images/                 # Theme images
        └── logo.png

Using Multiple Mixins

Combine multiple mixins in one theme:

/* themes/agency/static/css/src/input.css */

@layer components {
  /* Import multiple mixins */
  @import "../../../../../mixins/blog/static/css/blog.css";
  @import "../../../../../mixins/portfolio/static/css/portfolio.css";
  @import "../../../../../mixins/team/static/css/team.css";
}

@layer theme {
  /* Harmonise styling across all mixins */
  .blog-post-card,
  .portfolio-card-base,
  .team-member-card {
    border-radius: var(--radius-lg);
    border: 2px solid var(--color-border);
  }
}

Dark Mode Setup

Enable dark mode in your theme:

/* themes/my-blog/static/css/src/input.css */

@layer theme {
  /* Light mode (default) */
  :root {
    --color-surface: oklch(98% 0 0);
    --color-text-primary: oklch(20% 0 0);
  }

  /* Dark mode */
  .dark {
    --color-surface: oklch(20% 0 0);
    --color-text-primary: oklch(95% 0 0);
  }
}

Toggle button (add to base template):

<button id="theme-toggle" class="btn-base btn-intent-secondary btn-size-sm">
  <span class="light-icon">🌞</span>
  <span class="dark-icon">🌙</span>
</button>

<script>
  const toggle = document.getElementById("theme-toggle");
  const html = document.documentElement;

  toggle.addEventListener("click", () => {
    html.classList.toggle("dark");
    localStorage.setItem(
      "theme",
      html.classList.contains("dark") ? "dark" : "light",
    );
  });

  // Load preference
  if (localStorage.getItem("theme") === "dark") {
    html.classList.add("dark");
  }
</script>

Building Your CSS

# Install Tailwind CLI
npm install -D tailwindcss

# Build CSS (development)
npx tailwindcss -i themes/my-blog/static/css/src/input.css \
                -o themes/my-blog/static/css/dist/output.css \
                --watch

# Build CSS (production)
npx tailwindcss -i themes/my-blog/static/css/src/input.css \
                -o themes/my-blog/static/css/dist/output.css \
                --minify

Quick Checklist

Theme setup:

  • Create theme directory
  • Add theme.yaml configuration
  • Create input.css with layers
  • Create homepage template
  • Build CSS

Customisation:

  • Override design tokens
  • Customise mixin components
  • Add dark mode styles
  • Test responsive design
  • Add custom pages

Polish:

  • Add theme logo/images
  • Test with sample content
  • Verify all links work
  • Check accessibility
  • Test dark mode

Common Issues

CSS not loading

Check:

  1. Did you build the CSS?
  2. Is the CSS path correct in your base template?
  3. Are imports in input.css correct?

Template not found

Check:

  1. Is the template in the right directory?
  2. Are you using resolve_base_template() correctly?
  3. Is the template search path configured?

Styles not applying

Check:

  1. Are you using the correct layer (@layer theme)?
  2. Did you rebuild the CSS?
  3. Are class names spelled correctly?

Next Steps

You now know how to create a basic theme. For more details:

Summary

Creating a theme:

  1. Set up structure - Directories and configuration
  2. Create input.css - Import core, mixins, add custom styles
  3. Build templates - Use mixin layouts or create custom
  4. Customise tokens - Override colours, spacing, typography
  5. Build CSS - Compile with Tailwind
  6. Test everything - Pages, responsive, dark mode

With these steps, you can create professional themes that leverage MintyFlask's mixin system.