web  

Feb 20, 2015 • Michael Chen

When you built a blog from Jekyll, a beautiful, mobile-responsive theme passed as well. However, if you want to utilize a third party web front framework like Bootstrap, the built-in CSS file became potential sources of CSS conflict and delayed page loading. Starting a Jekyll blog from blank theme seems daunting, but, with the help of Bootstrap, the process becomes agreeable and enjoyable.

Before starting our next awesome blog, let’s see a sample Jekyll project and build a blueprint in mind.

$ lstree
  +-- _drafts/
  +-- _includes/        # directory for template partials
  |   +-- footer.html
  |   +-- head.html
  |   +-- header.html
  +-- _layouts/         # directory for main layouts
  |   +-- default.html
  |   +-- page.html
  |   +-- post.html
  +-- _posts/
  +-- _site/
  +-- css/              # place for CSS files
  +-- fonts/            # place for web fonts
  +-- images/
  +-- index.html        # index page
  +-- js/               # holder for JavaScript files
  +-- _config.yml       # project config file
  +-- 404.md            # page for 404 error
  +-- 500.md            # page for 50x errors

Most of the process building a blank Jekyll site is filling our own template files. They are default.html, post.html, and page.html, which are all in _layouts folder. Besides, you also need to edit index.html and don’t forget _config.yml.

Download needed CSS and JavaScript files and place them in the corresponding folders. Although we can utilize many CDNs to boost our loading speed, I still prefer a local files as fallbacks.

default.html is the root of our template; all other templates inherit from it. Therefore, let’s start from here.

<!DOCTYPE html>
<html lang="en">
  
  {% include head.html %}
  
  <body>
    
    {% include header.html %}
    
    {{ content }}
    
    {% include footer.html %}
    
  </body>
</html>

Here we keep a simple structure and delegate our HTML tags to other files. You may just put everything here if you prefer.

Let’s see our head.html file.

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  
  <!-- custom meta tags from page -->
  <meta name="description" content="{% if page.description %}{{ page.description }}{% else %}{{ site.description }}{% endif %}">
  <meta name="keywords" content="{% if page.keywords %}{{ page.keywords }}{% else %}{{ site.keywords }}{% endif %}">
  
  <!-- custom title from page -->
  <title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title>

  <!-- Bootstrap CSS file -->
  <link href="{{ "/css/bootstrap.min.css" | prepend: site.baseurl }}" rel="stylesheet">

  <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
  <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
  <!--[if lt IE 9]>
  <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
  <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  <![endif]-->

  <!-- Pygments style CSS file -->
  <link href="{{ "/css/monokai.css" | prepend: site.baseurl }}" rel="stylesheet">
  
  <!-- our own custem CSS file -->
  <link href="{{ "/css/site.css" | prepend: site.baseurl }}" rel="stylesheet">
  
  <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
  <script>
    (function() {
        if(typeof jQuery=='undefined') {
            document.write('<sc'
			   + 'ript src="/js/jquery-1.11.2.min.js"></sc'
			   + 'ript>');
        }
    }).call(this);
  </script>

  <!-- Bootstrap JS script -->
  <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
  <script>
    (function() {
	  if (typeof($.fn.modal) === 'undefined') {
	    document.write('<sc'
			   + 'ript async src="/js/bootstrap.min.js"></sc'
			   + 'ript>');
	  }
    }).call(this);
  </script>
</head>

There are several tricks here. First, set customized meta tags and title so that we can fill these informations from YAML front. Second, load JavaScript from available CDNs by default but write JS code for fallbacks. Here we use a document.write trick to add HTML tags here and now.

Then comes header.html. The template will make navigation bar. Simply follow standard Bootstrap navbar example here.

<div class="navbar navbar-default">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse"
              data-target="#mynavbar-content">
	     <span class="icon-bar"></span>
	     <span class="icon-bar"></span>
	     <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="{{ "/" | prepend: site.baseurl }}">
        {{ site.title }}
      </a>
    </div>

    <div class="collapse navbar-collapse" id="mynavbar-content">
      <ul class="nav navbar-nav">
        
        {% for page in site.pages %}
        {% if page.title %}
        
	    <li><a href="{{ page.url | prepend: site.baseurl }}">{{ page.title }}</a></li>
	    
        {% endif %}
        {% endfor %}
        
      </ul>
    </div>
  </div>
</div>

Next comes footer.html. You may refer this example and design your own footer. Here we use nested Bootstrap grids for layout.

<footer class="site-footer">
  <div class="container">
    <div class="row">
      <div class="col-sm-10 col-sm-offset-1 text-center">
        <div class="row">
	  <div class="col-sm-3 col-xs-12">
	    <img src="{{ "/images/mail.png" | prepend: site.baseurl }}"
	    alt="send e-mail to Michael Chen">
	  </div>
      <div class="col-sm-3 col-xs-12">
        <!-- put Twitter follow icon here -->
      </div>
	  <div class="col-sm-3 col-xs-12">
        <!-- put your own counter here -->
	  </div>
	  <div class="col-sm-12">
	    <div>{{ site.description }}</div>
	  </div>
        </div>
      </div>
    </div>
  </div>
</footer>

Now we finished our default.html. Let’s re-use it in post.html and page.html. First comes post.html.

---
layout: default
---
<div class="container">
  <header>
    <div class="page-header">
      <h1>{{ page.title }}</h1>
    </div>
    <p>{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}
    </p>
  </header>

  <article>
    {{ content }}
  </article>
</div>

Here comes page.html.

---
layout: default
---
<div class="container">
  <header>
    <div class="page-header">
      <h1>{{ page.title }}</h1>
    </div>
  </header>

  <article>
    {{ content }}
  </article>
</div>

Don’t forget index.html. You may design your impressive index page here.

---
layout: default
---
<div class="container">
  {% for post in paginator.posts %}
    <h1><a href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a></h1>
    <p>{{ post.excerpt | strip_html }}</p>
    </div>
  {% endfor %}

  <div class="text-center">
    {% if paginator.previous_page %}
      <a class="btn btn-default" href="{{ site.baseurl }}{{ paginator.previous_page_path }}">Previous</a>
      {% endif %}
      <span>{{ paginator.page }} / {{ paginator.total_pages }}</span>
    {% if paginator.next_page %}
      <a class="btn btn-default" href="{{ site.baseurl }}{{ paginator.next_page_path }}">Next</a>
    {% endif %}
  </div>
</div>

The final step is writing down your own configuration file. Just follow the example and customize it to fit your own need.

# Site settings
title: "Your next awesome blog"
#email: your-email@example.com
description: "site description"
keywords: "site keywords"
baseurl: ""
url: "http://example.com"

# Build settings
markdown: kramdown
permalink: pretty

# pagination
paginate: 5
paginate_path: "page:num"

highlighter: "pygments"

Then, write some posts test your blog. With the help of Bootstrap, we built a clean and responsive blog merely in steps. You may try some Bootstrap themes for eye-candy.