Creating your theme shell

The very first thing we're going to do is set up a basic theme shell, without any styling applied to it, that you can use as the foundation of this theme and any other you build after it.

The setup file and folder structure

We'll begin by getting the file and folder structure of your theme set up inside your local offline Ghost installation.

Go to content/themes in your local installation and create a new folder for your theme. As we mentioned in Chapter 3, Preparing for Theme Development, we'll name it LearningGhost.

Inside that folder, create five new files. The templates your Ghost theme will use are as follows:

  • default.hbs
  • index.hbs
  • post.hbs
  • tag.hbs
  • page.hbs

As you can see, the names of these template files correspond with the themeable areas talked about previously, with each one being responsible for the presentation of each of these areas.

Note that it's also possible to include an error.hbs file which allows you to create your own page for 400 and 500 errors. However, Ghost provides quite a nice looking error page by default, so we won't be making a customized version in this guide.

Additionally, create a sixth file inside the LearningGhost folder named package.json.

This is the file you'll enter your theme's proper name and version number into, so it will show up for users when selecting themes via the admin panel. In future, this file will also allow you to specify other assets that your theme depends on, such as Ghost plugins, so they can be automatically installed along with your theme.

As well as the files listed above, your theme will also contain assets such as CSS, JavaScript, font files, and images. In order for Ghost's {{assets}} helper (which we'll be using) to find these files, they must be contained inside a folder named assets.

First, create a new folder within your LearningGhost theme folder and name it assets. Within the assets folder, create an additional four folders named:

  • css
  • fonts
  • images
  • js

When you're done, your new theme folder should look like this:

The setup file and folder structure

Running the first CSS and JS compile into theme

Now that you have your theme structure in place, it's time to run the first compile from the LearningGhostSource project folder you created in the Creating a project folder section of Chapter 3, Preparing for Theme Development.

Open up a command window/terminal at LearningGhostSourcecompiler and run the following command:

grunt stylus

This command compiles all the files in the LearningGhostSourcestylus folder into your theme to give you a basic style sheet.

If you look at assetscss inside theme, you should now see a file named screen.css in there. This file contains the essential basis for your theme's styles, including a version of Normalize.css, typography settings, and a few other basics.

Now, in the same location, run the following command:

grunt uglify

This takes the two files in LearningGhostSourcejs, combines them, minifies them, and writes a file into your theme.

In assetsjs, you should now see a file named all.min.js.

One part of what this combined file does is to load Modernizr.js, which will help to ensure that the current standard code is understood by out-of-date browsers. The other part is a custom script that ensures videos, soundcloud embeds, and anything else entered via an iframe will resize responsively in your theme.

Adding basic code to template files and package.json

Using Sublime Text, as per the info in Chapter 3, Preparing for Theme Development, open up each of the files mentioned in the upcoming sections.

package.json

Enter the following code into this file:

{
  "name": "Learning Ghost",
  "version": "0.1.0"
}

Simply enter the name of your theme next to the name parameter, and the version number of your theme next to the version option.

This file will require more information in the future, but for the time being this is all that's required.

default.hbs

Enter the following code into this file:

<!DOCTYPE html>
<html>
<head>
    {{! Document Settings }}
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />

    {{! Page Meta }}
    <title>{{meta_title}}</title>
    <meta name="description" content="{{meta_description}}" />

    <meta name="HandheldFriendly" content="True" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <link rel="shortcut icon" href="{{asset "favicon.ico"}}">

    {{! Styles'n'Scripts }}
    <link href='//fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" type="text/css" href="{{asset "css/screen.css"}}" />
    <script type="text/javascript" src="{{asset "js/all.min.js"}}"></script>

    {{! Ghost outputs important style and meta data with this tag }}
    {{ghost_head}}
</head>
<body class="{{body_class}}">

    {{! Document header }}
    <header class="blog_header_lg" role='banner' itemscope itemtype='http://schema.org/WPHeader'>

      {{#if @blog.logo}}<a href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a>{{/if}}
      <h1 class="blog_title_lg"><a title="{{@blog.title}}" href='{{@blog.url}}'>{{{@blog.title}}}</a></h1>
      <p class="blog_description_lg"><a title="{{@blog.title}}" href='{{@blog.url}}'>{{{@blog.description}}}</a></p>

    </header>

    {{! Document main }}
    <main class="posts_area_lg">{{{body}}}</main>

    {{! Document footer }}
    <footer class="blog_footer_lg" itemscope itemtype='https://schema.org/WPFooter'>

      <p><a class="subscribe icon-feed" href="{{@blog.url}}/rss/">Subscribe!</a></p>
      <p>All content copyright <a href="{{@blog.url}}">{{{@blog.title}}}</a> &copy; 2013 &bull; All rights reserved.</p>
      <p>Proudly published with <a class="icon-ghost" href="http://tryghost.org">Ghost</a></p>

    </footer>

    {{! Ghost outputs important scripts and data with this tag }}
    {{ghost_foot}}
    <script language="javascript" type="text/javascript">responsive_iframes();</script>

</body>
</html>

This is the code that will wrap every page of your theme by default. Let's take a quick look at some of the Ghost-specific things being done here.

The wrapper code and {{{body}}} tag

First, we have the essential HTML5 html, head, and body tags. Rather than having this type of code in separate files loaded at the beginning and end of other templates, as you might in WordPress for example, this is all handled in a single file.

This is done by placing the {{{body}}} tag where you want the other template files of your theme to load. Note the triple curly braces to prevent HTML escaping as mentioned in Chapter 3, Preparing for Theme Development.

The meta content

Note the {{meta_title}} and {{meta_description}} tags used in the head section. These generate meta tag output dynamically depending on where the visitor is on the site.

The {{asset}} tag

The style.css and all.min.js files we just generated are loaded into the theme via the {{asset}} tag, for example, {{asset "css/screen.css"}}. This ensures Ghost always finds theme assets, and that they can be cached.

This tag is also used to load the default favicon: {{asset "favicon.ico"}}.

The {{ghost_head}} tag

This tag is essential and must always be included right before the end of the head section.

The {{body_class}} tag

This tag outputs different classes depending on where the user is on the site.

Document <header> section and {{blog}} object

In this section, we are loading the blog's logo, title, and description, each linked to the blog's main URL, so the content in the header acts as the home button. The @blog object contains all four of these pieces of data via Handlebars paths/dot notation, as well as any blog cover image, which we will discuss further in a later section:

  • {{@blog.logo}}: This outputs the blog logo image URL
  • {{{@blog.title}}}: This outputs the blog title string

    Note

    Use triple curly braces to allow users to include HTML such as <em> or <br />.

  • {{{@blog.description}}}: This outputs the blog description string

    Note

    Use triple curly braces to allow users to include HTML such as <em> or <br />

  • {{@blog.url}}: This outputs the blog root URL
  • {{@blog.cover}}: This outputs the blog cover image URL

Note this is a header in the HTML5 sense, that is, it contains header data relative to the overall document. It can still be styled via CSS to sit over to the side if you choose to create a twin column layout.

The document <footer> section

This is the HTML5 document footer section where we load copyright and attribution data, along with a site RSS feed if you choose.

The {{ghost_foot}} tag and extra scripts

The {{ghost_foot}} tag is also critical and should be included at the end of the body section. Among other things, this tag loads the jQuery library.

If you are including JavaScript that depends on jQuery, as does our responsive_frames() function, it should hence be run after the {{ghost_foot}} tag.

Extra notes

We have a few CSS classes in place that can be used to style the theme, such as blog_title_lg applied to the blog title. Note the _lg appended to the class. This is an abbreviation for the Learning Ghost theme name, and is used to namespace classes to ensure that plugins that might bring in additional CSS don't conflict by inadvertently using identical class names.

You'll also see application of semantic HTML5 tags such as header, main, and footer, which we will be doing throughout, as well as WAI-ARIA roles for accessibility, and Schema.org markup for SEO. We won't go into great detail on these three things in this guide, but recommend you thoroughly read up on them to inform your development.

index.hbs

Insert the following code into the index.hbs file:

{{!< default}}

{{#foreach posts}}

<article role="article" itemscope itemtype="http://schema.org/BlogPosting" class="{{post_class}}">

    <header>
      <time datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
      <h1 class="post_title_list_lg" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{{title}}}</a></h1>
    </header>

    {{excerpt}}

    <p><a href="{{url}}">Read More &rarr;</a></p>

    <footer>
      <p>{{{tags}}}</p>
    </footer>

</article>

{{/foreach}}

{{pagination}}

The preceding code is responsible for the display of your index area, that is, your paginated list of latest posts accessed via your homepage. This code is loaded into the default wrapper at the point the {{{body}}} tag was entered.

The {{!< default}} tag

This tag is essential for all template files other than default.hbs and should be placed on the very first line of each. The reason is that this tag tells Ghost that the template should be wrapped in the content of the default.hbs file.

The {{#foreach posts}}...{{/foreach}} block helper tags

These block helper tags iterate the post object, which holds all of the data pertaining to the page's current list of posts. Everything that appears between these opening and closing tags will be repeated for each post displayed.

The {{post_class}} tag

This tag will output different CSS classes per post, with the default output being a class of post and additional classes being added for each tag applied to the post in the format tag-<tagname>.

The {{date}} tag

There are two options for how the date of publishing can be output. Either a relative time can be shown, for example 1 month ago, or the actual date can be shown, for example, Thursday 15th May 2014.

To show a relative time, place the date tag as follows:

{{date timeago="true"}}

To show the actual date, use the tag like this:

{{date format="dddd DD MMM YYYY"}}

When using the actual date the exact way, its output can be controlled with different settings in the format option, which determine how the date is formatted by a script Ghost uses named Moment.js. See the Moment.js date formatting guide for details on the options available.

The {{url}} tag

This tag outputs the URL of the post's individual post. It can be used to create Read More links, social sharing links, and so on.

By default, it will output a relative URL, for example, /post-slug/, but it can also output an absolute URL, for example, http://www.yourdomain.com/post-slug/ by setting the absolute option to true:

{{url absolute="true"}}

This is useful to create social sharing links, as you'll see in the subsequent post.hbs section.

The {{title}} tag

This tag simply displays the title of the post.

The {{excerpt}} and {{content}} tags

In both the index area and the tag archive area, there are two ways of displaying the body of the post: the {{excerpt}} tag and the {{content}} tag.

The excerpt tag displays a clipped portion of the post, with length determined either in words or characters, and all HTML stripped. This prevents things such as images or videos being shown, so the excerpt is text only. By default, it shows the first 50 words. To show a different content, use either the words or characters option:

{{excerpt words="80"}}
{{excerpt characters="140"}}

The other option is the {{content}} tag which does not strip any HTML and shows the fully formatted post body with whatever images and videos that might be included. By default, the tag shows the whole post body but it too can be limited by either words or characters:

{{content words="80"}}
{{content characters="140"}}

The {{{tags}}} tag

This outputs all the tags applied to the current post, with each one automatically linked to the archive for that tag. Optionally, a prefix, separator, and suffix for the list of tags can be included, each of which can accept HTML, for example:

{{{tags prefix="<strong>Tagged with: </strong><em>" separator=" | " suffix="</em>"}}}

Tip

Note that if you do use HTML as per the preceding example, you will need to use triple curly braces to ensure it renders as expected.

The {{pagination}} tag

In the index and tag archive areas, the number of posts shown per page is set via Ghost's Posts per page admin field. Whenever there are more posts than the number saved under this setting, the {{pagination}} tag will output links reading Older posts and Newer Posts as well as a display showing Page <current> of <total>.

post.hbs

Insert the following code into the post.hbs file:

{{!< default}}

{{#post}}

<article role="article" itemscope itemtype="http://schema.org/BlogPosting" class="{{post_class}}">

    <header>
      <time datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format="dddd DD MMM YYYY"}}</time>
      <h1 itemprop="headline">{{{title}}}</h1>
    </header>

    {{content}}

    <footer>
      <p>{{{tags}}}</p>

      <address itemscope itemtype="https://schema.org/Person">
        {{#if author.image}}<div class="authorimage_nix"><img src="{{author.image}}" itemprop="image" /></div>{{/if}}
        <h4 class="nix_author">{{#unless author.image}}<i class="nix_icon-user"></i>{{/unless}}<span itemprop="author">{{{author.name}}}</span></h4>
        {{#if author.website}}<p><a href="{{author.website}}" itemprop="url" target="_blank" rel="author">{{author.website}}</a></p>{{/if}}
        {{#if author.location}}<div itemscope itemtype="https://schema.org/PostalAddress"><span itemprop="addressLocality">{{{author.location}}}</span></div>{{/if}}
      </address>

      {{#if author.bio}}
        {{{author.bio}}}
      {{/if}}

      <section class="share">
        <h4>Share this post</h4>
        <a class="icon-twitter" href="http://twitter.com/share?text={{encode title}}&url={{url absolute="true"}}"
          onclick="window.open(this.href, 'twitter-share', 'width=550,height=235'),return false;">
          Twitter
        </a>
        <a class="icon-facebook" href="https://www.facebook.com/sharer/sharer.php?u={{url absolute="true"}}"
          onclick="window.open(this.href, 'facebook-share','width=580,height=296'),return false;">
          Facebook
        </a>
        <a class="icon-google-plus" href="https://plus.google.com/share?url={{url absolute="true"}}"
          onclick="window.open(this.href, 'google-plus-share', 'width=490,height=530'),return false;">
          Google+
        </a>
      </section>
    </footer>

</article>

{{/post}}

This code controls the display of single posts viewed at their own URLs. You'll see many of the same template tags here as you saw in the previous two templates.

The primary differences in how you'll create your post.hbs file versus your index.hbs file are:

  • No block helper loop is required as there's only one post. Instead, use the {{post}}...{{/post}} tags to output the single post.
  • No need to link the title as you're already at the posts URL.
  • Inclusion of author profile information and social sharing links.

The {{author}} object

The following properties of the {{author}} object can all be accessed via Handlebars paths, as was done in the default section with the {{blog}} object:

  • {{{author.name}}}: This will output the author name string
  • {{{author.location}}}: This will output the author location string
  • {{{author.bio}}}: This will output the author bio string
  • {{author.website}}: This will output the URL from author "website" field
  • {{author.image}}: This will output the author image URL
  • {{author.cover}}: This will give an output of the author cover image URL

    Note

    The first three properties will use triple curly braces to allow users to include HTML.

tag.hbs

Insert the following code into the tag.hbs file:

{{!< default}}

<div class="tag_archive_name_lg">
<h1>{{tag.name}}</h1>
</div>

{{#foreach posts}}

<article role="article" itemscope itemtype="http://schema.org/BlogPosting" class="{{post_class}}">

    <header>
      <time datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time>
      <h2 class="post_title_list_lg" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{{title}}}</a></h2>
    </header>

    {{excerpt}}

    <p><a href="{{url}}">Read More &rarr;</a></p>

    <footer>
      {{{tags}}}
    </footer>

</article>

{{/foreach}}

{{pagination}}

In our base theme shell, the only difference between the tag archive and the index is the inclusion of {{tag.name}} in order to output the name of the tag whose archive is being viewed.

The tag.hbs template is most handy if you decide to have its presentation formatted differently for specific tags, such as video and image.

page.hbs

Insert the following code into the page.hbs file:

{{!< default}}

{{#post}}

<article role="article" class="{{post_class}}">

    <header>
      <h1 itemprop="headline">{{{title}}}</h1>
    </header>

    {{content}}

</article>

{{/post}}

The page template is functionally very similar to the post template, but stripped down and simplified. Static pages are designed to display basic information such as About Us or Contact Details and as such the only thing shown is the page title and its content.

Author information is accessible via this template, so it can be included if you wish.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset