In this multi part tutorial, you will learn how to create an Hexo Theme from scratch. I really love Hexo and use it everyday; unfortunately, as of today, the documentation for theme creation is pretty slim. Here is my attempt at fixing that.
Links to other sections
- Part 1: Setting up the project and creating the blog’s main index page
- Part 2: Finishing up the remaining pages
- Part 3: Wrapping up with Disqus comments, Google Analytics and the widgets
Prerequisites
- The very basics of blogging with hexo. If you are completely new, check the getting started guide on hexo.io.
- Some knowledge of Bootstrap
- Some knowledge of a JavaScript templating engine (we will use EJS)
Project Description
This project is all about creating a theme for Hexo and understanding in details how the Hexo engine
Since I don’t want to lose too much time on the HTML and CSS parts we are going to recreate the following theme in Hexo: http://getbootstrap.com/examples/blog/. It is one of the standard Getting Started Template Examples in the Boostrap documentation.
We are going to reuse the CSS and copy paste the HTML, piece by piece, until we have achieved what we want.
If you get lost or are only interested in the code, it is on github.
Project setup
Create a new hexo blog
Let’s get started with a brand new hexo installation.
# Create a new folder |
Create the theme folder
# Enter the theme folder |
Note: If you want to save the theme to git (as you should), initialise git inside /themes/bootstrap-blog-hexo/
.
Folder structure
Here are the files and folders we will need to get started:
|-- layout // .ejs templates |
Create these 2 folders and the _config.yml
file.
/layout/
will contain all our EJS templates/source/
will contain our assets (CSS files, external scripts and libraries)_config.yml
contains our theme configuration. Leave it empty for now.
Copy the bootstrap blog assets over
In our source folder, copy all the assets we need from the bootstrap blog template. View source from your browser and copy everything over or download this package and extract it in your source folder.
|-- layout |
Basics of Hexo
Before we write our first template file, let’s look at the basic of Hexo blog generation.
Page types
Corresponds to the 6 types of pages we can define in our theme, every singe HTML page generated in the public folder belongs to one of these templates:
Template | Fallback | Page Description |
---|---|---|
index |
None | This the home page of the blog, the main entry point. In our case it will display a list of blog excerpts. |
post |
index |
This is the detail page for posts. Here we will display only one post in full, with a comment section. |
page |
index |
This is the detail page for pages. Same as post but for ‘page type’ posts. |
archive |
index |
This is the archive page. It will display a list of all the posts in our blog with just titles and links to the detail page. |
category |
archive |
This is the category page. Similar to the archive page but filtered for one category. |
tag |
archive |
This is the tag page. Similar to the archive page page but filtered for one tag. |
In this part of the tutorial we will build the index
.
During the generation is Hexo will look for files named index.ejs
, post.ejs
, page.ejs
and so on. These templates are then rendered to create the static HTML pages.
Common Layout
Hexo supports the use of a common layout file that will be used by all the templates above.
This file has to be named layout.ejs
. It acts as a wrapper around the content rendered by the different page type templates above.
In our theme, the layout
will contain: the <html>
and <head>
tags, the header and menu as well as the footer and the sidebar. Basically all the elements that are common to all pages.
The different page templates will only be responsible for creating the actual content, that will be placed inside our main container.
Variables
Inside all our templates, we have access to some variables that are injected by the hexo engine. Here are some of them:
- Site
site
contains site wide information. For example, with site.posts
we can access all the posts in the blog. Useful if we want to display statistics in a widget for example.
- Page
page
is the main variable and contains a lot of information related to the current page, including all the post titles, dates, content and so on.
The properties of that object depend on which page template (index, post, archives) we are on. The full list is available here; but let’s look at them as we go.
- Config
config
is a JavaScript object representation of the main _config.yml
of the blog.
- Theme
theme
is a JavaScript object representation of the theme’s _config.yml
.
Theme’s layout creation
We will start by creating the /layout/layout.ejs
file discussed above.
The head section
Let’s start by creating a layout.ejs file and inserting the <html></html>
<html> |
Here we are extracting all the <head>
code into a partial view. Partial views promote separation of concern and reusability in our code.
The syntax is partial('path' [, arguments])
After creating that layout/_partial/head.ejs
file, we are going to copy the head code from the bootstrap source code:
<head> |
Pretty straightforward. All we have done is use a CSS helper to insert our style sheets.
The files in our source
folder will be copied at the root of our generated site, so source/
should not be included in the paths.
We will make the <title>
and meta tags dynamic down the line but let’s leave it like so for now.
The after footer section
The after footer section will be included just before the end of our <body>
section. In that partial view, we will include all our scripts.
Let’s modify the layout:
<html> |
And create the content of the new layout/_partial/after-footer.ejs
partial:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> |
Notice the use of a JS helper function for our local js files.
Top Menu
In a similar fashion, let’s create the top menu just after the opening <body>
tag.
|
layout/_partial/menu.ejs
partial content:
<div class="blog-masthead"> |
Note the use of the theme
global variable. theme
is the JS equivalent to the theme’s _config.yml
.
Here we are making the menu configurable in the theme’s configuration.
For this to work we will then need to add the config in _config.yml
:
# Header |
In menu.ejs
we enumerate through all the menu items in the config and create the corresponding links.
Header
The header will be placed just below the menu and contain the blog title and subtitle:
<div class="blog-header"> |
In here, we make use of the config
variable which corresponds to the main _config.yml
of the blog. It should have a title and subtitle properties configured.
When inserting the header in the layout, beware of the <div class="container"></div>
wrapper:
<html> |
Footer
The footer is all static for now, here is the content of the partial view:
<footer class="blog-footer"> |
Main content and sidebar
At this point, we are ready to wrap things up and add the main content as well as the sidebar.
Here is the final layout.ejs
:
<html> |
The body
variable corresponds to the content rendered from the different page type templates (cf above).
For the sidebar partial, we are simply going to hardcode the bootstrap template code for now:
<div class="sidebar-module sidebar-module-inset"> |
The index file
Now that our layout is in place, we are ready to create the first page type template: index.ejs.
Here is a trivial first version:
<span>Content</span> |
Useless ? Well, this allows us to test out theme in browser:
# Verify that everything is alright |
And open your browser at http://localhost:4000/. Tada !
Note: Don’t forget to update the theme in your blog’s config:
# Extensions |
Enumerate through blog posts
On the home page we want to display post excerpts.
First of all, let’s enumerate through the posts in our index.ejs:
<% page.posts.each(function(item){ %> |
- Get a list of posts for that page with
page.posts
- Pass an argument in a partial by using
<%- partial('name', args) %>
Article layout
Let’s create the article-excerpt.ejs
partial and adapt the code to our theme. Here is what I came up with:
<div class="blog-post"> |
- Link to the full post:
The link to the full post is created by concatenating config.root
(config option which really shoud be equal to /
) and item.path
which is the relative path or link to the full post.
- Post author
By default, Hexo does not have any author property in it’s post variable. But we can add whatever variable we want to the front matter.
If you want an author name to be displayed for a post, the front matter for your post should look something like
title: Hello World |
- Item excerpt vs. Item content
When writing a post with Hexo, you can use a <!-- more -->
tag to delimit the excerpt from the content. In our case, we are displaying the excerpt since this is a list of posts.
The user then has the possibility to click on a post’s title or on the read more link to view the entire post.
- Read More text
I have added a new property to my theme’s config, do not forget to add it to yours:
# Read More text |
Hopefully the rest of the code is easy enough to understand. At this point, I suggest you write a few additional posts than the default Hello World and play around with the results.
Pagination
The last thing we are going to tackle in this section is the pagination for the home page
Let’s start by including yet another partial to our index.ejs
:
<% page.posts.each(function(item){ %> |
And finish by writing our pagination partial view, layout/_partial/pagination.ejs
:
<nav> |
page.prev
: Previous page number. 0 if the current page is the first.page.next
: Next page number. 0 if the current page is the last.page.next_link
andpage.prev_link
are self explanatory.
You can adjust the post per page in the main config (per_page
property) if you don’t have enough posts to see the pagination in action.
That’s it for today, in the next section of this tutorial, we will finish all the remaining pages of the blog.