Skip to content

Grav Themes & Customization — From Quark to Your Own Design

DodaTech Updated 2026-06-27 5 min read

In this tutorial, you'll learn how theming works in Grav — from tweaking the default Quark theme to creating a fully custom design.

What You'll Learn

  • How Grav's theme system works
  • How to create a child theme
  • How to override templates and CSS
  • Theme configuration and inheritance
  • Installing themes via GPM

Why It Matters

Every site needs its own look. A default theme tells visitors "this is a template." A custom theme tells them "this is a professional product." Grav's theme system lets you override just the files you need — full control without rewriting everything.

Real-World Use

DodaTech's product documentation sites each have a branded theme built as a child of Quark. The child theme overrides the color scheme, fonts, and header layout but inherits all the responsive grid and typography from the parent. When Quark updates, the child theme gets bug fixes automatically.

The Default Theme: Quark

Grav ships with the Quark theme — a clean, Responsive Design based on the Spectre CSS framework.

Location: user/themes/quark/

quark/
├── templates/          # Twig templates
│   ├── partials/       # Reusable partials (header, footer)
│   ├── default.html.twig
│   ├── blog.html.twig
│   └── item.html.twig
├── css/                # Compiled CSS
├── js/                 # JavaScript
├── images/             # Theme images
├── blueprints.yaml     # Theme configuration definition
└── quark.yaml          # Default theme config

To set Quark as your theme, user/config/system.yaml should have:

pages:
    theme: quark

Creating a Child Theme

Never edit the parent theme directly. Updates will overwrite your changes. Instead, create a child theme.

Step 1: Create the Folder Structure

user/themes/
└── mytheme/
    ├── templates/
    ├── css/
    ├── js/
    ├── blueprints.yaml
    └── mytheme.yaml

Step 2: Create blueprints.yaml

user/themes/mytheme/blueprints.yaml:

name: My Theme
version: 1.0.0
description: Child theme of Quark
author:
    name: Your Name
    email: you@example.com
extends: quark

Step 3: Create mytheme.yaml

user/themes/mytheme/mytheme.yaml:

enabled: true

Step 4: Activate the Theme

In user/config/system.yaml:

pages:
    theme: mytheme

Clear cache and refresh:

bin/grav cache

Your site should now use mytheme — it looks identical to Quark because that's the parent.

Step 5: Override Specific Templates

Copy only the templates you want to change from user/themes/quark/templates/ to user/themes/mytheme/templates/:

# Copy the header partial to your child theme
cp user/themes/quark/templates/partials/header.html.twig \
   user/themes/mytheme/templates/partials/header.html.twig

Edit the copy. Now your custom header renders instead of Quark's. All other templates still inherit from Quark.

Customizing CSS

Add Custom CSS

Create user/themes/mytheme/css/custom.css:

:root {
    --primary-color: #3b82f6;
    --secondary-color: #06b6d4;
    --font-family: 'Inter', sans-serif;
}

.header {
    background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
    padding: 1rem 2rem;
}

.header a {
    color: white;
    text-decoration: none;
}

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 1.5rem;
    margin: 2rem 0;
}

.card {
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 1.5rem;
    transition: box-shadow 0.2s;
}

.card:hover {
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

Include It in the Template

Copy base.html.twig from Quark to your child theme and add the CSS link:

{% block stylesheets %}
    {% do assets.addCss('theme://css/custom.css', 10) %}
    {{ parent() }}
{% endblock %}

The {{ parent() }} call includes everything the parent template includes (Quark's CSS), plus your custom CSS on top.

Template Override Order

When Grav looks for a template, it checks in this order:

  1. Page-specific template — Based on the template frontmatter field
  2. Filename-based template — Based on the .md filename
  3. Child theme templates — Your theme's templates/
  4. Parent theme templates — Quark's templates/ (if your theme extends it)
  5. Fallback: default.html.twig — If nothing else matches

This cascade means you can override at any level.

Theme Configuration

Themes can expose configuration options. In your child theme's mytheme.yaml:

enabled: true
color_scheme: dark
header_style: sticky

Access in templates:

{% if theme.color_scheme == 'dark' %}
    <body class="dark-theme">
{% endif %}

Users can override these values in user/config/themes/mytheme.yaml.

Installing Third-Party Themes

# Search for themes
bin/gpm search theme

# Install a theme
bin/gpm install afterburner2

After installation, set it as active in system.yaml:

pages:
    theme: afterburner2

Common Theming Mistakes

Mistake Symptom Fix
Editing parent theme Changes lost on update Create a child theme instead
Missing extends in blueprints Theme loads but has no templates Add extends: quark to blueprints.yaml
Cache not cleared Old template still renders bin/grav cache
CSS not loading Wrong path in template Use theme://css/custom.css
Template not found 404 or blank page Check template filename matches exactly

Learning Path

flowchart LR
  A["What is Grav?"] --> B["Installation"]
  B --> C["Pages & Content"]
  C --> D["Navigation"]
  D --> E["Twig Templating"]
  E --> F["Themes
← You are here"]:::current F --> G["Taxonomy & Blog"] G --> H["Plugins & Admin"] H --> I["Configuration & Caching"] I --> J["Deployment & Maintenance"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

Practice Questions

  1. Why should you use a child theme instead of editing Quark directly? Answer: Updates to Quark will overwrite your changes. A child theme inherits from Quark but keeps your customizations separate.

  2. How does Grav find a template when rendering a page? Answer: It checks page-specific templates first, then filename-based, then child theme, then parent theme, and falls back to default.html.twig.

  3. How do you add custom CSS to a child theme? Answer: Create a .css file in css/, then add it via {% do assets.addCss('theme://css/custom.css') %} in the base template.

  4. What does {{ parent() }} do in a Twig block? Answer: It renders the parent theme's block content, allowing you to add to the block rather than replace it entirely.

  5. Challenge: Create a child theme that changes the header background to a gradient, adds a custom font, and displays a logo in the top-left corner. Override only the minimum templates needed.

What's Next

Your site has a unique look. Now let's add blog functionality:

Continue to Lesson 7: Taxonomy & Blogging — Tags, categories, and blog listing pages.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro