---
title: How can you tell that a web developer is working?
url: https://blog.octoperf.com/how-can-you-tell-that-a-web-developer-is-working/
date: '2015-08-26'
authors:
- gpereira
categories:
- Development
tags:
- Github
- Jekyll
- Open Source
- JSON
- Mac OS
- Linux
- Node.js
- Git
- JQuery
- SCSS
description: A blog's creation guide, from Jekyll base theme to Boostrap4. Using Grunt/Bower and hosted on GitHub pages.
---

# How can you tell that a web developer is working?

## You can hear him Grunting!

## The problem
I want my blog (the one your’re reading) to be statically and freely served, using my own domain name. 
I want it to be maintainable and easy to update. **And first of all, I really do not have much time for this, so it must be up and running in a few hours.** 

## The solution
**Bye bye WordPress, Welcome Jekyll**. I can handle a couple lines of PHP but it's not my cup of tea. And WP breaks the static rule, so as the maintainable one.
I previously used Jekyll to create the [OctoPerf](https://octoperf.com) website. And as you may ask yourself why the bad joke about Grunt: 
I used it with Bower to create the frontend of our [load testing solution](https://api.octoperf.com/ui/).

Jekyll is a really handy tool. The kind of tool that you can use even without first reading the [documentation](https://jekyllrb.com/docs/) (my preferred kind!).
And when you face a problem, the solution is placed in the first 3 results of Google. The first one often being their manual that you didn't read.   
But Jekyll doesn't come with dependencies management, and the couple [Grunt](https://gruntjs.com/)/[Bower](https://bower.io/) is to JavaScript what Maven or Ant/Ivy is to Java: a nice build solution. 


To create this blog I mixed the three of them, and have a Jekyll boosted with:

* JS dependencies managed by Bower,
* CSS and JS files references automatically included in my HTML pages.

**On top of that I wanted to give a try to Bootstrap4 and host it on GitHub pages**.
Bootstrap because I can't do anything Web-related without it, and GitHub pages to freely host my blog using my own domain name.

So stop being soiled by dirty PHP/MySQL and let's get started with static websites.

### Blog template

Even so my blog is on a private repository, [a public template is available](https://github.com/geraldpereira/blog-template). 
If you too are interested in creating yours this way, you may read this article. 
**You can also fork the blog-template repository, use it for your own and skip the rest of the reading.** The README contains all the commands you may need to run and deploy it.

Well ... I bet you'll come back at least once on this page to get more information about the template. 
But go on, fork it and build it by yourself, I would have done the same ;)

**To save you some of your precious time:**

* If you're not familiar with it, you may still need to read the [GitHub Pages part](#github-pages) to deploy your blog on it. 
* If you're lost within the Bower and Grunt configurations, provided that you dare edit it, head back to the [corresponding chapter](#bower-dependencies-management).
* The [rest of the article](#bootstrap-4) explain how I went from the default Jekyll template to a more advanced theme based on bootstrap 4, you may read it before customizing the CSS of your blog. 

### Prerequisites / installation
The prerequisites to build and host this blog are:

* **A Linux or Mac operating system** (I run on Ubuntu, so all commands are for this specific OS), 
* [Jekyll](https://jekyllrb.com/docs/) our static website generator (`sudo apt-get install ruby-full rubygems-integration` and `sudo gem install jekyll`),
* Node.js and its package manager Npm (`sudo apt-get install nodejs npm`),
* [Grunt](https://gruntjs.com/getting-started#installing-the-cli), our Javascript task runner (`sudo npm install -g grunt-cli`),
* [Bower](https://bower.io/#install-bower) to manage the blog dependencies (`sudo npm install -g bower`).

The coffee-script npm module should also be installed globally: `sudo npm install -g coffee-script`.

You may also need to run the command `ln -s /usr/bin/nodejs /usr/bin/node` if you have the error [/usr/bin/env: node: No such file or directory](https://github.com/nodejs/node-v0.x-archive/issues/3911) when running grunt or bower.

But first of all, you need a [GitHub account using SSH Keys](https://help.github.com/articles/generating-ssh-keys/) to host it (or an Amazon account if you prefer to [host Jekyll on CloudFront](https://blog.octoperf.com/hosting-jekyll-on-amazon-web-services/))

## GitHub pages
I first wanted to [create my blog as a user site](https://pages.github.com/). 
So I created a *geraldpereira* repository but failed to configure my DNS to use a custom URL in less than 20 minutes. Remember, I don't have to much time to devote to this.
I quickly gave up and [created a private *blog* repository](https://help.github.com/articles/creating-a-new-repository/).
 
Then I used the auto-generated pages :

1. Go to the settings of your blog repo: https://github.com/\<username\>/blog/settings or click on the <img class="inline" src='/img/blog/bootstrap4-blog-github-pages/github-pages-settings.png'/> button located on the right menu of your repository page.
2. Into the *GitHub Pages* section click on *Launch Automatic Page generator*.
2. Click on <img class="inline" src='/img/blog/bootstrap4-blog-github-pages/github-pages-layouts.png'/> as we will then replace the generated content by our Jekyll blog.
3. Select any layout and click on <img class="inline" src='/img/blog/bootstrap4-blog-github-pages/github-pages-publish.png'/>.
4. Visit https://github.com/\<username\>/blog/tree/gh-pages or select the *gh-pages* branch in your blog repo. You should see the source files of the generated content.
5. Visit http://\<username\>.github.io/blog/ to check that the blog is available. 
 
_Notes:_
 
> The master branch is private (for a private repo), but **the *gh-pages* branch is always public on GitHub**. 
> 
> This procedure can be skipped if you prefer to directly create a *gh-pages* branch on your blog repository. For once I preferred to follow the GitHub tutorial.
>
> I do not use [GitHub's embedded Jekyll build](https://help.github.com/articles/using-jekyll-with-pages/) as I will certainly use custom plugins that are not available there.


### Custom domain name
I bought *geraldpereira.com* at OVH, a french website hosting company. 

To use a custom domain name for your blog:

1. Create a file named CNAME at the root of your blog repository and put the url (no http://) as its content (*blog.geraldpereira.com* for my blog). 
You can [add files directly on GitHub](https://help.github.com/articles/creating-new-files/).
2. Configure a new DNS entry, eg. with OVH:

![OVH DNS](https://blog.octoperf.com/img/blog/bootstrap4-blog-github-pages/github-pages-ovh.png "OVH DNS")

_Note:_
 
> It seems to be more complicated to use a root domain name (eg. *geraldpereira.com*) for your site/blog and Github recommends to use a subdomain.
> I’m not very confident with my DNS configuration skills and I just ain't got time to achieve it. If you have a nice tutorial about it I may give it another try! 

### Remote to local

The next step is to pull your *blog* repo from GitHub. If you don’t know about git, you should read [that](https://help.github.com/articles/set-up-git/). 
**Your *blog* repository is divided in two branches**:
 
* *master*, that you can access at https://github.com/\<username\>/blog. It contains the 'source code' of your blog.
* *gh-pages*, available at https://github.com/\<username\>/blog/tree/gh-pages. It contains the 'compiled HTML code' of your blog.

To pull the *master* branch, type the command :

```sh
git clone git@github.com:<username>/blog.git blog
```

It creates a local folder named *blog* that should only contain a .git directory.

_Note:_
 
> This folder contains the *master* branch of your repository, not the *gh-pages* one. 
> To simplify, **the *master* branch contains the source code of your website**, and and the *gh-pages* contains the generated HTML / JS / CSS files. 
> **Only the *gh-pages* is served as HTML pages by GitHub** when you visit http://\<username\>.github.io/blog/ or at your custom domain name if you added the CNAME file.

To pull the *gh-pages* branch enter the command:

```sh
git clone -b gh-pages git@github.com:<username>/blog.git blog-gh-pages
```

It creates a local folder named *blog-gh-pages* that should contain the blog files generated by GitHub and your CNAME file.

### Local To remote
Later on you will probably need to **save your work**. To do so, go into the *blog* folder and type:
```sh
git add -A
git commit -m “Blog import”
git push
```

To publish your blog, simply do the same from the *blog-gh-pages* folder:
```sh
git add -A
git commit -m “Welcome Jekyll”
git push
```

The idea is to create a Jekyll project in the *blog* folder, and copy the generated files in the *blog-gh-pages* one.

_Note:_
 
> There is probably a way to use the same folder for both branches, but **I like to keep things simple, especially Git!**

## Jekyll static website

**Creating a new project is very simple.** Go into the blog folder and type :
```sh
jekyll new .
```

To check your blog on a local web server, use the command `jekyll serve` and go to http://127.0.0.1:4000.

![Jekyll is magic](https://blog.octoperf.com/img/blog/bootstrap4-blog-github-pages/jekyll-shia-labeouf-magic.gif "Jekyll is magic")

_Note:_
 
> You may copy the CNAME file from *blog-gh-pages* to *blog* (Jekyll will then include it in the generated content), and clean the *blog-gh-pages* folder content.

**To build the website run `jekyll build`.** A *\_site* folder is created. You just have to copy its content to *blog-gh-pages* to update your blog :
```sh
cp -R _site/* ../blog-gh-pages
```

A quicker way to do so is to build directly into *blog-gh-pages*:
```sh
jekyll build --destination ../blog-gh-pages/
```

_Notes:_
 
> Of course you have to [push your modifications](#local-to-remote) to the remote repo (on GitHub) to update your WebSite/Blog.
>
> There is no need to commit the generated code on the *master* branch. So you may also add a line `_site` in the *.gitignore* file of the *blog* folder.
 
We already have a working blog now. To write more posts, you may read [the manual](https://jekyllrb.com/docs/posts/) or just try to edit files and see what's happening.
When you run `jekyll serve`, changes are automatically visible on localhost (for all files except the *\_config.yml* one). 
By the way the *\_config.yml* holds all the [configuration of your Jekyll project](https://jekyllrb.com/docs/configuration/) (If you may read only one page of Jekyll's documentation, this is the one).

**This default theme is fine, but I want to add to it Bootstrap4, jQuery and Font-Awesome libraries**. I could download them by hand and include then in my project.
But I hate to do the same thing twice, so we are going to make this fully automatic using Bower and Grunt.
And even more, we are going to use Grunt to automatically create a 'dist' version of the *\_site* folder with code minimization and obfuscation.

Then I will customize the CSS theme to look like the [Bootstrap4 blog](https://getbootstrap.com/docs/4.0/examples/blog/) one. In [this section](#bootstrap-4) you can find more information about Jekyll syntax (loops, variables, etc.).

## Bower dependencies management
 
To install bower globally :
```sh
npm install -g bower
```

**Then you just need to create a *bower.json* file at the root of the blog using the `bower init` command.** 
The most important part is the *dependencies* section. It must list all external libraries used by your project:
  
```json
{
    "dependencies": {
     "bootstrap": "~4.0.0-alpha",
     "font-awesome": "~4.4.0"
    }
}
```

You can add an entry to this list using the command `bower install <dependency-name> --save`.

And the command `bower install` downloads all your libs in the *bower_components* folder. **No need to download them by hand.** 
You also just have to edit your Bower configuration file to update the versions of you dependencies.
I previously compared Grunt/Bower to Maven, but be aware that updating your libraries versions is not as smooth as in Java.
JavaScript is a dynamic / script language, and there won't be any compiler to warn you if something is broken.
You can rely on a [heavy usage of unit testing to avoid being stuck with old libraries, like we did for ou load testing tool](https://blog.octoperf.com/building-better-software/).

_Notes:_
 
> The bower.json file is only used for configuration purposes. So there is no need to include it in the generated site by Jekyll.
> To exclude it add the line `exclude: [bower.json]` to Jekyll configuration *\_config.yml*.
>
> There is also no need to commit the external libs, so you may also add the line `bower_components` to the *.gitignore* file.

Each package dependency should have its own *bower.json*. I.e. for Font Awesome check the '/bower_components/font-awesome/bower.json' file and its *main* section:
```json
{
    "main": [
        "less/font-awesome.less",
        "scss/font-awesome.scss"
      ]
}
```

Here are listed the entry-point files necessary to use the package. **Grunt will use it to inject the references toward these files into your HTML.**

In some cases you may need to override them. To continue with the Font Awesome example, our bower.json contains the following overrides section:
```json
{
    "overrides": {
        "font-awesome": {
          "main": [
            "css/font-awesome.css"
          ]
        }
      }
}
```

Doing so we tell Grunt to point to the CSS file instead of using the LESS and SCSS. 
 
_Note:_
 
> We could also keep the SCSS file and import it in our 'css/main.scss'. And that's probably a cleaner way to do it.

## Grunt builds

Bower alone is fine but not sufficient. **I want to automatically inject the references to my libs into my HTML files. Grunt is the tool for this task.**

To use Grunt we first need a *package.json* file:
```json
{
  "name": "Blog",
  "version": "0.0.1-SNAPSHOT",
  "author": "Gérald Pereira",
  "dependencies": {},
  "devDependencies": {
    "grunt-contrib-clean": "~0.6.0",
    "grunt-contrib-copy": "~0.8.1",
    "grunt-contrib-concat": "~0.5.1",
    "grunt-usemin": "~3.1.1",
    "grunt-include-source": "~0.6.1",
    "grunt": "~0.4.5",
    "grunt-contrib-htmlmin": "~0.4.0",
    "grunt-wiredep": "~2.0.0",
    "grunt-contrib-uglify": "~0.9.2",
    "grunt-contrib-cssmin": "~0.13.0",
    "grunt-http-server": "~1.10.0",
    "grunt-bower-install-simple": "~1.1.4",
    "grunt-shell": "~1.1.2"
  }
}
```

The *devDependencies* section references all dependencies that will be used by the build process. You can add some using the command `npm install dependency-name --save-dev`.

Then when we start `npm install`, a *node_modules* folder is created (it contains all our build libs) and we can start using Grunt.

_Note:_
 
> We need to exclude the *node_modules* folder and other configuration files from Jekyll generated site. To do so just add the line `exclude: [bower.json, GruntFile.js, package.json, node_modules]` to the *\_config.yml* file.

### Configuration in GruntFile.js

The next step is to create a *GruntFile.js*, that defines the process of your build:

```javascript
module.exports = function (grunt) {

   // Project configuration.
   grunt.initConfig({
       pkg: grunt.file.readJSON('package.json'),

       // To download bower dependencies in bower_components folder
       'bower-install-simple': {
           options: {
               color: false,
               production: false,
               directory: 'bower_components'
           }
       },
       // To include js/*.js in _includes/foot.html file
      includeSource: {
          options: {               
          },
          dev: {
              files: {
                  '_includes/head.html': '_includes/head.html'
              }
          },
       },
       // To includes bower dependencies in _includes/head.html file
       wiredep:{
           dev : {
               src : [ '_includes/head.html', '_includes/foot.html'],
               ignorePath : /\.\./,
               // ignorePath : /\.\.\//, would write 
               // dependencies without the leading /
               exclude: []
           }
       }
   });

   grunt.loadNpmTasks('grunt-bower-install-simple');
   grunt.loadNpmTasks('grunt-include-source');
   grunt.loadNpmTasks('grunt-wiredep');

   // Default task(s).
   grunt.registerTask('build:dev', [
        'bower-install-simple', 
        'includeSource:dev', 
        'wiredep:dev'
   ]);
};
```

* You may add the configuration for each grunt module you use in the `grunt.initConfig` part (here *bower-install-simple*, *wiredep* and *includeSource* modules),
* You must load each module using the `grunt.loadNpmTasks('grunt-module-name');` syntax.
* Finally you can declare tasks using the line `grunt.registerTask('taskName', ['module1', 'module2:profileA', 'module2:profileB']);`.

When launching the command `grunt taskName`, Grunt executes *module1*, then *module2* with the *profileA* profile, and ends with *module2*, with the *profileB* profile.

### Referencing dependencies in HTML

To inject references to external CSS and JS libraries, we created the `build:dev` task.

1. This task starts *bower-install-simple* which does simply the equivalent for the `bower install` command.
2. It then run a *includeSource* to include JavaScript files present in the *js* folder into the '\_includes/foot.html' file.
3. It ends with *wiredep* to include Bower dependencies (JS and CSS) into '\_includes/head.html' and '\_includes/foot.html' files.

#### IncludeSource
**IncludeSource is used to inject the references to our JavaScript files** into '\_includes/foot.html'. 
It's better to inject all JS into the footer to decrease pages loading times. 
And we don’t need to inject references to our CSS files as Jekyll comes with a *.scss* file compiler. It generates a single *main.css* file.

To include source files into the HTML, we must write special comments to tell this Grunt module where and what references to inject.
For example the comment:
```html
<!-- include: "type": "js", "files": "js/*.js", "baseUrl": "/" -->
<!-- /include -->
```

Is transformed to:
```html
<!-- include: "type": "js", "files": "js/*.js", "baseUrl": "/" -->
<script src="https://blog.octoperf.com/js/app.js"></script>
<script src="https://blog.octoperf.com/js/toc.js"></script>
<!-- /include -->
```

This way if you create a new JS file for your blog or website, you just need to run a `grunt build:dev` command.

#### Wiredep
**Wiredep is used to inject the bower dependencies** into our HTML files.
Here we inject them into '_includes/head.html' for the CSS ones and '_includes/foot.html' for the JS.

The `ignorePath : /\.\./` configuration line is used to format the injected references. You may use `ignorePath : /\.\.\//`  to write dependencies without the leading `/`.

Just like for InclureSource you need to write special comments into your HTML files to do the reference injection.

In the *head.html*:
```html
<!-- bower:css -->
<!-- endbower -->
```

Becomes:
```html
<!-- bower:css -->
<link rel="stylesheet" href="https://blog.octoperf.com/bower_components/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="https://blog.octoperf.com/bower_components/font-awesome/css/font-awesome.css" />
<!-- endbower -->
```

And in the *foot.html*:
```html
<!-- bower:js -->
<!-- endbower -->
```

Becomes:
```html
<!-- bower:js -->
<script src="https://blog.octoperf.com/bower_components/jquery/dist/jquery.js"></script>
<script src="https://blog.octoperf.com/bower_components/bootstrap/dist/js/bootstrap.js"></script>
<!-- endbower -->
```

This first step with Grunt is already a sweet productivity gain for my Jekyll usage. But it can do a lot more ... 

### Packaging distribution blog

For our [load testing website](https://octoperf.com/) we use a Jekyll plugin to minimize and obfuscate our code: [jekyll-minifier](https://github.com/digitalsparky/jekyll-minifier).

It is really easy to put it in place, just install the gem `gem install jekyll-minifier` and add the following lines to your *\_config.yml* Jekyll configuration file:

```yaml
gems:
  - jekyll-sitemap
  - jekyll-minifier

jekyll-minifier:
  exclude:
      - 'app.js'
      - 'blog/feed.xml'
```

But this plugin is pretty long to execute (about 16 seconds to minify OctoPerf.com). And when you run `jekyll serve`, it is processed every time you save a file.
So you always have to wait that long to **check your modifications in a browser**. We ended commenting the plugin in the configuration, but we often forget to uncomment it before deploying ...

Here comes the *build:dist* task (the complete configuration is available on my *blog-template* GitHub repository):

```javascript
grunt.registerTask('build:dist', [
        'build:dev',
        'shell:jekyll',
        'clean:dist',
        'copy:dist',
        'useminPrepare',
        'concat:generated',
        'cssmin:generated',
        'uglify:generated',
        'usemin',
        'htmlmin:dist',
        'clean:tmp',
        'http-server:dist'
    ]);
```

1. Running the *build:dev* task ensures that all dependencies are downloaded and wired.
2. The *shell:jekyll* simply runs the `jekyll build` command to make sure the *\_site* folder is present.
3. *clean:dist* removes the content of our *dist* folder. The *dist* folder is '../blog-gh-pages'.
4. *copy:dist* copies the relevant files from *\_site* to '../blog-gh-pages' (HTML, CNAME, feed.xml, CSS and fonts). So every time we run this task the blog *gh-pages* branch is replaced with the new content (No more need to manually copy the files from *\_site* to '../blog-gh-pages'). 
5. From *useminPrepare* to *usemin*, it's the same module. Guess what? It's called **Usemin**. It does the minimization and obfuscation of the CSS and JS files. Se bellow for more information about it.
6. *htmlmin:dist* compresses the HTML files.
7. *clean:tmp* removes the folder *.tmp* generated by Usemin.
8. And finally *http-server:dist* runs a local HTTP server http://127.0.0.1:8282/ so that **we can check that the *dist* version of our blog is flawless**.

_Note:_

> You ust run **`grunt build:dist` with the `--force` option** because we write files outside of the current repository.

#### Usemin

The Usemin Grunt plugin aggregates multiple JavaScript file into compressed, uglyfied, and fewer ones. **It let's you save a lot of bandwidth and get better response times for your web pages.**

To use it we must put special comments around JS references in our '\_includes/foot.html' file:

```html
<!-- build:js /js/vendor.js -->
<!-- bower:js -->
<script src="https://blog.octoperf.com/bower_components/jquery/dist/jquery.js"></script>
<script src="https://blog.octoperf.com/bower_components/bootstrap/dist/js/bootstrap.js"></script>
<!-- endbower -->
<!-- endbuild -->

<!-- build:js /js/app.js -->
<!-- include: "type": "js", "files": "js/*.js", "baseUrl": "/" -->
<script src="https://blog.octoperf.com/js/app.js"></script>
<script src="https://blog.octoperf.com/js/toc.js"></script>
<!-- /include -->
<!-- endbuild -->
```

Becomes :
```html
<!-- build:js /js/vendor.js -->
<!-- bower:js -->
<script src="https://blog.octoperf.com/js/vendor.js"></script>
<!-- endbower -->
<!-- endbuild -->

<!-- build:js /js/app.js -->
<!-- include: "type": "js", "files": "js/*.js", "baseUrl": "/" -->
<script src="https://blog.octoperf.com/js/app.js"></script>
<!-- /include -->
<!-- endbuild -->
```


It can also do the same for CSS files :
```html
<!-- build:css /css/vendor.css -->
<!-- bower:css -->
<link rel="stylesheet" href="https://blog.octoperf.com/bower_components/font-awesome/css/font-awesome.css" />
<!-- endbower -->
<!-- endbuild -->
```


_Notes:_

> The *concat:generated*, *cssmin:generated*, *uglify:generated* are generated by *useminPrepare*. We don't need to configure them.
>
> In my opinion, this *dist* task still lacks one important thing: a cache buster. These modules change the name of the *vendor.js* or *app.js* files each time you build.
> So when you update your blog you are sure the browser of your visitors will re-download them. It prevents your users from JS errors or CSS glitches. 
> Usemin comes with [filerev](https://github.com/yeoman/grunt-filerev) and there's also [cacheBust](https://github.com/hollandben/grunt-cache-bust). I may find the time to add them soon.

**Whoa! That was intense!** That's a lot a configuration to do. I'm not a bower/grunt master so it's a bit tedious to put all of this in place. 

![Grunt Mind Blown](https://blog.octoperf.com/img/blog/bootstrap4-blog-github-pages/grunt-mind-blown.gif "Grunt Mind Blown")

But now comes the funny part: switching from the base Jekyll theme to the Bootstrap4 blog one.

## Bootstrap 4

**I really appreciate Bootstrap and I wanted to try the last version as soon as it was released.**
We already have Bootstrap, jQuery and FontAwesome available thanks to Bower and Grunt. 
And there is a [clean blog theme](https://getbootstrap.com/docs/4.0/examples/blog/) on the getbootstrap site. 

So I downloaded it, copied the CSS file as '\_scss/\_blog.scss' (you need to include it in the 'css/main.scss' file) and used the provided *index.html* as my default template.

This chapter takes a *quick* look at the migration from the base Jekyll theme to the Bootstrap 4 one. 
Topics covered here should be sufficient to understand the whole refactor. So there is no need to detail every modification.   

### Header.html for the navigation
**The bootstrap sample *index.html* file must be split to fit into Jekyll. The most interesting part is the header menu** (the '\_includes/header.html' not to be confused with '\_includes/head.html' that contains the page title, metas, and the css references).

The bootstrap theme comes with a static header:
```html
<div class="blog-masthead">
 <div class="container">
   <nav class="nav blog-nav">
     <a class="nav-link active" href="#">Home</a>
     <a class="nav-link" href="#">New features</a>
     <a class="nav-link" href="#">Press</a>
     <a class="nav-link" href="#">New hires</a>
     <a class="nav-link" href="#">About</a>
   </nav>
 </div>
</div>
```

That we transform to this:


```html
{% assign pageURL = page.url | downcase | split: '/' %}
<div class="blog-masthead">
  <div class="container">
    <nav class="nav blog-nav">
      <a class="nav-link {% if pageURL contains 'index.html' %}active{% endif %}" href="{{ site.baseurl }}/">{{ site.title }}</a>
      {% for page in site.pages %}
      {% if page.title %}
      {% assign menuURL = page.url | downcase | split: '/' %}
      <a class="nav-link {% if pageURL == menuURL %}active{% endif %}" href="{{ page.url | prepend: site.baseurl }}">{{ page.title }}</a>
      {% endif %}
      {% endfor %}
    </nav>
  </div>
</div>
```


We add the homepage link and them loop over all the available pages to add a new link into the header menu: `{% for page in site.pages %} ... {% endfor %}`.

We also have to check if the current link points to the current page to add the *active* class (It displays a tiny white triangle under the current menu): `{% if currentURL == pageURL %}active{% endif %}`.
The `{% assign currentURL = page.url | downcase | split: '/' %}` syntax is used to declare variables. Here *pageURL* is the current page URL and and *menuURL* is the current menu URL.

### Blog Post layout
The next step is to create the blog post layout '\_layouts/post.html'.


```html
<div class="col-sm-4 col-sm-push-8 blog-sidebar">
   <div class="sidebar-module sidebar-module-inset">
       <h4>About</h4>
       {{ page.description }}
   </div>
   <div class="sidebar-module">
       <h4>TOC</h4>
       <ul id="toc"></ul>
   </div>
</div>

<div class="col-sm-8 col-sm-pull-4 blog-main">
   <div class="blog-post">
       <h1 class="blog-post-title">{{ page.title }}</h1>

       <p class="blog-post-meta">{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}</p>

       {{ content }}
   </div>
</div>
```


We have a right sidebar that contains the post description (the About part) followed by a table of contents (TOC). So this sidebar must come on top of the post content when the frame size is reduced. 

Bootstrap is very convenient for this. We place the HTML of the sidebar before the one of the blog post content, and push it on the right for screen sizes SM and above (**using *col-sm-push-8* on the sidebar and *col-sm-pull-4* on the content**).

_Note:_

> It could be nice to have the TOC with a fixed top position. It would then be always visible, even when the visitor scrolls down on a long article such as this one!

Every post markdown file processed by Jekyll must start with `---`. For example, this post *.markdown* file starts with:
```yaml
---
layout: post
title:  "How can you tell that a web developer is working?"
date:   2015-08-26 11:24:03
description: This blog's creation guide, from <strong>Jekyll</strong> base theme to <strong>Boostrap4</strong>. Using <strong>Grunt/Bower</strong> and hosted on <strong>GitHub pages</strong>. 
categories: jekyll bower grunt 
tags: bootstrap4 github
image: bootstrap4-blog-github-pages/grunt.jpg
---

# You can hear him **Grunt**ing!
...

```
**Notice that the HTML code above references the post data using the syntax `{{ page.data }}`.** It lets us inject the title, name and description.

Also, everything that comes after the second `---` is the *content* of the post. It is referenced in the HTML using the `{{ content }}` syntax.

_Note:_

> Post markdown files are located in the '\_posts' folder.

### Table Of Contents using jQuery
The table of contents is generated using jQuery. **A simpler way to generate a TOC is to use 
the [kramdown {:toc} marker](https://kramdown.gettalong.org/converter/html.html#toc).** 
But this solution works only with a TOC placed on top of the article content. 

I started from a [tutorial to display anchors on headers](https://ben.balter.com/2014/03/13/pages-anchor-links/) and adapted it to also create my TOC.
 
It is all in the 'js/toc.js' file:

```javascript
$(function () {
   var toc = $("#toc");

   return $("h2, h3").each(function (i, el) {
       var $el, icon, id;
       $el = $(el);
       id = $el.attr('id');
       icon = '<i class="fa fa-link"></i>';
       if (id) {
           var isH2 = $el.prop("tagName") === 'H2';
           var tocLink = isH2 ? "toc-link-h2" : "toc-link-h3";
           var li = $("<li />").html($("<a />").addClass(tocLink).attr("href", "#" + id).html($el.text()));
           var html = isH2 ? li : $("<ul />").html(li);
           toc.append(html);
           return $el.prepend($("<a />").addClass("header-link").attr("href", "#" + id).html(icon));
       }
   });
});
```

This code snippet parses all the H2 and H3 headers of the page (H1 is the page title) and adds a link to the table of content for each one. 
This is basic [jQuery](https://jquery.com/) code. The only difficulty is to check for the header type (H2 or H3) to display them hierarchically.

### SCSS Integration

You may have noticed that this blog doesn't exactly look like the Bootstrap theme. No?

In fact I wanted something more personal, so I made slight modifications (on fonts mostly).

#### SCSS

SCSS lets us use variables, import external files, extend CSS classes, and much more. It's really a must have. **I switched from LESS With bootstrap 3 to SASS with bootstrap 4**, and the syntaxical differences are so minor that it does not even feel like a change.

First to include the Bootstrap SCSS in our own we must add the line `@import "../bower_components/bootstrap/scss/bootstrap";` to our 'css/\_main.scss'.
The generated *main.css* file then includes both ours and bootstraps css classes.

_Note:_

> The manual addition of the reference to Bootstrap 4 SCSS can probably be automated using the [proper Grunt module](https://github.com/eduardoboucas/grunt-sass-import).
> I'll give it a try soon.

Once Bootstrap is included we just have to import our own '\_scss/\_variables.scss' before it to override any variable:

```scss
@import "variables";
@import "../bower_components/bootstrap/scss/bootstrap";
@import "common";
...
```

For example, to use a black background and a blue font color, we override the corresponding variables in *\_variables.scss*:
```scss
$body-bg: black;
$body-color: blue;
```

We can use an other font the same way: 
```scss
@import url(https://fonts.googleapis.com/css?family=Share+Tech+Mono);
$font-family-sans-serif: 'Share Tech Mono', monospace !default;
```

_Note:_

> To know all available Bootstrap variables, take a look at the '/bower_components/bootstrap/scss/\_variables.scss' file.

## Conclusion

Well, I'm rather satisfied with the Jekyll/Grunt/Bower mix. It's not that simple to put in place. 
Especially because the grunt ecosystem lacks some consistency. But **once done it's really a big improvement for my productivity**.
 
There should be more posts about it coming soon:

* A more in-depth usage of Boostrap 4,
* SCSS imports using grunt,
* Sitemap file generation,
* Disqus integration,
* Google analytics integration,
* etc.

So stay tuned!
