Examples for custom Liquid template tags for Jekyll
Author: | Admin |
Title: | Example for custom Liquid tags for Jekyll |
Language: | en-US |
Created: | 17:51 on Friday, 08. October 2021 |
Modified: | 17:51 on Friday, 08. October 2021 |
Keywords: | jekyll, ruby, tag, liquid |
Excerpt: | Create a (very) simple Ruby plugin that implements a custom Liquid template tag for a Jekyll site. Includes a few examples like a tag to embed videos. |
Tags: | Jekyll |
Page layout: | nonav |
Table of Contents
This page contains a couple of examples how to implement custom Liquid template tags as Jekyll plugins. Such plugins are of varying complexity, depending on the desired functionality, but normally not to difficult
Some Ruby and general programming knowledge is excepted for understanding
this article. The basic idea is that a tag does something and outputs valid HTML, which replaces
the tag in the source document. A tag can also take parameters and has access to all of the
document, including its meta data. It also can access site data which is usually located in the
data
directory. Each tag is technically a Jekyll plugin written in Ruby plugin and must reside as
single .rb
file in the _plugins
directory of your site’s source code folder. There is no need to
list them in the _config.yaml
since _plugins
is always parsed when building the site.
While it is technically possible to have multiple tags in the same source file, I would advise against it as it makes things messier and harder to control. One file per tag, name the file like the tag to make clear what it does.
For the first example, we write a simple tag and name it sep
(which shall stand for „content
SEParator”). It’s purpose will be very simple: Output a horizontal line, using the <hr> HTML tag.
Styling is done via CSS. The code is simple enough to be understood with even basic Ruby knowledge.
Will these plugins work for Github pages?
That depends on how you deploy your page. If you build locally and deploy only the _site
- then yes,
everything will work, because _site
is the compiled HTML output and does not require plugins.
If you let GitHub build your page, then many (or all) 3rd party plugins will NOT work, because GitHub allows only a limited subset of Jekyll plugins for reasons of security.
Here is the first example code:
This is the first and most simple version. It renders a <hr> tag, using a fixed (hard coded) CSS
class named content-separator
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# all tags must be part of the Jekyll module and inherit from Liquid::Tag
module Jekyll
class SepTag < Liquid::Tag
# constructor, init the superclass and remember text (the parameter string)
def initialize(tag_name, text, tokens)
super
@text = text # we do not really need this in our example.
end
# the render() method must return the generated HTML
# output the result. That's all, really :)
def render(context)
output = "<hr class=\"content-separator\">"
return output
end
end
end
Liquid::Template.register_tag('sep', Jekyll::SepTag)
This works, but it is fairly limited. Obviously, it would be nice to pass the name of the CSS class
as parameter, wouldn’t it? That way, you could use the tag to render different type of content
separators. Fortunately, this is easy to implement with only a couple more lines as shown in the
next version of the plugin. All Liquid Tags can take an arbitrary number of parameters and all
parameters are passed to the tag’s constructor as a single string in the second parameter name
text
. What you do with these argument is completely up to you, for this simple plugin, we
interpret the argument string as a CSS class (or more than a single class when the parameter
contains spaces).
For more advanced tags, having multiple arguments are likely something desirable, so using a common format like JSON would certainly be a good idea. More on that later.
Now, here is a version of the the separator
tag that can take a parameter and use it as a CSS
class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Tags are part of the Jekyll module and must inherit from Liquid::Tag
module Jekyll
class SepTag < Liquid::Tag
# constructor, remember text which holds the parameters passed
# to the tag as a single string
# also, set a default in case no parameter was passed.
def initialize(tag_name, text, tokens)
super
@defaultclass = "default-content-separator"
@text = text.blank? ? @defaultclass : text
end
# render the ouput. Make sure, we have a valid cssclass
def render(context)
params = @text.split("|")
cssclass = params[0].strip
cssclass.blank? ? @defaultclass : cssclass
output = "<hr class=\"" + cssclass + "\">"
return output
end
end
end
Liquid::Template.register_tag('sep', Jekyll::SepTag)
Well, that’s it. We have our tag and can use it with or without a parameter.
1
2
3
4
{% sep %} renders a HR with the default CSS class default-content-separator
{% sep foobar %} renders a HR of class foobar.
{% sep foobar thin %} renders a HR of class "foobar thin".
Here is an example of 2 separators using different CSS classes
The CSS:
1
2
3
4
5
6
7
8
9
hr.content-separator {
height: 5px;
border: none;
margin: 1em 30px;
background-color: $accent_color;
}
hr.content-separator.thin {
height: 3px;
}
Another example of a tag, this time a bit more complex
Here is a slightly more complex tag. It embeds videos from either YouTube or other sites and offers a couple of options. This tag accepts various arguments which must be formatted as valid JSON. You can either pass a full URL to a compatible video (mp4 or webm) or a YouTube video ID.
Why JSON for the parameters?
Because it works and is straightforward for most web developers and tech folk nowadays. It has become one of the most popular formats for passing and storing data on the web and unless your data sets become very complex, it’s fairly easy to use. JSON knowledge is also considered a basic skill requirement for many developers.
The following example embeds a video directly by its URL.
{% vid { "url": "https://i.imgur.com/PTRPi4I.mp4", "width": "95%" } %}
This will embed a YouTube video
{% vid { "id": "wB8jCwkO-sw", "width": "95%" } %}
The width
parameter is optional and sets the container width. It must be in valid CSS format
including the unit. Other valid parameters are wrapper
to set the CSS class for the wrapper
element, while style
can be used to set CSS attributes for the wrapper element directly. The
style
and width
parameters are mutually exclusive.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# Tags are part of the Jekyll module and must inherit from Liquid::Tag
require 'json'
module Jekyll
# implements a simple video tag
class VidTag < Liquid::Tag
# constructor, remember text which holds the parameters passed
# to the tag as a single string (JSON format expected)
def initialize(tag_name, text, tokens)
super
@text = text
end
# render the ouput.
def render(context)
# paramter string must exist
if ( !@text.nil? && !@text.empty? )
jdata = JSON.parse(@text)
if( jdata.key?('url') || jdata.key?("id") )
output = ""
#allow a CSS class for the wrapper element
wrapperclass = jdata.key?("wrapper") ? jdata["wrapper"] : ""
# and a custom width
width = jdata.key?("width") ? jdata["width"] : ""
# reasonable defaults for the <video> tag
options = jdata.key?("options") ? jdata["options"] : "controls muted"
yt = jdata.key?("id") ? 1 : 0
if ( width != "" )
output = '<div style="width: ' + width + '; margin:auto;" class="' + wrapperclass + '">'
else
output = '<div class="' + wrapperclass + '">'
end
output += '<div class="ytcontainer">'
if (yt == 1)
id = jdata.key?("id") ? jdata["id"] : ""
if( id == "" )
return ""
end
output += '<iframe class="yt" allowfullscreen src="https://www.youtube.com/embed/' + id + '"></iframe>'
else
output += '<video class="yt" ' + options + ' poster="' + jdata["url"].strip + '">'
output += '<source src="' + jdata["url"].strip + '" type="video/mp4">'
output += '<source src="' + jdata["url"].strip + '" type="video/webm">'
output += '</video><'
end
output += '</div></div>'
return output
end
return ""
end
return ""
end
end
end
Liquid::Template.register_tag('vid', Jekyll::VidTag)
This CSS fragment is required for the code to work
1
2
3
4
5
6
7
8
9
10
11
12
13
14
div.ytcontainer {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
}
iframe.yt, video.yt {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}