Building a tag cloud for jekyll

Posted by Markus Benning on July 22, 2018

This will show how to create a tag cloud for a jekyll blog using ruby to render the HTML and CSS to style it.

Start with an empty jekyll project or an existing project:

jekyll new jekyll-tagcloud-demo && cd jekyll-tagcloud-demo

Make sure that the archives plugin is configured to generate archive and tag index pages:

The jekyll-archives plugin must be listed in the Gemfile:

gem 'jekyll-archives'

And in the _config.yaml:

plugins:
  - jekyll-archives

jekyll-archives:
  enabled:
    - year
    - month
    - tags
    - categories
  layout: 'page'
  layouts:
    year: year
    month: month
    tag: tag_and_category
  permalinks:
    year: '/:year/'
    month: '/:year/:month/'
    tag: '/tag/:name/'
    category: '/category/:name/'

The 3 layouts for index pages must be created. Example layouts are available from the jekyll-archive plugin documentation.

Copy the following code to _plugins/tagcloud.rb:

module Jekyll
  # render a tag cloud
  class RenderTagCloud < Liquid::Tag
    def initialize(tag_name, text, tokens)
      super
    end

    def render(context)
      base_url = context['site.baseurl']
      tags = context['tags']
      return unless tags.class == Array
      tags.reject! {|i| i.nil? }
      site_tags = context['site.tags']
      by_length = site_tags.values.map(&:length)
      max = by_length.max
      min = by_length.min
      cloud = tags.map do |t|
        tag_size = site_tags[t].length
        "<div class=\"tagcloud-tag #{size_tag(min,max,tag_size)}\">"\
          "<a href=\"#{base_url}/tag/#{t.downcase}\">"\
          "#{t}"\
          "</a>"\
          "</div>"
      end.join("\n")
      "<div class=\"tagcloud\">#{cloud}</div>"
    end

    private

    def size_tag(min, max, count)
      diff = (max - min)
      if count < (min + (diff * 0.1))
        'tagcloud-tag-s'
      elsif count < (min + (diff * 0.3))
        'tagcloud-tag-m'
      elsif count < (min + (diff * 0.5))
        'tagcloud-tag-l'
      else
        'tagcloud-tag-xl'
      end
    end
  end
end

Liquid::Template.register_tag('render_tagcloud', Jekyll::RenderTagCloud)

Then create a tags.html file with following content:

---
layout: page
title: All Tags
permalink: /tags/
---

<div class="tagcloud">

{% capture site_tags %}{% for tag in site.tags %}{{ tag | first }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
{% assign tags = site_tags | split:',' | sort %}
{% render_tagcloud %}

</div>

It will generate a tag cloud for all site tags.

If you like to have a tag cloud under every post you may add the following to your _layout/post.html:


{% assign tags = page.tags | sort %}
{% render_tagcloud %}

This will generate the HTML for tag clouds like:

<div class="tagcloud">
<div class="tagcloud-tag tagcloud-tag-xl"><a href="/tag/jekyll">jekyll</a></div>
<div class="tagcloud-tag tagcloud-tag-xl"><a href="/tag/tagcloud">tagcloud</a></div>
</div>

Whats missing is still some CSS to style the tags.

An example sass would be:

.tagcloud {
  .tagcloud-tag {
    display: inline;
    white-space: nowrap;
    padding: 0.6em;
  }
  .tagcloud-tag-s a {
    // default font size
    color: $cyan;
  }
  .tagcloud-tag-m a {
    font-size: 1.1em;
    color: $blue;
  }
  .tagcloud-tag-l a {
    font-size: 1.3em;
    color: $indigo;
  }
  .tagcloud-tag-xl a {
    font-size: 1.6em;
    color: $red;
  }
}