Палитра — это один файл
Пикер в шапке этой страницы умеет несколько палитр — и ни одна из них не захардкожена ни в CSS, ни в JS. Каждая палитра — это toml-файл, из которого сборка генерирует всё остальное. Разберём, как это устроено, и перекрасим страницу прямо из статьи.
#Своя тема без форка
Наивная модель: хочу перекрасить сайт под себя — открою CSS темы и поменяю цвета. Боль наступает при первом же обновлении темы: твои правки лежат в её файлах, и каждый апдейт — это merge-конфликт. Классический выбор «форкни и отстань от апстрима — или терпи чужие цвета». Оба варианта плохие, и оба растут из одной ошибки: цвет считается оформлением, хотя на самом деле это данные — таблица «имя переменной → значение», которой просто негде лежать, кроме чужого CSS.
#Один toml
Здесь палитра — это данные, а не стили. Каждая — файл data/themes/<id>.toml: пара служебных полей плюс цветовые переменные, у которых ключи — буквально имена CSS-переменных без --:
name = "Моя" # имя в пикере
weight = 80 # позиция в списке пикера
"bg-deep" = "#101418"
"bg-surface" = "#1a2027"
"border" = "#2b3138"
"text-primary" = "#d8dee6"Подсвечены единственные две строки, которые не являются CSS-переменными. Всё остальное — прямая таблица «имя → цвет». Ключевые из них:
| Ключ | Что красит |
|---|---|
bg-deep | самый глубокий фон страницы |
bg-surface | поверхности: карточки, коллауты, виджеты |
border | рамки и хайрлайны |
text-primary | основной текст |
text-muted | подписи, меты, второстепенное |
На сборке партиал head/palettes.html превращает эти файлы в CSS: блок :root { … } для палитры по умолчанию плюс по блоку [data-theme="<id>"] { … } на каждую — и вливает результат первым в общий CSS-бандл. Какая палитра дефолтная — решает параметр сайта defaultTheme: это просто id одного из файлов. Переключение темы — смена одного атрибута data-theme на <html>; каскад делает остальное. Поэтому же смена мгновенная: браузеру не нужно ничего дозагружать, все палитры уже в CSS, который он получил с первым байтом страницы.
В прототипе темы палитры жили JS-таблицей: поповер «Вид» держал массив цветов и подменял переменные через style.setProperty прямо на <html>. При переезде таблицу разобрали на toml-файлы, а генерацию CSS увезли на сборку. Теперь JS значений палитр не знает вовсе — он только читает их список.
#Пикер и его JSON
Откуда пикер знает, какие палитры есть? Из тех же данных: партиал кладёт на страницу <script type="application/json" id="dg-themes"> со списком {id, name, group, weight, свотч-цвета} — свотчи собираются из четырёх фиксированных ключей палитры (bg-deep, bg-surface, text-primary, text-muted), так что миниатюра в пикере честно показывает будущий холст, карточку и текст. Поповер в шапке читает этот список и рисует кнопки. Виджет ниже читает тот же самый список — и по клику ставит тот же самый атрибут:
Заметь честную разницу: виджет меняет только атрибут, а пикер ещё и записывает выбор в хранилище — поэтому после перезагрузки победит его версия. И применяется сохранённый выбор до первой отрисовки, крошечным инлайн-скриптом в <head>: страница ни на кадр не вспыхивает дефолтной палитрой.
Теперь главное — расширяемость. Сайту нужна фирменная палитра? Положи data/themes/mine.toml на стороне сайта: Hugo мержит данные темы и сайта (сайт побеждает при совпадении имён), и файл сам попадёт и в CSS, и в пикер. Не нравится палитра из комплекта темы — переопредели её файл с disabled = true, и она исчезнет отовсюду. Ни строчки в теме, ни форка.
Теперь ты можешь добавить сайту собственную палитру одним файлом — не трогая ни CSS, ни JS, ни шаблоны темы.
#Что дальше
Серия «Анатомия темы» на этом закончена: гайд — папка, серия — папка гайдов, палитра — файл. Всё показанное — и десяток компонентов, до которых мы не дошли, — собрано на одной странице-полигоне:
Полигон → Kitchen-sink Все компоненты темы на одной странице — смотреть и копироватьА про то, что происходит на сборке — обложки, поиск, чип версии, — есть отдельный разбор в «Приёмах».