Skip to content

Grav Theme Configuration — Blueprints, Form Fields and Theme Settings

DodaTech Updated 2026-06-27 8 min read

In this tutorial, you'll learn Grav theme configuration — creating blueprints.yaml and theme.yaml files, defining form fields for the Admin panel, exposing configurable theme options, and building a settings page for your theme.

What You'll Learn

  • The role of blueprints.yaml in theme configuration
  • Defining form fields for Admin panel settings
  • The theme.yaml file for default theme values
  • Accessing theme configuration in templates
  • Creating custom theme options (colors, layouts, social links)
  • Best practices for theme configuration structure

Why It Matters

In WordPress, theme options are typically built with the Customizer API or option pages. In Grav, theme configuration is YAML-based. A blueprints.yaml file defines what settings appear in the Admin panel. A theme.yaml file stores default values. Twig templates access these values through config.themes.THEMENAME. This approach is simpler, file-based, and Git-friendly — no database storage, no admin interface code, just structured YAML.

Real-World Use

A design agency builds a customizable Grav theme for clients. The theme has options for: primary color, font family, layout (sidebar left, sidebar right, full width), social media links, footer content, and logo upload. Each option is defined in blueprints.yaml. Clients change settings through the Admin panel, and the changes are stored in theme.yaml. Templates use config.themes.mytheme.primary_color to apply the selected color. No PHP code needed for any of this.

Learning Path

flowchart LR
    A["Custom Twig Extensions"] --> B["Theme Configuration
← You are here"]:::current B --> C["Theme Assets"] C --> D["Theme Inheritance"] D --> E["Theme Languages"] E --> F["Plugin Architecture"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

theme.yaml — Default Values

Every theme should have a theme.yaml file that defines default configuration values:

user/themes/mytheme/theme.yaml:

# General
enabled: true
color_scheme: light

# Layout
layout:
    sidebar_position: right
    sidebar_width: 300
    container_width: 1200

# Colors
colors:
    primary: '#3498db'
    secondary: '#2ecc71'
    background: '#ffffff'
    text: '#333333'
    link: '#2980b9'

# Typography
fonts:
    heading: 'Inter'
    body: 'Inter'

# Social
social:
    twitter: 'https://twitter.com/dodatech'
    github: 'https://github.com/dodatech'
    linkedin: ''

# Footer
footer:
    copyright: 'Built by DodaTech'
    show_social: true

blueprints.yaml — Admin Form Fields

The blueprints.yaml file defines the form fields that appear in the Admin panel when editing the theme:

user/themes/mytheme/blueprints.yaml:

name: My Theme
version: 1.0.0
description: A customizable theme for documentation sites
icon: paint-brush
author:
    name: DodaTech
    email: support@dodatech.com
homepage: https://dodatech.com
keywords: grav, theme, documentation

form:
    validation: loose
    fields:
        enabled:
            type: toggle
            label: Theme enabled
            highlight: 1
            default: 1
            options:
                1: Enabled
                0: Disabled
            validate:
                type: bool

        color_scheme:
            type: select
            label: Color scheme
            default: light
            options:
                light: Light
                dark: Dark
                auto: Auto (follows system)

        layout.sidebar_position:
            type: select
            label: Sidebar position
            default: right
            options:
                left: Left
                right: Right
                none: No sidebar

        layout.sidebar_width:
            type: range
            label: Sidebar width (px)
            default: 300
            validate:
                min: 200
                max: 400
                step: 10

        layout.container_width:
            type: range
            label: Container width (px)
            default: 1200
            validate:
                min: 800
                max: 1600
                step: 50

        colors.primary:
            type: colorpicker
            label: Primary color
            default: '#3498db'

        colors.secondary:
            type: colorpicker
            label: Secondary color
            default: '#2ecc71'

        colors.background:
            type: colorpicker
            label: Background color
            default: '#ffffff'

        colors.text:
            type: colorpicker
            label: Text color
            default: '#333333'

        colors.link:
            type: colorpicker
            label: Link color
            default: '#2980b9'

        fonts.heading:
            type: select
            label: Heading font
            default: Inter
            options:
                Inter: Inter
                Roboto: Roboto
                'Open Sans': Open Sans
                Lato: Lato
                'Source Sans Pro': Source Sans Pro

        fonts.body:
            type: select
            label: Body font
            default: Inter
            options:
                Inter: Inter
                Roboto: Roboto
                'Open Sans': Open Sans
                Lato: Lato
                'Source Sans Pro': Source Sans Pro

        social.twitter:
            type: text
            label: Twitter URL
            size: large

        social.github:
            type: text
            label: GitHub URL
            size: large

        social.linkedin:
            type: text
            label: LinkedIn URL
            size: large

        footer.copyright:
            type: text
            label: Copyright text
            size: large

        footer.show_social:
            type: toggle
            label: Show social links in footer
            default: 1
            options:
                1: Yes
                0: No

Form Field Types

Grav Admin supports many form field types:

Type Use Example
text Single line text Site title
textarea Multi-line text Footer content
select Dropdown selection Layout choice
toggle On/off switch Enable feature
range Slider Width, count
colorpicker Color selector Primary color
file File upload Logo image
editor Rich text editor Custom CSS
hidden Invisible field Version tracking
section Visual section separator Grouping fields

Nested Field Names

Use dot notation for nested configuration values:

colors.primary:
    type: colorpicker
    label: Primary color

layout.sidebar.position:
    type: select
    label: Sidebar position

social.links.twitter:
    type: text
    label: Twitter URL

These map to config.themes.mytheme.colors.primary, config.themes.mytheme.layout.sidebar.position, etc.

Accessing Theme Config in Templates

{# Get theme configuration values #}
{% set primary = config.themes.mytheme.colors.primary %}
{% set sidebar_pos = config.themes.mytheme.layout.sidebar_position %}
{% set container_width = config.themes.mytheme.layout.container_width %}

{# Use in template #}
<style>
    :root {
        --primary: {{ primary }};
        --secondary: {{ config.themes.mytheme.colors.secondary }};
        --background: {{ config.themes.mytheme.colors.background }};
        --text: {{ config.themes.mytheme.colors.text }};
    }
</style>

<div class="container" style="max-width: {{ container_width }}px">
    <main class="content">
        {{ page.content|raw }}
    </main>

    {% if sidebar_pos != 'none' %}
    <aside class="sidebar sidebar-{{ sidebar_pos }}"
           style="width: {{ config.themes.mytheme.layout.sidebar_width }}px">
        {% include 'partials/sidebar.html.twig' %}
    </aside>
    {% endif %}
</div>

Fallback Values

Always provide fallbacks in case the config value is not set:

{% set primary = config.themes.mytheme.colors.primary|default('#3498db') %}
{% set show_social = config.themes.mytheme.footer.show_social|default(true) %}

Dynamic CSS with Theme Config

Inject theme colors as CSS custom properties:

{% do assets.addInlineCss("
:root {
    --color-primary: #{config.themes.mytheme.colors.primary|default('#3498db')};
    --color-secondary: #{config.themes.mytheme.colors.secondary|default('#2ecc71')};
    --color-background: #{config.themes.mytheme.colors.background|default('#ffffff')};
    --color-text: #{config.themes.mytheme.colors.text|default('#333333')};
    --color-link: #{config.themes.mytheme.colors.link|default('#2980b9')};
    --font-heading: '#{config.themes.mytheme.fonts.heading|default('Inter')}';
    --font-body: '#{config.themes.mytheme.fonts.body|default('Inter')}';
}
") %}

Validation Rules

Add validation to prevent incorrect values:

layout.sidebar_width:
    type: range
    label: Sidebar width (px)
    default: 300
    validate:
        min: 200
        max: 400
        step: 10
        required: true

footer.copyright:
    type: text
    label: Copyright text
    validate:
        maxlength: 200

Theme Configuration File Location

After saving settings in the Admin panel, Grav stores the values in:

user/config/themes/mytheme.yaml

This file overrides the defaults in theme.yaml. Both files are readable in templates through the same config path.

Learning Path

flowchart LR
    A["Custom Twig Extensions"] --> B["Theme Configuration
← You are here"]:::current B --> C["Theme Assets"] C --> D["Theme Inheritance"] D --> E["Theme Languages"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

Common Mistakes

  1. Not creating blueprints.yaml: Without blueprints.yaml, the Admin panel shows no settings for your theme. Users cannot customize the theme through the admin interface.

  2. Mismatched field names: The field names in blueprints.yaml must match the configuration keys in theme.yaml. A field named colors.primary maps to config.themes.mytheme.colors.primary.

  3. Forgetting to provide defaults in templates: If a theme option is not set, config.themes.mytheme.someOption returns null. Always use |default(value) in templates.

  4. Using unsupported field types: Not all form field types are available. Check the Admin plugin documentation for supported types. Using an unsupported type causes the field to not render.

  5. Overwriting user/config/themes/mytheme.yaml: This file is user-specific configuration. In a Git workflow, commit theme.yaml (defaults) but add user/config/themes/mytheme.yaml to .gitignore.

Practice Questions

  1. What is the purpose of blueprints.yaml in a theme? Answer: It defines the form fields that appear in the Admin panel when editing theme settings. Each field maps to a configuration value accessible in templates.

  2. How do you access a theme configuration value in a Twig template? Answer: Use config.themes.THEMENAME.PATH. For example, config.themes.mytheme.colors.primary accesses the primary color setting.

  3. What happens when a user saves theme settings in the Admin panel? Answer: Grav writes the values to user/config/themes/mytheme.yaml. These values override the defaults in theme.yaml but are accessed through the same config path.

  4. How do you provide a fallback for a config value in a template? Answer: Use the |default() filter: {% set primary = config.themes.mytheme.colors.primary|default('#3498db') %}.

  5. Challenge: Build a complete theme configuration for a portfolio site. Create blueprints.yaml with sections for: general settings (logo upload, favicon, enable dark mode), layout settings (grid columns: 2/3/4, container width, show sidebar), colors (primary, secondary, accent, background, text, card background), typography (heading font, body font, base font size, line height), hero section (enable hero, headline, subtitle, background image, CTA text and URL), social links (Twitter, GitHub, LinkedIn, Dribbble, YouTube), footer (copyright text, show social, columns: 1/2/3/4), and custom code (header JS, footer JS, custom CSS). Create theme.yaml with sensible defaults and access all values in templates with fallbacks.

FAQ

How do I add a file upload field to theme settings?

Use the file field type with destination and accept parameters: type: file, destination: 'theme://images/uploads', accept: ['image/png', 'image/jpeg'].

Can I create conditional fields that show/hide based on other values?

Yes, use the show_on property: show_on: { 'config.themes.mytheme.layout.sidebar_position': 'left' }. The field only appears when sidebar position is 'left'.

How do I organize a large number of theme options?

Use the section field type to group related fields. Each section acts as a collapsible group in the Admin panel, keeping the settings page organized.

What is the difference between `theme.yaml` and `user/config/themes/mytheme.yaml`?

theme.yaml ships with the theme and defines defaults. user/config/themes/mytheme.yaml is created by the Admin panel when users customize settings and overrides the defaults.

Can I validate theme configuration values?

Yes. Use the validate key with properties like min, max, required, pattern (regex), and maxlength. Invalid values are rejected when saving in the Admin panel.

Mini Project

Goal: Create a fully configurable Grav theme with 20+ options.

  1. Create theme.yaml with default configuration values
  2. Create blueprints.yaml with form fields organized in sections
  3. Include at least 6 different field types (text, select, toggle, colorpicker, range, file)
  4. Create a Twig template that reads theme config and applies it dynamically
  5. Generate CSS custom properties from theme colors
  6. Add layout options that change template structure (sidebar, columns, width)
  7. Add social links that render conditionally
  8. Add a logo upload field with fallback to text logo
  9. Add a custom code section (header/footer JS and CSS)
  10. Test that all settings render correctly and save properly

What's Next

Now your theme is configurable. Next, learn asset management:

Continue to Lesson 21: Theme Assets — CSS/JS pipeline, asset merging, Minification, and loading strategies.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro