Welcome to the third and final entry in the “Building an Assemble.io Blog” series. In Building an Assemble.io Blog: Part 1, I covered the basics including installation, layouts, adding entries, and displaying a posts list. In Building an Assemble.io Blog: Part 2, I abstracted common elements of the template into partials, added support for non-markdown pages, and added a home page featuring the latest blog entry.

This entry covers static blog assets, integrates Bootstrap styles into the blog, and adds Disqus comments for user feedback and interactivity.

Notes: The code in this tutorial is hosted on GitHub at https://github.com/webercoder/assembleio-blog. This post begins at the example-7 tag. Feel free to follow along if you’d like, or make you own!

Static Assets

All blogs have static assets such as images, scripts, and stylesheets that need to be deployed alongside the blog engine. Grunt can be configured to handle and deploy these assets.

Let’s start by adding a favicon to the site. I’ve picked this single color rubix cube icon for now. First make the static resources directory and add the favicon to it:

% mkdir src/static
% mv ~/location/of/favicon.ico src/static

Let’s configure Grunt to copy resources from our static folder to the build folder. We’ll be using grunt-contrib-copy for this task. First install the package:

% npm install --save-dev grunt-contrib-copy

Pop open our Gruntfile.js and add the copy code:

module.exports = function(grunt) {

    'use strict';

    grunt.initConfig({

        // Tasks here
        assemble: {
            options: {
                layout: 'default.hbs',
                layoutdir: './src/layouts/',
                partials: './src/layouts/partials/**/*.hbs',
                helpers: './src/helpers/**/*.js'
            },
            blog: {
                files: [{
                    cwd: './src/content',
                    dest: './build/',
                    expand: true,
                    src: ['**/*.md', '**/*.hbs']
                }]
            }
        },

        copy: {
          'static-assets': {
            files: [{
              expand: true,
              src: [
                './**/*'
              ],
              dest: 'build/',
              filter: 'isFile',
              cwd: 'src/static/'
            }]
          }
        }

    });

    // Load plugins for the above tasks
    grunt.loadNpmTasks('assemble');
    grunt.loadNpmTasks('grunt-contrib-copy');

    // The default task or other custom tasks
    grunt.registerTask('default', ['assemble', 'copy']);

};

I’ve added the copy block, grunt.loadNpmTasks('grunt-contrib-copy'), and registered copy to run as part of the default grunt.registerTask call. The copy block will copy everything in the src/static folder into build/.

Now run grunt and load the site in your browser (/build/index.html). You should see the favicon in your address bar or somewhere around the page title.

Update: Sadly the favicon will not appear if you are browsing the site from a subdirectory, as I advised above. Browsers automatically load favicon’s from the website’s root directory, and since favicon.ico is not in the root above but in /build, the browser will not retrieve it. We can get creative with the Assemble relative linking and add a <link> element to our header, or you can load the blog from the root of your web server. I will update the blog to use the first option in the future.

Git Checkpoint: The code above is available on the tag example-8 in the example Git repository.

Basic Styling

The visual design of the example blog is as barebones as it gets. Using Bootstrap we can add a clean new look to the site in minutes rather than days. In this post, I’ll cover how to integrate Bootstrap into this static publishing engine.

Note: Bootstrap uses a 12-column grid to simplify creating a layout. I won’t be covering this in detail, however. If you would like to know more, check out the official Bootstrap documenation.

First download Bootstrap from Bootstrap’s website and place bootstrap.min.css in to src/static/css.

Next change src/layouts/partials/header.hbs to look like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>{{title}} - My Blog</title>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1.0">
    <link href="{{relative page.dest 'build/css/bootstrap.min.css'}}" rel="stylesheet">
  </head>
  <body>
    <nav class="navbar navbar-inverse">
      <div class="container">
        <div class="navbar-header">
          <a class="navbar-brand" href="{{relative page.dest 'build/index.html'}}">My Blog</a>
        </div>
      </div>
    </nav>
    <div class="container">

When linking to bootstrap.min.css, I’ve used the {{relative page.dest ...}} handlebar tag to create a relative link from the current page location. Doing this keeps our code portable so it can be run under any directory.

Additionally, the new tags inside the body define a Bootstrap navbar and a container. The navbar is very basic, containing only a single link to the home page. <div class="container"> is required by Bootstrap for styling purposes and is closed off in src/layouts/partials/footer.hbs:

</div> <!-- container -->
</body>
</html>

Next add a row and column to the page template, src/layouts/page.hbs. This will add our content to Bootstrap’s grid, allowing the content to flow as Bootstrap’s authors designed.

{{>header}}
<div class="row">
	<div class="col-sm-12">
		{{>body}}
	</div>
</div>
{{>footer}}

You can later add other columns inside the row as long as all the col-*-n classes add up to 12. For example, we could do a two column layout using the following code:

<div class="row">
	<div class="col-sm-4">
		...
	</div>
	<div class="col-sm-8">
		...
	</div>
</div>

In the snippet above the second column will be 2x the width of the first column.

Finally, add similar classes to src/layouts/page.hbs:

{{>header}}
<div class="row">
	<div class="col-sm-12">
		{{>body}}
	</div>
</div>
{{>footer}}

With all that saved, run grunt and then browser to the build directory like before. Looks much nicer! If you would like to know more about Bootstrap, start with their Getting Started guide.

Git Checkpoint: The code above is available on the tag example-9 in the example Git repository.

Disqus Comments

As a static publishing engine, writing our own database-powered commenting system defeats the purpose. You could do something fancy like creating a JAWS Stack with AWS services or baking your own server side solution; or you can use Disqus. We’ll go with the latter for simplicity.

First signup for a Disqus account on disqus.com. At the time of writing, you can register a new site with Disqus by clicking the top-right gear to expose a menu, and clicking “Add Disqus to Site.” Fill in the required information and click submit. Finally grab the universal code and add it to src/layouts/partials/footer.hbs between a new Boostrap row and column:

  {{#unless data.exclude}}
    <div class="row">
      <div class="col-sm-12">

        <div id="disqus_thread"></div>
        <script type="text/javascript">
            /* * * CONFIGURATION VARIABLES * * */
            var disqus_shortname = 'The shortname that you picked';

            /* * * DON'T EDIT BELOW THIS LINE * * */
            (function() {
                var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
                dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
                (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
            })();
        </script>
        <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>

      </div>
    </div>
  {{/unless}}

</div> <!-- container -->
</body>
</html>

After running grunt once again, go to the site and view a blog entry. Comments should be live!

Git Checkpoint: The code above is available on the tag example-10 in the example Git repository.

Conclusion

In this entry, we fleshed out our blog with static assets, Bootstrap styles, and a turn-key commenting system.

I hope you enjoyed the series! Feel free contact me if you have questions.