Blogging on GitHub29 Jun 2014.
The way GitHub decided to organize its web space is unusual; it’s based on an “orphan branch” within the same repository as your project, which is basically a “parallel universe” within the same, well, universe as the repo you already have. This means that you typically have to clone your repository twice on the same PC, once for your code and again for your web site, but you are storing two copies of the complete history of both “halves” of your repo. That is weird to me. (In theory you only need one clone of your repo, but then git would have to delete your entire source tree whenever you want to edit your web site. Unsettling, no? Why can’t I just have the web site in a subfolder, let’s say,
/www in
master?)
They recommend installing Jekyll on your local computer to be able to preview your web site, but installing Jekyll on Windows this page underneath the download links for the Ruby Installer for Windows, under the heading “Development Kit”. Later I found instructions for installing Jekyll on Windows. But then I had problems with “yajl” and “wdm” on two different PCs (see sidebar) and one of the PCs still can’t run github’s (old) version of Jekyll.
rem If github still uses jekyll 1.5.1, the yajl version is v1.1.0 gem uninstall yajl-ruby gem install yajl-ruby -v 1.1.0 --platform=rubyAnd in this situation it seems like bundler might cause a problem with Ruby-2.0.0 so if in doubt, don't use bundler to start jekyll. Finally, if jekyll won't start and says "cannot load such file -- wdm", run gem install wdm. github-pages gem instead of the normal jekyll gem.: terrible error handling. you.
So in desperation I installed github-pages on another machine and, when invoking jekyll, forced version 1.5.1 by running jekyll _1.5.1_ on the command line instead of jekyll. After getting errors about `yajl` and `wdm` yet again, and fixing those, the error stack trace changed to involve pygments/mentos.rb, line 303, in start.
By deleting text from my post, section-by-section, I finally found the problem: I had written ~~~none to disable syntax highlighting, when I should have used ~~~text.
Once everything starts working, Jekyll still proves to be flaky. Sometimes it spews several errors on startup, the last one being jekyll 2.1.0 | Error: no implicit conversion of true into String.
Solution: just run Jekyll again and Hope It Works This Time.
Anyway, see GitHub’s official install instructions.
The documentation of Jekyll is backwards. The introductory pages give you all the minor details first; the key information comes later. For example, it isn’t until the eighth section.
Instead, it appears that a much better way to get started with Jekyll is to use Jekyll Bootstrap. The Quick Start page tells you exactly how to set it up, and it does a better job explaining what Jekyll is and how it works than the Jekyll documentation. Then ‘Using Themes’ tells you how to set a theme (because the default theme is … well, it has big red headings! Not my cup of tea), and once you’ve worked out those two things you can pretty much start blogging in your site’s
_posts folder.
Another source of themes is this web site..
But there’s another framework for theming Jekyll called Poole and the two available themes not only look good, they scale very well to various browser sizes. Mobile? Check. I found out about Poole through a helpful blog post by Joshua Lande.:
jekyll serve --drafts --watch --source Loyc-web --destination Loyc-preview
serve (rather than
build) causes it to serve the preview at,
--drafts asks Jekyll to render unpublished drafts as if they were published, and
--watch causes the preview to be automatically updated in response to changes (this works on one of my PCs but not another, I don’t know why). The default
--destination is
_site within the source folder. And don’t name your batch file
jekyll.bat like I did. You won’t like the result…
Once I’m happy with my web site, I push to GitHub and I’m live!
Syntax highlighting fails
On Windows you need a bunch of extra install steps to install “pygments” in order to support syntax highlighting with the widest number of languages, but it’s easier to use “Rouge” instead. I have found that if you have neither Pygments nor Rouge installed, Jekyll will crash with a strange message when it encounters a page that requests syntax highlighting:)
No, Jekyll isn’t good at error messages. To solve this quickly,
- Install Rouge:
gem install rouge
- Remove the
pigmentsoption from your
_config.yml, if there is one
- Add this option:
highlighter: rouge
- If applicable, restart
jekyll servewhenever you change
_config.yml
To create code blocks, Jekyll recommends the use of
{%...%} blocks like this:
{% highlight cpp %} // Code {% endhighlight %}
But this, of course, is not standard Markdown code. I want to write my posts in the language of Markdown, not Jekyll; I should be able to use a standard “fence” instead:
~~~cpp // Code ~~~
But by default, syntax highlighting is not supported in fences. To enable this feature, change your Markdown parser from “kramdown” to “redcarpet” by setting the
markdown option in _config.yml:
markdown: redcarpet
The names you have to use on the fence tend to be longer than just file extensions. For example,
~~~cs is not recognized as C#, you have to use
~~~csharp, and
~~~yml isn’t recognized as
.yml, you have to use
~~~yaml. Use
~~~text to ensure that no language auto-detection occurs (GitHub’s old version of Jekyll doesn’t do that, though.)
Unfortunately, redcarpet and kramdown have different sets of advanced features. Kramdown seems more flexible to me, but redcarpet appears to be The Standard GitHub Flavored Markdown.
Update: As of 2016,.”
Update: As of August 1, 2014, commiting a
_config.yml that uses
rouge now causes “Page build failure” on GitHub with a misleading error message like “The file
_posts/2014-08-01-blah.md contains syntax errors.” Before you commit & push, you must set
highlighter: pygments in
_config.yml, even if you don’t care to install pygments locally.
ProTip: Jekyll won’t easily let you write the literal character combination
{% or
{{, not even inside code blocks. You could write
{{ "{%" }} or
{{ "{{" }} instead, but if you are not intending to use Liquid (Jekyll’s templating engine), a better option is to wrap the entire page in
{% raw %} ... {% endraw %}, after the front-matter.
2017 Update: raw/endraw are now somehow buggy; Jekyll 3.5.2 can no longer handle a paragraph like this one, and GitHub’s internal copy of Jekyll tends to email you an error if you wrap an entire document in
{% raw %}. As a last resort I’ve used the zero-width space U+200B in
{% and
{{ to make Jekyll ignore it. This is bad, of course, as a user that copies the text will unknowingly pick up a zero-width space.
How I set up this site
I decided to use the Poole-based “Hyde” theme, so I downloaded
hyde-master.zip from GitHub. The provided Usage instructions are a bit confusing and incomplete so I wrote this in case it helps. I added the prefix “(Hyde)” on issues that only apply to Poole or Hyde.
Earlier I generated my index.html using GitHub’s “Automatic Page Generator”. This thing generates not just
index.htmlbut a bunch of css files and images and javascript files, plus a file in the root directory called
params.jsonwhich contains a description of the content you provided for
index.html, including the page text. Since “Hyde” uses a different folder structure than the “Automatic Page Generator” and doesn’t share any of the same css, I thought I would confuse myself if I kept both… so I deleted all of GitHub’s generated stuff, such as the
cssfolder, the
imagesfolder,
params.json(which had the same content as my README anyhow),
_includes/header.html,
_includes/footer.html, and
index.htmlitself.
(Hyde) After unzipping
hyde-master.zipI copied most of its contents over my
gh-pagesworking copy, excluding .gitignore, _config.yml, README.md, CNAME, README.md, LICENSE.md (lest it be confused with the license of my site content), and the _posts folder.
(Hyde) Hyde expects the following options to exist in
_config.yml:
Other themes also use
title,
description,
baseurland possibly other options. Here are some settings recognized by Jekyll itself that you should probably also have:
# Jekyll settings: markdown: redcarpet encoding: UTF-8 highlighter: pygments # rouge now causes Page Build Failure on GitHub paginate: 10 # blog posts per page paginate_path: "blog/page:num" # optional, see below safe: true lsi: false
(Hyde) The
baseurloption is a problem if you want it to refer to the root of the domain. The
Jekylldocumentation implies you should use “” in this case, and offers sample code that doesn’t work correctly with “/”; for example, this code is in the Jekyll site template (
Ruby200\lib\ruby\gems\2.0.0\gems\jekyll-2.1.0\lib\site_template):
<ul class="posts"> {% for post in site.posts %} <li> <span class="post-date">{{ post.date | date: "%b %-d, %Y" }}</span> <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a> </li> {% endfor %} </ul>
For some reason this produces bogus links if baseurl is “/”. But Hyde will break if you use “/” because it uses raw concatenation in
/_includes/head.html:
<link rel="stylesheet" href="{{ site.baseurl }}public/css/poole.css"> <link rel="stylesheet" href="{{ site.baseurl }}public/css/syntax.css"> <link rel="stylesheet" href="{{ site.baseurl }}public/css/hyde.css">
Note that a slash is needed before ‘
public’. To fix this I changed all instances of
{{ site.baseurl }}to
{{ site.baseurl }}/in all of Hyde’s HTML files (be sure to replace all of them!)
Perhaps the same problem would arise if you are not at the root of the domain; i.e. if your web space is at
username.github.io/projectname. In that case Jekyll wants
baseurlto be projectname while Hyde expects projectname/.
By default, the home page (
index.html) just shows your blog posts, in full, in chronological order. I didn’t want the blog to be the home page so I renamed
index.htmlto
blog.html(giving it the front-matter
layout: pageand
title: Blog), but this caused all the blog posts to disappear! Jekyll has a bizarre limitation here: you are only allowed to show your blog on a single page, and that page must be named
index.html. However, you can use
/blog/index.htmlinstead if you add the line
paginate_path: "blog/page:num"to
_config.yml.
(Hyde) By default, the sidebar lists all pages that use the
layout: pageoption in their front-matter (in
_includes/sidebar.htmlyou can see the for-loop that does this), and the
title: ...option is used as the link text. After that there is a “Download” link, a “GitHub project” link, and a version number. I did not want the “Download” link, or the version number, or “All Rights Reserved”, so I removed those parts from
_includes/sidebar.html. Of course, if you don’t like how the links are auto-generated, you can change the code to suit you.
I created
index.mdand put some content in it:
--- layout: default title: Home --- # Welcome! This is the home page!
I added my
.sideboxclass to
sidebar.html(see Using custom CSS below)
(Hyde) I switched to a different preset color scheme by changing
<body>to
<body class="theme-base-0d">in
/_layouts/default.html.
(Hyde) I edited
/public/css/hyde.cssto adjust the theme to my liking. In particular I reduced the various
margin-leftand
margin-rightvalues, shrunk the font a little, and tweaked the sidebar color. Since this blog contains code (and what GitHub blog doesn’t?) wide margins would cause scroll bars or wrapping on every code snippet, and we don’t want that.
At the top of the blog I made a “List of all posts” link, which goes to
blog-list.html:
--- layout: default title: Blog index --- <h1>Blog index</h1> <div class="home"> <ul class="posts"> {% for post in site.posts %} <li> <span>{{ post.date | date: "%b %-d, %Y" }}</span>: <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a> </li> {% endfor %} </ul> <p class="rss-subscribe">subscribe <a href="{{ "atom.xml" | prepend: site.baseurl }}">via Atom</a></p> </div>
(Hyde) if you have enough blog posts to cause pagination, Hyde shows the “Blog” link multiple times, one for each page! This turned out to be slightly difficult to fix; after a little while I realized that Jekyll, or more specifically Liquid, does not support Ruby expressions. In fact, it hardly supports any expressions at all! Check it out: Liquid contains only 9 operators in total. There is not even a
notoperator, and there is no regex matching (or if there is, it is implemented as something other than an operator.) Anyway, the unwanted pages can still be filtered out, although not with the precision I am used to. In
_includes/sidebar.html, I changed this code:
{% assign pages_list = site.pages %} {% for node in pages_list %} {% if node.title != null %} {% if node.layout == "page" %} <a class="sidebar-nav-item{% if page.url == node.url %} active {% endif %}" href="{{ node.url }}">{{ node.title }}</a> {% endif %} {% endif %} {% endfor %}
by adding an
unlessstatement:
{% assign pages_list = site.pages %} {% for node in pages_list %} {% if node.title != null and node.layout == "page" %} {% unless node.url contains '/page' %} <a class="sidebar-nav-item{% if page.url == node.url %} active {% endif %}" href="{{ node.url }}">{{ node.title }}</a> {% endunless %} {% endif %} {% endfor %}
.
Writing blog posts
Writing a blog post in Jekyll is easy. Just create a text file in the
/_posts folder (that’s in your
gh-pages branch if you are using GitHub) and give it a name like
2014-12-31-file-name.md, but with the correct date, of course. Inside the file, add front matter:
--- title: Title of your blog post layout: post --- The content of the post goes here.
Jekyll puts the output HTML in
2014/12/31/file-name.html, or
2014/12/31/file-name/index.html instead if your site is using the
permalink: pretty option in
_config.yml.
If you’d like to get shorter URLs by eliminating part of the date, change the
permalink option in
_config.yml. For example I use this:
permalink: /:categories/:year/:title.html
Or see here (why call it “permalink” when you can change the URL schema at any time?)
Although Jekyll supports blogging, it is incomplete as a blogging engine since it is strictly designed to serve static.
Jekyll Bootstrap currently supports four comment engines including Disqus but I am not sure whether I trust these commercial entities with my comments… although, er, very few comments have ever been left on this blog.
Hopefully GitHub itself will provide a comment engine someday, since the GitHub site obviously supports comments very well. Good news though! It’s possible to co-opt the GitHub issue tracker to manage blog comments! The technique is described here and I decided to use it! There’s no need to limit comments to blog posts either, I can put them on any page I want, although it does take a little extra effort.
How to set up:
Add configuration to
/_config.yml:
github: user: qwertie project: loyc
In
_layouts/default.html, include
... {{ content }} {% include comments.html %} </div> </body>
Add my copy of
/_includesfolder, and add
/res/css(oh, you don’t have a
/res/cssfolder? Either create that folder, or put
To add comments to a page, create an issue in your github project (with whatever name and description you want) and then set
commentIssueIdline in the front matter of your blog post to match the issue:
--- layout: post title: "Blogging on GitHub" commentIssueId: 42 ---
Ivan says you also have to register an “OAuth application”, so I did that for
loyc.net, but comments are still working when I look at my Jekyll preview at
localhost:4000.
Using custom CSS in Markdown
Sometimes I like to place sidebars in my posts, which I call “.sidebox” in CSS because some websites already have a “.sidebar” class to describe the main sidebar:
>
Hey, look over here!This is a sidebar.
Markdown supports HTML, so you can add a style block like this one at the top of a
.md file or in your shared
_includes\head.html file, and then use it like this:
<div class="sidebox">This is a <i>sidebar</i>.</div>
Note that all the text inside the
<div> tag is treated as HTML, not Markdown. In kramdown you can also use the “block attribute”
{: .sidebox} after a paragraph to create a sidebar, or use a “refdef” which allows the sidebox to contain multiple paragraphs. Both of these approaches are better because you can use Markdown syntax inside the sidebar… but also worse because only kramdown will understand your code.
Visitor data
I am using Google Analytics to track page views. After you sign up you’ll be give a JavaScript snippet to put in your web pages; I inserted mine right before
</body> in
/layouts/default.html.
Table-of-contents generation
Kramdown has a feature for automatic generation of a table of contents JavaScript table-of-contents builder.
Markdown everywhere mdwiki.!
You can even use MDWiki without a web server, if you view
mdwiki.html in Firefox (it doesn’t work in Google Chrome), which means you can use it for offline Markdown previews (which is great because, at least on the Windows, tools for editing Markdown are generally quite limited.)
Published on CodeProject