Page Sections in Jekyll
Jekyll, Static Sites
The objective is to set up a services page that will have multiple sections. Each section will contain a block of HTML and an image.
It should be easy to edit section content in obvious markdown files, and we should avoid duplication as much as possible.
Data Driven Sections
Jekyll can read data files in either YAML
, JSON
or CSV
format. Data is loaded from files with these formats located in the _data
directory.
Data files allow us to specify files to be used as includes or images, and help avoid repetition in templates. You just set up the data file correctly, and edit the (content) source files without touching the template.
Data Setup
Though I have tinkered with using CSV
format data files (to make it easier to harvest user generated content), I prefer the YAML
format - basically because I prefer how the data looks in a text editor. If you do use CSV
, you need to include a header row.
For this post, we’ll focus on the YAML
format.
Create a new data file: /_data/service-sections.yml
.
Enter data, being careful with whitespace and tabs:
- title: "Pleated Blinds"
description: "domestic/pleated-blinds.md"
image: "assets/images/medium-images/pleated-blinds.jpg"
image-title:
service-category: domestic
- title: "Roller Blinds"
description: "commercial/roller-blinds.md"
image: "assets/images/medium-images/Rollerblinds.jpg"
image-title:
service-category: commercial
- title: "Plantation Shutters"
description: "domestic/plantation-shutters.md"
image: "assets/images/medium-images/plantation-shutters.jpg"
image-title:
service-category: domestic
- title: "Vertical Blinds"
description: "domestic/vertical-blinds.md"
image: "assets/images/medium-images/vertical-blinds.jpg"
image-title:
service-category: domestic
- title: "Roman Blinds"
description: "domestic/roman-blinds.md"
image: "assets/images/medium-images/roman-blinds.jpg"
image-title:
service-category: domestic
In this case, the data file serves as a way of referencing the files that contain the content.
Under each YAML
block, the description
field is a path to a markdown include. I’ve tried to make this as logical as possible, by having an appropriate directory structure within the _includes
directory: _includes/services/service-name/content-file.md
… so if you want to add content to the section on “Roller Blinds” on the “Commercial” services page, you need to edit _includes/services/commercial/roller-blinds.md
.
In a similar way, we can reference images, titles and other data for each section.
The HTML Include
The section data are looped through within a HTML include, which is in turn inserted in the service_page
template. IN this case, the include is _includes/layouts/services-sections.html
.
The include does the following:
- Sets up a for loop that goes through each data block in the specified data file.
- Makes section display conditional
- Builds variable content into the correct HTML markup
Sections displayed must have a service-category that corresponds to the page service-category (which needs to be set in the page front-matter). This allows us to easily define section content for different service pages from within the data file.
For example, this is _includes/layouts/services-sections.html
:
{% for section in site.data.service-sections %}
{% if page.service-category == section.service-category %}
<div class="row section">
<div class="col-sm-6">
<h2>{{ section.title }}</h2>
<!--
To properly render markdown includes as HTML, the include tag must be
captured as a string, and this must be passed to the `markdownify` filter.
-->
{% capture section_description %}{% include services/{{ section.description }} %}{% endcapture %}
{{ section_description | markdownify }}
<!--
The following will not parse the markdown include - it won't be properly
rendered into HTML and will display as the original markdown.
-->
{% comment %}
{% include services/{{ section.description }} %}
{% endcomment %}
</div>
<div class="col-sm-5 col-sm-offset-1">
<div class="thumbnail">
<!--
Build the image markup for this section. If an image-caption has been
defined, display it.
-->
<img src="{{ site.baseurl }}/{{ section.image }}" title="{{ section.title }}" alt="{{ section.title }}" class="img-responsive">
{% if nil != service.img-caption %}
<div class="img-caption">{{ service.img-caption }}</div>
{% endif %}
</div>
</div>
</div>
<hr>
{% endif %}
{% endfor %}
This is included within the index.html
page of each service. For example, domestic/index.html
.
index.html
The service page (service-name/index.html
) front matter sets the page title, meta description etc. It also sets the service-category, which is used in the layouts/services-sections.html
include to build the correct section content. For example, this is domestic/index.html
:
---
layout: service-page
title: Domestic Blinds & Shutters
group: navigation
title: Domestic Blinds and Shutters
description: Quality domestic blinds & shutters in Limerick, Clare, Tipperary
service-category: domestic
intro-content: domestic/domestic.md
intro-image: assets/images/logo.png
intro-image-title: "Irish Blinds & Shutters"
extra-content: domestic/domestic-col-2.md
---
{% capture service_intro %}
{% include services/{{ page.intro-content }} %}
{% endcapture %}
{% capture service_extra %}
{% include services/{{ page.extra-content }} %}
{% endcapture %}
<div class="row">
<div class="col-md-6">
<header class="post-header">
<h1>{{ page.title }}</h1>
</header>
{{ service_intro | markdownify }}
</div>
<div class="col-md-5 col-md-offset-1">
<div class="thumbnail hidden-sm">
<img src="{{ site.baseurl }}/{{ page.intro-image }}" title="{{ page.intro-image-title }}" alt="{{ page.intro-image-title }}" class="img-responsive">
</div>
{% if page.col-2-content != empty %}
{{ service_extra | markdownify }}
{% endif %}
</div>
</div>
<hr>
<!-- Include the service sections here. -->
{% include layouts/services-sections.html %}
<!-- End service sections -->
<div class="row">
<div class="col-md-6">
<h2>Contact Us Now to Get Started</h2>
{% include general/body-phone.html %}
</div>
</div>
Conclusion
Did we achieve the objectives of data-driven page sections? Kind of. Overall it’s a pretty good solution - it means that we can manage site content in a fairly straightforward way.
There are still more edit points than I’d like, and I don’t like the way that the service sections are bunched up in a single data file.
I’d prefer to be able to create separate data files for each page category - but I had trouble creating a dynamic reference to the data file whilst setting up the for loop. I still have a lot to learn about Jekyll (and Ruby), so it could well be that I’m missing something obvious.
Resources
- Jekyll Data Files
- Interesting article on dynamic navigation in Jekyll
- Reference to markdown includes
comments powered by Disqus