Development of Custom Pico Theme (Twig)
A Pico theme is a directory in themes/ with Twig templates and static files. Minimal theme consists of single index.twig file. Full theme for real project includes several templates, partial blocks, and styles.
Theme Structure
themes/my-theme/
index.twig # base template (used by default)
blog.twig # blog page type template
post.twig # single post template
service.twig # service page template
404.twig # error page
partials/
header.twig
footer.twig
nav.twig
meta.twig
css/
theme.css
js/
theme.js
images/
theme.yml # theme metadata
Pico Variables in Twig
Pico passes standard variable set to each template:
{{ site_title }} {# site name from config.yml #}
{{ base_url }} {# site root #}
{{ theme_url }} {# theme directory URL #}
{{ meta.title }} {# current page title #}
{{ meta.description }} {# description from frontmatter #}
{{ meta.date }} {# page date #}
{{ meta.template }} {# template name #}
{{ content }} {# HTML page content #}
{{ pages }} {# array of all pages #}
{{ current_page }} {# current page object #}
{{ prev_page }} {# previous page in same dir #}
{{ next_page }} {# next page #}
{{ is_front_page }} {# true if homepage #}
Base Template index.twig
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
{{ meta.title ? meta.title ~ ' | ' : '' }}{{ site_title }}
</title>
{% if meta.description %}
<meta name="description" content="{{ meta.description }}">
{% endif %}
<link rel="stylesheet" href="{{ theme_url }}/css/theme.css">
</head>
<body class="{{ meta.template ?: 'default' }}{% if is_front_page %} front-page{% endif %}">
{% include theme_url ~ '/partials/nav.twig' %}
<main>
<h1>{{ meta.title }}</h1>
{{ content }}
</main>
{% include theme_url ~ '/partials/footer.twig' %}
<script src="{{ theme_url }}/js/theme.js" defer></script>
</body>
</html>
Navigation
{# partials/nav.twig #}
<nav>
<a href="{{ base_url }}" class="{{ is_front_page ? 'active' : '' }}">
{{ site_title }}
</a>
<ul>
{% for page in pages %}
{# Show only first-level pages #}
{% if page.id|split('/')|length == 1 and not page.hidden %}
<li>
<a href="{{ page.url }}"
class="{{ current_page.id starts with page.id ? 'active' : '' }}">
{{ page.title }}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</nav>
Blog Template with Post List
{# blog.twig #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ meta.title }} | {{ site_title }}</title>
<link rel="stylesheet" href="{{ theme_url }}/css/theme.css">
</head>
<body class="blog-index">
{% include theme_url ~ '/partials/nav.twig' %}
<main class="container">
<h1>{{ meta.title }}</h1>
{{ content }}
<div class="posts-grid">
{% for page in pages %}
{% if page.id starts with 'blog/' and page.id != 'blog/' and not page.hidden %}
<article class="post-card">
{% if page.meta.date %}
<time datetime="{{ page.meta.date|date('Y-m-d') }}">
{{ page.meta.date|date('d.m.Y') }}
</time>
{% endif %}
<h2><a href="{{ page.url }}">{{ page.title }}</a></h2>
{% if page.meta.description %}
<p>{{ page.meta.description }}</p>
{% endif %}
</article>
{% endif %}
{% endfor %}
</div>
</main>
{% include theme_url ~ '/partials/footer.twig' %}
</body>
</html>
Template Switching via Frontmatter
---
Title: Services
Template: service
---
Pico looks for service.twig template in theme. If not found — uses index.twig.
theme.yml
# themes/my-theme/theme.yml
name: My Theme
version: 1.0.0
author: Dev Name
Working with Custom Meta Fields
Pico passes all frontmatter fields to meta object:
---
Title: Heading
Hero_image: hero.jpg
Show_sidebar: true
Custom_field: value
---
{% if meta.hero_image %}
<img src="{{ base_url }}/content/services/{{ meta.hero_image }}" alt="{{ meta.title }}">
{% endif %}
{% if meta.show_sidebar %}
{% include theme_url ~ '/partials/sidebar.twig' %}
{% endif %}
Field names in template are lowercase regardless of frontmatter spelling.







