PBS 181 of X: Reusable Snippets with Jekyll Includes (GitHub Pages)
In this instalment, we’ll wrap up our initial exploration of how Jekyll renders Markdown files to create fully formed HTML pages by learning about how Jekyll implements reusable snippets with its includes feature. These reusable snippets can be used both to enhance Jekyll layouts and to provide advanced rendering for our content that goes beyond what Markdown alone can provide.
Matching Podcast Episode
You can also Download the MP3
Read an unedited, auto-generated transcript with chapter marks: PBS_2025_06_06
Instalment Resources
- The instalment ZIP file — pbs181.zip
PBS 180 Challenge Solution
The challenge set at the end of the previous instalment was to add a Bootstrap 5 nav bar that lists your site’s pages to the top of every page on your site. The idea is to give quick access to any page from any other page.
For your convenience, you can quickly access my sample solution by downloading a snapshot of the demo site at the appropriate commit here, or you can clone the repo and checkout the commit with the tag pbs180-challenge-solution
.
Because the challenge was to add the nav bar to all pages, the only file I needed to update was the default layout, which is _layouts/default.html
in our site’s input folder (docs
). Here’s the full updated file:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page.title }} | {{ site.title }}</title>
{%- comment %}Include Bootstrap 5 compiled CSS{% endcomment %}
<link rel="stylesheet" href="{{ '/assets/css/style.css' | relative_url }}">
</head>
<body>
<div class="container">
{%- comment %}The page header region{% endcomment %}
<div class="row">
<header class="col-12 my-3">
{%- comment %}The navigation bar arcross the very top of the page{% endcomment %}
<nav class="navbar navbar-light bg-light navbar-expand-sm rounded-pill border border-1">
<div class="container-fluid">
{%- comment %}The site branding part of the nav bar{% endcomment %}
<a class="navbar-brand" href="{{ '/' | relative_url }}">{{ site.title }}</a>
{%- comment %}The expand toggle that will appear on the right of the nav bar when the navigation below is collapsed{% endcomment %}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
{%- comment %}The collapsing list of pages{% endcomment %}
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
{%- assign nav_pages = site.html_pages | where_exp: "item", "item.title != 'Home'" %}
{%- for p in nav_pages %}
<a class="nav-link {%- if p.url == page.url %} active{% endif %}" {%- if p.url == page.url %} aria-current="page"{% endif %} href="{{ p.url | relative_url }}">{{ p.title }}</a>
{%- endfor %}
</div>
</div>
</div>
</nav>
</header>
</div>
{%- comment %}The main content region{% endcomment %}
<div class="row">
<main class="col-12">
{{ content }}
</main>
</div>
</div>
{%- comment %}Include Bootstrap 5 JavaScript{% endcomment %}
<script src="{{ '/assets/js/bootstrap.bundle.min.js' | relative_url }}"></script>
</body>
</html>
At first glance, this file has expanded a lot, but that’s a little deceptive because a lot of the new lines are blank lines for better spacing, and the addition of comments to explain the page structure.
Bootstrap 5 Navbars require quite complex markup, so this seemed the opportune time to add comments to the file. Also, this gave me a good opportunity to introduce Liquid comments and explain why you might choose to use them over HTML comments.
A Note on Liquid Comments
You can add comments to both your Jekyll layouts and your site’s content files using Liquid comment tags. These are parts of lines or multiple lines wrapped with {% comment %}
and {% endcomment %}
. Nothing between the opening and closing tags will appear in the generated website’s files. Liquid objects and tags within Liquid comments are ignored, so you can use them to temporarily deactivate parts of your files while debugging problems.
Liquid comment tags support Liquid’s while-space control syntax (-
signs on the insides of delimiters as described in the core Liquid docs), This means you can simultaneously add spacing lines and useful information for your future self to your source files without either being visible in the generated HTML!
To understand this, let’s focus on just one small snippet from the top of the updated default theme:
<title>{{ page.title }} | {{ site.title }}</title>
{%- comment %}Include Bootstrap 5 compiled CSS{% endcomment %}
<link rel="stylesheet" href="{{ '/assets/css/style.css' | relative_url }}">
The -
symbol cuddled to the left of the opening delimiter for the opening comment
tag ({%-
) tells Jekyll when it renders the page it should erase all white space to the left of the opening comment
tag, in other words to delete the preceding blank line.
This means that these four lines of HTML+Liquid in the default layout appear in the generated HTML files as simply:
<title>About | PBS Jekyll Demo Site</title>
<link rel="stylesheet" href="/pbs-jekyll-demoSite/assets/css/style.css">
With that little note on comments out of the way, let’s look at how my sample solution actually works!
How the Sample Solution Works
If you examine the front matter at the top of the regular site pages (about.md
& links.md
), you’ll see they don’t specify a layout, so that means they’re using _layouts/default.html
. At the top of the home page (index.md
), the front matter does specify a layout; it specifies home_page
, so that means the first layout that will be wrapped around the home page is _layouts/front_page.html
. However, if you examine the front matter at the top of _layouts/front_page.html,
you’ll find that it also specifies a layout, specifically, default
, so that means that the home page gets wrapped twice, once by _layouts/front_page.html
, and then again by _layouts/front_page.html
. So, anything we add to the top of _layouts/default.html
will appear at the top of every single page. Clearly, this is the file we need to add our nav bar to.
The nav bar needs to be within the body of the page, and it needs to be inside the top-level Bootstrap container, so it needs to be somewhere within <html> → <body> → <* class="container">
.
When starting this challenge, the page had an extremely simplistic structure — the container tag was at the HTML 5 semantic tag <main>
, and all the content was contained within that tag.
When there was no nav bar, this was semantically correct because each page contained only content, and the correct semantic tag for a page’s content is <main>
. But a nav bar is not part of a page’s content; it is part of each page’s visible header region, so the appropriate semantic tag for containing the nav bar is <header>
.
So, semantically we need to end up with our content still in a <main>
tag, and our new nav bar in a new <header>
tag, and all of that somehow contained in a valid Bootstrap 5 layout. What we need then is a two-row Bootstrap 5 grid layout with one full-width column in each row. The way I chose to accomplish this is with the following top-level structure:
<html>
<body>
<div class="container">
<div class="row">
<header class="col-12 my-3">
<!-- NAV BAR HERE -->
</header>
</div>
<div class="row">
<main class="col-12">
<!-- CONTENT HERE -->
</main>
</div>
</div>
</body>
</html>
Note: If you’ve gotten a little rusty when it comes to Bootstrap grid’s layout system, here’s the relevant documentation.
For the nav bar itself, the first thing to remember is that the correct semantic tag for a navigation element is <nav>
, so whatever cool Bootstrap features we use, the tag that should contain it all should be a <nav>
.
To implement the nav bar, I leaned very heavily on the Bootstrap 5 documentation for nav bars because my own Bootstrap had gotten quite rusty!
I chose to use a nav bar that could collapse so I could make it responsive on small screens, and I chose to use just two components (ignoring the hamburger button for expanding and collapsing the bar on small screens) — a text-only brand heading, and a flat list of links in a basic nav.
By combining those choices with the documentation, I devised the following big-picture structure:
<nav class="navbar navbar-light bg-light navbar-expand-sm rounded-pill border border-1">
<div class="container-fluid">
<a class="navbar-brand">SITE BRAND LINKING TO HOME PAGE HERE</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse">
<div class="navbar-nav">
PAGE LINKS HERE
</div>
</div>
</div>
</nav>
Let’s break that down a little. To make any <nav>
be a Bootstrap 5 nav bar, it needs the class navbar
at the very least. To give it a useful colour, I added the class bg-light
(specifying a light background), and because I wanted it to stand out a bit, I added a 1px border with the classes border
and border-1
, and because I don’t like sharp edges, I made it pill-shaped with the class rounded-pill
. Finally, to specify that I want the nav bar to collapse at the very smallest size (xs
), but to expand for all other sizes, I added the class navbar-expand-sm
to specify that it should expand itself into a normal bar without a hamburger for all sizes bigger than extra small, i.e for sizes sm
and above.
Next, to add more than a single thing into the nav bar and have it display nicely, all the content needs to be wrapped in a fluid container, hence the <div class="container-fluid">
directly inside the <nav>
and wrapping everything else.
The first thing in the bar, starting from the left, will be the brand, hence, the first child within the fluid container is a link with the class brand
that I’m going to make link to the front page and have the site’s title as its text.
The docs explicitly say that if you want a collapsible bar, the second child must be the hamburger button that will only be visible on the sizes below the expansion size specified with an navbar-expand-*
class, hence the <button class="navbar-toggler">
containing the <span class="navbar-toggler-icon">
that becomes the hamburger icon when needed.
The last child is the container to wrap what ever it is that will be expanded on most screens but collapsed on phone screens, which goes in the <div class="navbar-collapse">
. Note that this div must have an id
, and that ID must match both the Bootstrap data attribute data-bs-target="#SOME_ID"
and the accessibility attribute aria-controls="SOME_ID"
for the hamburger button to work correctly when the nav is collapsed. I chose to use the ID navbarNavAltMarkup
, so hence the matching tags:
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">…</button>
…
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
Finally, the links we want to be expanded most of the time but collapsed on phone screens go into a <div class="navbar-nav">
.
OK, so that explains the HTML 5 semantic tags, and the Bootstrap 5 tags and classes, the final piece of the puzzle is the Liquid objects and tags for inserting the two pieces of content — the brand link, and the list of page links.
To add the brand link, we need two liquid objects (placeholders) to inject the details for the <a class="nav-brand">
tag — one to add the URL inside an href=""
attribute, and one to add the site’s title within the tag’s content:
<a class="navbar-brand" href="{{ '/' | relative_url }}">{{ site.title }}</a>
The link is simply the correctly filtered relative URL /
, and the text is the site’s title
property, which is available via the Liquid variable site
(which is a dictionary).
Finally, we need a loop to add links to every page but the current home page. We’ve already seen that we get the list of all HTML pages from the Liquid variable site.html_pages
. We’ve also already seen how we can use Jekyll’s where_exp
Liquid filter to remove the home page from that list. And finally, we’ve see how we can do all this within a Liquid assign
tag so we can save the filtered list of pages to a new variable. I chose to use the variable name nav_pages
, hence this line to gather the information before the loop:
{%- assign nav_pages = site.html_pages | where_exp: "item", "item.title != 'Home'" %}
Now that our list of pages to link to is in the nav_pages
variable, we can loop over them with a Liquid for
tag of the form:
{%- for p in nav_pages %}
CODE FOR LINK HERE
{%- endfor %}
To add the link, we need to construct an <a class="nav-link">
tag with the correctly filtered relative URL to the page as the href=""
property and the page’s title as the tag’s content. We could do that with the simple line:
<a class="nav-link" href="{{ p.url | relative_url }}">{{ p.title }}</a>
But for a little added flair, I wanted to mark the current page as being the current page using the Bootstrap 5 class active
and the accessibility property aria-current="page"
. To do that, I needed to use two Liquid if
tags to insert the class and the accessibility property on just the nav bar link for the current page. The key to that is to find a condition that is only true for that one link in the list. The URL of the link being rendered is p.url
, and the Liquid variable page
always represents the current page, so the needed condition is simply p.url == page.url
. Putting it all together, I get the final line:
<a class="nav-link {%- if p.url == page.url %} active{% endif %}" {%- if p.url == page.url %} aria-current="page"{% endif %} href="{{ p.url | relative_url }}">{{ p.title }}</a>
Having explained all this in great detail, it’s worth stepping back a moment to take stock of how much of this challenge was blowing the dust off skills from much earlier in this series, and how much was actually new to us and related to Jekyll/Liquid:
- We had to refresh our memory on the HTML 5 semantic tags
<main>
,<header>
, and<nav>
. - We had to refresh our memory of Bootstrap 5 grid layouts
- We had to refresh our memory of Bootstrap 5 nav bars
- We had to test-drive our new understanding of how Jekyll layouts are used to wrap the content in our Markdown files. 🆕
- We had to test-drive our new understanding of Liquid objects (placeholders), the Liquid tags
assign
,for
, andif
, and the Liquid filterrelative_url
. 🆕
So, only a small fraction of the challenge solution is actually new ground; most is actually a refresher on previously exposed concepts.
Reusable Snippets with Jekyll Includes
In the previous instalment, we learned how Jekyll layouts are used to define the HTML that goes around our content. We described how layouts can be nested into a Russian Nesting Doll-like structure with the original content wrapped in a layout wrapped in a layout, etc.. This simple structure provides the bulk of the theming on typical Jekyll sites, but it’s not quite sufficient to cover all needs. That’s why Jekyll also supports reusable snippets that can be reused in multiple files.
These reusable snippets, or Jekyll includes to give them their proper name, can be used within both layouts and content files.
Rather than trying to explain their use in the abstract, we’ll use worked examples to both illustrate how they work, and explore why they’re useful.
How Jekyll Includes Work
To include a reusable snippet in another file, you first need to create the snippet. These are simply files saved in the special folder _includes
. The include
Liquid tag is used to include a snippet in a file. The basic syntax is simply:
{% include SOME_FILE.SOMETHING %}
The file path specified in the include
tag is interpreted as being relative to the _includes
folder.
The file path in the include
tag can contain Liquid objects (placeholders), and the snippets themselves can also contain Liquid objects and tags.
You can even pass named arguments into snippets by adding space-delimited key='Value'
attributes after the path in your include
tags. Jekyll makes these named arguments available to Liquid objects and tags by adding them as keys to the special Liquid variable include
(this variable only exists within snippets).
Worked Example 1 — Facilitating Non-Nesting Layouts with Includes
If you’d like to play along, our starting point will be our demo site with the sample solution described above incorporated into it. You can download a snapshot of the site at the appropriate commit here, or you can clone the repo and check out the commit with the tag pbs181-startingPoint
.
When we last left our demo site after the challenge described earlier, the front page was different from every other page, having it’s own custom layout, but that layout was wrapped within the default layout, so we were able to customise the home page without duplicating the standard CSS and JavaScript includes that are needed on every page on our site.
Now, let’s update the site to use a completely different layout for the home page, one that inherits none of the default layout’s standard structure. To do this, we’ll need to create a layout that does not ask to be wrapped in the default layout. But that means our new home page layout won’t automatically inherit the standard CSS and JavaScript includes already defined in the default theme.
We could duplicate these parts of the HTML markup in our new layout, but then if we ever needed to change the standard CSS or JavaScript includes, we’d need to remember to make our change in two places, and if we ever forgot to do that, we’d introduce bugs to our site. With just two non-nested layouts that would already be brittle, but on large real-world sites, you could have many such layouts. There must be a better way‽
Of course, there is 🙂
We need to move the HTML tags that import the CSS and JavaScript into reusable snippets, and then reuse those same snippets in both the default layout and our new layout.
The first step is to create a new file named _includes/html_head_common.html
into which we’ll add the standard content that we always need within our HTML <head>
tag at the top of every page:
{%- comment %}Ensure all pages are rendered as UTF-8{% endcomment %}
<meta charset="utf-8" />
{%- comment %}Set the viewport as per the Bootstrap 5 docs{% endcomment %}
<meta name="viewport" content="width=device-width, initial-scale=1">
{%- comment %}Include Bootstrap 5 compiled CSS{% endcomment %}
<link rel="stylesheet" href="{{ '/assets/css/style.css' | relative_url }}">
For your convenience, you’ll find a copy of this file in the instalment ZIP as includes_html_head_common.html
. You can create the file from scratch or just copy this file to a new folder inside docs
called _includes
in the demo site and rename it to html_head_common.html
.
Next, create a new file named _includes/html_body_end.html
into which we’ll add the standard JavaScript libraries we always want included at the very end of the <body>
tag:
{%- comment %}Include Bootstrap 5 JavaScript{% endcomment %}
<script src="{{ '/assets/js/bootstrap.bundle.min.js' | relative_url }}"></script>
Again, for your convenience, you’ll find a copy of this file in the instalment ZIP as includes_html_body_end.html
, so you can create your file from scratch or copy and rename the one from the ZIP so it becomes _includes/html_body_end.html
.
We now have two snippets we can use in any layouts we design to act as outermost layouts. For now, we only have one such layout — the default — so let’s edit _layouts/default.html
to make use of our two new snippets. You’ll find the full updated version of the file in the instalment ZIP as layouts_default.html,
so you can copy and rename that file, but here are the changed lines:
<!DOCTYPE HTML>
<html>
<head>
{%- comment %}Include the snippet with the standard content for the head tag{% endcomment %}
{% include html_head_common.html %}
{%- comment %}Default to a title consisting of the page title and site title{% endcomment %}
<title>{{ page.title }} | {{ site.title }}</title>
</head>
<body>
…
{%- comment %}Include the standard body-end snippet{% endcomment %}
{% include html_body_end.html %}
</body>
</html>
Note that we have replaced the HTML markup that is now captured in our snippets with include
tags pointing to the appropriate snippets. The paths we use to specify the snippets are relative to the _includes
folder.
We can now rewrite our front page layout to make the page completely different from all other pages. If you look at _layouts/front_page.html
before we make our changes, you’ll notice two things:
- The layout does not define an entire HTML page
- The reason for this is that it uses its front matter to request that it be wrapped with the default layout
Our updated layout will define an entire HTML page, and won’t request it be wrapped by anything. To do that without code duplication, it will include the same snippets that the updated default layout now uses.
You’ll find the full updated version of _layouts/front_page.html
in the instalments zip as layouts_front_page.html
, so you can copy and re-name that, or you can replace the file’s current content with the following:
<!DOCTYPE HTML>
<html>
<head>
{%- comment %}Include the snippet with the standard content for the head tag{% endcomment %}
{% include html_head_common.html %}
{%- comment %}Use the site title as the window title{% endcomment %}
<title>{{ site.title }}</title>
</head>
<body class="bg-light">
<div class="container">
{%- comment %}Start a card with a grid layout embeded within it{% endcomment %}
<div class="card m-5">
<div class="container-fluid">
{%- comment %}Add the site title as a full-width row{% endcomment %}
<header class="row">
<div class="col-12 card-body">
<h1 class="display-1 text-center">{{ site.title }}</h1>
</div>
</header>
{%- comment %}Add the illustration, content, and page buttons as a row with multiple cols{% endcomment %}
<main class="row">
<div class="col-12 col-xl-6">
<img src="{{ 'assets/siteIllustration.png' | relative_url }}" alt="An Illustration showing the PBS logo over the GitHub and Jekyll Logos with a plus between them" class="img-fluid">
</div>
<div class="col-12 col-xl-6 card-body">
<p class="lead card-text">{{ content }}</p>
</div>
<div class="col-12 card-body text-end">
{%- assign list_pages = site.html_pages | where_exp: "item", "item.title != 'Home'" %}
{%- for p in list_pages %}
<a href="{{ p.url | relative_url }}" class="btn btn-outline-primary mx-1">{{ p.title }}</a>
{%- endfor %}
</div>
</main>
</div>
</div>
</div>
{%- comment %}Include the standard body-end snippet{% endcomment %}
{% include html_body_end.html %}
</body>
</html>
Worked Example 2 — Using Includes for Advanced Image Markup
For such a simple syntax, it is really amazing how much of a document’s content can be captured with pure Markdown. However, Markdown has some short-comings, and one of those is its representation of images. Markdown’s syntax includes support for basic images that have a source URL, title, and alternative text, but there’s no way to use pure Markdown to define a figure, that is, an image with a caption.
We can define a reusable snippet that takes three arguments to add support for figures to our content with a Jekyll include.
The three arguments we need are:
- The image’s path, which we’ll name
src
. - The alternative text for the image, which we’ll name
alt
. - The figure’s caption, which we’ll name
caption
.
Start by creating a file named _includes/figure.html
which will contain the generic markup for a nice HTML figure. You’ll find a copy of this file in the instalment ZIP as includes_figure.html
, you can create the file from scratch or copy and rename it as usual. Here is the file’s full content:
<figure class="mx-5 border border-1 rounded text-center">
<img class="img-fluid" src="{{ include.src | relative_url }}" alt="{{ include.alt }}">
<figcaption class="text-muted">{{ include.caption | markdownify }}</figcaption>
</figure>
Notice that we are making use of three arguments described above. We’ll need to pass those three each time we use our snippet.
Also, notice that for the caption, we’re using the markdownify
Liquid filter to render any markdown syntax within the caption as HTML.
Finally, I want to draw your attention to the Bootstrap 5 utility classes I’m using to style the figure. The first class, mx-5
sets a wide margin on the left and right sides of the figure (i.e., on the x-axis). The next three classes define the desired border; border
enabled a border on all four sides, border-1
sets the width to one pixel, and rounded
specifies that we want rounded corners. Finally, text-center
centre-aligns the content of the entire figure.
To see our new figure snippet in action, let’s use it to add a figure to our About page by editing about.md
and adding the series logo between the two paragraphs of text.
Before we can include an image, we’ll need to add one to the site, so copy illustrations_pbs_logo.png
from the instalment zip and add it to the demo site as illustrations/pbs_logo.png
.
To add the logo to the page, add the following code to about.md
between the two paragraphs of text:
{% include figure.html src="/illustrations/pbs_logo.png" alt="The silhouette of a ninja head wrapped with curly braces" caption="The _Programming by Stealth_ Logo" %}
You can manually paste the above line between the two paragraphs, or you can copy and re-name the file about-1.md
from the instalment ZIP.
Notice how we pass our three needed arguments. Also, notice that our caption contains the Markdown syntax to italicize the name of the series. This only renders properly because we used the markdownify
filter in the snippet.
Passing Large Arguments to Includes with the capture
Tag
Our above example works great for figures with small captions, but what if we needed to add a really long caption? Or one containing Liquid objects (placeholders)?
The solution here is the Liquid capture
tag. This tag creates a new variable from the content within it. The generic syntax is simply:
{% capture name_of_new_variable %}
As much content as you like here, and it can include Liquid syntax if you wish!
{% endcapture %}
We can make use of this construct to pass a lot of content to a snippet in a practical way.
As an example, let’s add a final figure to the bottom of the About page. We’ll add the Bartificer Creations logo with detailed alternative text and a long caption explaining its design. First, add the image to the site by copying the file illustrations_bartificer_logo.png
and adding it to the demo site as illustrations/bartificer_logo.png
. Next, add the following lines to the end of about.md
:
{% capture bartificer_logo_alt -%}
A green lower-case letter 'b' wrapped in orange chevrons over the word 'bartificer' mostly in green except for the letters 'art' which are in orange to match the chevrons. All this is over a dashed orange line below which is the word 'creations' in green.
{%- endcapture -%}
{%- capture bartificer_logo_caption -%}
The Bartificer Creations Logo. The name *Bartificer* is a [portmanteau](https://www.wordnik.com/words/portmanteau) of *Bart* and *Artificer*, because an Artificer is a skilled craftsperson.
{%- endcapture %}
{%- include figure.html src="/illustrations/bartificer_logo.png" alt=bartificer_logo_alt caption=bartificer_logo_caption %}
You can manually paste the above lines at the very bottom of about.md
, or you can copy and rename the file about-2.md
from the instalment ZIP.
A challenge — Create a Snippet to Add Side-Notes
Using the demo site as it currently stands at the end of this instalments worked examples (available as the commit tagged pbs181-challenge-startingPoint
and downloadable here), add a reusable snippet for creating nicely presented side-notes to any of the site’s content.
- The snippet needs to receive its contents as an argument named
content
. - Markdown within note contents should be correctly handled
- The note should be rendered using a Bootstrap 5 card.
Use this as an opportunity to refresh your understanding of the various Bootstrap utility classes to render the card in what you consider to be a nice way.
To make sure your snippet works well, add one or more notes to one or more pages in the demo site.
For bonus credit, you can add support for an optional title for each note. If no title is passed, the note should have no header region, but if a title is passed, it should be rendered using the correct markup for a Bootstrap note header.
For extra bonus credit, add support for an optional More info … URL, which should be rendered as a Bootstrap button in a Bootstrap card footer.
Final Thoughts
We’ve now learned all we need to create a functional local theme for a Jekyll site. We can use Jekyll Layouts to create the overall page structures, and we can leverage Jekyll includes to create reusable snippets for use both within our layouts and within our content. We’ve also learned how to compile Sass to regular CSS and to include the resulting stylesheet in our layouts. We’ve also learned how to add static assets like JavaScript files and images to our layouts.
We’ve learned how Jekyll uses the Liquid templating language to inject content into our layouts, and how YAML front matter can be used to control Jekyll’s behaviour and to set values for Liquid variables.
Basically, we now understand the how Jekyll converts Markdown content to fully formed HTML pages. This marks an important milestone in the series.
After a short hiatus, we’ll shift our focus from rendering content to organising our content. Jekyll is a content management system, after all, so we need to understand exactly how it manages our content!