Hugo vs Jekyll:静的サイトジェネレーターテーマの壮大な戦い

この記事では、上位2つの静的サイトジェネレーターのテーマを作成する際のニュアンスを比較します。

私は最近、2つのプロジェクトのドキュメントサイトのテーマを作成するタスクを引き受けました。どちらのプロジェクトにも同じ基本機能が必要でしたが、一方はJekyllを使用し、もう一方はHugoを使用しています。

典型的な開発者の合理性では、明らかに1つの選択肢しかありませんでした。私は両方のフレームワークで同じテーマを作成し、読者の皆様に並べて比較することにしました。

この投稿は、包括的なテーマ作成ガイドではありませんが、どちらのジェネレーターでもテーマを作成するプロセスを理解することを目的としています。これが私たちがカバーするものです:

  • テーマファイルの編成方法
  • コンテンツを置く場所
  • テンプレートのしくみ
  • pagesオブジェクトを使用してトップレベルメニューを作成する
  • データリストからネストされたリンクを使用してメニューを作成する
  • テンプレートをまとめる
  • スタイルの作成
  • GitHubPagesを構成してデプロイする方法

これが私が作成しようとしているテーマのくだらないワイヤーフレームです。

一緒にビルドすることを計画している場合は、テーマをビルドするときにローカルで提供すると役立つ場合があります。両方のジェネレーターがこの機能を提供します。Jekyllの場合はを実行しjekyll serve、Hugoの場合はを実行しhugo serveます。

メインコンテンツ領域と非常に重要なサイドバーメニューの2つの主要な要素があります。それらを作成するには、HTMLページの生成方法をサイトジェネレーターに指示するテンプレートファイルが必要です。テーマテンプレートファイルを適切な方法で整理するには、最初にサイトジェネレータが期待するディレクトリ構造を知る必要があります。

テーマファイルの編成方法

Jekyllはgemベースのテーマをサポートしており、ユーザーは他のRubygemと同じようにインストールできます。このメソッドはgem内のテーマファイルを非表示にするため、この比較のために、gemベースのテーマは使用していません。

あなたが走るとjekyll new-theme 、ジキルはあなたのために新しいテーマを足場にします。これらのファイルは次のようになります。

. ├── assets ├── Gemfile ├── _includes ├── _layouts │   ├── default.html │   ├── page.html │   └── post.html ├── LICENSE.txt ├── README.md ├── _sass └── .gemspec 

ディレクトリ名は適切に説明的です。この_includesディレクトリは、すべてにバターを塗るのとほぼ同じ方法で、さまざまな場所で再利用する小さなコード用です。(私だけ?)

_layoutsディレクトリは、サイトのページの異なる種類のテンプレートが含まれています。この_sassフォルダーは、サイトのスタイルシートを構築するために使用されるSassファイル用です。

を実行すると、新しいHugoテーマをスキャフォールディングできhugo new theme ます。これらのファイルがあります:

. ├── archetypes │   └── default.md ├── layouts │   ├── 404.html │   ├── _default │   │   ├── baseof.html │   │   ├── list.html │   │   └── single.html │   ├── index.html │   └── partials │   ├── footer.html │   ├── header.html │   └── head.html ├── LICENSE ├── static │   ├── css │   └── js └── theme.toml 

あなたはいくつかの類似点を見ることができます。Hugoのページテンプレートファイルはに押し込まれていlayouts/ます。_defaultページタイプには、list.htmlとのファイルがあることに注意してくださいsingle.html

Jekyllとは異なり、Hugoはこれらの特定のファイル名を使用して、リストページ(すべてのブログ投稿へのリンクがあるページなど)と単一ページ(ブログ投稿の1つなど)を区別します。layouts/partials/ディレクトリには、バターの再利用可能なビットを含み、およびスタイルシートファイルがスポットで取り出していstatic/css/

どちらのサイトジェネレーターでもある程度のカスタマイズが可能であるため、これらのディレクトリ構造は明確に設定されていません。たとえば、Jekyllではコレクションを定義でき、Hugoではページバンドルを利用できます。これらの機能を使用すると、コンテンツを複数の方法で整理できますが、ここでは、いくつかの簡単なページを配置する場所を見てみましょう。

コンテンツを置く場所

次のようなサイトメニューを作成するには:

Introduction Getting Started Configuration Deploying Advanced Usage All Configuration Settings Customizing Help and Support 

それぞれのサブセクションを含む2つのセクション(「はじめに」と「高度な使用法」)が必要です。

Jekyllは、コンテンツの場所に厳密ではありません。それはあなたのサイトのルートにあるページを期待し、そこにあるものは何でも構築します。Jekyllサイトのルートでこれらのページを整理する方法は次のとおりです。

. ├── 404.html ├── assets ├── Gemfile ├── _includes ├── index.markdown ├── intro │   ├── config.md │   ├── deploy.md │   ├── index.md │   └── quickstart.md ├── _layouts │   ├── default.html │   ├── page.html │   └── post.html ├── LICENSE.txt ├── README.md ├── _sass ├── .gemspec └── usage ├── customizing.md ├── index.md ├── settings.md └── support.md 

Jekyll構成でサイトソースの場所を変更できます。

Hugoでは、レンダリングされたすべてのコンテンツがcontent/フォルダーに含まれている必要があります。これにより、Hugoが404.htmlサイトコンテンツなどの不要なページをレンダリングしようとするのを防ぎます。content/Hugoでディレクトリを整理する方法は次のとおりです。

. ├── _index.md ├── intro │ ├── config.md │ ├── deploy.md │ ├── _index.md │ └── quickstart.md └── usage ├── customizing.md ├── _index.md ├── settings.md └── support.md 

ヒューゴにとって、_index.mdそしてindex.md異なることを意味します。各セクションに必要なページバンドルの種類(子を持たないリーフ、またはブランチ)を知っておくと役立ちます。

物をどこに置くかがわかったところで、ページテンプレートを作成する方法を見てみましょう。

テンプレートのしくみ

Jekyllページテンプレートは、Liquidテンプレート言語で作成されています。中括弧を使用して、ページのタイトルなどの可変コンテンツをページに出力します{{ page.title }}

Hugoのテンプレートも中括弧を使用していますが、Goテンプレートを使用して構築されています。構文は似ていますが、異なります{{ .Title }}

LiquidテンプレートとGoテンプレートはどちらもロジックを処理できます。Liquidは、タグ構文を使用して論理演算を示します。

{% if user %} Hello {{ user.name }}! {% endif %} 

また、Go Templatesは、関数と引数を中括弧構文に配置します。

{{ if .User }} Hello {{ .User }}! {{ end }} 

テンプレート言語を使用すると、1つのスケルトンHTMLページを作成し、定義した領域に可変コンテンツを配置するようにサイトジェネレーターに指示できます。defaultJekyllとHugoの2つの可能なページテンプレートを比較してみましょう。

Jekyll’s scaffold default theme is bare, so we’ll look at their starter theme Minima. Here’s _layouts/default.html in Jekyll (Liquid):

  {%- include head.html -%}  {%- include header.html -%} {{ content }} {%- include footer.html -%}   

Here’s Hugo’s scaffold theme layouts/_default/baseof.html (Go Templates):

  {{- partial "head.html" . -}}  {{- partial "header.html" . -}} {{- block "main" . }}{{- end }} {{- partial "footer.html" . -}}   

Different syntax, same idea. Both templates pull in reusable bits for head.html, header.html, and footer.html.  These show up on a lot of pages, so it makes sense not to have to  repeat yourself.

Both templates also have a spot for the main content, though the Jekyll template uses a variable ({{ content }}) while Hugo uses a block ({{- block "main" . }}{{- end }}). Blocks are just another way Hugo lets you define reusable bits.

Now that you know how templating works, you can build the sidebar menu for the theme.

Creating a top-level menu with the pages object

You can programmatically create a top-level menu from your pages. It will look like this:

Introduction Advanced Usage 

Let’s start with Jekyll. You can display links to site pages in your Liquid template by iterating through the site.pages object that Jekyll provides and building a list:

    {% for page in site.pages %}
  • {{ page.title }}
  • {% endfor %}

This returns all of the site’s pages, including all the ones that you might not want, like 404.html. You can filter for the pages you actually want with a couple more tags, such as conditionally including pages if they have a section: true parameter set:

    {% for page in site.pages %} {%- if page.section -%}
  • {{ page.title }}
  • {%- endif -%} {% endfor %}

You can achieve the same effect with slightly less code in Hugo. Loop through Hugo’s .Pages object using Go Template’s range action:

    {{ range .Pages }}
  • {{.Title}}
  • {{ end }}

This template uses the .Pages object to return all the top-level pages in content/ of your Hugo site. Since Hugo uses a specific folder for the site content you want rendered, there’s no additional filtering necessary to build a simple menu of site pages.

Creating a menu with nested links from a data list

Both site generators can use a separately defined data list of links to render a menu in your template. This is more suitable for creating nested links, like this:

Introduction Getting Started Configuration Deploying Advanced Usage All Configuration Settings Customizing Help and Support 

Jekyll supports data files in a few formats, including YAML. Here’s the definition for the menu above in _data/menu.yml:

section: - page: Introduction url: /intro subsection: - page: Getting Started url: /intro/quickstart - page: Configuration url: /intro/config - page: Deploying url: /intro/deploy - page: Advanced Usage url: /usage subsection: - page: Customizing url: /usage/customizing - page: All Configuration Settings url: /usage/settings - page: Help and Support url: /usage/support 

Here’s how to render the data in the sidebar template:

{% for a in site.data.menu.section %} {{ a.page }} 
    {% for b in a.subsection %}
  • {{ b.page }}
  • {% endfor %}
{% endfor %}

This method allows you to build a custom menu, two nesting levels deep. The nesting levels are limited by the for loops in the template. For a recursive version that handles further levels of nesting, see Nested tree navigation with recursion.

Hugo does something similar with its menu templates. You can define menu links in your Hugo site config, and even add useful properties that Hugo understands, like weighting. Here’s a definition of the menu above in config.yaml:

sectionPagesMenu: main menu: main: - identifier: intro name: Introduction url: /intro/ weight: 1 - name: Getting Started parent: intro url: /intro/quickstart/ weight: 1 - name: Configuration parent: intro url: /intro/config/ weight: 2 - name: Deploying parent: intro url: /intro/deploy/ weight: 3 - identifier: usage name: Advanced Usage url: /usage/ - name: Customizing parent: usage url: /usage/customizing/ weight: 2 - name: All Configuration Settings parent: usage url: /usage/settings/ weight: 1 - name: Help and Support parent: usage url: /usage/support/ weight: 3 

Hugo uses the identifier, which must match the section name, along with the parent variable to handle nesting. Here’s how to render the menu in the sidebar template:

    {{ range .Site.Menus.main }} {{ if .HasChildren }}
  • {{ .Name }}
    • {{ range .Children }}
    • {{ .Name }}
    • {{ end }}
    {{ else }}
  • {{ .Name }}
  • {{ end }} {{ end }}

The range function iterates over the menu data, and Hugo’s .Children variable handles nested pages for you.

Putting the template together

With your menu in your reusable sidebar bit (_includes/sidebar.html for Jekyll and partials/sidebar.html for Hugo), you can add it to the default.html template.

In Jekyll:

  {%- include head.html -%}  {%- include sidebar.html -%} {%- include header.html -%} {{ content }} {%- include footer.html -%}   

In Hugo:

  {{- partial "head.html" . -}}  {{- partial "sidebar.html" . -}} {{- partial "header.html" . -}} {{- block "main" . }}{{- end }} {{- partial "footer.html" . -}}   

When the site is generated, each page will contain all the code from your sidebar.html.

Create a stylesheet

Both site generators accept Sass for creating CSS stylesheets. Jekyll has Sass processing built in, and Hugo uses Hugo Pipes. Both options have some quirks.

Sass and CSS in Jekyll

To process a Sass file in Jekyll, create your style definitions in the _sass directory. For example, in a file at _sass/style-definitions.scss:

$background-color: #eef !default; $text-color: #111 !default; body { background-color: $background-color; color: $text-color; } 

Jekyll won’t generate this file directly, as it only processes files with front matter. To create the end-result  filepath for your site’s stylesheet, use a placeholder with empty front matter where you want the .css file to appear. For example, assets/css/style.scss. In this file, simply import your styles:

--- --- @import "style-definitions"; 

This rather hackish configuration has an upside: you can use Liquid template tags and variables in your placeholder file. This is a nice way to allow users to set variables from the site _config.yml, for example.

The resulting CSS stylesheet in your generated site has the path /assets/css/style.css. You can link to it in your site’s head.html using:

Sass and Hugo Pipes in Hugo

Hugo uses Hugo Pipes to process Sass to CSS. You can achieve this by using Hugo’s asset processing function, resources.ToCSS, which expects a source in the assets/ directory. It takes the SCSS file as an argument.

With your style definitions in a Sass file at assets/sass/style.scss, here’s how to get, process, and link your Sass in your theme’s head.html:

{ $style := resources.Get "/sass/style.scss" }  

Hugo asset processing requires extended Hugo, which you may not have by default. You can get extended Hugo from the releases page.

Configure and deploy to GitHub Pages

Before your site generator can build your site, it needs a  configuration file to set some necessary parameters. Configuration files  live in the site root directory. Among other settings, you can declare  the name of the theme to use when building the site.

Configure Jekyll

Here’s a minimal _config.yml for Jekyll:

title: Your awesome title description: >- # this means to ignore newlines until "baseurl:" Write an awesome description for your new site here. You can edit this line in _config.yml. It will appear in your document head meta (for Google search results) and in your feed.xml site description. baseurl: "" # the subpath of your site, e.g. /blog url: "" # the base hostname & protocol for your site, e.g. //example.com theme: # for gem-based themes remote_theme: # for themes hosted on GitHub, when used with GitHub Pages 

With remote_theme, any Jekyll theme hosted on GitHub can be used with sites hosted on GitHub Pages.

Jekyll has a default configuration, so any parameters added to your configuration file will override the defaults. Here are additional configuration settings.

Configure Hugo

Here’s a minimal example of Hugo’s config.yml:

baseURL: //example.com/ # The full domain your site will live at languageCode: en-us title: Hugo Docs Site theme: # theme name 

Hugo makes no assumptions, so if a necessary parameter is missing, you’ll see a warning when building or serving your site. Here are all configuration settings for Hugo.

Deploy to GitHub Pages

Both generators build your site with a command.

For Jekyll, use jekyll build. See further build options here.

For Hugo, use hugo. You can run hugo help or see further build options here.

You’ll have to choose the source for your GitHub Pages site. Once done, your site will update each time you push a new build. Of course, you can also automate your GitHub Pages build using GitHub Actions. Here’s one for building and deploying with Hugo, and one for building and deploying Jekyll.

Showtime!

All the substantial differences between these two generators are under the hood. All the same, let’s take a look at the finished themes, in two color variations.

Here’s Hugo:

Here's Jekyll:

Wait who won?

?

Both Hugo and Jekyll have their quirks and conveniences.

From this developer’s perspective, Jekyll is a workable choice for simple sites without complicated organizational needs. If you’re looking to render some one-page posts in an available theme and host with GitHub Pages, Jekyll will get you up and running fairly quickly.

Personally, I use Hugo. I like the organizational capabilities of its Page Bundles, and it’s backed by a dedicated and conscientious team that really seems to strive to facilitate convenience for their users. This is evident in Hugo’s many functions, and handy tricks like Image Processing and Shortcodes. They seem to release new fixes and versions about as often as I make a new cup of coffee - which, depending on your use case, may be fantastic, or annoying.

If you still can’t decide, don’t worry. The OpenGitDocs documentation theme I created is available for both Hugo and Jekyll. Start with one, switch later if you want. That’s the benefit of having options.