Шаблоны

Шаблон — HTML-файл, который определяет внешний вид страницы. Один файл в папке _layouts/ — и готово.

Быстрый старт

Шаг 1. Создайте файл _layouts/my-page.html:

<!DOCTYPE html>
<html>
<head>
  <title>{{ note.Title() }}</title>
</head>
<body>
  <h1>{{ note.Title() }}</h1>
  {{ note.HTMLString() | unsafe }}
</body>
</html>

Шаг 2. Укажите шаблон в заметке:

---
layout: my-page
title: Моя страница
---

Текст страницы в markdown.

Готово. Страница использует ваш шаблон.

Что доступно в шаблоне

note — текущая заметка

{{ note.Title() }}         — заголовок из frontmatter
{{ note.HTMLString() }}    — весь контент как HTML
{{ note.Permalink() }}     — URL страницы
{{ note.ReadingTime() }}   — время чтения в минутах
{{ note.PathID() }}        — уникальный ID для data-атрибутов

note.M() — доступ к frontmatter

{{ note.M().GetString("author", "Unknown") }}
{{ note.M().GetInt("version", 1) }}
{{ note.M().GetBool("featured", false) }}
{{ note.M().Has("custom_field") }}

nvs — доступ к другим заметкам

{{ sidebar := nvs.ByPath("/_sidebar.md") }}
{{ if sidebar }}
  {{ sidebar.HTMLString() | unsafe }}
{{ end }}

{{ about := nvs.ByPermalink("/about") }}

asset() — подключение файлов

<link rel="stylesheet" href="{{ asset("style.css") }}">
<script src="{{ asset("app.js") }}"></script>

PartialRenderer — контент по частям

PartialRenderer разбирает markdown на логические блоки. Полезно для лендингов, FAQ, карточек.

Introduce() — вступление

Возвращает контент до первого заголовка:

{{ intro := note.PartialRenderer().Introduce() }}
<div class="intro">
  {{ intro.ContentHTML | unsafe }}
</div>

Sections(level) — секции по уровню заголовков

Собирает секции под заголовками нужного уровня. Каждая секция содержит:

  • TitleHTML — текст заголовка (без тега <h3>)
  • ContentHTML — контент до следующего заголовка того же или выше уровня

Пример: FAQ из markdown

Часто задаваемые вопросы о сервисе.

### Как начать работу?

Зарегистрируйтесь и создайте первый проект.

### Сколько стоит?

Базовый тариф бесплатный.

### Есть ли API?

Да, документация на сайте.

Шаблон:

{{ intro := note.PartialRenderer().Introduce() }}
<p class="lead">{{ intro.ContentHTML | unsafe }}</p>

<div class="faq">
  {{ range s := note.PartialRenderer().Sections(3) }}
    <details>
      <summary>{{ s.TitleHTML | unsafe }}</summary>
      <div>{{ s.ContentHTML | unsafe }}</div>
    </details>
  {{ end }}
</div>

Section(title) — секция по заголовку

Находит конкретную секцию по тексту заголовка:

{{ faq := note.PartialRenderer().Section("FAQ") }}
{{ if faq }}
  <div class="faq-section">
    {{ faq.ContentHTML | unsafe }}
  </div>
{{ end }}

Пример: карточки фич

<div class="features-grid">
  {{ range s := note.PartialRenderer().Sections(3) }}
    <div class="feature-card">
      <h3>{{ s.TitleHTML | unsafe }}</h3>
      {{ s.ContentHTML | unsafe }}
    </div>
  {{ end }}
</div>

Фильтр unsafe

HTML экранируется по умолчанию. Чтобы вывести разметку — добавьте | unsafe:

{{ note.HTMLString() | unsafe }}
{{ b.ContentHTML | unsafe }}

Без фильтра теги отобразятся как текст: &lt;p&gt;...

Вложенные asset-зависимости

Если шаблон импортирует другой файл с asset(), движок может не увидеть зависимость. Обходное решение — добавить комментарий:

{{ import "blocks" }}

<!-- {{ asset("style.css") }} -->

{{ yield main_layout() content }}
  ...
{{ end }}

Комментарий не попадает в HTML, но зависимость подхватится.

Синтаксис Jet

Шаблоны используют движок Jet:

{{ переменная }}                       — вывод
{{ if условие }}...{{ end }}           — условие
{{ range item := список }}...{{ end }} — цикл
{{ block имя() }}...{{ end }}          — определение блока
{{ yield имя() }}                      — вызов блока
{{ include "путь" данные }}            — вставка шаблона

Подробнее — в документации Jet.

Выборка и сортировка заметок — в запросах к заметкам.

Продвинутые паттерны организации шаблонов — в лучших практиках.