Why I love data files in Jekyll

Chen Hui Jing / @hj_chen

🇲🇾 👾 🏀 🚲 🖌️ 👟 💻 🖊️ 🎙 🦊 🥑 🧗 🏳️‍🌈
Chen Hui Jing
Jing
@hj_chen
Nexmo Developer Relations

🥑 Developer Advocate 🥑

Nexmo

What are data files?

  • Custom data that can be accessed via the Liquid templating system
  • Supported formats:
    • YAML
    • JSON
    • CSV
    • TSV
  • Helps avoid repetition in your templates
  • Keeps the focus on the content

Using data files (1/2)

Place data files (supported formats only) in the _data folder

Folder structure

Jekyll will recognise them during site generation

Data will be accessible via site.data.NAME_OF_FILE

Using data files (2/2)

speakers.yml

gloria:
  name: "Gloria Soh"
  twitter: "Gloriasohss"
  shortcode: "gloria"
  bio: "Gloria is a CSS queen who is bitcoin rich."

jacob:
  name: "Jacob Tan"
  twitter: "jacobtyq"
  shortcode: "jacob"
  bio: "Plays too much Factorio… and Developer by day"
  
sarah:
  name: "Sarah Lienert"
  twitter: "SazzarJ"
  shortcode: "sarah"
  bio: "Sarah likes to bake things for Talk.CSS and teach children."

speakers.html

<ul class="l-speakers c-speakers">
  {% for speaker in site.data.speakers %}
  <div class="l-speaker c-speaker">
    <figure>
      <img class="c-speaker__img" src="{{ site.url }}/assets/img/speakers/{{ speaker[1].shortcode }}.jpg" srcset="{{ site.url }}/assets/img/speakers/{{ speaker[1].shortcode }}@2x.jpg 2x" alt="{{ speaker[0].name }}"/>
      {% if speaker[1].twitter %}
      <figcaption><a class="c-speaker__link" href="https://twitter.com/{{ speaker[1].twitter }}">@{{ speaker[1].twitter }}</a></figcaption>
      {% elsif speaker[1].github %}
      <figcaption><a class="c-speaker__link" href="https://github.com/{{ speaker[1].github }}">@{{ speaker[1].github }}</a></figcaption>
      {% else %}
      <figcaption><span class="c-speaker__link">@{{ speaker[1].shortcode }}</span></figcaption>
      {% endif %}
    </figure>
    <p class="c-speaker__intro">{{ speaker[1].bio }}</p>
  </div>
  {% endfor %}
</ul>

Liquid templating engine

Open-source template language created by Shopify and written in Ruby

Features include:

  • Logical and comparison operators (==, !=, <, >, <=, >=, or, and)
  • Truthy and falsy
  • Control flow
  • Iteration
  • Variables
  • Filters

Truthy and falsy in Liquid

All values in Liquid are truthy except nil and false

Strings and arrays, even when empty, are truthy

Check for emptiness using blank or empty

Control flow

if Executes a block of code only if a certain condition is true
unless The opposite of if – executes a block of code only if a certain condition is not met
elsif / else Adds more conditions within an if or unless block
case / when Creates a switch statement to compare a variable with different values. case initialises the switch statement, and when compares its values

Relevant questions

unless

Your best substitute for not

{% unless post.special %}
<header class="c-event__header">
  <h2 class="c-event__title">
    {{ post.title }}
  </h2>
  <p><strong>Event location:</strong> {{ post.location }}</p>
  <p><strong>Event date:</strong> {{ post.event-date | date: "%b %-d, %Y" }}</p>
</header>

<a href="https://www.meetup.com/SingaporeCSS/events/{{ post.event-id }}/" class="c-rsvp"><span>RSVP</span><span>at meetup.com</span></a>

<div class="c-event__content">
  {{ post.content | markdownify }}
</div>
{% endunless %}
  • Controls rendering when conditional returns nil or false
  • Prevent rendering of empty HTML elements
  • Useful when used in conjunction with front matter variables

Iteration

Iterator Description Parameters
for Repeatedly executes a block of code limit, offset, range, reversed
cycle Loops through a group of strings and outputs them in the order that they were passed as parameters (must be used within for loop) cycle group
tablerow Generates an HTML table (has to be wrapped with <table> tags) limit, offset, range, cols

Display only latest post

SingaporeCSS home page
{% for post in site.posts limit:1 %}
<article class="c-upcoming-event {{ post.css }}">
  {% unless post.special %}
  <header class="c-event__header">
    <h2 class="c-event__title">
      {{ post.title }}
    </h2>
    <p><strong>Event location:</strong> {{ post.location }}</p>
    <p><strong>Event date:</strong> {{ post.event-date | date: "%b %-d, %Y" }}</p>
  </header>
  
  <a href="https://www.meetup.com/SingaporeCSS/events/{{ post.event-id }}/" class="c-rsvp"><span>RSVP</span><span>at meetup.com</span></a>
  
  <div class="c-event__content">
    {{ post.content | markdownify }}
  </div>
  {% endunless %}
</article>
{% endfor %}

Display list of 3 posts before the latest one

SingaporeCSS home page
<h2>Past meetups</h2>
<ul class="l-past-events c-past-events">
  {% for post in site.posts offset:1 limit:3 %}
    <li class="l-past-event c-past-event">
      <a class="c-past-event__link" href="{{ post.url | prepend: site.baseurl }}">
        <span class="c-past-event__meta">{{ post.event-date | date: "%b %-d, %Y" }}</span>
        <h3>{{ post.title }}</h3>
      </a>
    </li>
  {% endfor %}
</ul>
<a class="l-archive__link c-archive__link" href="{{ "/archives" | prepend: site.baseurl }}">View full list</a>

Variables

Possible to create new Liquid variables in addition to predefined ones

assign Creates a new variable
capture Captures the string inside of the opening and closing tags and assigns it to a variable
increment Creates a new number variable, and increases its value by one every time it is called
decrement Creates a new number variable, and decreases its value by one every time it is called

Custom front matter variables can be defined and accessed via the page.VARIABLE_NAME syntax

Establishing relationships across data files

speakers.yml

gloria:
  name: "Gloria Soh"
  twitter: "Gloriasohss"
  shortcode: "gloria"
  bio: "Gloria is a CSS queen who is bitcoin rich."

jacob:
  name: "Jacob Tan"
  twitter: "jacobtyq"
  shortcode: "jacob"
  bio: "Plays too much Factorio… and Developer by day"
  
sarah:
  name: "Sarah Lienert"
  twitter: "SazzarJ"
  shortcode: "sarah"
  bio: "Sarah likes to bake things for Talk.CSS and teach children."

videos.yml

s2502:
  title: "CSS grid for noobs"
  link: "https://youtu.be/0Laanmn3zWc"
  shortcode: "s2502"
  description: "Shirlaine and Gloria share their experience of learning and using CSS grid. Video from JuniorDev.SG."

s2601:
  title: "Frontend development for distributed teams"
  link: "https://youtu.be/xj-MbcBfu6Y"
  shortcode: "s2601"
  description: "Ardy shares how frontend development is done at Skyscanner, with a distributed team of more than 400 engineers."

s2602:
  title: "Faux sub-grid"
  link: "https://youtu.be/ChnITWfUfFU"
  shortcode: "s2602"
  description: "Using CSS grid but sad that subgrid isn't here yet? Zell has open-sourced his Sass-powered workaround."

meetups.yml

25:
  colour:
    name: lime
    hex: "#00FF00"
    rgba: rgba(0, 255, 0, 1)
  videos:
    - ref: s2501
    - ref: s2502
  speakers:
    - ref: ed
    - ref: gloria
    - ref: shirlaine

26:
  colour:
    name: indigo
    hex: "#4b0082"
    rgba: rgba(75, 0, 130, 1)
    text: "#fff"
  videos:
    - ref: s2601
    - ref: s2602
    - ref: s2603
  speakers:
    - ref: zell
    - ref: sheldon
    - ref: ardy

Using assign to simplify custom data (1/2)

40:
  colour:
    name: lightseagreen
    hex: "#20b2aa"
    rgba: rgba(32, 178, 170, 1)
  videos:
    - ref: s4001
    - ref: s4002
    - ref: s4003
    - ref: s4004
  speakers:
    - ref: alex
    - ref: ujjwal
    - ref: linhan
    - ref: mike
{% assign meetup = site.data.meetups[page.meetup] %}
<div class="c-videos">
  /* other code */

  {% for videos in meetup.videos %}
  {% assign video = site.data.videos[videos.ref] %}
  <div class="c-video">
    <a class="c-video__link" href="{{ video.link }}">
      <img class="c-video__img" src="{{ site.url }}/assets/img/videos/talk-{{ page.meetup }}/{{ video.shortcode }}.jpg" srcset="{{ site.url }}/assets/img/videos/talk-{{ page.meetup }}/{{ video.shortcode }}@2x.jpg 2x" alt="Link to {{ video.title }} video"/>
    </a>
    <p class="c-video__desc">{{ video.description }}</p>
  </div>
  {% endfor %}

  /* moar code */
</div>

Using assign to simplify custom data (2/2)

40:
  colour:
    name: lightseagreen
    hex: "#20b2aa"
    rgba: rgba(32, 178, 170, 1)
  videos:
    - ref: s4001
    - ref: s4002
    - ref: s4003
    - ref: s4004
  speakers:
    - ref: alex
    - ref: ujjwal
    - ref: linhan
    - ref: mike
{% if meetup.colour %}
{% assign colour = meetup.colour %}
<div class="c-colour">
  {% if colour.text %}
  <div class="c-swatch" style="background-color:{{ colour.hex }};color:{{ colour.text }}">
  {% else %}
  <div class="c-swatch" style="background-color:{{ colour.hex }}">
  {% endif %}
    <div class="c-swatch__txt">
      <p>{{ colour.name }}</p>
      <p>{{ colour.hex }}</p>
      <p>{{ colour.rgba }}</p>
    </div>
  </div>
<h4>CSS colour of the month</h4>
</div>
{% endif %}

Count of speakers with forloop.length

Variables can be used outside the for loop they were declared in.

{% for speaker in site.data.speakers %}{% assign count = forloop.length %}{% endfor %}

So far, {{ count }} lovely people have spoken at Talk.CSS, we want you to be one of them too. If you're thinking, “but I have nothing to talk about”, we can help. Anything related to CSS will work. It could be something you debugged at work, or about a new property you read about, or something you built, or even a rant about why something doesn't work the way you want it.

☝️ speakers.md ☝️

Using capture for complex strings

Variables created through {% capture %} are strings.

Can create complex strings using other variables created with assign.

{% if page.css %}
{% capture special_styles %}/assets/css/{{ page.css }}.css{% endcapture %}
<link rel="stylesheet" href="{{ special_styles | prepend: site.baseurl }}">
{% endif %}

SingaporeCSS website workflow (1/2)

speakers.yml

ollie:
  name: "Ollie Monk"
  twitter: "ollie_monk"
  shortcode: "ollie"
  bio: "A modern Renaissance man with a wide range of interests. Looking for his headphones. Clearly did not write this bio himself."

jinjiang:
  name: "Zhao Jinjiang"
  twitter: "zhaojinjiang"
  shortcode: "jinjiang"
  bio: "0.1x engineer. Loves football. Does frontend."

post.markdown

---
layout: post
title: "Talk.CSS #43"
date: 2019-08-07 19:00:00 +0800
location: HOOQ
event-date: 2019-09-04 19:00:00 +0800
categories: meetup
meetup: 43
event-id: 261692436
novideos: true
---

videos.yml

s4301:
  title: "Lean animations"
  link: "https://youtu.be/_QN_ahQN0Ic"
  shortcode: "s4301"
  description: "Jinjiang shares some very practical tips on how to quickly add animations to your design or product."

s4302:
  title: "An exploration of CJSS - A CSS based web framework"
  link: "https://youtu.be/x1U9tPwRiiw"
  shortcode: "s4302"
  description: "CSS-in-JS? How about JS-in-CSS instead? Yishu ponders some deep questions about web development, plus LIVE-CODING!"

s4303:
  title: "Sometimes you gotta abuse Grid"
  link: "https://youtu.be/9DwzDzCNDwI"
  shortcode: "s4303"
  description: "Sheldon encountered a problem and solved it the best way he could, with Grid. Context is everything, my friends."

post.markdown

---
layout: post
title: "Talk.CSS #43"
date: 2019-08-07 19:00:00 +0800
location: HOOQ
event-date: 2019-09-04 19:00:00 +0800
categories: meetup
meetup: 43
event-id: 261692436
---

If you are also using YML…

There is a difference between lists and dictionaries.

41:
  colour:
    name: steelblue
    hex: "#4682b4"
    rgba: rgba(70, 130, 180, 1)
    text: "#fff"
  videos:
    - ref: s4101
    - ref: s4102
    - ref: s4103
    - ref: s4104
  speakers:
    - ref: van
    - ref: dylan
    - ref: weiyuan
    - ref: ollie
  • All members of a list are lines at the same indentation level starting with a "- "
  • A dictionary is represented by key/value pairs in the form key: value, where the colon must be followed by a space
  • Complicated data structures like lists of dictionaries, or dictionaries with lists as values are possible

SingaporeCSS website

Source code: https://github.com/SingaporeCSS/singaporecss.github.io

On the web: https://singaporecss.github.io

SingaporeCSS

Thank you!

Websitehttps://www.chenhuijing.com

Twitter@hj_chen

Medium@hj_chen

Codepen@huijing

Font is Public Sans by USWDS