DEV Community

C. Plug
C. Plug

Posted on

2 1

Creating Custom Tags in Jekyll, with an actual example

Shields.io is a "service for concise, consistent, and legible badges in SVG and raster format, which can easily be included in GitHub readmes or any other web page."

Shields.io | Shields.io

Concise, consistent, and legible badges

favicon shields.io

Their service takes query parameters to their API endpoint to generate a badge / shield. For example:

https://img.shields.io/static/v1?label=Find%20me%20on&message=GitHub&color=181717&style=flat&logo=github
Enter fullscreen mode Exit fullscreen mode

becomes:

My GitHub

I was planning to use this in my personal blog, when I realized a problem; it is hard to maintain.
While it may be rare to change the content of those shields, it would be nice to have a maintainable way to put a shield on my blog.

Since I use Jekyll, I can define a custom 'Liquid tag.' Just like we can embed stuff from compatible services with liquid tags here in DEV.to, e.g., {% github repo %} and {% embed website %}, we can make a custom tag, say, {% shields_io payload %}, to display a shield.

How does Liquid tag on Jekyll work?

The Liquid tag on Jekyll takes the following format:

{% tag_name [tag_argument] %}
Enter fullscreen mode Exit fullscreen mode

It has a tag name, and one optional argument.

See the problem here? There is at most one value accepted as an argument. Shields.io takes way more than that, and putting query parameter here does not solve anything.

There is still hope, though; let's take a look at the code we'll be writing.

module Jekyll
  class CustomTag < Liquid::Tag
    def initialize(tag_name, arg, parse_context)
      super
      # @type [String]
      @arg = arg
    end
    def render(_context)
      "The argument is #{@arg}" # return the render result here
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

The arg argument is the argument passed from the tag. It's a string. So we can do something with the input.

And looking back at the original problem, we are trying to pass a set of key-value pairs to an API endpoint.

So, I decided to pass a JSON payload here; it can be prettified for our purposes, and Ruby supports JSON deserialization out of the box.

How to turn it into the URL

Since Ruby can turn the input JSON into a hash, we can iterate on this hash and construct the query parameter.

So the idea is to deserialize the JSON and store it in a variable:

    def initialize(tag_name, input, parse_context)
      super
      # @type [Hash]
      @config = JSON.load(input.strip)
    end
Enter fullscreen mode Exit fullscreen mode

And construct the query parameter. I also decided to include href and alt for other purposes into the JSON payload, but they are not relevant for the request. So I extract their values and remove it from the input hash before turning the rest into the query parameter.

def render(_context)
  href = @config[:href]
  alt = @config[:alt]
  @config.delete(:href)
  @config.delete(:alt)
  shield_tag = <<HTML
  <img src="https://img.shields.io/static/v1?#{hash_to_query}"
HTML
  if alt != nil
    shield_tag += " alt=\"#{alt}\" />"
  else
    shield_tag += " />"
  end
  if href != nil
    <<HTML
<a href="#{href}">
#{shield_tag}
</a>
HTML
  else
    shield_tag
  end
end

private
def hash_to_query
  @config.to_a.map { |k, v|
    "#{k}=#{v}"
  }.join '&'
end
Enter fullscreen mode Exit fullscreen mode

Result

Here is my creation - copy the last two files and run a local server!

The shields_to Custom Jekyll Liquid Tag

What

Shields.IO is a web service to create a 'badge' you may find in a README in many GitHub repositories.
All you need to craft is a URL, and the service will interpret your request.

Example shield by URL

https://img.shields.io/static/v1?label=Find%20me%20on&message=GitHub&color=181717&style=flat&logo=github

Find me on GitHub

There is one issue with this approach; the URL is too long to handle for human.
I wrote these files to create a Liquid tag for Jekyll that makes it easier to create those shields.

How to use

Copy custom-tags.rb into $SOURCE_DIR/_plugins directory, where $SOURCE_DIR is . (your Jekyll project root) by default and may change if you have source key in your _config.yml.

A restart of bundle exec jekyll serve is required should you be running one already.

The syntax is:

{% shields_io <JSON here> %}

The idea is to pass a JSON object consisting of query parameters.

{
  "href": "https://www.github.com/clpsplug",
  "label": "Find me on",
  "message": "GitHub",
  "color": "181717",
  "style": "flat",
  "logo": "github",
  "alt": "GitHub"
}
key required? content
href NO A URL. Specifying this key will turn the shield into a clickable link
label NO The leftside text of the shield. If left, it will be "static"
message YES The rightside text of the shield.
color NO The color of the right side (some styles may ignore this value.) This can be color name (see Shields.io for supported names) or hex color code. Hex color codes must not contain #. If left, 'inactive' is used.
style NO The shield style, see Shields.io for valid values. If left, 'plastic' is used.
logo NO Service name or Simple Icons icon name; display on the left of the leftside text.
alt NO, but strongly recommended The alternative text for the image. This should be specified for accessibility reasons and when the service fails for any reason.

Notes

  • JSON payload should be passed without being surrounded by quotes; quotes will break them.
  • Liquid tags can span across multiple lines, so it is safe to prettify your JSON.

License

MIT

view raw README.md hosted with ❤ by GitHub
require 'json'
module Jekyll
class ShieldsIOTag < Liquid::Tag
def initialize(tag_name, input, parse_context)
super
# @type [Hash]
@config = JSON.parse input.strip, { symbolize_names: true }
end
def render(_context)
href = @config[:href]
alt = @config[:alt]
@config.delete(:href)
@config.delete(:alt)
shield_tag = <<HTML
<img src="https://img.shields.io/static/v1?#{config_to_query}"
HTML
if alt != nil
shield_tag += " alt=\"#{alt}\" />"
else
shield_tag += " />"
end
if href != nil
<<HTML
<a href="#{href}">
#{shield_tag}
</a>
HTML
else
shield_tag
end
end
private
def config_to_query
@config.to_a.map { |k, v|
"#{k}=#{v}"
}.join '&'
end
end
end
Liquid::Template.register_tag('shields_io', Jekyll::ShieldsIOTag)
view raw custom-tags.rb hosted with ❤ by GitHub
---
layout: your_layout
---
<p>
{% shields_io {
"href": "https://www.github.com/clpsplug",
"label": "Find me on",
"message": "GitHub",
"color": "181717",
"style": "flat",
"logo": "github",
"alt": "GitHub"
} %}
</p>
view raw example.liquid hosted with ❤ by GitHub

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs