The Ultimate Guide to Making a Custom Ghost Theme

On
Ghost blogging platform logoIf you're new to Ghost blogging platform, you must give it a try. For those who're already using it, this detailed guide will help you code your very own Ghost theme from scratch. Making a Ghost theme is quite easy if you have a working knowledge of HTML & CSS. This tutorial will make a sample theme with minimal responsive design to show how you can get started with making a custom Ghost theme. You can extend this sample theme to create a unique and appealing design for your own blog. This is going to be a long article, so grab your coffee and get ready to take the notes. You'll need an active Ghost instance with FTP/SFTP access for applying the theme creation process discussed below. If you can't find a good web host, you can start with a local Ghost installation on your PC. Let's get started and make a Ghost theme.

Ghost blogging platform logo Note: The following tutorial simply guides you to build a basic Ghost theme skeleton without any fancy design elements. Classes and ID names used for CSS styling can be changed as per your preferences.

Essentials - Before You Code A Ghost Theme

Before we go ahead, let's discuss some of the basic things you must be aware of that'll help you code better themes in the long run. For making a simple Ghost theme you need to know just three languages. But, if you're looking to make a flexible and highly customizable theme, several other popular web languages are needed.

Following are some of the web technologies and languages one must learn to create awesome Ghost themes.

Apart from HTML & CSS, you must also learn Handlebars, JavaScript, jQuery and JSON. For now, you can skip JavaScript, JQuery and JSON. The three important languages we need to build our starter Ghost theme are HTML, CSS and Handlebars.

So, what is Handlebars? It's a powerful and flexible templating language that uses expressions for the building blocks of the theme. A typical Handlebar expression is surrounded by double curly braces - for example, {{crunchData}}.

Don't panic, this language is quite easy-to-use and user friendly. Use of Handlebars expressions ensures that your theme data crunching logic/code remains isolated from the theme design code. This essentially means that your Ghost theme is more modular and flexible and can be extended and modified quite easily.

Anatomy of a Ghost Theme

Now let's try to understand the structure of a typical Ghost theme. Surprisingly, the bare minimum requirement to run a Ghost theme is just two mandatory files. Rest of the files and directories are included to give the theme, a more proper shape.

Anatomy of a Ghost theme The two mandatory files required to run a Ghost theme are index.hbs and post.hbs. Without these two files, your theme won't run at all. Now let me explain the function of each file included in the theme directory structure shown above.

  • index.hbs - This mandatory file includes the template of the list of posts to be displayed on the blog's home page.
  • post.hbs - This mandatory file includes the template of the post content displayed on a single post page.
  • page.hbs - This optional file includes the template to render static page's content on a single page. If this file is not present, post.hbs template is automatically used by Ghost.
  • tag.hbs - This optional file includes the template to render tag archive page's content on a single page. If this file is not present, index.hbs template is automatically used by Ghost.
  • error.hbs - This optional file renders the 404 or similar kind of pages whenever some error occurs. If this file is not present, Ghost uses a different file already included in its core.
  • package.json - This file is used to provide several options associated with the theme. At the bare minimum, you can provide name and version of the theme.
  • default.hbs - Though this file is optional, it is quite important when designing a production theme for a live blog. This file contains the skeleton of the HTML template (header, footer, body) that's repeated in almost all of the blog pages.
  • assets (directory) - As the name implies, this folder contains all the assets used in building the theme. These assets may be CSS files, images, JavaScript code and font files. It's always advisable to create separate sub folders for all these resources to avoid cluttering of all the assets. Remember, it's not mandatory to keep the name of this directory 'assets' because a theme can work fine even with a different folder name, BUT, this name is chosen for two simple reasons. Firstly, Ghost includes an inbuilt {{asset <resource path>}} Handlebar expression that eases the assets inclusion within the theme design, and secondly, this Handlebar expression also supports automated caching of theme assets.
  • partials (directory) - This is yet another optional yet important directory that contains template snippets that can be used anywhere within the theme. For example, you can create a partial template file of displaying all the featured posts within this folder. Once created, you can include this template anywhere within the main theme files to render the relevant content.

Creating and Activating Blank Theme

Now, let's create a blank theme and activate it within our Ghost installation. All the themes reside in /content/themes/ directory. Access your Ghost installation directory tree through FTP software and go to themes directory. You'll find a default theme Casper already included in this directory. Unless you're already using a different theme, this default theme is your currently activated theme.

Ghost theme directory Now create a new folder named skeleton in the themes directory. Once created, your theme directory structure will look something like the image shown below.

Sample Ghost theme folder Inside the newly created skeleton theme folder, create three blank files named index.hbs, post.hbs and package.json. Open package.json file in your favorite text editor and enter the name and version of the theme in the following format.

{
"name": "Skeleton",
"version": "0.1.0"
}

Save the file after providing both the options. At this point, you may need to restart your Ghost server installation so that it can detect the new theme presence. Once restarted, log in to your Ghost blog dashboard and head over to Settings → General → Theme option.

Ghost theme changing within dashboard Click the drop-down menu and select the Skeleton - 0.1.0 option. Save your settings and open the home page of your blog. At this point, you'll see a completely blank web page within your browser tab. If you examine the source code of your blank home page, you'll find it completely empty. Congratulations, you've just installed and activated a blank Ghost theme on your blog.

Making Default HTML Skeleton

As I mentioned before, default.hbs file contains the generic HTML structure that's used by almost all the types of blog pages. So let's quickly create it to start giving our theme a good shape. Create a blank default.hbs file in the skeleton folder where you've already created index.hbs and post.hbs files.

Now copy the following code in this new 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 Tags for Responsive Behaviour }}
    <meta name="HandheldFriendly" content="True" />
    <meta name="MobileOptimized" content="320" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

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

    {{! Everything else gets inserted here }}
    {{{body}}}    

    {{! Ghost outputs important scripts and data with this tag }}
    {{ghost_foot}}
</body>
</html>
Most of the code shown above is generic and is found in most web pages. Let's dissect this code and understand its important parts.

The first thing you'll notice is different style of comments used in this file. Quite similar to Handlebars expressions (helpers), the comments are also included in double curly braces by placing an exclamation mark right after the opening braces. For example.

{{! This is a comment - Handlebars style }}
All the dark green color statements in the code shown above are Handlebars comments. The other HTML tags (meta tags and other basic tags) are self explanatory. We'll, focus on the red color Handlebars helpers that actually output the theme related data.

  • {{meta_title}} - This helper outputs the page title.
  • {{meta_description}} - This one is also fairly simple. It echoes the page description.
  • {{ghost_head}} - This important Handlebars helper outputs important meta data that is included by Ghost engine whenever a page is fetched by the reader. Some of the important tags that are added to the header by this helper are blog RSS link tag and the meta tag revealing the version of Ghost through which the page has been generated.
  • {{body_class}} - This one is made for theme developers. This helper assigns relevant CSS classes to the body of the page dynamically. For example, it adds .post class for a simple blog post, .page for a static page and .featured for a featured post. This way, developers and power users can easily target the elements within the body through these CSS classes.
  • {{{body}}} - Pay special to this Handlebars helper! You can see it has triple curly braces on both the sides. This ensures that instead of raw HTML, the actual output is returned. In other words, it prevents HTML from escaping from within the body which may lead to garbled output.
  • {{ghost_foot}} - And last but not the least, this important helper outputs the necessary scripts and other data that may be necessary to run the blog. For example, it ensures that jQuery library is included in the web page.
So that completes our theme's default HTML skeleton that will be used by all the common pages generated by Ghost.

Creating Common Partial Templates

Instead of hard-coding header, footer, and pagination templates within post and page template files, we'll keep them separate as partial templates so that they can be easily modified and maintained without affecting other parts of the theme.

Partial header template - Create a blank file header.hbs in the partials folder. Open it in a text editor and paste the following code in it.

{{! Check for Blog Cover Existence }}
{{#if @blog.cover}}
<header role='banner' id="site-header" style="background: url({{@blog.cover}}) no-repeat center">
{{else}}
<header role='banner' id="site-header">
{{/if}}

{{! Check for Blog Logo Existence }}
{{#if @blog.logo}}
<a id="site-logo" href="{{@blog.url}}"><img src="{{@blog.logo}}" alt="{{@blog.title}}" /></a>
{{/if}}
 <h1 id="site-title">
  <a href='{{@blog.url}}'>
  {{@blog.title}}
  </a>
 </h1>
 <h2 id="site-description">
  <a href='{{@blog.url}}'>
  {{@blog.description}}
 </a>
 </h2>
</header>
Let's dissect the header partial template code which is quite straightforward. Ghost allows users to upload large blog cover image as well as a logo for the blog. Both the images are optional and it's entirely up to the user whether he wants to use both, one of them or none of them.

In simple words, the template first detects whether a blog cover is available for use in the theme. If yes, it is included in the theme as the header section background image. After that, the template code looks whether blog logo image is available or not. If yes, it is also included in the header section. Code after that is fairly simple which includes blog title and description within the header section.

And now let's focus on the handlebar expressions used in this template code.

  • {{#if ...}} {{else}} {{/if}} - This is one of the heavily used conditional statements in handlebars style to render select content on the basis of different conditions. You can see we've written two <header> statements while using this conditional statement. Though the same logic can be coded within a single <header> tag, for simplicity's sake we've demonstrated the conditional logic through two separate statements.
  • @blog.cover - This tag returns the URL of the blog cover photo, if it is present else it resolves to false.
  • @blog.logo - As the name implies, this tag returns the logo URL, if it is present else it resolves to false.
  • @blog.url - This tag returns the web address of the blog instance and is picked from the config.js file present in the root directory of the installation.
  • @blog.title - This important tag displays blog's title specified in the appropriate field in the dashboard settings.
  • @blog.description - The same applies for this tag which returns the blog description specified by the user, if any.
You can see how we've isolated the header code in a separate partial template. This header template can be used for different pages like single post pages, static pages, tag archive pages and even for custom error page.

Partial footer template - Now that our partial header template is complete, it's time to quickly code a generic footer template for our theme. Create a blank file footer.hbs in the partials directory and paste the following code in it.

<footer id="site-footer">
 <div class="copyright">Copyright <a href="/" rel="nofollow">{{@blog.title}}</a> © <time class="copyright-date" datetime="{{date format="YYYY"}}">{{date format='YYYY'}}</time> • All rights reserved.</div>
 <div class="ghost-credit">Powered by <a class="icon-ghost" href="http://ghost.org" target="_blank">Ghost</a></div>
</footer>
As you can see above, the footer template is fairly straightforward and includes a new handlebar expression to crunch the time and date. You can freely change this template to include your preferred sections within the theme footer. Let's take a look at the new handlebar expression used in the partial footer template.

  • {{date}} - This flexible handlebar expression is used in different parts of the theme for date handling functions. Here, it is used to display the copyright statement year that is automatically fetched as the current year in 4 digit year format. This tag can output the date and time in different formats.
Instead of creating a simple footer, you can include a three column footer with custom content sections. It all depends on your imagination and the features you want to include in your theme.

Partial pagination template - Although you can include pagination section within the page and post templates through a simple handlebars expression, creating a separate partial template with exploded custom section gives you more control over the layout and formatting. Create a blank file pagination.hbs in the partials directory and paste the following code in it.

<nav id="pagination" role="pagination">
    {{#if pagination.prev}}
     <a class="newer-posts" href="/page/{{pagination.prev}}/">← Newer Posts</a>
    {{/if}}
    {{#if pagination.next}}
     <a class="older-posts" href="/page/{{pagination.next}}/">Older Posts →</a>
    {{/if}}
</nav>
This code will display old and new archive pages of posts in reverse chronological order aligned to left and right of the content column. The code is very simple with some new handlebars expressions discussed below.

  • {{pagination.prev}} - As the name implies, this expression will return a hyperlink to the older posts archive page in the current context.
  • {{pagination.next}} - This one also functions as the above one and returns the hyperlink to the newer post archive page in the current page context.
Ghost theme also provides a generic pagination expression that includes the current page number as well as the total number of post pages. If you prefer that format, you can use that too.

Creating Index (Home) Page Template

And now comes the important home page template that is used for the index page of the blog. Generally, we display the latest posts on this page in reverse chronological order and that's what we're going to do with our template. You can either display post excerpts on this page or can opt for displaying complete posts. We're going to use the former approach as it's well suited for the homepage template.

There are several other custom options like zebra stripping the post list, customizing featured post and many more. To keep things simple, we'll render a basic post excerpt list on this page. Open blank index.hbs file in a text editor and paste the following code in it.

{{!< default}}
{{> header}}
<main id="content-wrapper" role="main">
 {{#foreach posts}}  
  <article class="{{post_class}}" role="article" itemscope itemtype="http://schema.org/Article">  
  <header class="post-header">
  <h1 class="post-title" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{title}}</a></h1>
  <span class="post-byline">Posted on <time class="post-date" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time> by {{author}}</span>
  </header>
  <div class="post-excerpt" itemprop="articleBody">
   {{excerpt}}<span>...</span>
  </div>
  <div class="post-readmore">
  <a class="readmore-link" href="{{{url}}}" rel="bookmark">Read More →</a>
  </div>
  </article>  
 {{/foreach}}
  {{#if pagination}}
   {{> pagination}}
  {{/if}}
</main>
{{> footer}}
If you open the home page of your blog at this stage, you'll see the list of posts with header and footer sections without any style and formatting. At a glance, you can immediately deduce that the HTML code of the template shown above is all about looping through the latest posts and displaying them as excerpts. There are several new and important handlebars expressions in this template. We'll omit the expressions explained earlier in the previous template codes.

  • {{!< default}} - This is one of the important handlebars expressions used in Ghost themes. The opening double curly braces are continued with an exclamation mark and less than a symbol. After that is the name of the default template we've coded earlier. What's the effect of this expression? This expression instructs the Ghost engine to copy the template code right below it and insert it replacing the {{{body}}} expression in the file default.hbs. In simple words, we're exporting the index template code and inserting it at the desired place within the default template.
  • {{> header}} - And, this one is another interesting handlebar expression one may use heavily while designing full-fledged production themes. The greater than symbol right after the opening curly braces indicate the import of external code. After that is the name of the template file from where the code needs to be imported. In simple words, this expression instructs Ghost engine to pull the partial template code header we've coded earlier and to insert it exactly at the place where this expression is written.
  • {{#foreach ...}} ... {{/foreach}} - This is one of the looping handlebars expressions we've used to fetch the collection of latest post excerpts.
  • {{post_class}} - This unique handlebars expression can be used to stylize your theme as it dynamically adds one or more than one CSS classes to the article section of the excerpt.
  • {{{url}}} - As the name implies, this expression returns the URL of the post in the current context of the loop. Notice the use of three curly braces to prevent escaping HTML output.
  • {{title}} - Again a simple expression used to fetch the title of the post.
  • {{author}} - This important handlebars expression fetches the author of the current post in the loop.
  • {{excerpt}} - Again, the name itself tells what this expression is all about. It can take several parameters to customize the excerpt output.
  • {{> pagination}} - Quite similar to the inclusion of partial header template discussed above, this expression pulls the code from partial pagination template we've coded earlier and inserts it replacing the expression itself.
  • {{> footer}} - And last but not the least, this expression imports the partial footer template and appends it at the end of the current template right below the pagination code imported in the previous step.
You can experiment with this template modifying the existing code to customize the post list as per your preferences. Instead of excerpts, you can also opt for displaying entire post content on the home page. In that case, you only have to replace the {{excerpt}} expression with an appropriate alternate discussed below.

Creating Single Post Page Template

The next template we're going to code is the one that is most heavily accessed by inbound traffic. We're talking about single post pages displaying the entire content of a post. Open the blank file post.hbs created earlier in a text editor and paste the following code into it.

{{!< default}}
{{> header}}
<main id="content-wrapper" role="main">
{{#post}}
<article class="{{post_class}}" role="article" itemscope itemtype="http://schema.org/Article">
<header class="post-header">
<h1 class="post-title" itemprop="headline">{{{title}}}</h1>
<span class="post-byline">Posted on <time class="post-date" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time> by {{author}}</span>
</header>
<div class="post-content" itemprop="articleBody">
{{content}}
</div>
{{#if tags}}
<div class="post-tags">
<span class="tag-list">{{tags prefix="Posted in: " separator=", "}}</span>
</div>
{{/if}}    
        {{#if author}}
        <footer id="post-footer">
        <address itemscope itemtype="http://schema.org/Person">
{{#if author.image}}
        <div class="author-image">
<img src="{{author.image}}" itemprop="image" /></div>
{{else}}
        <div class="author-image">
<img src="/assets/images/avatar.jpg" itemprop="image" /></div>
{{/if}}
        <h4 class="author-name">
<span itemprop="author">{{author.name}}</span></h4>
{{#if author.website}}
        <div class="author-website">
<a href="{{author.website}}" itemprop="url" rel="nofollow">{{author.website}}</a></div>
{{/if}}
        </address>
<div class="author-bio">
<p>{{author.bio}}</p>
</div>
</footer>
        {{/if}}
</article>
{{/post}}    
    
{{#if pagination}}
    {{> pagination}}
{{/if}}
</main>
{{> footer}}
This simple post template displays the entire content of the post and an author bio section right below it. Ghost let's user to upload the author avatar, image, website URL and short bio from within the dashboard. The template pulls the data provided by the user, if any. Following are some of the new handlebars expressions used in this template.

  • {{#post}} ... {{/post}} - This important handlebars expression starts the post context within a single post page. Right after this expression, you can access various post related data enclosed within the matching closing expression tag.
  • {{content}} - This simple yet powerful expression outputs the entire post content in HTML form. In case, you want to display entire post content instead of excerpts on the home page, you can use this expression.
  • {{tags prefix="Posted in: " separator=", "}} - If post author has tagged the post, this expression will display all the tags associated with the post. It can take multiple parameters like custom text that can be displayed before the tag list and a custom separator character for multiple tags.
  • {{author.image}} - If the post author image is available, Ghost will automatically pick it and will display it through this expression.
  • {{author.name}} - This one is for displaying the name of the post author.
  • {{author.website}} - If author's website URL is available, this expression can display it.
  • {{author.bio}} - And last but not the least, this important handlebars expression fetches the short bio of the post author, if available.
The author bio section is entirely optional and can be replaced with something else. You can also embed DISQUS comments section right below the post content. Since this is just a skeleton theme, I've skipped integration of comments. Rest of the expressions included in the template is already explained in the previous sections.

Creating Static Page Template

Next one is the template code for every static page created on the blog. Though Ghost supports custom templates for individual static pages, we're going to concentrate on a generic template used for all the pages. Open a blank file page.hbs in the text editor and paste the following code in it. Remember, except the partial templates that reside in the partials directory, all other template files are saved within the root directory of the theme.

{{!< default}}
{{> header}}
 <main id="content-wrapper" role="main">
 {{#post}}
  <article class="{{post_class}}" role="article" itemscope itemtype="http://schema.org/Article">
   <header class="post-header">
   <h1 class="post-title" itemprop="headline">{{{title}}}</h1>
   </header>
    <div class="post-content" itemprop="articleBody">
    {{content}}
    </div>
  </article>
 {{/post}}    
 </main>
{{> footer}}
Comparatively, a static page template is much simpler than a single post page template. We are displaying the page title and content without any author bio section. All the handlebars expressions used in this template are already explained in the previous sections. If you want to add custom content to the page template, you can freely modify this template.

Creating Tag Archive Page Template

The following template renders the tag archive pages. It's more or less similar to the index page archive we've created earlier with minor changes. Create a blank file tag.hbs in a text editor and paste the following code in it.

{{!< default}}
{{> header}}
<main id="content-wrapper" role="main">
<header class="tag-archive-heading">
<h1>{{tag.name}}</h1>
<hr>
</header>
 {{#foreach posts}}  
  <article class="{{post_class}}" role="article" itemscope itemtype="http://schema.org/Article">  
  <header class="post-header">
  <h1 class="post-title" itemprop="headline"><a href="{{{url}}}" rel="bookmark">{{title}}</a></h1>
  <span class="post-byline">Posted on <time class="post-date" datetime="{{date format="YYYY-MM-DD"}}" itemprop="datePublished">{{date format='dddd DD MMM YYYY'}}</time> by {{author}}</span>
  </header>
  <div class="post-excerpt" itemprop="articleBody">
   {{excerpt}}<span>...</span>
  </div>
  <div class="post-readmore">
  <a class="readmore-link" href="{{{url}}}" rel="bookmark">Read More →</a>
  </div>
  </article>
 {{/foreach}}
  {{#if pagination}}
   {{> pagination}}
  {{/if}}
</main>
{{> footer}}
The template code first displays a large heading displaying the tag name with a horizontal rule right below it. Thereafter, all the posts falling under that tag are displayed as title and excerpt combination on all the subsequent archive pages. Following is the new handlebars expression used in this template.

  • {{tag.name}} - This expression displays the tag name whose archive is being displayed on the page.
Instead of excerpts, you can also display entire content for each post though it is not recommended for archive pages. If you want to simplify it further, you can also opt to display just the hyperlinked post titles on all the tag archive pages.

Creating Error (404) Page Template

By default, Ghost uses an inbuilt 404 template included in its core whenever a situation arises. But, you can always code a custom template for the same that matches your theme. Create a blank file error.hbs in a text editor and paste the following code in it.

<!DOCTYPE html>
<html>
<head>
</head>
 <body>
 <h3>Custom template for 404 page!</h3> 
 </body>
</html>
As you can see, I've taken a minimal template structure for this page. I leave this as an exercise for you to design a 404 page for your theme as per your needs. This finishes all the template files we require for our skeleton theme.

Stylizing the Theme

And now comes the important part of giving our skeleton theme a basic and proper shape through CSS code that can be extended to make a production grade Ghost theme. Within your Ghost installation folder, go to assets → css subfolder and create a blank file named style.css. Now open the file default.hbs once again and add the following line anywhere in its <head>...</head> tag.

<link rel="stylesheet" type="text/css" href="{{asset "css/style.css"}}" />
This will include the CSS stylesheet with every web page of our theme. The only handlebar expression used in this code snippet is explained below.

  • {{asset <path>}} - Through this expression, we can not only include and use various assets (scripts, images, fonts) residing in the assets folder but can also ensure they're automatically cached by Ghost engine for optimal performance.
The next step involves writing the CSS code for our theme. Open the style.css file we've created earlier in your favorite text editor and paste the following code in it.

* {
  margin: 0;
  padding: 0;
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}
     
    article,
    aside,
    details,
    figcaption,
    figure,
    footer,
    header,
    hgroup,
    nav,
    section,
    summary {
    display: block;
    }
     
    audio,
    canvas,
    video {
    display: inline-block;
    *display: inline;
    *zoom: 1;
    }
         
    audio:not([controls]) {
    display: none;
    height: 0;
    }     
         
    [hidden] {
    display: none;
    }     
         
    html {
    font-size: 100%; /* 1 */
    -webkit-text-size-adjust: 100%; /* 2 */
    -ms-text-size-adjust: 100%; /* 2 */
    }     
   
    html,
    button,
    input,
    select,
    textarea {
    font-family: sans-serif;
    }     
         
    body {
    margin: 0;
    }
             
    a:active,
    a:hover,
    a:focus {
    outline: 0;
    }     
         
    h1 {
    font-size: 2em;
    margin: 0.67em 0;
    }
     
    h2 {
    font-size: 1.5em;
    margin: 0.83em 0;
    }
     
    h3 {
    font-size: 1.17em;
    margin: 1em 0;
    }
     
    h4 {
    font-size: 1em;
    margin: 1.33em 0;
    }
     
    h5 {
    font-size: 0.83em;
    margin: 1.67em 0;
    }
     
    h6 {
    font-size: 0.75em;
    margin: 2.33em 0;
    }     
        
    abbr[title] {
    border-bottom: 1px dotted;
    }     
        
    b,
    strong {
    font-weight: bold;
    }
     
    blockquote {
    margin: 1em 40px;
    }     
        
    dfn {
    font-style: italic;
    }     
         
    mark {
    background: #ff0;
    color: #000;
    }     
        
    p,
    pre {
    margin: 1em 0;
    }     
       
    code,
    kbd,
    pre,
    samp {
    font-family: monospace, serif;
    _font-family: 'courier new', monospace;
    font-size: 1em;
    }     
        
    pre {
    white-space: pre;
    white-space: pre-wrap;
    word-wrap: break-word;
    }
        
    q {
    quotes: none;
    }     
       
    q:before,
    q:after {
    content: '';
    content: none;
    }
     
    small {
    font-size: 75%;
    }     
        
    sub,
    sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
    }
     
    sup {
    top: -0.5em;
    }
     
    sub {
    bottom: -0.25em;
    }     
        
    dl,
    menu,
    ol,
    ul {
    margin: 1em 0;
    }
     
    dd {
    margin: 0 0 0 40px;
    }
         
    menu,
    ol,
    ul {
    padding: 0 0 0 40px;
    }     
        
    nav ul,
    nav ol {
    list-style: none;
    list-style-image: none;
    }     
       
    img {
    border: 0; /* 1 */
    -ms-interpolation-mode: bicubic; /* 2 */
    }     
        
    svg:not(:root) {
    overflow: hidden;
    }     
         
    figure {
    margin: 0;
    }     
         
    form {
    margin: 0;
    }     
         
    fieldset {
    border: 1px solid #c0c0c0;
    margin: 0 2px;
    padding: 0.35em 0.625em 0.75em;
    }     
         
    legend {
    border: 0; /* 1 */
    padding: 0;
    white-space: normal; /* 2 */
    *margin-left: -7px; /* 3 */
    }     
         
    button,
    input,
    select,
    textarea {
    font-size: 100%; /* 1 */
    margin: 0; /* 2 */
    vertical-align: baseline; /* 3 */
    *vertical-align: middle; /* 3 */
    }     
         
    button,
    input {
    line-height: normal;
    }     
         
    button,
    html input[type="button"], /* 1 */
    input[type="reset"],
    input[type="submit"] {
    -webkit-appearance: button; /* 2 */
    cursor: pointer; /* 3 */
    *overflow: visible; /* 4 */
    }     
        
    button[disabled],
    input[disabled] {
    cursor: default;
    }     
         
    input[type="checkbox"],
    input[type="radio"] {
    box-sizing: border-box; /* 1 */
    padding: 0; /* 2 */
    *height: 13px; /* 3 */
    *width: 13px; /* 3 */
    }     
         
    input[type="search"] {
    -webkit-appearance: textfield; /* 1 */
    -moz-box-sizing: content-box;
    -webkit-box-sizing: content-box; /* 2 */
    box-sizing: content-box;
    }     
         
    input[type="search"]::-webkit-search-cancel-button,
    input[type="search"]::-webkit-search-decoration {
    -webkit-appearance: none;
    }     
         
    button::-moz-focus-inner,
    input::-moz-focus-inner {
    border: 0;
    padding: 0;
    }     
         
    textarea {
    overflow: auto; /* 1 */
    vertical-align: top; /* 2 */
    }     
         
    table {
    border-collapse: collapse;
    border-spacing: 0;
    }

    li > ul, li > ol {
        margin-bottom: 0;
        margin-top: 0;
    }



/* Custom styles */

html {
    font-family: Georgia, "Times New Roman", Times, serif;     
    height: 100%;
}

body { 
    font-family: Georgia, "Times New Roman", Times, serif;
    font-size: 16px;
    line-height: 26px;
    color: #111;
    background-color: #f6faff;  
    height: 100%;
}

.leftalign, img[align="left"] {
    display: block;
    float: left;
    margin: 0 15px 20px 0;
}

.rightalign, img[align="right"] {
    display: block;
    float: right;
    margin: 0 0 20px 15px;
}

.middlealign, img[align="middle"] {
    display: block;
    margin-right: auto;
    margin-left: auto;
    text-align: center;
    float: none;
    clear: both;
}

img {
    display: block;
    max-width: 100%;
    height: auto;    
}

a {
    background: transparent;
    color: #336f9a;
    text-decoration: underline;
}

a:hover {
    text-decoration: none;
}

hr {
    -moz-box-sizing: content-box;
    box-sizing: content-box;
    height: 0;
    margin-bottom: 1em;
    color: #eee;
}

ul {    
    list-style-type: square;
}

code, tt {
    padding: 1px 3px;
    font-family: Inconsolata, monospace, sans-serif;    
    white-space: pre-wrap;
    border: 1px solid #E3EDF3;
    background: #F2E1C1;
    border-radius: 2px;
}

pre {    
    margin: 1.6em 0;
    border: 1px solid #E3EDF3;
    width: 100%;
    padding: 10px;
    font-family: Inconsolata, monospace, sans-serif;    
    white-space: pre;
    overflow: auto;
    background: #f0f0f0;
    border-radius: 3px;
}

pre code, tt {
    font-size: inherit;
    white-space: -moz-pre-wrap;
    white-space: pre-wrap;
    background: transparent;
    border: none;
    padding: 0;
}

blockquote {    
    border-left: 5px solid #eee;
    font-style: italic;
    margin-left: 25px; 
    padding-left: 10px;   
}

input[type="text"] {    
    border: 1px solid #ccc; 
    font-size: 15px;
    padding: 5px;
    box-shadow: 0px 1px 1px rgb(204, 204, 204) inset;
    width: 50%;
}

textarea {
    border: 1px solid #ccc; 
    font-size: 15px;
    padding: 5px;
    box-shadow: 0px 1px 1px rgb(204, 204, 204) inset;
    width: 90%;
}

input[type="submit"], button[type="submit"] {    
    padding: 5px 10px;
    border-radius: 3px;
    background-color: #e1e1e1;    
}

#wrapper {
    position: relative;    
    max-width: 780px;
    margin: 0 auto;   
    background-color: #fff;
    padding: 25px 35px 0 35px; 
    min-height: 100%;  
    border-left: 5px solid #efe6d8;
    border-right: 5px solid #efe6d8;
}

#content-wrapper {    
    margin-top: 20px;    
    padding: 0 10px;    
}

#site-footer { 
    clear: both; 
    font-size: 12px;             
    padding: 15px 10px;     
    border-top: 1px solid #d0d0d0;  
    overflow: auto;      
}

#site-header {  
    border-bottom: 5px solid #efefef;
    padding: 25px 0 0 0;  
    position: relative;    
    margin: 0 auto;   
    display: block;    
    text-align: center;
    color: #fff;    
    background-size: cover !important;         
}

#site-header a {
    color: #000;
    text-decoration: none;
}

#site-logo img {
    border: 1px solid #dedee0;
    background-color: #f8f6f7;
    padding: 3px;
    display: block;
    max-height: 100px;
    max-width: 100px;
    width: auto;
    margin: 0 auto;
    line-height: 0;    
    -webkit-border-radius: 50px;
       -moz-border-radius: 50px;
        -ms-border-radius: 50px;
         -o-border-radius: 50px;
            border-radius: 50px;            
}

#site-title, #site-description {
    font-family: Georgia, Arial, "Times New Roman", serif;    
}

#site-title {   
    font-weight: bold;
    margin-top: 40px; 
    margin-bottom: 12px;
    font-size: 32px;    
}

#site-description {
    margin-top: -1px;
    margin-bottom: 8px;    
    font-size: 16px;
    padding: 0 5px;
}

.post {
    margin-bottom: 25px;
    padding: 10px;
}

.post-title {
    font-size: 26px;
    line-height: 1.5em;
}

h1.post-title {
    margin: 0;
    font-weight: normal;
}

.post-title a {
    color: #1c81af;
    text-decoration: none;
}

.post-title a:hover {
    color: #084681;
}

.post-byline {
    font-size: 13px;
    color: #7b7b7b;    
}

.post-header {
    margin-bottom: 8px;
}

.post-content h1, .post-content h2, .post-content h3, .post-content h4, .post-content h5 {
    font-weight: normal;
    margin: 0;
}

.post-tags {
    margin: 15px 0 10px 0;
    font-size: 15px;
}

#post-footer {    
    margin-top: 25px;    
    background-color: #f0f8ff;
    border: 1px solid #d6d6d6;
    padding: .567em;
    height: auto;        
    word-wrap: break-word;
    min-height: 120px;  
    clear: both;     
}

#post-footer img {   
    float: left;   
    margin-right: 18px;
    margin-bottom: 10px;
    border: 1px solid #dedee0;
    background-color: #fff;
    padding: 5px;
    display: block;
    max-height: 100px;
    max-width: 100px;
    width: auto;    
    line-height: 0;        
    -webkit-border-radius: 50px;
       -moz-border-radius: 50px;
        -ms-border-radius: 50px;
         -o-border-radius: 50px;
            border-radius: 50px;
}

#post-footer h4 {
    font-size: 18px;
    margin: 0 0 5px 0;
}

#post-footer p {
    margin: 0;
}

.author-bio {
    font-size: 15px;
    line-height: 25px;
}

.author-website {
    font-weight: bold;
    margin: 0 0 5px 0;
}

.tag-list {
    font-style: italic;
}

.even-post {
    background-color: #f7f7f7;
    border: 1px solid #efefef;
}

.older-posts {    
    float: right;    
}

.newer-posts {
    float: left;    
}

#pagination {
    padding: 30px 10px 50px;    
    text-align: center;     
    margin: 10px 0;         
}

.copyright {
    float: left;
}

.ghost-credit {
    float: right;   
}

.error-content {
    border: 1px solid #eee;
    margin: 25px auto;    
    background-color: #efefef;
    padding: 10px;
    text-align: center;
}

#share-post {
    width: 100%;    
    float: right;
    padding: 6px 20px;
    background: #f9f9f9;
    border: 1px solid #ddd;
    border-left: 0;
    border-right: 0;
    margin: 15px 0;    
    font-weight: bold;   
}

.tag-archive-heading {
    color: #888;    
}


/* Responsive stuff */

@media all and (max-width: 750px) {
    #wrapper {
        border-left: none;
        border-right: none;
        padding: 10px 15px 0 15px;
    }
}

@media all and (max-width: 600px) {
    #wrapper {
        padding: 5px 5px 0 5px;
    }

    .ghost-credit, .copyright {
        float: none;
    }

    #site-footer {
        text-align: center;
    }
}

@media all and (max-width: 450px) {
    #wrapper{
        padding: 0;
    }

    #content-wrapper {
        padding: 0 5px;
    }

    #post-footer {
        text-align: center;
    }

    #post-footer img {
        float: none;
        margin: 0 auto 5px;
    }    

    #site-logo img {
        max-height: 80px;
        max-width: 80px;    
        -webkit-border-radius: 40px;
           -moz-border-radius: 40px;
            -ms-border-radius: 40px;
             -o-border-radius: 40px;
                border-radius: 40px;
    }   

    .leftalign, img[align="left"], .rightalign, img[align="right"], .middlealign, img[align="middle"] {
        float: none;
    }

    input[type="text"], textarea {
        width: 100%;
    }
}

@media all and (max-width: 400px) {
    #site-title {
        font-size: 25px;
    }

    .post-title {
        font-size: 23px;
    }
}

@media all and (max-width: 320px) {
    #site-logo img {
        padding: 3px;
    }

    #subscribe-post {
        display: none;
        visibility: hidden;
    }

    #site-title, .post-title {
        font-size: 20px;
    }   

    #site-description {
        font-size: 14px;
        font-weight: normal;
    }   

    .post-excerpt, .post-readmore, .newer-posts, .older-posts, .post-content {
        font-size: 15px;        
    }

    .post-excerpt, .post-content {
        line-height: 25px;
    }
}
You can freely modify the CSS code shown above to give a better shape to the theme. One can also replace the CSS reset used in this theme with their preferred version. For your convenience, I've zipped all the template files in a single package so that you can quickly use this skeleton theme with your Ghost instance.

Download Skeleton Ghost Theme (ZIP) 12KB

Simply unzip and upload the theme folder to the themes folder of your Ghost installation. If your Ghost server asks for the zip version, you can directly upload the zip file. If you have any questions related to the template files and general Ghost theme development, feel free to ask through the comments below.