The API Handyman blog now uses Jekyll and Github pages among other things.
This readme files explains a few things about how this website is coded, build and deploy.
To have a link appearing on H1, H2, H3 sections, you need to use Anchor JS.
- add
anchor.min.js
to pagehead
(see_includes/head.html
)
<head>
...
<script type="text/javascript" src="{{ "/js/anchor.min.js" | prepend: site.baseurl | prepend: site.github.url }}" ></script>
</head>
- define wich items should be handle by Anchor JS (see
_layouts/post.html
)
<script language="JavaScript">
anchors.options = {
placement: 'left'
};
anchors.add('.blog-container h1');
anchors.add('.blog-container h2');
anchors.add('.blog-container h3');
</script>
The dynamic TOC is handled by Bootstrap TOC
- add CSS and JS in head (see
_includes/head.html
)
<head>
...
<link rel="stylesheet" href="{{ "/css/bootstrap-toc.min.css" | prepend: site.baseurl | prepend: site.github.url }}">
...
<script type="text/javascript" src="{{ "/js/bootstrap-toc.min.js" | prepend: site.baseurl | prepend: site.github.url }}" ></script>
...
</head>
- define a nav item (see
_layouts/post.html
)
<nav id="toc" data-spy="affix" data-offset-top="400"></nav>
- add some javascript to populate the
#toc
, here we only retrieve the elements which are in the#posts
div
<script language="javascript">
$(function() {
var $myNav = $('#toc');
var $myScope = $('#post');
Toc.init({
$nav: $myNav,
$scope: $myScope
});
$('body').scrollspy({
target: '#toc'
});
});
</script>
- the TOC scrolling is handled thanks to the bootstrap affix class which is slighly customized to handle the 50px top nav bar
.affix {
top:50px;
position:fixed;
}
By default, jekyll return all posts on home page and categories pages. It's not really efficient. Fortunately it's possible to get an infinite pagination by using two jekyll plugins and a little bit of javascript.
Activating pagination on home page is fairly easy, you just have to follow the instructions provided in Jekyll Documentation.
- include jekyll-paginate plugin in Gemfile:
group :jekyll_plugins do
[... other gems ...]
gem "jekyll-paginate"
end
- download the added dependancy with this command line
bundle install
- include jekyll-paginate plugin _config.yml gems:
gems:
[... other gems ...]
- jekyll-paginate
- set pagination configuration in _config.yml
# number of items per page
paginate: 5
# page folder's name template
# you'll get something like http://myblog.com/page3/
paginate_path: "page:num"
- update your home template like this to loop on paginated posts (note that your home page MUST be an .html file and not a .md one):
{% for post in paginator.posts %}
{% include post-summary.html %}
{% endfor %}
But it works only for ALL posts, if you want to have pagination for categories page, you have to use another plugin Jekyll Paginate Category Plugin.
- include jekyll-paginate-category plugin in Gemfile:
group :jekyll_plugins do
[... other gems ...]
gem "jekyll-paginate-category"
end
- download the added dependancy with this command line
bundle install
- include jekyll-paginate-category plugin _config.yml gems:
gems:
[... other gems ...]
- jekyll-paginate-category
- set category pagination configuration in _config.yml
# folder where categories pages will be generated
# you'll get something like http://myblog.com/categories/mycategory/page3/
category_dir: "categories"
# layouts used for each page
category_layout: "_layouts/category.html"
- update your home template like this to loop on paginated posts:
{% if page.paginator %}
{% assign paginator = page.paginator %}
{% endif %}
{% for post in paginator.posts %}
{% include post-summary.html %}
{% endfor %}
Note that it's almost the same thing as seen with jekyll-paginate, you can use exactly the same layout for all posts and categories, take a look at _layout/paginated.html
which is used by index.html
(all posts) and _layouts/paginated.html
(categories) in this repository.
The bonus thing is that the plugin generates all categories pages without doing anything.
I needed to define some value specific to each category (a subtitle), I've define these value in the _data/categories.yml
file.
These values are used in _includes/header.html
:
{% if site.data.categories[page.title].subtitle %}
...
{% assign subtitle = site.data.categories[page.title].subtitle %}
{% endif %}
To add infinite scroll we need to have a next link in the page to load the next page.
Pagination controls are defined within _includes/pagination.html
which provides two types of controls:
- simple next/previous buttons
- next/previous and page numbers
Once infinite scroll is activated, these controls are hidden.
Infinite scrool is handled by some javascript in js/infinitescroll.js
, this script is adapted from this post.
The principle is:
- to trigger next page loading once the scroll is near the end of the current page
- the next page's URL is retrieved from the
#next
link - once the next page is load is
#main
div content is added to the current#main
div
- Code blocks are styled with Prism
- the top left and bottom toolbars are created using bootstrap
- the copy button use ClipboardJS
- and there's also some CSS/JavaScript/JQuery
The css/prism.css
and js/prism.js
are generated with the Prism download page which allow you to choose exactly what you need.
My configuration:
- Theme:
- Default
- Languages:
- Markup
- C-Like
- JavaScript
- Bash
- GraphQL
- JSON
- SQL
- YAML
- Plugins:
- Line highlight
- Line numbers
This website uses two custom jekyll plugins to highlight code:
- _plugins/codefilehighlight.rb (
codefile
) to highlight included code files - _plugins/codehighlight.rb (
code
) to highlight inline code
Both plugins use configuration in _config.yml
:
## code highlight
### disabling default code highlighter
highlighter: none
### codefilehighlight parameter: where code files are stored
coderoot: code
### codefilehighlight parameter: number of visible lines in code block
codeblocksize: 20
Highlight code between code
and endcode
.
{% code language:yaml numbers:false highlight:"1, 3-4" %}
some: inline
code: which
will: be
hightlighted: by prism
{% endcode %}
Parameters:
- language (mandatory): the language used
- numbers (optional): showing line numbers or not (true by default)
- highlight (optional): a set of line index (
1
) or range (3-4
) separated by,
to highlight
Include a code file and highlight it.
{% code file:somefile.yaml language:yaml numbers:false highlight:"1, 3-4" %}
Parameters:
- file (mandatory): the name of the file to include. The file is loaded from
coderoot
folder (defined in_config.yml
) if filename starts withcoderoot
valuecoderoot/page.codefiles
if value provided in document's yaml frontcoderoot/page.permalin
by default
- language (optional): the language used, if not provied the filename extension is used
- numbers (optional): showing line numbers or not (true by default)
- highlight (optional): a set of line index (
1
) or range (3-4
) separated by,
to highlight
As this website uses custom Jekyll plugins it can't be deployed automatically by Github Page, I'm using Travis CI to handle that.
On each commit on the master branch, travis builds the website using scripts/build.sh
as defined in .travis.yml
configuration.
env:
global:
- JEKYLL_ENV=production
script: scripts/build.sh
This script is mainly used to be sure I do not forget to define JEKYLL_ENV variable which is used to include or not Google Analytics and Disqus scripts in the pages.
If the build is OK, Travis deploys it the gh-pages
branche of with scripts/deploy.sh
.
This scripts uses some environment variables:
- DEPLOY_REPO (optional, default: current repo): The repos where the build should be deployed
- DEPLOY_BRANCH (required): The branch where the build should be deployed
- BUILD_TARGET (optional, default: _site): Where is the result of the build
- DEPLOY_TARGET (optional, default: .deploy): Where should be cloned the targeted branch
- GH_TOKEN (optional but needed when running on travis ci): A github access token
- GH_USER (optional but needed when running on travis ci): A github username needed to do the git push
- GH_EMAIL (optional but needed when running on travis ci): A github email needed to do the git push
- PUBLISH_MESSAGE (optional, default: publish): The commit message
Travis configuration in .travis.yml:
env:
global:
- DEPLOY_BRANCH=gh-pages
deploy:
provider: script
skip_cleanup: true
script: scripts/deploy.sh
on:
branch: master
Note the skip_cleanup: true
which allow to keep the site generated in _site
by the build.
The GH_TOKEN, GH_USER and GH_EMAIL used by Travis are defined on the Travis website with hidden env variables for the repo.
Here's the list of tools used:
- Bootstrap to build easily a responsive website
- Bootstrap TOC for dynamic TOC sidebar on posts
- Anchor JS to add links on H1, H2, H3 sections titles on posts
- Gist embed to embed gists easily
- Font Awesome for social links in footer
- Jekyll to build the static website
- Jekyll Paginate Category Plugin for pagination on categories pages
- Jekyll Twitter Plugin to embed tweets easily
- Github Page to host the generate static website
- Travis CI to build and deploy the static website
- Prism to highlight code blocks
- ClipboardJS for copy button on code blocks