Front matter is a YAML block defined between ---
delimiters that is placed at the very top of markdown files to define metadata for that specific file.
---
name: Nicolas Dao
title: 'Managing director'
image_path: /uploads/nic.png
email: [email protected]
linkedin: nicolasdao
position: 40
publish: true
---
Hello, I'm Nic, and this text is a content example for the page called {{ page.title }}.
Each variable in that YAML block is then accessible in your page via the page
API.
IMPORTANT: The naming convention kind of matters. Use snake-case to name your properties (e.g.,
first_name
insteasd offirstName
). Nothing will break if you don't but if you do, those fields will be properly formatted when using CMS such as CloudCannon. A field namedfirst_name
will be displayed as an input with aFirst Name
label.
Liquid is the templating language used by Jekyll. This allows to insert Ruby logic inside your HTML.
<div>
{% assign sorted = site.people | sort: 'position' %}
{% for person in sorted %}
{% if person.publish %}
<div class="row">
<ul>
<li>Name: {{ person.name }}</li>
<li>Title: {{ person.title }}</li>
<li>Intro: {{ person.content }}</li>
</ul>
</div>
{% endif %}
{% endfor %}
</div>
Liquid is the templating language used by Jekyll. This allows to insert Ruby logic inside your HTML.
<div class="{% if page.topmenu == 'home' %}no-background{% elsif page.topmenu == 'contact' %}background{% endif %}">
<!-- Some HTML -->
</div>
<div>
{% assign sorted = site.people | sort: 'position' %}
{% for person in sorted %}
{% if person.publish %}
<div class="row">
<ul>
<li>Name: {{ person.name }}</li>
<li>Title: {{ person.title }}</li>
<li>Intro: {{ person.content }}</li>
</ul>
</div>
{% endif %}
{% endfor %}
</div>
If you need the index in the loop:
<div>
<ul>
{% for person in site.people %}
<li>Name: {{ person.name }}</li>
<li>Zero-based index: {{ forloop.index0 }}</li>
<li>Index: {{ forloop.index }}</li>
<li>Is first: {{ forloop.first }}</li>
<li>Is last: {{ forloop.last }}</li>
<li>Position: {{ forloop.index }}/{{ forloop.length }}</li>
{% endfor %}
</ul>
</div>
Full API details at https://learn.cloudcannon.com/jekyll/looping-in-liquid/
This block is used to perform complex operations that would be hard to add inline. The next example shows how to create a new header_style
variable:
{%- liquid
assign header_style = 'font-weight:' | append: block.settings.font_weight | append: ';'
if block.settings.font_size > 0
assign header_style = header_style | append: 'font-size:' | append: block.settings.font_size | append: 'px;'
endif
-%}
- Use plural: Try if possible to use plural in your collection. This helps Jekyll and CMS that use Jekyll to make smart decisions when managing collections.
- Use lowercase dash separated: For each
.md
file under your collection folder, use dash-separated lowercase file name. This convention helps if the collection used to create web pages as this file name is used in the URL (e.g.,_persons/nicolas-dao.md
).
A collection is like a local DB in markdown which is globally available via the site.yourCollection
API. An entry is a .md file. The metadata are expressed using liquid and content can also be added. For example, let's create a people
collection:
- Create a new
_persons
folder in your root repository. - Under the
_persons
repository, create a newnicolas-dao.md
file as follow:
---
name: Nicolas Dao
title: 'Managing director'
image_path: /uploads/nic.png
email: [email protected]
linkedin: nicolasdao
position: 40
publish: true
---
Hello, I'm Nic, and this text is a content example.
- Refer it in an HTML page:
<div>
{% assign sorted = site.persons | sort: 'position' %}
{% for person in sorted %}
{% if person.publish %}
<div class="row">
<ul>
<li>Name: {{ person.name }}</li>
<li>Title: {{ person.title }}</li>
<li>Intro: {{ person.content }}</li>
</ul>
</div>
{% endif %}
{% endfor %}
</div>
If you wish Jekyll to generate a new page for each item in your collection, in your _config.yml
file, declare the collection as follow:
collections:
persons:
output: true
This section is irrelevant to Jekyll itsefl. Some CMS (e.g., CloudCannon) use the collection naming convention to make some smart choices in regards to the type of input to use to manage the page's front matter inputs.
For example, lets imagine this page:
---
title: 'Jekyll tutorial 101'
author: Nicolas Dao
---
Blabla
Written by {{ page.author }}
If no authors
collection exist, CloudCannon will maintain this page's author
field as a text input. However, CloudCannon automatically change the text input to a select input using the specific author in the option list if we add an authors
collection to the website as follow:
_authors/
|____nicolas-dao.md
|____stephen-king.md
and replace the above page's front matter as follow:
---
title: 'Jekyll tutorial 101'
author: nicolas-dao
---
Blabla
{% for author in site.authors %}
{% if page.author and author.name and author.path contains page.author %}
Written by {{ author.name }}
{% endif %}
{% endfor %}
If the front matter is updated as follow:
---
title: 'Jekyll tutorial 101'
authors:
- nicolas-dao
- stephen-king
---
Then CloudCannon changes the input to a multi select.
Blog posts are nothing more than a special type of collection. The only aspects that make it special are:
- The root folder must be named
_posts
(therefore posts are globally accessible viasite.posts
). - The
.md
file must be prefixed with the post's timestamp (e.g.,2020-10-21-your-post-title.md
).
This section covers the following configurations:
By default, assuming that there is a post under _posts/2020-10-21-your-post-title.md
, the path to the blog is formatted as follow: https://example.com/2020/10/21/your-post-title
To replace the 2020/10/21
prefix with your own, add a permalink
property in the _config.yml
:
permalink: /:title/
With this config, the previous post is accessible at https://example.com/your-post-title
However, the most likely scenario is that the blog post lives under specific categories. You can specify those categories in the post's front matter
---
layout: default
categories:
- blog
- tech
---
Hello, I'm a tech post
To use those categories in the URL path, update the permalink
as follow:
permalink: /:categories/:title/
When that's done, the previous post is accessible at https://example.com/blog/tech/your-post-title
Depending on the use case, you may have to access various post metadata via the site.posts
API. The following snippet demonstrates how to list some of the most common properties:
{% for post in site.posts %}
<h3>{{ post.title }}</h3>
<ul>
<li>{{ post.url }}</li>
<li>{{ post.path }}</li>
<li>{{ post.date }}</li>
{% for category in post.categories %}
<li>{{ category }}</li>
{% endfor %}
</ul>
{% endfor %}
The above snippet prints something similar to this:
Hello Post Title
/blog/tech/hello-again/
_posts/2020-12-06-hello-again.md
2020-12-06 00:00:00 +1100
blog
tech
Creating a landing page that lists all the blog posts is equivalent to creating an standard HTML page and use the site.posts
variable to loop through all posts. Example, blog/index.html
{% for post in site.posts %}
<!--Services Block-->
<div id="post_{{forloop.index}}" class="blog-post services-block col-md-6 col-sm-6 col-xs-12">
<div class="inner-box">
<div class="image">
<a href="{{post.url}}"><img src="{{post.image}}" alt="" /></a>
</div>
<div class="lower-content" style="text-align: unset;">
<h3 style="height: 70px;"><a href="{{post.url}}">{{post.title}}</a></h3>
<p style="margin-top: 30px;height: 130px;">{{ post.content | extract_text }}</p>
<div class="row clearfix" style="margin-bottom: 40px;margin-left: 0px;">
{% for author in site.authors %}
{% if post.author and author.name and author.path contains post.author %}
<div class="row" style="width:380px;">
<div class="col-xs-3">
<a href="{{ author.linkedin }}" target="_blank">
<img src="{{ author.image_path }}" style="width: 60px; border-radius: 32px; border: 1px solid #ececec;">
</a>
</div>
<div class="col-xs-9">
<div class="row">
<div class="col-xs-12">
<a href="{{ author.linkedin }}" target="_blank">{{ author.name }}</a>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<p style="font-size: small;font-style: italic;">{{ post.date | format_date }}</p>
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
Adding a search functionality is as simple as using some basic Javascript to filter tiles based on text.
Jekyll allows to add custom logic via Plugins
. To create your own custom logic:
- Create a new
_plugins
folder. - Create a new
.rb
file under the_plugins
folder (e.g.,extract_text.rb
). - Define your custom module and register it to Liquid. The next example removes HTML tags and truncate the text to its first 200 characters:
# Declare module of your plugin under Jekyll module
module Jekyll::ExtractText
# Each method of the module creates a custom Jekyll filter
def extract_text(input)
# extract_text filter business logic
# Return result
"#{input.gsub(/<(.*?)>/, '')[0...200]}..."
end
end
# Register your custom filter
Liquid::Template.register_filter(Jekyll::ExtractText)
- Apply the filter function to some text in your Liquid template:
<p>{{ post.content | extract_text }}</p>
Please refer to the Blog section.
{{ section | jsonify }}