Обложки, поиск и другая магия сборки
Обложка для соцсетей, полнотекстовый поиск и чип версии в мете статьи выглядят как рантайм-магия — но происходят целиком на сборке. Разберём все три механизма: у каждого простое устройство и одна честная оговорка.
#Обложка без дизайнера
Наивная модель: обложки для шаринга рисуют руками. На тридцатой статье это боль, а после переименования заголовка — боль вдвойне: картинка теперь врёт, перерисовывай.
Здесь обложки генерирует сборка, а внешний вид описывает стиль — папка в assets/og/, имя папки = ключ стиля. Внутри два обязательных файла: base.png — подложка 1200×630, где живёт вся графика, и layout.toml — раскладка текстовых блоков поверх неё:
[[block]]
text = "{title}"
font = "Inter-ExtraBold.ttf"
size = 66
color = "#e6dfd2"
x = 76
y = 270
width = 1010 # автоперенос длинных заголовковПлейсхолдеры — {title}, {kicker}, {meta}, {domain}, {part} — собирает партиал, так что стиль языко-нейтрален: тексты приходят из статьи, раскладка — из стиля. Блоки рисуются по порядку; неизвестные ключи игнорируются — старый стиль переживёт новую версию темы. Опциональный home.png — полностью запечённый постер для главной и рубрик; если его нет, общая обложка генерируется тем же пайплайном из base.png. А папка без layout.toml стилем просто не считается.
Выбирается стиль параметром сайта ogImages.style, для отдельной страницы — og_style во front matter. А поверх любого стиля работает авторский оверрайд: положи og.png в бандл статьи — и генератор уступит место твоей картинке. Свой стиль сайт добавляет папкой: ассеты темы и сайта мержатся, сайт побеждает.
Текст на обложке рисует движок картинок Hugo, и у него есть пределы: межбуквенный интервал не поддерживается (аппроксимируется кеглем), а выравнивание вправо работает только для моноширинных шрифтов — ширина строки считается по числу символов. Дизайнь стили с этим в голове, а не выясняй на готовой обложке.
#Поиск, который читает всё
Наивная модель поиска на статике — индекс из заголовков и описаний: он найдёт статью по названию и промолчит про термин из середины текста. Здесь движок другой: Pagefind строит полнотекстовый индекс по уже собранному сайту. Поэтому сборка — две команды:
полная сборка сайтаhugo && pagefind --site publicPagefind — standalone-бинарь, Node не нужен. Он проходит по готовому HTML и берёт только то, что размечено темой: тело статьи — целиком, крошки и минуты для карточки результата — отдельными метаданными, а панели, виджеты и прочий хром в индекс не попадают вовсе. Код-блоки индексируются с пониженным весом: поиск по имени функции обязан работать, но код не должен забивать выдачу. Русская морфология — из коробки, по языку страницы: запрос «обложек» найдёт «обложка». Скрипт поиска и сам индекс грузятся лениво, при первом открытии модалки: пока не искал — не скачивал ни байта.
Проверь на этой странице: открой поиск и набери слово из середины текста — например, «кегль». Найдётся, с подсветкой и выдержкой. А если запустить голый hugo server без второй команды, модалка честно подскажет, чем индекс собирается, — тихо, без красных ошибок.
Все три механизма — build-time. Браузеру достаются готовая картинка, готовый индекс и готовая строка; в рантайме никто ничего не вычисляет и ни к каким сервисам не ходит.
#Чип версии
В мете этой статьи стоит чип «v0.0.1». Тема понятия не имеет, что это значит: чип — произвольная строка из version во front matter, а если поля нет — из versionDefault сайта. Сайт про язык программирования напишет туда версию языка, сайт про базу данных — «PostgreSQL 17», а это демо пишет версию самой темы — и тема рендерит всё одинаково, как есть. Маленькая деталь, но именно из таких складывается агностичность: тема не знает, о чём твой сайт.
Теперь ты можешь собрать сайт с обложками и полнотекстовым поиском двумя командами — и объяснить, почему команд именно две.
#Что дальше
Это был взгляд снаружи — что сборка делает для читателя. Взгляд изнутри — как устроены сами страницы — в серии «Анатомия темы»: про папки-бандлы, арифметику серий и палитры-файлы.