Getting social with Jekyll

2013-08-13 19:08

Jekyll (and Octopress, by extension) uses the Liquid templating language to process your site’s template on the way to final rendered pages. It’s easy to extend generation of normal <title> and <meta> tags to include Open Graph tags, making your site a better citizen of the social graph.

Though only widely implemented by Facebook, some no-name social network, the Open Graph protocol is available for anyone to structure their content. Based on the “website” object type, I’d like to include the following information on my pages:

  • The canonical URI
  • Post or page title
  • Localization information
  • A preview image URI, both SSL and non-SSL, along with dimensions and MIME type
  • The name of my blog
  • A short description of any given page’s content
  • Links from the content page to the Facebook page and my personal account as owner.

With that in mind I made a few additions to my site template.

First off, some configuration in my site’s _config.yml to choose a default share image and to name my Facebook page URI and my personal account’s FBID.

  1. # Open Graph
  2. og_image: images/cooltrainer-fb.png # Default image for Open Graph tag
  3. og_image_type: image/png
  4. og_image_width: 300
  5. og_image_height: 300
  6. fb_profile_id:
  7. fb_admins: 542154016

It’s necessary to declare the og and fb RDFa namespaces. I use the HTML5 prefix syntax instead of XHTML-style xmlns.

  1. <!DOCTYPE html>
  2. <html prefix="og: fb:">

The type, site_name, and title tags are simplistic and come straight from the site configuration in _config.yml except title which is overridden by the post/page title from the YAML front-matter in every post:

  1. <meta property="og:title" content="{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% else %}{% endif %}">
  2. <meta property="og:type" content="website">
  3. <meta property="og:site_name" content="{{ site.title }}">

I’m an American anglophone:

  1. <meta property="og:locale" content="en_US">

Canonical URI is a little trickier, since that variable needs to be composed from site.url and friends. The default Octopress theme comes with a capture for a canonical variable, and I’ve replicated it in my custom theme with a slight variation.

  1. {% capture canonical %}{{ site.url }}{% if site.permalink contains '.html' %}{{ page.url }}{% elsif site.permalink contains category_dir %}{{ '/' | page.url | remove:'index.html' | '/' }}{% else %}{{ page.url | remove:'index.html' | '/' }}{% endif %}{% endcapture %}
  1. <meta property="og:url" content="{{ canonical }}">

Description relies on another variable capture you most likely already have, which substitutes an excerpt of text when no page.description is defined in your page YAML front-matter. I then sanitize it and truncate to 300 characters, the maximum Facebook will display by default for a stream attachment description.

  1. {% capture description %}{% if page.description %}{{ page.description }}{% else %}{{ content | raw_content }}{% endif %}{% endcapture %}
  1. <meta property="og:description" content="{{ description | strip_html | condense_spaces | truncate:300 }}">

Preview image og:image tag and friends are the most frustrating due to the necessary separate tags for SSL and non-SSL image resources. I also specify MIME type and image dimensions manually since Facebook’s Open Graph parser refused to see my 300x300px image as larger then 200x200px otherwise and continually discarded it in favor of a random large photo from the page.

My image tags check for the substring https:// in site.url to decide whether or not to show og:image:secure_url as well as to replace it with http:// for og:image. I host Cooltrainer SSL-only, but this should be a general solution for those who may host mixed or with no SSL at all.

My _config.yml defines a default share image, images/cooltrainer-fb.png, but individual pages can override that default by defining og_image, og_image_type, og_image_width, and og_image_height in their YAML front-matter.

  1. <meta property="og:image" content="{% if site.url contains 'https://' %}{{ site.url | replace:'https://','http://' }}{% else %}{{ site.url }}{% endif %}/{% if page.og_image %}{{ page.og_image }}{% else %}{{ site.og_image }}{% endif %}">
  2. {% if site.url contains 'https://' %}<meta property="og:image:secure_url" content="{{ site.url }}/{% if page.og_image %}{{ page.og_image }}{% else %}{{ site.og_image }}{% endif %}">{% endif %}
  3. <meta property="og:image:type" content="{% if page.og_image %}{{ page.og_image_type }}{% else %}{{ site.og_image_type }}{% endif %}">
  4. <meta property="og:image:width" content="{% if page.og_image %}{{ page.og_image_width }}{% else %}{{ site.og_image_width }}{% endif %}">
  5. <meta property="og:image:height" content="{% if page.og_image %}{{ page.og_image_height }}{% else %}{{ site.og_image_height }}{% endif %}">

I use the Facebook-specific fb:profile_id to attach the content to the page on Facebook so it can direct people to follow the page when content is shared. fb:admins links the page to my personal account which allows me access to Insights for this domain.

  1. {% if site.fb_profile_id != empty %}<meta property="fb:profile_id" content="{{ site.fb_profile_id }}">{% endif %}
  2. {% if site.fb_admins != empty %}<meta property="fb:admins" content="{{ site.fb_admins }}">{% endif %}

Lastly, I added “Share” buttons for quick sharing with Facebook, Twitter, and Reddit by creating a new include, source/_includes/social_share.html, and including it in my post template.

  1. {% include social_share.html %}

The include itself is very simple, just a container div and three image anchors.

I avoid any privacy-violating tracking Javascript, so using the official Facebook Like and Send buttons, the Tweet button, or a Reddit button is out of the question.

I opted instead for static sharing links for all three, including the deprecated but still-functioning sharer.php URI for Facebook, the “tweet” intent URI for Twitter, and the “submit” URI for Reddit. The three links are composed in mostly the same way as the Open Graph tags with the additional consideration that they must be escaped, including replacing spaces with %20.

The icons I use are part of the Social media icons pack by Tom Plechacek.

  1. <div id="social-share">
  2. <a href="{{ canonical }}&p%5Bimages%5D%5B0%5D={{ site.url }}/{% if page.og_image %}{{ page.og_image }}{% else %}{{ site.og_image }}{% endif %}&p%5Btitle%5D={% if page.title %}{{ page.title | escape | replace:' ','%20' }}{% else %}{{ site.title | escape | replace:' ','%20' }}{% endif %}&p%5Bsummary%5D={% if description != empty %}{{ description | strip_html | condense_spaces | truncate:150 | replace:' ','%20' }}{% endif %}"><img src="/images/share-fb.png" title="Share on Facebook" alt="Facebook share button"></a>
  3. <a href="{{ canonical }}&text={% if page.title %}{{ page.title | escape | truncate:80 | replace:' ','%20' }}{% else %}{{ site.title | escape | truncate:80 | replace:' ','%20' }}{% endif %}&via=okeeblow"><img src="/images/share-twitter.png" title="Share on Twitter" alt="Twitter share button"></a>
  4. <a href="{{ canonical }}&title={% if page.title %}{{ page.title | escape | replace:' ','%20' }}{% else %}{{ site.title | escape | replace:' ','%20' }}{% endif %}"><img src="/images/share-reddit.png" title="Share on Reddit" alt="Reddit share button"></a>
  5. </div>

That’s all there is to it. You can “View source” on this or any other page on Cooltrainer to see Open Graph in action:

  1. <meta property="og:title" content="Getting social with Jekyll">
  2. <meta property="og:type" content="website">
  3. <meta property="og:url" content="">
  4. <meta property="og:locale" content="en_US">
  5. <meta property="og:site_name" content="">
  6. <meta property="og:description" content="Wherein normal tags just aren't good enough.">
  7. <meta property="og:image" content="">
  8. <meta property="og:image:secure_url" content="">
  9. <meta property="og:image:type" content="image/png">
  10. <meta property="og:image:width" content="300">
  11. <meta property="og:image:height" content="300">
  12. <meta property="fb:profile_id" content="">
  13. <meta property="fb:admins" content="542154016">

The functional share buttons should be visible to the bottom right. Try one of them out by sharing with your Jekyll-using friends :)