Разработка кастомной темы Pico (Twig)
Тема Pico — директория в themes/ с Twig-шаблонами и статикой. Минимальная тема состоит из одного файла index.twig. Полная тема для реального проекта включает несколько шаблонов, частичные блоки и стили.
Структура темы
themes/my-theme/
index.twig # базовый шаблон (используется по умолчанию)
blog.twig # шаблон типа страницы blog
post.twig # шаблон отдельного поста
service.twig # шаблон страницы услуги
404.twig # страница ошибки
partials/
header.twig
footer.twig
nav.twig
meta.twig
css/
theme.css
js/
theme.js
images/
theme.yml # метаданные темы
Переменные Pico в Twig
Pico передаёт в каждый шаблон стандартный набор переменных:
{{ site_title }} {# название сайта из config.yml #}
{{ base_url }} {# корень сайта #}
{{ theme_url }} {# URL директории темы #}
{{ meta.title }} {# заголовок текущей страницы #}
{{ meta.description }} {# описание из frontmatter #}
{{ meta.date }} {# дата страницы #}
{{ meta.template }} {# имя шаблона #}
{{ content }} {# HTML-содержимое страницы #}
{{ pages }} {# массив всех страниц #}
{{ current_page }} {# объект текущей страницы #}
{{ prev_page }} {# предыдущая страница в той же директории #}
{{ next_page }} {# следующая страница #}
{{ is_front_page }} {# true если главная страница #}
Базовый шаблон index.twig
<!DOCTYPE html>
<html lang="ru">
<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>
Навигация
{# partials/nav.twig #}
<nav>
<a href="{{ base_url }}" class="{{ is_front_page ? 'active' : '' }}">
{{ site_title }}
</a>
<ul>
{% for page in 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.twig #}
<!DOCTYPE html>
<html lang="ru">
<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>
Переключение шаблона через frontmatter
---
Title: Услуги
Template: service
---
Pico ищет шаблон service.twig в теме. Если не найден — использует index.twig.
theme.yml
# themes/my-theme/theme.yml
name: My Theme
version: 1.0.0
author: Dev Name
Работа с кастомными мета-полями
Pico передаёт все frontmatter-поля в объект meta:
---
Title: Заголовок
Hero_image: hero.jpg
Show_sidebar: true
Custom_field: значение
---
{% 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 %}
Имена полей в шаблоне — нижний регистр независимо от написания в frontmatter.







