Ghost blog enhancements

This post is about the various enhancements that I have added to the Ghost system for this blog. This is a follow-up on the other post regarding how to convert a Ghost blog into a static website.

This blog in itself is mainly for the purpose a certain AI project that I am working on.

All of the enhancements discussed here are visible live in this very blog.

Customizing the tag header image

By default Ghost picks up the header image defined for the home page and use the same image for the header of all tag pages. Here a tag page is the page that you landed on after clicking on a tag in the blog's tag cloud, which shows the list of all posts that has the tag.

My problem with this is that visually a tag page (i.e., the blog tag page) now looks almost identical to the home page, so it takes some effort to tell them apart.

Fortunately the tag header image can be easily restyled from CSS. Following is an example:

.main-header.tag-head  {
    background-image: url("newtagimg.jpg") !important;

Display Post Images on Home Page

The Ghost home page displays a list of recent posts in the all-text format. It is much preferable that we are able to display an image thumbnail for each post, since it is visually much more pleasing.

This article shows a way on how to do this, using the header image defined for each post as the thumbnail.

However, if you do not have the header image defined for every post, then you will be seeing a broken-image icon for those posts. To handle this, for the image element inserted you should add an 'onerror' attribute as follows:

<img src="{{image}}" onerror="'none'" />

Adding universal translator

This is fairly straightforward, just go to Google website translator and get the embed code for inserting into your blog. The visitors to your blog will then be able to view your posts in one of the more than 100 languages.

For my posts, the quality of translation by this Google service is actually pretty poor in many cases. I kept this feature only because the TAI open-source project described in this blog is meant to serve the entire humanity, as such I wish to broaden its reach as much as possible, even if the translation is quirky.

Adding a tag cloud

A tag cloud is very useful for providing quick viewing and navigation to different types of posts in a blog. For some reasons this feature is not built into Ghost's template engine (as of version 0.8.0), so I have to find outside hacks to make this happen.

I found an article that talks about how to add such a tag cloud. Unfortunately it is in Chinese, but it does work.

The only problems that I found are:

  1. The post count is not showing, and as such some tags appear to be clipped on the right side. This is remedied by using CSS to hide the elements that contain a missing post count. I traced the problem on the server side to the file tag_cloud.js:

    tagCloudOptions = { limit: 'all', include: ['post_count'].join(','), context: 'internal' }; 
    return api.tags.browse(tagCloudOptions).then(function(tags){

    where the result from the call to api.tags.browse does not return post_count in it. Turns out that the post count is not in tags.tags.post_count as the original code expected, but rather in tags.tags.count.posts. The query also must be changed from:

    tagCloudOptions = { limit: 'all', include: ['post_count'].join(','), context: 'internal' }; 


    tagCloudOptions = { limit: 'all', include: 'count.posts', context: 'internal' }; 

    Once adjusted for these difference then it works fine.

Adding QR code

QR code is very handy for opening a webpage on a mobile device quickly and easily through the mobile's camera. Here I have added a QR code on every page, so anyone can open the page by simply using a smartphone's camera to scan it.

My solution is to add the QR code from my own Javascript code as follows:

    var url = location.href;
    var qrcode = '<img src="|0&chl=' + url + '" target="_blank" title="Show QR Code"></img>';
    var sidebar = $('.sidebar');

Adding a navigation bar

When you are in the middle of reading a long post, Ghost does not make it easy to navigate around. It takes some effort scroll back to the top of the page, to find the place for making comment, or to go to the blog home page, etc. I solved this problem by adding a navigation bar at the top of all pages in the blog.

The behavior of this bar is as follows:

  1. The bar offers buttons for navigating to the home page, the top of the page, the comment section, and the help page.
  2. When the page is scrolled down the bar will disappear
  3. When the page is scrolled up the bar will appear, which makes the bar easily available with just a simple scroll or swipe from anywhere.
  4. The bar can be collapsed and reopened as needed.
  5. The state of the bar (i.e., whether collapsed or not) is remembered using a site-wide non-expiring cookie, so it will keep it state when the blog is revisited later.
  6. If user is a Ghost admin (found by using JavaScript code to inspect browser's localStorage as follows),

    var session = JSON.parse(localStorage.getItem('ghost:session'));
    isAdmin = ! $.isEmptyObject(session&&session['authenticated']);

then also add another button for quick navigation to the Ghost admin for editing the page.

You can test out these features (except for the admin one) right on this page.

Customizing the error page

The default 404 error page for Ghost is rather simplistic, not vertically centered, also looks a little off to the side when viewed on small screen mobile devices.

As such I have customized it further by modifying the file core/server/views/user-error.hbs

Unfortunately I also found out that once my Ghost blog is converted to a static website using Buster, then somehow it will auto-navigate to the home page on 404 error. Perhaps I still need to do something on the the AWS S3 site that I use to host the blog, but I will leave it at that since this feature is not that important to me.

Display math formulas

A key piece of my TAI project is its machine learning capability, and as such there are many discussions around machine learning algorithms in this blog, which involves a lot of math.

It order to display math formulas in a nice way I choose to use MathJax for the purpose. More about this can be found in this post. As an example, here is a random math formula rendered using MathJax:

\[f(a) = \frac{1}{2\pi i} \oint_\gamma \frac{f(z)}{z-a}, dz\]

I have tried the following approaches:

  1. Insert the following into Ghost's code injection footer area. This renders math formulas on the run-time pages.

    <script type="text/javascript" async src="//"></script>

    Down side: the math formulas on the admin pages still rendered in its raw format. This makes it more tedious while composing a post.

  2. Also insert the same script above into the file core/server/views/default.hbs. Result: the math formulas now gets rendered in the Ghost admin area, which is nice. However, newly inserted MathJax statements do not get rendered dynamically, so I still have to refresh the entire page in order to see it rendered, which is not good.
  3. Next step: find a way to have MathJax statements rendered dynamically in the admin. Here is a jsfiddle example here which might be helpful. More on this later.

Custom search

I tried to use the Google Custom Search for supporting searching within this blog (see the How to add Google Custom Search to your Ghost blog link below), but had to give up on it. Problems found including double search fields, and malformed search result.


I added a feature that allows a user to see the list of posts visited, displayed in the latest-first order. This is so that a user is able to find the recently visited post, resume reading of a long post, etc.

This was done entirely using client-side JavaScript code, including storing and retrieving information in a cookie, render the list of posts, rendering time stamps in the timeago format, etc.

You can find how it works here. It is also accessible from the nav bar at the top of every post.

Update: alright, so the above solution is only sufficient for storing the history of a handful of posts, due to the limited capacity of a cookie (~4KB). As such I have updated my code to use local storage instead.

Display wrap-around images

Long post with nothing but text is hard to read, and having some images sprinkled through the post helps somewhat to improve its readability. However, an in-text image in Ghost may span almost the entire width of the browser and occupy too much screen real estate. A better way is to display smaller images with text wrap around them.

I settled on the following design (mostly achieved through CSS):

  1. Add a CSS style definition such as the following:

    .post-content img[src$='#postHeaderImg'] { 
        float: left;
        left: 0;
        transform: initial;
        -webkit-transform: initial;
        margin-right: 30px;
        margin-bottom: 0px;
        max-width: 50%;
  2. Whereever an image is needed in the post, enter it using the following Markdown syntax:


where in the above you are supposed to replace the image path to what you wanted. This will cause the image to stick to the left side, with the text flowing around it.

You should make it responsive by refining the CSS definition so that this behaves well on browser of all sizes. To test it out, just resize the width of your browser now to see the effect.

Image caption

The above is just a random image used here to demonstrate the added feature of having a caption under an image. The image shows <b>the Great Wave off Kanagawa</b>, a woodblock print by the Japanese artist <b>Hokusai</b>. Having a caption under an image is handy sometimes, especially when the image is not just decorative but also requires some additional explanation.

This article Adding image captions to Ghost shows a way to do it. You can use CSS to style it further to get what you wanted.

Note that the method mentioned above uses the alt attribute of the image element as the source of the caption, and additional DOM elements are created using JavaScript code to achieve this effect. You can also add HTML syntax inside the caption (see the heavier font weight used to display the names of the artwork and the artist) and it will get rendered properly.

Adding comment counts

When viewing a list of posts, such as when viewing from the home page or a tag page, it is useful to be able to see the number of comments on each of those posts. This way a user is then able to follow the discussion by checking if the count has changed from his/her last visit.

Since we are using the Disqus comment service here, here are some simple instructions about how to display comment counts on the home page.

Adding context-sensitive tooltips

One way to view additional material (say, a video) is inserting it inline. But sometimes this is too disruptive to the flow of the text, so a typical solution is to navigate to another page where the material is, when user demands it. This is also somewhat disruptive. Yet another solution is to use tooltips, so the target material can be displayed in a bubble when needed, and dismissed when done, without changing losing the position on the page.

Here are two live examples showing YouTube#1, and another video YouTube#2. Just click using a mouse (or touch it if on touch device) the underlined text, and the video will then appear floating over the text.

I implemented this on top of the Ghost system using the wonderful jQuery library qTip2 from Craig Thompson. It is not hard if you know how to program in JavaScript, but it is a little too involved to be described right here. If you are interested I'd recommend that you just go look at the demo examples offered on the qTip2 website.

To-do list

Following are some features that I'd like to add, but have not gotten around to do them just yet. By all means please let me know if you have answers for the issues below

  1. Adding the post counts in the tag cloud.
  2. Sort the items in the tag cloud by post cloud, or perhaps even style them based on counts.
  3. Add Google Custom Search, so that it is possible to find posts based on text search. This was tried earlier, but somehow the resulting layout (as done by Google's code) was quirky and unusable.
  4. Need a more efficient file method to deploy to my static website hosted on Amazon AWS S3. As I write new posts and added code libraries to support the new features that I wanted, it takes longer and longer to crawl the site using Buster, and then upload them to S3. This is because currently Buster creates files with fresh timestamps so there is no easy way to locate newly changed file, so I ended up having to upload everything to S3 everytime, even if only one file has been changed. Need a way to upload only recently updated files.
    Update: the combination of HTTrack and Winscp worked out reasonably well.
  1. The Ghost publishing platform.
  2. Buster, brute force static site generator for Ghost.
  3. Google website translator
  4. MathJax: a library for displaying math formulae, useful when discussing machine learning algorigthms.
  5. Adding a comment count to your blog with Disqus
  6. How to add Google Custom Search to your Ghost blog - did not work
  7. Display Post Images on Home Page
comments powered by Disqus