Dev Notes

Software Development Resources by David Egan.

Page Sections in Jekyll


Jekyll, Static Sites
David Egan

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


comments powered by Disqus