Advertisement

Adding Ghost Template Tags and Markup

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

This post is part of a series called Building a Ghost Theme From Scratch.
Organizing Ghost Theme Files and Folders
Styling Our Ghost Theme With LESS

Welcome to the third part in our series on building a theme for Ghost. This instalment will take you through the remaining steps needed to finalize your theme's templates.


Writing the "index.hbs" File

Now that we have the theme's wrapper code running via the "default.hbs" file, it's time to get some content on the page. We're going to be coding up our "index.hbs" file which is responsible for diplaying our latest posts/homepage.

We'll begin by adding the appropriate template tags that this file will use, and we'll add our markup after that.

Note: This section assumes you are using a fresh local installation of Ghost with the default first "Welcome to Ghost" post published. If your installation isn't a fresh one bear in mind some of the content in our example screenshots may differ to the content in your own.

Before starting you may wish to create a second post in your Ghost site and set "Posts per page" to "1" via "Settings > General" in your admin panel. This way you can test to ensure your theme's pagination is displaying correctly.


Adding the Template Tags

Step 1:

Open up the "index.hbs" file in your code editor.

Step 2:

Below the {{!< default}} template tag, initiate the loop that will iterate through your latest posts by adding the following code:

{{#foreach posts}}
{{/foreach}}

Whatever you add between those two tags will be output for each post displayed.

Step 3:

Add the post date, post title, post content and tags by placing the following code in between the {{foreach}} template tags:

{{date format='dddd DD MMM YYYY'}}
{{title}}
{{content}}
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}

Notes on the above:

  • The date format is controlled by moment.js. By changing the value of format="" (according to the instructions on the moment.js site) you can easily determine how the date reads.
  • The {{content}} template tag outputs the full content of a post. You can alternatively use the {{excerpt}} template tag to output a short post introduction.
  • The {{content}} template tag will also accept a words="100" parameter to restrict the number of words output. Similarly, the {{excerpt}} template tag will accept a words="50" or characters="50" parameter.
  • In the {{tags}} template tag the value of separator="" can be changed to anything you wish to use as a separator between displayed post tags, be it commas or anything else.

Step 4:

Add pagination by placing the following code after the closing {{/foreach}} template tag:

{{#if pagination}}
{{{pagination}}}
{{/if}}

Step 5:

Check that your complete set of "index.hbs" template tags now looks like this:

{{!< default}}
{{#foreach posts}}
{{date format='dddd DD MMM YYYY'}}
{{title}}
{{content}}
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}

Step 6:

Save the file, then in your browser reload your Ghost site front end, typically: http://localhost:2368/. You should see something like this (the actual content will reflect the content of your most recent Ghost post):

GhostTheming_PostLoopAdded

Check that you see the following:

  • The date
  • The post title ("Welcome to Ghost" in our example)
  • The post content
  • The tags (Ghost adds "Getting Started" as the default tag. Though not shown in the above image, you should see this tag displayed on the bottom of the "Welcome to Ghost" post. If you don't see any tags check that the post has tags via the post editor in Ghost admin)
  • Pagination at the bottom, if you created a second post and set your posts per page to 1. It should appear as "Page 1 of 2 Older Posts →"

If anything is missing, please double check the template tags added to your file.


Adding Markup

We're now going to begin the process of adding markup including semantic (i.e. meaningful) HTML5, WAI-ARIA roles, Schema.org as required, as well as namespaced CSS classes that will be used to style the theme later.

Each piece of markup will be added one piece at a time so you get the chance to look at how each one relates to the template tag(s) it surrounds. The goal is for the code added to be as minimal and meaningful as possible.

Note: All CSS classes should include namespacing to ensure they don’t clash with any other styles the site might be loading. In this case, we are preceding all class names with a name that illustrates the function of the class, while appending each one with "_uber" to clearly identify it relates to our theme. You can, of course, apply any naming convention you'd prefer.

Step 1:

Add <main> html5 tags to indicate the main content area of the site.

The opening <main> html tag should be placed after the {{!< default}} template tag and will be:

<main class="main_uber" role="main">

The closing </main> tag should be placed at the end of the document:

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
{{date format='dddd DD MMM YYYY'}}
{{title}}
{{content}}
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}
</main>

Step 2:

Add <article> html5 tags to indicate article content.

The opening <article> html tag should read:

<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">

The <article> html tags should wrap everything inside the post loop:

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
{{date format='dddd DD MMM YYYY'}}
{{title}}
{{content}}
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}
</article>
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}
</main>

Step 3:

Add <header> html5 tags to the article.

The opening <header> html tag should read:

<header class="postheader_uber">

The <header> tags should wrap the {{title}} and {{date}} template tags:

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
<header class="postheader_uber">
{{date format='dddd DD MMM YYYY'}}
{{title}}
</header>
{{content}}
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}
</article>
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}
</main>

Step 4:

Wrap <time> html5 tags around the post published date. The opening <time> html tag should read:

<time class="date_uber" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">

The <time> tags should wrap the {{date format='dddd DD MMM YYYY'}} template tag as you can see below:

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
<header class="postheader_uber">
<time class="date_uber" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
{{title}}
</header>
{{content}}
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}
</article>
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}
</main>

Step 5:

Wrap <h1> html tags and a link to the single post around the {{title}} post title tag. The opening <h1> and <a> html tags should read:

<h1 class="posttitle_uber" itemprop="headline"><a href="{{{url}}}" rel="bookmark">

The <h1> and <a> html tags should wrap the {{title}} template tag as you can see below::

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
<header class="postheader_uber">
<time class="date_uber" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
<h1 class="posttitle_uber" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{title}}</a></h1>
</header>
{{content}}
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}
</article>
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}
</main>

Step 6:

Add a <div> wrapper around the post content. The opening <div> html tag should read:

<div class="postcontent_uber" itemprop="articleBody">

The <div> html tags should wrap the {{content}} template tag:

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
<header class="postheader_uber">
<time class="date_uber" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
<h1 class="posttitle_uber" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{title}}</a></h1>
</header>
<div class="postcontent_uber" itemprop="articleBody">
{{content}}
</div>
{{#if tags}}
Tags: {{tags separator=" | "}}
{{/if}}
</article>
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}
</main>

Step 7:

Add a <div> wrapper around the tag display. The opening <div> html tag should read:

<div class="posttags_uber">

The <div> html tags should wrap the {{tags}} template tag, inside the {{#if tags}} helper:

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
<header class="postheader_uber">
<time class="date_uber" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
<h1 class="posttitle_uber" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{title}}</a></h1>
</header>
<div class="postcontent_uber" itemprop="articleBody">
{{content}}
</div>
{{#if tags}}
<div class="posttags_uber">
Tags: {{tags separator=" | "}}
</div>
{{/if}}
</article>
{{/foreach}}
{{#if pagination}}
{{{pagination}}}
{{/if}}
</main>

Step 8:

Add a <div> wrapper around the pagination display. The opening <div> html tag should read:

<div class="pagination_uber">

The <div> html tags should wrap the {{pagination}} template tag, inside the {{#if pagination}} helper:

{{!< default}}
<main class="main_uber" role="main">
{{#foreach posts}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
<header class="postheader_uber">
<time class="date_uber" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
<h1 class="posttitle_uber" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{title}}</a></h1>
</header>
<div class="postcontent_uber" itemprop="articleBody">
{{content}}
</div>
{{#if tags}}
<div class="posttags_uber">
Tags: {{tags separator=" | "}}
</div>
{{/if}}
</article>
{{/foreach}}
{{#if pagination}}
<div class="pagination_uber">
{{{pagination}}}
</div>
{{/if}}
</main>

Step 9:

Save the "index.hbs" file.


Testing Your Completed File

Your "index.hbs" file is now complete and ready for testing. To do so, go to your Ghost front end at http://localhost:2368/.

At a glance you won't see many differences from before, just a larger and hyperlinked post title. But view the source of the page and you should see that all the markup you added is now present on your page.

Check that each piece of markup listed above is present in the code. If anything is missing repeat the step(s) shown above to add what is missing.


Writing the "post.hbs" File

We're now down to the last of the three template files for your theme. The "post.hbs" file controls the display of your single posts, i.e. your posts when viewed at their individual url.

The code of the "post.hbs" file is very much like the "index.hbs" file, so instead of stepping through each line this time I'll give you the code for the entire file, then I'll point out where the differences are.

Step 1:

Open up your "post.hbs" file in your code editor.

Step 2:

Add the following code:

{{!< default}}
<main class="main_uber" role="main">
{{#post}}
<article class="article_uber" role="article" itemscope itemtype="http://schema.org/Article">
<header class="postheader_uber">
<time class="date_uber" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
<h1 class="posttitle_uber" itemprop="headline">{{title}}</h1>
</header>
<div class="postcontent_uber" itemprop="articleBody">
{{content}}
</div>
{{#if tags}}
<div class="posttags_uber">
Tags: {{tags separator=" | "}}
</div>
{{/if}}
	<div class="sharepost_uber">
	<h4 class="share_uber">Share this post: &nbsp;</h4>
	<a class="share_uber icon-twitter" target="_blank" href="http://twitter.com/share?text={{title}}&url={{url absolute="true"}}"><span class="hidethis_uber">Twitter</span></a>
	<a class="share_uber icon-facebook" target="_blank" href="http://www.facebook.com/sharer.php?u={{url absolute="true"}}"><span class="hidethis_uber">Facebook</span></a>
	<a class="share_uber icon-gplus" target="_blank" href="https://plus.google.com/share?url={{url absolute="true"}}"><span class="hidethis_uber">Google+</span></a>
	</div>
		{{#if author}}
		<footer class="postfooter_uber">
		<address itemscope itemtype="http://schema.org/Person">
		{{#if author.image}}
		<div class="authorimage_uber"><img src="{{author.image}}" itemprop="image" /></div>
		{{/if}}
		<h4 class="authorname_uber"><span itemprop="author">{{author.name}}</span></h4>
		{{#if author.website}}
		<div class="authorwebsite_uber"><a href="{{author.website}}" itemprop="url">{{author.website}}</a></div>
		{{/if}}
		</address>
		<div class="authorbio_uber">
		<p>{{author.bio}}</p>
		</div>
		</footer>
		{{/if}}
</article>
{{/post}}
{{#if pagination}}
<div class="pagination_uber">
{{{pagination}}}
</div>
{{/if}}
</main>
  • The {{#foreach posts}}{{/foreach}} template tags have been replaced with {{#post}}{{/post}}. This is because there is only one post to display, rather than a series of posts
  • The hyperlink has been removed from the title because we are already at the post's URL so it is unnecessary.
  • First indented section: Links have been added to share the post on Twitter, Facebook and Google+.
  • Second indented section: <footer> html5 tags have been added within the article, to be displayed if information is available on the post author. Author information can only be displayed in single post view. We are not using all of the available author template tags here - you can read more about the full range of template tags available at: http://docs.ghost.org/themes/

Step 3:

Save the "post.hbs" file.


Testing Your Completed File

In order to test your "post.hbs" file properly you should fill in some of the fields in the author bio of your Ghost installation, under "Settings > User". Add an author image, a website address and a bio.

Go to your Ghost front end at http://localhost:2368/, then click on the hyperlinked title of the first post.

Check to make sure you see the following:

  • The date
  • The post title
  • The post content
  • The post tags
  • Sharing links to Twitter, Facebook and Google+.
  • Author image, name, website and bio

If anything is missing repeat the steps above in order to double check the template file code.


Writing Partial Template Files

Now that your three main template files are complete, we're going to wrap up writing your template files by creating two "partials"; one to add a common header to your theme, and one to add a common footer.

Let's start with the header partial template file.

Step 1:

In your preferred code editor, create a new file then save it into your "UberTheme > partials" folder as "header.hbs".

Step 2:

Add the following code to it and save:

<div role='banner' class="header_uber">
{{#if @blog.logo}}<a class="logo_uber" href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a>{{/if}}
<h1 class="blogtitle_uber">
<a title="{{@blog.title}}" href='{{@blog.url}}'>
{{{@blog.title}}}
</a>
</h1>
<p class="blogdescription_uber">
<a title="{{@blog.title}}" href='{{@blog.url}}'>
{{@blog.description}}
</a>
</p>
</div>

This code will display the logo (if present), blog title and blog description. Now to load this partial template file into the theme.

Step 3:

Open your "index.hbs" file and your "post.hbs" file.

Step 4:

Into each file, immediately after the {{!< default}} template tag, add the following:

{{> header}}

Step 5:

In your browser, refresh your Ghost home page and your single post view.

At the top of each you should now see the blog title "Ghost" and the blog description "Just a blogging platform" (or the title and description relevant to your blog if you've changed it).

Step 6:

Create another new file then save it into your "UberTheme > partials" folder as "footer.hbs".

Step 7:

Add the following code to it and save:

<footer class="footer_uber">
<a class="subscribe_uber icon-rss" target="_blank" href="{{@blog.url}}/rss/"><span class="hidethis_uber">Subscribe!</span></a>
<div class="footertext_uber">All content copyright <a href="/">{{@blog.title}}</a> &copy; 2013 &bull; All rights reserved.</div>
<div class="footertext_uber">Proudly published with <a class="icon-ghost" href="http://tryghost.org" target="_blank">Ghost</a></div>
</footer>

This code will add an RSS subscription link, copyright message and attribution to Ghost.

Step 8:

Go back to your "index.hbs" and "post.hbs" files.

Step 9:

Into each file, on the very bottom line, add the following and then save:

{{> footer}}

Step 10:

Again, go to your browser and refresh your Ghost home page and your single post view. At the bottom of each page you should now see your RSS link, copyright message and Ghost attribution.

Your Ghost theme template files are now complete!

You've added all the handlebars template tags you need, and added semantic, SEO friendly and accessible markup.


Coming Up Next

Now that the templating stage of your theme is done it's time to move on to the styling stage.

In the next part of this tutorial series you'll learn how to style your typography and layout, create your overall look and make your theme responsive.

Helpful tip: Before moving on to styling it's a great idea to make a copy of your theme. Put it aside somewhere for easy access later as you now have a bare bones skeleton you can use as a base for future themes.

Advertisement