Стартовый Jekyll шаблон для создания статических сайтов

Создадим стартовый Jekyll шаблон на основе которого в дальнейшем можно будет разрабатывать статические сайты. В работе будут использованы такие технологии как html, scss, webp, npm.

Оглавление статьи

  1. Почему Jekyll
  2. Базовая установка
  3. Создание проекта
  4. Базовая настройка
  5. Архитектура шаблонов
  6. Страницы, статьи и коллекции
  7. Автоматизация создания различных типов записей
  8. Шаблоны для типов записей
  9. Включаемые области и пользовательские данные
  10. Ресурсы проекта
  11. Seo оптимизация
  12. Постраничная пагинация
  13. Изображения на сайте
  14. Подведем итоги

Почему Jekyll

Jekyll - это генератор статических страниц, который позволяет создавать веб-сайты, состоящие из статических HTML-файлов, без необходимости использования серверных технологий или баз данных. Благодаря чему вместо покупки даже самомального виртуального хостинга можно использовать бесплатный GitHub Pages.

По функциональности он практически не уступает тем же самым CMS системам для введения блога, но на много производительней и прост в освоении. Идеальный вариант для создания новостных порталов, блогов и портфолио. В частности данный сайт написан на Jekyll.

С официальным источником Jekyll можно ознакомиться здесь.

Базовая установка Jekyll

Полная инструкция по установке Jekyll описана на официальном сайте, здесь же я приведу пример установки на дистрибутивы семейства Debian.

Установите Ruby и других необходимых зависимостей.

BASH
sudo apt install ruby-full build-essential zlib1g-dev

Настройки для учетной записи пользователя. Введите в терминале представленные команды попорядку.

BASH
echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc
echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc
echo 'export PATH="$HOME/gems/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Установка Jekyll и Bundler.

BASH
gem install jekyll bundler

Создание проекта Jekyll

Полную инструкцию по создании проекта Jekyll описана на официальном сайте.

Создадим Jekyll проект в каталоге mysite. Для этого в терминале введем следующую команду.

BASH
jekyll new mysite

Переходим в каталог mysite.

BASH
cd mysite

Устанавливаем расширения прописанные в файле Gemfile.

BASH
bundle install

Запускаем проект в режиме разработки.

BASH
bundle exec jekyll serve

Переходим в браузере по ссылке http://localhost:4000. Так выглядит стандартный шаблон Jekyll, который в рамках этой статьи мы переделаем и улучшим.

Стандартный шаблон Jekyll
Так выглядит стандартный шаблон Jekyll.

Базовая настройка Jekyll

Подкорректируем конфигурационные файлы Jekyll для дальнейшей работы.

Gemfile конфиг

Gemfile используется для управления зависимостями и плагинами в вашем проекте. Здесь мы будем указывать дополнительный функционал для нашего шаблона например пагинацию, конвертацию изображений в webp формат и т.п.

Откроем файл Gemfile и отредактируем его следующим образом.

GEMFILE
# Указывает источник, из которого следует загружать расширения для проекта.
source "https://rubygems.org"

# Версия jekyll
gem "jekyll", "~> 4.3.2"

# Используемые в проекте расширения
group :jekyll_plugins do
  # RSS
  # https://github.com/jekyll/jekyll-feed
  gem "jekyll-feed", "~> 0.12"
end

# Добавление расширений, которые предоставляют информацию и возможность работы с часовыми поясами (timezone).
# Они полезны, когда вам нужно работать с датами и временем в разных часовых поясах.
platforms :mingw, :x64_mingw, :mswin, :jruby do
  gem "tzinfo", ">= 1", "< 3"
  gem "tzinfo-data"
end

# Ускоритель производительности для просмотра каталогов в Windows
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]

# Заблокирует расширение `http_parser.rb` на версии `0.6.x` в сборках JRuby,
# поскольку более новые версии расширения не имеют аналога Java
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]

Здесь мы удалили gem "minima", "~> 2.5", который отвечает за установку и использования шаблона minima по умолчанию. В рамках статьи мы будем создавать свой собственный шаблон.

_config.yml конфиг

Содержит системные, пользовательские глобальные переменные, а так же настройки для установленных расширений. Доступ к глобальным переменным будет осуществляться так: {{ site.myvariable }}.

Откроем файл _config.yml и отредактируем его следующим образом.

YML
title: Основной заголовок сайта
email: your-email@example.com
description: Основное описание сайта
baseurl: "" # подпуть вашего сайта, например. /blog
url: "" # базовое имя хоста и протокол вашего сайта, например. http://example.com

# Подключение плагинов
plugins:
  - jekyll-feed

# Исключение файлов и каталогов из сборки
# Файлы и каталоги которые не попадут в каталог _site
# exclude:
#   - .sass-cache/
#   - .jekyll-cache/
#   - gemfiles/
#   - Gemfile
#   - Gemfile.lock
#   - node_modules/
#   - vendor/bundle/
#   - vendor/cache/
#   - vendor/gems/
#   - vendor/ruby/

Здесь мы удалили twitter_username и github_username, это пользовательские переменные, используемые в шаблоне minima. Если в рамках нашего шаблона нужно будет создать ссылки на социальные сети, то мы их добавим позже.

Так же удалили подключение шаблона theme: minima.

Установим зависимости

Для того чтобы установить все расширения и зависимости, указанные в файле Gemfile выполним в терминале команду.

BASH
bundle install

На экране должно отобразиться примерно следующее уведомление.

Bundle complete! 6 Gemfile dependencies, 32 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

Запустим проект в режиме разработки.

BASH
bundle exec jekyll serve

Перейдем по ссылке http://127.0.0.1:4000, где нас встретит пустая страница. На этом базовую настройку и подготовку к дальнейшей работе над шаблоном мы завершили.

Архитектура шаблонов Jekyll

В этом разделе мы познакомимся с тем как устроенна архитектура шаблонов Jekyll, заложим фундамент для дальнейшего создания разделов сайта.

Структура проекта по умолчанию после установки выглядит так.

BASH
./
├── _posts
│    └── 2023-09-03-welcome-to-jekyll.markdown
├── .editorconfig
├── .gitattributes
├── .gitignore
├── 404.html
├── _config.yml
├── about.markdown
├── Gemfile
├── Gemfile.lock
├── index.markdown
└── README.md

Я специально не включил некоторые каталоги такие как _site, .jekyll-cache и т.п. Так как они создаются непосредственно при сборке проекта и не участвуют в его создании.

Jekyll умеет работать как с файлами формата .markdown или .md, так и с форматом .html. По умолчанию в корне проекта располагаются файлы 404.html, about.markdown и index.markdown. Которые отвечают за отображение страниц "404", "О нас" и "Главная страница" соответственно. Это статические страницы используемые на сайте.

Как видно из примера статические страницы можно создавать в корне шаблона, но в дальнейшем когда проект будет разрастаться, искать нужные страницы вперемешку с конфигурационными файлами будет не удобно. Поэтому я предлагаю создать отдельный каталог в корне шаблона и назвать его _pages. Нижний прочерк _ в названии файлов и каталогов, указывает Jekyll не перемещать их в каталог _site, который хранит собранный проект.

Создадим в корне проекта каталог _pages и переместим в него файлы 404.html, about.markdown и index.markdown. Должно получиться следующее.

BASH
./_pages/
├── 404.html
├── about.markdown
└── index.markdown

Далее укажем Jekyll, чтобы он при сборке проекта просматривал файлы внутри каталога _pages. Для этого откроем файл _config.yml и перед конструкцией exclude добавим.

YML
# Включение файлов и каталогов в сборку
include:
  - _pages

Перезапустим проект в режиме разработки. Теперь если обновить главную страницу в браузере, то мы увидим вместо пустой страницы, файлы содержащиеся в каталоге _site. Так произошло потому что Jekyll при сборке проекта не нашел index файлы в корне проекта, так как мы его перенесли. Чтобы исправить этот момент, откроем файлы 404.html, about.markdown и index.markdown и добавим настройку permalink.

INDEX.MARKDOWN
---
layout: home
permalink: /
---
ABOUT.MARKDOWN
---
layout: page
title: About
permalink: /about
---
404.HTML
---
layout: default
permalink: /404
---

Теперь страницы по указанным нами ссылкам должны успешно отобразиться.

Страницы, статьи и коллекции

В Jekyll для отображения контента имеются несколько типов записей.

  • _pages - страницы отображают контент, который не имеет категорий.
  • _posts - страницы отображают контент с статьями, который имеет категории.
  • collections - страницы отображают контент с пользовательским типом записи.

Любой тип записи имеет некоторую конструкцию настройки, которая позволяет парсеру Jekyll правильно собрать страницу. Так как Jekyll выступает так же в роле шаблонизатора, то нам не нужно дублировать одну и туже разметку на всех страницах это позволяет сконцентрироваться только на контенте при заполнении содержимого страницы.

Расширение jekyll-sitemap

Расширение автоматически создает на основе имеющегося контента sitemap.xml файл, который в последствии поможет в seo продвижении.

Перед созданием типов записей установим расширение jekyll-sitemap с которым более детально можно ознакомиться на официальном сайте. Откроем файл Gemfile и добавим в группу плагинов расширение.

GEMFILE
# Добавляет sitemap
# https://github.com/jekyll/jekyll-sitemap
gem "jekyll-sitemap"

Установим новое расширение jekyll-sitemap.

BASH
bundle install

Добавим расширение jekyll-sitemap в список плагинов в файле _config.yml.

YML
# Подключение плагинов
plugins:
  - jekyll-feed
  - jekyll-sitemap

Тип записи "Страницы"

Детальное описание по работе со страницами описана в официальной документации Jekyll.

Мы уже знаем, что страницы находятся в каталоге _pages и если возникнет необходимость в создании новой страницы, то нам достаточно создать новый файл в каталоге _pages в формате .md или .html. Формат markdown и md это синонимы одного и того же типа файла, поэтому если есть надобность в использовании формата .md, то лучше сразу давать такой тип файлу вместо более длинного markdown.

Для страниц я предпочитаю работать с форматом html, но в принципе так же можно оставить формат md, так как в Jekyll умеет парсить html теги внутри md файла. Каждому файлу в каталоге _pages я изменю расширение с markdown на html, вы же если хотите, можете укоротить расширение до md.

BASH
./_pages/
├── 404.html
├── about.html
└── index.html

Откроем файл index.html и отредактируем его.

HTML
---
layout: home
title: Главная страница шаблона
description: Описание главной страницы шаблона
permalink: /
sitemap: true
---

<h1>Главная страница</h1>

<p>Пример главной страницы</p>

То что находится между разделителями --- --- является конфигурацией для шаблонизатора, используя указанные в них параметры он будет собирать страницу. Ниже разделителя располагается сам контент страницы.

Рассмотрим каждый параметр отдельно.

  • layout - название шаблона, который будет использоваться как основа страницы. Более подробно про шаблон будет ниже.
  • title - заголовок страницы, который переопределяет стандартный заголовок прописанный в _config.yml
  • description - описание страницы, которое переопределяет стандартное описание прописанное в _config.yml
  • permalink - ссылка на страницу, так как страницы были перенесены в отдельный каталог _pages, то нужно всегда указывать этот параметр
  • sitemap - является параметром расширения jekyll-sitemap и отвечает за карту сайта. По умолчанию всегда включен для всех страниц, если необходимо исключить какую либо страницу из sitemap.xml, то нужно указать явно этот параметр sitemap: false для каждой такой страницы.

Тип записи "Статьи"

Детальное описание по работе со статьями описана в официальной документации Jekyll.

Тип записи Статьи находятся в каталоге _posts и если возникнет необходимость в создании новой записи, то нужно создать новый файл в каталоге _posts в формате .md или .html.

Для файлов с постами так же как и для страниц, выбираем формат html или md, я оставлю md.

BASH
./_posts/
└── 2023-09-03-welcome-to-jekyll.md

Как мы можем заметить название поста отличается от названия страницы, в нем присутствует приставка в виде даты создания 2023-09-03-welcome-to-jekyll.html. Это не обязательно, но необходимо для создания архивных страницы. Поэтому рекомендую придерживаться этого правила в названии файлов этого типа записи.

Так же в будущем постов может стать очень много и хорошо бы их структурировать по годам и месяцам. Для этого создадим внутри каталога _posts, каталог с годом записи 2023, а внутри каталога 2023 создадим каталог с числом месяца 09. Переместим в него файл с постом, должно получиться так.

BASH
./_posts/
└── 2023
  └── 09
    └── 2023-09-03-welcome-to-jekyll.html

Откроем файл 2023-09-03-welcome-to-jekyll.html и изменим его содержимое.

MD
---
layout: post
title:  "Добро пожаловать в Jekyll!"
date:   2023-09-03 12:02:32 +0600
description: Тестовая вступительная запись
category: jekyll
tags: [jekyll, html, css]
published: true
sitemap: true
excerpt_separator: "<!--more-->"
---

Тестовая вступительная запись

<!--more-->

Вы найдете этот пост в своем каталоге `_posts`. Отредактируйте его и перестройте сайт, чтобы увидеть изменения. Вы можете перестроить сайт разными способами, но наиболее распространенным способом является запуск `jekyll serve`, который запускает веб-сервер и автоматически восстанавливает ваш сайт при обновлении файла.

Jekyll требует, чтобы файлы сообщений блога имели имена в следующем формате:

`YEAR-MONTH-DAY-title.md`

Где `Год` — четырехзначное число, `Месяц` и `День` — двузначные числа, а `md` — это расширение файла, представляющее формат, используемый в файле. После этого добавьте необходимую вступительную часть. Взгляните на источник этого поста, чтобы получить представление о том, как это работает.

Jekyll также предлагает мощную поддержку фрагментов кода:

def print_hi(name)
  puts "Hi, #{name}"
end
print_hi('Tom')
#=> prints 'Hi, Tom' to STDOUT.
Проверьте [Jekyll docs][jekyll-docs] для получения дополнительной информации о том, как максимально эффективно использовать Jekyll. Сообщите обо всех ошибках/запросах на новые функции по адресу [Jekyll’s GitHub repo][jekyll-gh]. Если у вас есть вопросы, вы можете задать их на [Jekyll Talk][jekyll-talk]. [jekyll-docs]: https://jekyllrb.com/docs/home [jekyll-gh]: https://github.com/jekyll/jekyll [jekyll-talk]: https://talk.jekyllrb.com/

Для наполнения контента содержимым используется Markdown разметка. С большинством параметров мы уже знакомы, рассмотрим новые для нас.

  • date - дата добавления статьи
  • category - название категории к которой относится статья, может быть во множественном числе, например categories: [раз, два], можно создать результирующую страницу с записями определенной категории
  • tags - что-то на подобии категорий только без результирующей страницы
  • published - статус публикации статьи, если false страница не будет отображаться
  • permalink - для статей можно не указывать, он будет автоматически сгенерирован согласно шаблону который мы в дальнейшем настроем
  • excerpt_separator - разделитель между коротким описанием и полным описание статьи

Для того чтобы переопределить стандартный вид ссылки на статью, нужно будет добавить параметр permalink в файл _config.yml. Более подробно о ссылках читайте в официальной документации.

YML
permalink: /blog/:name-:categories.html

В данном примере ссылка на любую статью будет начинаться со слова blog затем имя файла со статьей - название категории и заканчиваться ссылка будет форматом .html. В принципе можно не добавлять формат .html, но браузер Firefox может тогда не открыть страницу.

Тип записи "Коллекции"

Детальное описание по работе с коллекциями описана в официальной документации Jekyll.

Тип записи Коллекции это особый вид пользовательской записи у него нет заранее определенного каталога, он нужен для того чтобы как то по-особенному сгруппировать связанный контент, например рецепты, портфолио, выступления и т.п.

Для того чтобы создать коллекцию, откроем файл _config.yml и добавим.

YML
# Коллекции
collections:
  recipe:
    output: true
    permalink: /:collection/:name-:i_day:i_month:short_year
  • collections - перечисление пользовательских коллекций
    • recipe - любое пользовательское имя коллекции, в данном примере мы будем создавать рецепты, ниже представлены параметр коллекции recipe, у каждой коллекции будут свои настройки
      • output - определяет нужно ли создавать для каждой коллекции отдельную страницу
      • permalink - шаблон ссылки на страницу коллекции, здесь у нас идет в начале ссылки имя коллекции, затем название страницы коллекции и потом день, месяц, год добавления записи.

После того как мы добавили в конфигурационный файл коллекцию давайте теперь ее создадим. Для этого в корне шаблона создаем каталог с именем нашей коллекции _recipe. Внутри данного каталога создаем файл например borscht-soup.md со следующим содержимым.

MD
---
layout: post
title: Как приготовить борщ по классическому рецепту
date: 2023-08-31 13:39 +0600
description: Налейте в кастрюлю холодную воду, выложите мясо и поставьте на средний огонь. Бульон будет вкуснее, если использовать именно мясо на кости.
published: true
sitemap: false
excerpt_separator: "<!--more-->"
---

Налейте в кастрюлю холодную воду, выложите мясо и поставьте на средний огонь. Бульон будет вкуснее, если использовать именно мясо на кости.

<!--more-->

## Начало готовки

Налейте в кастрюлю холодную воду, выложите мясо и поставьте на средний огонь. Бульон будет вкуснее, если использовать именно мясо на кости.

Перезапустим проект в режиме разработки и откроем страницу http://127.0.0.1:4000/recipe. Мы увидим ссылку на страницу с рецептом причем ее имя сгенерировалось согласно, указанному в файле _config.yml шаблону. Если перейти по этой ссылке мы должны увидеть содержимое контента файла borscht-soup.md.

Автоматизация создания различных типов записей

Можно было заметить, что создавать вручную файлы с записями не совсем удобно. Было бы здорово как-нибудь автоматизировать этот процесс. Есть одно решение, связанное с установкой определенного расширения и настройкой шаблонов для каждого типа записи.

Добавим в файл Gemfile в группу плагинов новое расширение jekyll-compose.

GEMFILE
# Добавляет команды для создания записи, страницы или черновика.
# https://github.com/jekyll/jekyll-compose
gem "jekyll-compose"

Установим новое расширение jekyll-compose.

BASH
bundle install

Откроем файл _config.yml и ниже определений коллекций добавим следующие конфигурации.

YML
# Набор правил по умолчанию для создаваемых страниц через терминал
jekyll_compose:
  auto_open: true
  default_front_matter:
    drafts:
      description:
      category:
      tags: []
      published: false
      sitemap: false
      excerpt_separator: 
    posts:
      description:
      category:
      tags: []
      published: true
      sitemap: true
      excerpt_separator: 
    recipe:
      layout: post
      description:
      excerpt_separator: 

Рассмотрим параметры по подробней.

  • jekyll_compose - настройки расширения jekyll_compose
    • auto_open - при создании файла будет автоматически открывать его в редакторе
    • default_front_matter - форматы записей
      • drafts - черновики, записи которые не будут отображаться на сайте
      • posts - статьи
      • recipe - рецепты

В настройках этой конфигурации мы можем предопределить заранее какие-то параметры, а так же добавить не стандартные пользовательские, чтобы затем использовать их на странице.

Обозначим редактор кода по умолчанию в котором будет открываться созданный файл записи, для IDE PhpStorm это будет phpstorm, для VSCode это будет code.

BASH
export JEKYLL_EDITOR=phpstorm

# Или

export JEKYLL_EDITOR=code

Список команд для манипулирования материалом на сайте.

BASH
# Создаст новую страницу
# Страницы будут создаваться в корне проекта, перенести их в каталог _page придется вручную
bundle exec jekyll page "My New Page"

# Создаст новую статью
# Статья создастся в каталоге _posts перенести ее в 2023/09 придется вручную
bundle exec jekyll post "My New Post"

# Создаст новую коллекцию
bundle exec jekyll compose "My New Post" --collection "name-collection"

# Создаст новый черновик
# Черновики создаются в каталоге _drafts
bundle exec jekyll draft "My new draft"

# Опубликует указанный черновик
bundle exec jekyll publish _drafts/my-new-draft.md

# Отправит активную статью в черновик
bundle exec jekyll unpublish _posts/2014-01-24-my-new-draft.md

Попробуйте создать любой тип записи выполнив команду в терминале. Должен создастся файл с указанным типом записи, автоматически открыться в заданном редакторе и иметь предопределенное в файле _config.yml содержимое параметров, которые останется лишь немного дополнить и подкорректировать.

На сколько это можно было сделать мы упростили себе рутину по созданию файлов с записями.

Шаблоны для типов записей

Ранее мы познакомились с таким параметров как layout в записях. Он содержал название шаблона, который используется как основа для страницы. В этом разделе мы подробнее разберем эту тему, создадим свой шаблон и применим его к записям.

В корне проекта создадим каталог _layouts. Внутри каталога _layouts создадим файл default.html. Он будет содержать базовый шаблон для сайта, от которого другие шаблоны будут в дальнейшем наследоваться.

Прежде чем приступить к заполнению шаблона default.html содержимым, расширим базовый функционал Jekyll шаблонизатора, установив расширение jekyll-contentblocks. Для этого откроем файл Gemfile и в группу плагинов добавим новое расширение.

GEMFILE
# Предоставляет механизм для передачи содержимого со страниц в их родительские макеты.
# https://github.com/rustygeldmacher/jekyll-contentblocks
gem 'jekyll-contentblocks'

Данное расширение предоставит механизм для передачи содержимого со страниц в их родительские макеты.
Установим его.

BASH
bundle install

Теперь можно приступить к созданию основного шаблона для страницы сайта. Отредактируем файл default.html следующим образом.

HTML
<!DOCTYPE html>
<html class="no-js" lang="{{ page.lang | default: site.lang | default: 'ru' }}">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>{{ page.title }}</title>

  <base href="{{ site.baseurl }}">
</head>
<body>
<div class="layout">
  {% contentblock header %}

  <main class="layout__main">
    {% contentblock content %}
  </main>

  {% contentblock footer %}
</div>

{% contentblock script %}
</body>
</html>

Конструкция {{ }} выводит содержимое переменной на странице. В данном примере {{ page.lang | default: site.lang | default: 'ru' }} будет выводиться содержимое переменной lang, которая берется внутри разделителя --- --- для текущей записи, отображаемой на странице. Если переменная lang не будет найдена, тогда будет браться содержимое глобальной переменной lang из файла _config.yml. Если такой переменной и там не будет, то по умолчанию подставится значение ru.

Конструкции создаются благодаря установленному расширению jekyll-contentblocks. Здесь мы резервируем место под определенный контент, который будет находиться внутри страниц, наследующихся от шаблона default.html. Каждому зарезервированному блоку задается уникальное имя.

В каталоге _layouts создадим еще один шаблон который будет наследоваться от default.html и назовем его base.html. Содержимое шаблона base.html будет следующим.

HTML
---
layout: default
---

{% contentfor header %}
<header>
  Шапка сайта
</header>
{% endcontentfor %}

{% contentfor content %}
  {{ content }}
{% endcontentfor %}

{% contentfor footer %}
<footer>
  FOOTER
</footer>
{% endcontentfor %}

{% contentfor script %}
<script>
  console.log('Test')
</script>
{% endcontentfor %}

Первое, что бросается в глаза это наличие разделителя --- --- внутри шаблона base.html и его отсутствие в шаблоне default.html. Так как шаблон default.html ни от кого не наследуется, а является главным родительским шаблоном, наличие блока с разделителем в нем не обязателен, хотя ни кто не мешает его добавить при необходимости использовать какие-либо пользовательские переменные.

В шаблоне base.html напротив необходимо добавить блок с разделителем и переменной layout, которая должна содержать имя файла, от которого нужно наследоваться. В данном случае у нас это layout: default так как наследование происходит от файла default.html.

Теперь давайте подключим созданные шаблоны к главной странице сайта, файл которой находится в каталоге _pages/index.html. Для этого откроем файл index.html и отредактируем его изменив параметр layout на значение base.

HTML
---
layout: base
title: Главная страница шаблона
description: Описание главной страницы шаблона
permalink: /
sitemap: true
---

<h1>Главная страница</h1>

<p>Пример главной страницы</p>

При сборке главной страницы шаблонизатором Jekyll содержимое контентной области файла index.html попадет внутрь конструкции {% contentfor content %} в файле base.html, в свою очередь содержимое блоков {% contentfor %} внутри файла base.html попадет в файл default.html в зарезервированные блоки {% contentblock %} и так со всеми блоками, на выходе получится единая цельная страница.

Следует для каждого файла внутри каталогов _pages, _posts и _recipe изменить параметр layout на base.

Включаемые области и пользовательские данные

Jekyll как и многие другие шаблонизаторы позволяет выносить крупные или повторяющиеся блоки в отдельные файлы и затем подключать их в нужных местах страницы. Что позволяет не дублировать одну и туже разметку на разных страницах сайта.

Пользовательские данные позволяют хранить большой массив данных в отдельном файле. Затем использовать и манипулировать ими на страницах.

В данном разделе, чтобы ознакомиться с этим инструментарием в Jekyll мы создадим многоуровневое навигационное меню. Которое как мне кажется должно в полной мере раскрыть эту тему.

В корне проекта создадим каталог _includes в нем будут находиться включаемые, повторяемые конструкции сайта. Так же в корне проекта создаем каталог _data в котором будут содержаться файлы с пользовательскими данными.

Каталог _data

Начнем с каталога _data, создадим в нам файл main-nav.yml. Название может быть абсолютно любым, главное чтобы было понятно, что содержит файл. В нашем случае файл main-nav.yml будет содержать вложенный список ссылок для основного навигационного меню.

YML
items:
  - title: Главная
    url: /

  - title: Рецепты
    url: /recipe

    links:
      - title: Первые блюда
        url: '#'

      - title: Вторые блюда
        url: '#'
        links:
          - title: Европейская кухня
            url: '#'
          - title: Восточная кухня
            url: '#'
          - title: Азиатская кухня
            url: '#'
          - title: Русская кухня
            url: '#'

      - title: Десерт
        url: '#'
        links:
          - title: Торты
            url: '#'
          - title: Пироги
            url: '#'
          - title: Коктейли
            url: '#'

  - title: Контакты
    url: '#'

Каталог _includes

Теперь внутри каталога _includes создадим файлы navigation.html и navigation-item.html. Файл navigation.html будет хранить основную разметку навигации. Файл navigation-item.html будет выводить вложенные ссылки. Такое разделение нам нужно, чтобы мы могли в рекурсивном цикле пройтись по всем ссылкам в файле main-nav.yml, вместо того, чтобы вручную прописывать каждые вложенные ссылки.

Чтобы подключить включаемую область в другой файл нужно использовать конструкцию {% include foo-bar.html %}, где foo-bar.html полное имя с расширением подключаемого файла. Все включаемые файлы берутся из каталога _includes. Если внутри каталога _includes будут другие каталоги, а в них уже файлы это необходимо будет отобразить, например {% include folder/foo-bar.html %}.

Содержимое файла navigation.html будет следующим.

HTML
{% if site.data.main-nav.items %}
<nav class="navigation">
  <ul class="navigation__list">
    {% include navigation-item.html items=site.data.main-nav.items %}
  </ul>
</nav>
{% endif %}

Чтобы получить доступ к данным файла main-nav.yml нужно обратиться к переменной site.data.main-nav, где site.data это служебная конструкция, а main-nav имя файлы с данными. Первым делом мы проверяем существует ли данные со ссылками и если они существуют, то выводим навигационное меню.

Затем мы подключаем шаблон navigation-item.html, который выводит ссылки навигации. В данный шаблон через пользовательскую переменную items мы передаем содержимое файла main-nav.yml.

Содержимое файла navigation-item.html будет следующим.

HTML
{% for item in include.items %}
{% assign url = page.url | replace: '/index.html', '' %}
<li class="navigation__item {% if item.url == url %}navigation__item--active{% endif %}">
  <a class="navigation__link" href="{{ site.baseurl}}{{ item.url }}">{{ item.title }}</a>
  {% if item.links %}
  <ul class="navigation__sub-list">
    {% include navigation-item.html items=item.links %}
  </ul>
  {% endif %}
</li>
{% endfor %}

Для того чтобы внутри файла navigation-item.html получить переданные ссылки необходимо обратиться к пользовательской переменной items через конструкцию include.items, где include это служебное слово, а items это имя переменной которое мы сами дали в файле navigation.html.

В примере выше мы в цикле проходимся по каждой ссылке и выводи ее данные такие как title, url. Если пункт ссылки содержит вложенность links, то мы подключаем файл navigation-item.html передавая внутрь его вложенные ссылки. Происходит рекурсия при которой файл внутри себя подключает сам себя. И так до тех пор, пока не будут выведены все вложенные ссылки.

Так же идет сравнение активной ссылки в браузере, с ссылкой текущего пункта меню {% if item.url == url %}, если они равны происходит присваивание класса navigation__item--active, чтобы по особенному можно было стилизовать активный пункт меню.

Шаблон _base.html

Мы успешно создали включаемую область на страницу в виде навигации с использованием пользовательских данных. Теперь осталось лишь отобразить навигацию на странице. Для этого откроем файл _layouts/base.html и в шапку сайта подключим навигацию.

HTML
---
layout: default
---

{% contentfor header %}
<header>
  {% include navigation.html %}
</header>
{% endcontentfor %}

{% contentfor content %}
  {{ content }}
{% endcontentfor %}

{% contentfor script %}
<script>
  console.log('Test')
</script>
{% endcontentfor %}

Зайдем на главную страницу сайта, мы должны увидеть навигационное меню со всеми, указанными ссылками в файле main-nav.yml. Теперь мы можем очень легко и удобно администрировать навигационное меню, добавляя либо удаляя ссылки не затрагивая при этом саму верстку.

Вид навигации на главной странице
Должны увидеть навигационное меню со всеми, указанными ссылками в файле main-nav.yml.

Ресурсы проекта

Какой сайт обходится без стилей, скриптов и прочих ресурсов. В этом разделе создадим соответствующие разделы и подключим вспомогательные ресурсы.

По умолчанию Jekyll не умеет работать с sass/scss стилями, для этого необходимо установить соответствующее расширение.

Расширение jekyll-sass-converter

Откроем файл Gemfile и добавим в группу плагинов расширение jekyll-sass-converter. Для более детальной настройки ознакомьтесь с официальной документацией.

GEMFILE
# Sass/Scss конвертер
# https://github.com/jekyll/jekyll-sass-converter
gem 'jekyll-sass-converter'

Установим новое расширение jekyll-sass-converter.

BASH
bundle install

После установки расширения настроим его отредактировав файл _config.yml.

YML
sass:
  sass_dir: _scss
  style: compressed
  • sass_dir - название каталога с scss стилями
  • style - параметр сжатия стилей

Иерархия файлов

Далее создадим два каталога _scss для стилей и assets для остальных файлов, такие как шрифты, картинки, скрипты и прочее.

Внутри каталога _scss продублируем иерархию файлов и их содержимое, основываясь на описанной ранее статье создание gulp сборки. За некоторым исключением, файлы main.scss и critical.scss нужно будет разместить в каталоге assets/css если этого каталога нет создаем.

BASH
./_scss
├── core
│    ├── base
│    │     ├── _base.scss
│    │     ├── _container.scss
│    │     └── _fonts.scss
│    └── helpers
│        ├── _functions.scss
│        ├── _mixins.scss
│        └── _variables.scss
└── scaffolds
    ├── components
    │    └── _button.scss
    └── sections
        └── _browser-upgrade.scss

./assets
└── css
  ├── main.scss
  └── critical.scss

Если не понятно содержимое файлов ознакомьтесь с исходным кодом Gulp борки.

Инициализция NPM

Инициализируем npm для того, чтобы можно было использовать различные сторонние решения например normalize.css .

BASH
npm init -y

Установим normalize.css.

BASH
npm i -D normalize.css

Содержимое основных файлов стилей

Содержимое файла main.scss.

SCSS
---
---
@import "core/helpers/functions";
@import "core/helpers/variables";
@import "core/helpers/mixins";

@import "core/base/fonts";

@import "scaffolds/components/button";

@import "scaffolds/sections/browser-upgrade";

Содержимое файла critical.scss.

SCSS
---
---
@import "core/helpers/functions";
@import "core/helpers/variables";
@import "core/helpers/mixins";

@import "../../node_modules/normalize.css/normalize";

@import "core/base/base";
@import "core/base/container";

//@import "scaffolds/components/name";

//@import "scaffolds/sections/name";

Обратите внимание, что в файлах main.scss и critical.scss присутствует разделитель --- ---, в данном случае он не содержит в себе переменные и необходим лишь для того, чтобы Jekyll понял что данный файл является основным и собрал его.

Запустим проект в режиме разработки.

SCSS
bundle exec jekyll serve

Откроем каталог _site который содержит собранный проект и зайдем в каталог assets/css. Как можно увидеть в каталоге содержатся два собранных файла css, которые теперь мы можем подключить на страницы.

Подключение стилей на страницах

Так как критические стили critical.css нужно будет размещать inline внутрь тега <style> хорошо было бы, чтобы его содержимое автоматически выносилось в этот тег. Для подключения одного файла внутрь другого мы уже знаем используется конструкция {% include %}, но она не умеет подключать файлы, находящиеся за пределами каталога _includes, а критические стили у нас за пределами этого каталога.

Чтобы каждый раз вручную не переносить файл critical.css из каталога _site/assets/css в каталог includes, чтобы потом его подключить. Мы добавим в наш Jekyll шаблон пару плагинов.

Нам понадобится плагин file_exists, который проверят файл на существование. И плагин root_include, который подключает файлы за пределами каталога _includes.

В корне шаблона создадим каталог _plugins и внутри данного каталога создаем два файла: file_exists.rb и include_absolute.rb.

Содержимое файла file_exists.rb.

RB
module Jekyll
    class FileExistsTag < Liquid::Tag

        def initialize(tag_name, path, tokens)
            super
            @path = path
        end

        def render(context)
            # Pipe parameter through Liquid to make additional replacements possible
            url = Liquid::Template.parse(@path).render context

            # Adds the site source, so that it also works with a custom one
            site_source = context.registers[:site].config['source']
            file_path = site_source + '/' + url

            # Check if file exists (returns true or false)
            "#{File.exist?(file_path.strip!)}"
        end
    end
end

Liquid::Template.register_tag('file_exists', Jekyll::FileExistsTag)

Содержимое файла include_absolute.rb.

RB
class RootInclude < Liquid::Tag
  def initialize(_tag_name, markup, _parse_context)
    super
    @markup = markup.strip
  end

  def render(context)
    expanded_path = Liquid::Template.parse(@markup).render(context)
    root_path = File.expand_path(context.registers[:site].config['source'])
    final_path = File.join(root_path, expanded_path)
    read_file(final_path, context)
  end

  def read_file(path, context)
    file_read_opts = context.registers[:site].file_read_opts
    File.read(path, **file_read_opts)
  end
end

Liquid::Template.register_tag('root_include', RootInclude)

Чтобы подключить стили откроем файл _layouts/default.html и ниже тега <base> добавим.

HTML
<!-- Критические стили   -->
{% assign path = '_site/assets/css/critical.css' %}
{% capture fileExists %}{% file_exists {{ path }} %}{% endcapture %}
{% if fileExists == 'true' %}
{% capture critical %}{% root_include {{ path }} %}{% endcapture %}
<style>{{ critical | scssify }}</style>
{% endif %}

<!-- Стили -->
<link rel="preload" as="style" href="{{ '/assets/css/style.css?v=' | relative_url }}{{ site.version | default: '0.0.1' }}" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | relative_url }}{{ site.version | default: '0.0.1' }}"></noscript>

Здесь мы проверяем существования файла по пути _site/assets/css/critical.css, если файл существует то мы создаем переменную critical в которую помещаем его содержимое. После чего выводим это содержимое на странице, прогнав его через фильтр минификации scssify.

Далее стандартным способом подключаем стили прогнав путь до файла через фильтр relative_url, который преобразует абсолютный url в относительный. Конструкция onload="this.rel='stylesheet'" позволит загрузить стили после загрузки всей страницы, что положительно сказывается на Page Speed показателях.

В терминале выйдем из режима разработки, нажав клавиши Ctrl + C. Удалим каталог _site, чтобы проверить успешно ли собирается проект и правильно ли внедряются критические стили. Если мы сейчас просто запустим команду работы над проектом в режиме разработки, то при сборке шаблона критические стили не подключатся, так как файл critical.css создается позже чем собираются html страницы.

Чтобы исправить этот нюанс, необходимо прежде собрать проект, а уже потом запускать режим разработки. Так же обратите внимание на новую опцию --livereload, благодаря нее браузер будет автоматически обновляться при изменении файлов шаблона.

BASH
jekyll build && bundle exec jekyll serve --livereload

Обновим главную страницу и откроем исходный код в браузере. Как видим критические стили минифицированы и отображаются внутри тега <style>. Более того если мы изменим стили в scss файлах изменения автоматически применятся на странице и мы увидим обновленный вид.

Исходный код главной страницы
Обновим главную страницу и откроем исходный код в браузере.

Остальные ресурсы Assets

Внутри каталога assets создайте каталоги:

  • favicons - фавиконки
  • fonts - шрифты
  • icons - svg иконки, черно-белые и цветные
  • images - картинки
  • js - javascript скрипты

Должна получиться примерно следующая структура. Примерную структуру и содержимое файлов можно подсмотреть здесь.

BASH
./assets/
├── css
     ├── critical.scss
     └── style.scss
├── favicons
├── fonts
├── icons
├── images
└── js
    └── main.js

Создадим файл assets/js/main.js и подключим его на странице, изменив содержимое файла _layouts/base.html.

HTML
{% contentfor script %}
<script src="{{ '/assets/js/main.js?v=' | relative_url }}{{ site.version | default: '0.0.1' }}" type="module"></script>
{% endcontentfor %}

Раздел получился довольно большим, местами тяжелым. Важно понимать, что это лишь пример один из всевозможных вариантов, вы вправе создавать и использовать только те каталоги которые будете использовать. Моя задача лишь показать некоторый пример как это может выглядеть, а ваша задача уловить суть и сделать, так как вам надо.

Highlight расширение

Необязательный раздел, некоторое дополнение к теме о стилях. Если открыть файл статьи, который Jekyll создал по умолчанию, то можно увидеть следующую конструкцию.

MD
{% highlight ruby %}
def print_hi(name)
  puts "Hi, #{name}"
end
print_hi('Tom')
#=> prints 'Hi, Tom' to STDOUT.
{% endhighlight %}

Это обертка, чтобы выводить код и т.п. в красивом оформленном виде, с подсветкой синтаксиса. Примерно как это выглядит на данном сайте. Данная функциональность по умолчанию включена в Jekyll только не имеет стилей. Для того чтобы ей придать стилевой оформление установим и настроим некоторые расширения.

Откроем Gemfile и добавим в группу плагинов новые расширения kramdown и rouge.

GEMFILE
# Hightlight
# https://jun711.github.io/web/how-to-highlight-code-on-a-Jekyll-site-syntax-highlighting/
# https://bnhr.xyz/2017/03/25/add-syntax-highlighting-to-your-jekyll-site-with-rouge.html
# rougify help style
# rougify style monokai > _sass/scaffolds/components/_syntax.scss
gem "kramdown"
gem "rouge"

Установим новые расширения.

BASH
bundle install

Откроем файл _config.yml и ниже конструкции plugins добавим настройки для расширений.

YML
# Подсветка синтаксиса
markdown: kramdown
kramdown:
  input: GFM
  syntax_highlighter: rouge

Посмотрим доступные стили для подсветки синтаксиса кода. Для этого в терминале введем команду.

BASH
rougify help style

Появится следующий перечень стилей.

available themes:
  base16, base16.dark, base16.light, base16.monokai, base16.monokai.dark,
  base16.monokai.light, base16.solarized, base16.solarized.dark,
  base16.solarized.light, bw, colorful, github, github.dark, github.light,
  gruvbox, gruvbox.dark, gruvbox.light, igorpro, magritte, molokai, monokai,
  monokai.sublime, pastie, thankful_eyes, tulip

Выберем любой из них и установим.

BASH
rougify style monokai > ./_scss/scaffolds/components/_hightlight.scss

В указанном каталоге должен появиться файл _hightlight.scss со стилями, который будет содержать цветовое оформление в стиле monokai.

Перезапустим шаблон в режиме разработки.

BASH
jekyll build && bundle exec jekyll serve --livereload

Теперь сгенерированный файл с стилями подключим к шаблону, для этого откроем файл assets/css/style.scss и добавим.

SCSS
---
---
@import "core/helpers/functions";
@import "core/helpers/variables";
@import "core/helpers/mixins";

@import "core/base/fonts";

@import "scaffolds/components/button";
@import "scaffolds/components/hightlight";

@import "scaffolds/sections/browser-upgrade";

Откроем статью созданную Jekyll по умолчанию у меня она располагается по ссылке /jekyll/2023/09/03/welcome-to-jekyll.html и увидим что выбранная цветовая схема успешно применилась.

Оформление кода
Увидим что выбранная цветовая схема успешно применилась.

Seo оптимизация

В этом разделе рассмотрим Seo оптимизацию, добавим Open Graph, Twitter Cards, Google Analytic, Yandex Metrika и т.п.

Seo расширения

Откроем файл Gemfile и добавим в группу плагинов расширение jekyll-seo-tag. Для более детальной настройки ознакомьтесь с официальной документацией.

GEMFILE
# SEO
# https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/installation.md
gem "jekyll-seo-tag"

Установим новое расширение jekyll-seo-tag.

BASH
bundle install

После установки расширения настроим его отредактировав файл _config.yml.

YML
plugins:
  - jekyll-seo-tag
  - jekyll-feed
  - jekyll-sitemap

Создадим файл head.html внутри каталога _includes. Теперь откроем файл _layouts/default.html и вынесем все содержимое тега <head> в созданный файл head.html.

HTML
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>{{ page.title }}</title>

<base href="{{ site.baseurl }}">

<!-- Критические стили   -->
{% assign path = '_site/assets/css/critical.css' %}
{% capture fileExists %}{% file_exists {{ path }} %}{% endcapture %}
{% if fileExists == 'true' %}
{% capture critical %}{% root_include {{ path }} %}{% endcapture %}
<style>{{ critical | scssify }}</style>
{% endif %}

<!-- Стили -->
<link rel="preload" as="style" href="{{ '/assets/css/style.css?v=' | relative_url }}{{ site.version | default: '0.0.1' }}" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | relative_url }}{{ site.version | default: '0.0.1' }}"></noscript>

В файле _layouts/default.html подключаем head.html.

HTML
<html class="no-js" lang="{{ page.lang | default: site.lang | default: 'ru' }}">
<head>
  {% include head.html %}
</head>
<body>
<div class="layout">
  {% contentblock header %}

  <main class="layout__main">
    {% contentblock content %}
  </main>

  {% contentblock footer %}
</div>

{% contentblock script %}
</body>
</html>

Теперь базовый шаблон не содержит ни чего лишнего и далее мы можем акцентироваться на наполнении файла head.html прописывая в него различные конструкции.

Подключим Seo расширения в файл head.html сразу после стилей.

HTML
{% seo title=false %}
{% feed_meta %}
  • seo - подключит Open Graph, Twitter Cards, мета информацию description, ld+json в общем основные составляющие для seo продвижения
  • feed_meta - подключит ссылку на RSS ленту

Google и Yandex аналитика

Откроем файл _config.yml и дабавим пользовательские переменные, которые будут хранить идентификаторы от Google Analytic и Yandex Metrika.

YML
google_analytics: UA-XXXXXXXXX-X
yandex_metrika: XXXXXX

В файле head.html ниже подключенных расширений подключим скрипты с Google Analytic и Yandex Metrika.

HTML
{% if jekyll.environment == 'production' and site.google_analytics and site.google_analytics != 'UA-XXXXXXXXX-X' %}
<!-- Google Analytics -->
<script>
    if(!(window.doNotTrack === "1" || navigator.doNotTrack === "1" || navigator.doNotTrack === "yes" || navigator.msDoNotTrack === "1")) {
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
            (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
            m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

        ga('create', '{{ site.google_analytics }}', 'auto');
        ga('send', 'pageview');
    }
</script>
{% endif %}

{% if jekyll.environment == 'production' and site.yandex_metrika and site.yandex_metrika != 'XXXXXX' %}
<!-- Yandex Metrika -->
<script>
    (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
        m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
    (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
    ym('{{ site.yandex_metrika }}', "init", {});
</script>
{% endif %}

Скрипты будут подключены по условию если в переменная среды JEKYLL_ENV будет равна значению production, а так же пользовательская переменная site.google_analytics которую мы создали в файле _config.yml должна существовать и не должна быть равна значению UA-XXXXXXXXX-X и XXXXXX.

Для того чтобы задать окружение для переменной среды, выполните в терминале команду.

BASH
export JEKYLL_ENV=production

# Или

export JEKYLL_ENV=development

Теперь когда получите идентификатор от какой-либо из этих платформ и пропишите его в файле _config.yml, при сборке шаблона должны подключится соответствующие скрипты.

Постраничная пагинация

В предыдущих разделах мы научились создавать статьи. При создании статьи мы определяли ее в определенную категорию через параметр category либо categories. Категории позволяют сгруппировать статьи и в дальнейшем показывать список тех статей чья категория была выбрана. И как правило при большом количестве материала принято разделять его на страницы, к примеру не более девяти статей на страницу.

Расширение jekyll-paginate-v2

В Jekyll подобный функционал возможен благодаря расширению jekyll-paginate-v2. Более подробно с настройкой расширения jekyll-paginate-v2 можно познакомиться на официальном сайте.

Добавим расширение в файл Gemfile в группу плагинов.

GEMFILE
# Пагинация
# https://github.com/sverrirs/jekyll-paginate-v2
gem "jekyll-paginate-v2"

Установим новое расширение jekyll-paginate-v2.

BASH
bundle install

Далее откроем файл _config.yml и пропишем в нем следующие правила.

YML
# Подключение плагинов
plugins:
  - jekyll-seo-tag
  - jekyll-feed
  - jekyll-sitemap
  - jekyll-paginate-v2

# Пагинация
pagination:
  enabled: true
  per_page: 9
  permalink: '/page/:num/'
  title: ':title - Страница :num'
  limit: 0
  sort_field: 'date'
  sort_reverse: true
  trail:
    before: 2
    after: 2
  • enabled - активировать пагинацию
  • per_page - количество статей на странице
  • permalink - вид ссылки на другие страницы
  • title - основной заголовок страницы
  • limit - максимальное количество страниц пагинации, 0 без лимита
  • sort_field - сортировка статей по дате
  • sort_reverse - сортировка статей от новых к старым
  • trail - задает количество страниц до и после текущей активной страницы, которые будут видны в пагинации

Шаблон blog.html

В каталоге _layouts создадим файл blog.html, это будет шаблон для страниц со списком статей.

HTML
---
layout: base
---
<section class="blog">
  <div class="container">
    <h2 class="blog__title"><span>{{ page.title }}</span></h2>
    <div class="blog__content">
      {% if paginator.posts[0] %}
      <ul class="blog__list">
        {% for post in paginator.posts %}
        <li class="blog__item">
          <a class="blog__link" href="{{ post.url }}">
            <time class="blog__date" datetime="{{ post.date | date: " %Y-%m-%dT%H:%M
            " }}">{{ post.date | date: '%d.%m.%Y' }}</time>
            <h3 class="blog__title">{{ post.title | markdownify | strip_html | truncatewords: 15 }}</h3>
            <div class="blog__desc">{{ post.description | default: post.excerpt | markdownify | strip_html |
              truncatewords: 40 }}
            </div>
          </a>
        </li>
        {% endfor %}
      </ul>

      {% if paginator.page_trail %}
      <ol class="blog__pagination">
        {% if paginator.previous_page %}
        <li class="blog__pagination-item blog__pagination-item--prev">
          <a class="blog__pagination-link" href="{{ paginator.previous_page_path }}" aria-label="Предыдущее">
            <--
          </a>
        </li>
        {% endif %}

        {% for trail in paginator.page_trail %}
        {% if page.url == trail.path %}
        {% assign active = 'page-pagination__item--active' %}
        {% else %}
        {% assign active = '' %}
        {% endif %}
        <li class="blog__pagination-item {{ active }}">
          <a class="blog__pagination-link" href="{{ trail.path | remove: 'index.html' }}">{{ trail.num }}</a>
        </li>
        {% endfor %}

        {% if paginator.next_page %}
        <li class="blog__pagination-item blog__pagination-item--next">
          <a class="blog__pagination-link" href="{{ paginator.next_page_path }}" aria-label="Следующее">
            -->
          </a>
        </li>
        {% endif %}
      </ol>
      {% endif %}
      {% else %}
      <p>Записей нет</p>
      {% endif %}

      <div class="blog__seo">
        {{ content }}
      </div>

    </div>
  </div>
</section>

Довольно большая разметка получилась, но здесь только самое главное.

  • {{ page.title }} - выводит заголовок страницы, страницу blog мы создадим ниже
  • {% if paginator.posts[0] %} - проверяет наличие статей, если статей нет выводим <p>Записей нет</p>
  • {% for post in paginator.posts %} - в цикле выводим каждую статью
  • {{ post.url }} - ссылка на статью
  • {{ post.date | date: " %Y-%m-%dT%H:%M" }} - дата создания статьи
  • {{ post.title | markdownify | strip_html | truncatewords: 15 }} - заголовок статьи, здесь применяется ряд фильтров которые удаляют markdown и html разметку и обрезают предложение до 15 слов
  • {{ post.description | default: post.excerpt | markdownify | strip_html | truncatewords: 40 }} - описание статьи, если у статьи не будет описания по умолчанию будет браться значения из краткого описания, здесь применяется ряд фильтров которые удаляют markdown и html разметку и обрезают предложение до 40 слов
  • {% if paginator.page_trail %} - проверка активна ли постраничная навигация
  • {% if paginator.previous_page %} - ссылка на предыдущую страницу пагинации
  • {% for trail in paginator.page_trail %} - перечисление доступных страниц пагинации
  • {% if paginator.next_page %} - ссылка на следующую страницу пагинации
  • {{ content }} - выводит контентную часть страницы blog которую мы создадим ниже

Страница blog.html

В каталоге _pages создадим файл blog.html со следующим содержимым.

HTML
---
layout: blog
title: Блог
description: Статьи с рецептами
permalink: /blog
pagination:
  enabled: true
---

Описание страницы Блог

Для проверки пагинации продублируем имеющуюся статью в каталоге _posts и в файле _config.yml изменим значение параметра per_page на 1. Чтобы выводилась одна статья на страницу и того мы должны будем увидеть пагинацию.

Страница блог
Чтобы выводилась одна статья на страницу и того мы должны будем увидеть пагинацию.

Изображения на сайте

Хорошо бы если каждая будь то статья, страница или коллекция имела изображение, некую миниатюру. В этом разделе мы создадим структуру для хранения картинок, установим ряд расширений которые позволят конвертировать формат картинки в Webp, создавать картинки различного разрешения и т.п.

Расширение mini_magick

Расширение mini_magick позволит изменять размер изображений в соответствии с пользовательскими настройками. Более детально с этим расширением можно познакомиться здесь и здесь.

Добавим расширение mini_magick в файл Gemfile в группу плагинов.

GEMFILE
# Обрезка и изменение размера изображений
# https://github.com/zroger/jekyll-minimagick
# https://github.com/MattKevan/Jekyll-MiniMagick-new
# https://www.kevan.tv/2016/10/17/automatic-image-resizing-with-jekyll-and-imagemagick/
gem "mini_magick"

Далее создадим в каталоге _plugins файл с названием jekyll_minimagick.rb и вставим в него содержимое этого файла. В данный файл я внес небольшие свои правки для того чтобы в будущем не было проблем с файлами webp. Содержимое файла jekyll_minimagick.rb должно получиться таким.

RB
require 'mini_magick'

module Jekyll
  module JekyllMinimagick

    class GeneratedImageFile < Jekyll::StaticFile
      # Initialize a new GeneratedImage.
      #   +site+ is the Site
      #   +base+ is the String path to the <source>
      #   +dir+ is the String path between <source> and the file
      #   +name+ is the String filename of the file
      #   +preset+ is the Preset hash from the config.
      #
      # Returns <GeneratedImageFile>
      def initialize(site, base, dir, name, preset)
        @site = site
        @base = base
        @dir  = dir
        @name = name
        @dst_dir = preset.delete('destination')
        @src_dir = preset.delete('source')
        @commands = preset
        @relative_path = File.join(*[@dir, @name].compact)
        @extname = File.extname(@name)
      end

      # Obtains source file path by substituting the preset's source directory
      # for the destination directory.
      #
      # Returns source file path.
      def path
        File.join(@base, @dir.sub(@dst_dir, @src_dir), @name)
      end

      # Use MiniMagick to create a derivative image at the destination
      # specified (if the original is modified).
      #   +dest+ is the String path to the destination dir
      #
      # Returns false if the file was not modified since last time (no-op).
      def write(dest)
        dest_path = destination(dest)
        dest_path ['_site/'] = ''

#         puts dest_path

        return false if File.exist? dest_path and !modified?
        self.class.mtimes[path] = mtime

        FileUtils.mkdir_p(File.dirname(dest_path))
        image = ::MiniMagick::Image.open(path)
        image.combine_options do |c|
          @commands.each_pair do |command, arg|
            c.send command, arg
          end
        end
        image.write dest_path

        true
      end

    end

    class MiniMagickGenerator < Generator
      safe true

      # Find all image files in the source directories of the presets specified
      # in the site config.  Add a GeneratedImageFile to the static_files stack
      # for later processing.
      def generate(site)
        return unless site.config['mini_magick']

        site.config['mini_magick'].each_pair do |name, preset|
          Dir.chdir preset['source'] do
           Dir.glob(File.join("**", "*.{png,jpg,jpeg,gif}")) do |source|
              site.static_files << GeneratedImageFile.new(site, site.source, preset['destination'], source, preset.clone)
             end
          end
        end
      end
    end

  end
end

Устанавливаем расширение mini_magick.

BASH
bundle install

Откроем файл _config.yml и пропишем следующие настройки.

RB
# Обрезка и изменение размера изображений
mini_magick:
  thumbnail:
    source: images/original
    destination: images/thumbnail
    resize: "530x390^"
    gravity: "center" # NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
    extent: "530x390"
  recipe:
    source: images/original
    destination: images/recipe
    resize: "550x450^"
    gravity: "center"
    extent: "550x450"
  full:
    source: images/original
    destination: images/full
    resize: "1920x500^"
    gravity: "center"
    extent: "1920x500"
  • thumbnail / recipe / full - различные варианты миниатюр, каждый вариант со своими настройками
  • source - источник оригинальной картинки
  • destination - источник картинки определенного варианта
  • resize / extent - новый размер картинки, знак ^ означает что картинка будет сохранять свои пропорции
  • gravity - какую часть картинки расширение будет стремиться сохранить при обрезке

Можно добавлять сколько угодно форматов картинок, расширения будет автоматически их создавать на основе оригинальной картинки.

Создание структуры для картинок

Теперь в корне проекта создадим каталог images, внутри данного каталога создадим каталог где будут храниться оригинальные изображения original. Далее нужно повторить структуру каталога _posts. В общем у нас должно получиться следующее.

BASH
./images/
├── original
│    └── 2023
└──      └── 09

Для хранения картинок отдельной статьи нужно создать каталог для нее и назвать его так же как называется файл статьи, за исключением даты создания, указываем только имя и помещаем в этот каталог картинки. Например:

BASH
./images/
├── original
│    └── 2023
        └── 09
            └── welcome-to-jekyll
                └── cover.jpg

Запустим проект в режиме разработки.

BASH
jekyll build && bundle exec jekyll serve --livereload

В каталоге images должны появиться каталоги с нашими настроенными в файле _config.yml типами изображений.

BASH
./images/
├── full
├── original
├── recipe
└── thumbnail

Все каталоги кроме original не нужны нам в репозитории потому добавим их в файл .gitignore.

BASH
images/full
images/recipe
images/thumbnail

Если открыть каталог _site/images, то мы увидим ту же самую структуру каталогов с картинками. Помимо стандартных картинок формата jpg и прочее должны так же появится картинки формата webp.

Теперь, чтобы вставить картинку на сайт, откроем статью и разместив в ней следующую разметку.

BASH
<picture>
  <source srcset="/images/thumbnail/2023/09/welcome-to-jekyll/cover.webp" type="image/webp">
  <img src="/images/thumbnail/2023/09/welcome-to-jekyll/cover.jpg" alt="">
</picture>

Здесь мы указали относительный путь до нужной картинки формата thumbnail, так же мы могли бы указать другой формат например recipe.

BASH
<picture>
  <source srcset="/images/recipe/2023/09/welcome-to-jekyll/cover.webp" type="image/webp">
  <img src="/images/recipe/2023/09/welcome-to-jekyll/cover.jpg" alt="">
</picture>

Подведем итоги

Мы проделали большую кропотливую работу, затронули все основные моменты. Конечно еще можно было много всего добавить, например поиск по сайту, svg спрайты, создать компоненты для удобной вставки тех же картинок и многое другое, лишь бы была фантазия.

Надеюсь данная статья хоть немного поможет вам разобраться с этим интересным инструментом под название Jekyll.

Я продолжу дальше работать над этим шаблоном доводя его до ума. Актуальная версия шаблона будет доступна в моем репозитории. Надеюсь когда вы будете читать эти строки шаблон будет полностью готов и вы сможете подчеркнуть еще больше идей для себя.

Предыдущая статья Gulp сборка для верстки с использованием Pug шаблонизатора Следующая статья Паттерны и алгоритмы для работы с двумерными циклами