Now things are UGLY

junk plane

Scratch that, this theme is REALLY UGLY right now.

Beginnings of a new theme

You may ask, "Why would you ever do this?". Which is a fair question. Recall in my earlier post about a theme, where this theme is going to be approached with an agile mindset? Then how implementations are going to be prioritized based on the highest business value? Well, this is my first iteration. This is my MVP of a blog. GREAT! How did I get to this point?

Creating a Hexo Theme

Directory setup

First, I needed to understand what are the basics of a Hexo theme. Once there was a very basic understanding, I knew I needed a directory to contain this theme. So under the themes directory, I created the following file structure:

shrebv
|-- layout
|--|-- _partials
_config.yml

Creating a Layout

No, it was time to create layout/layout.ejs. This file represents the outer shell of every single page that will be generated from Hexo. This pattern is familiar with new JS single-page application frameworks, so this file should be pretty quick and easy to generate. The first iteration of my shrebv layout came to be:

<!DOCTYPE html>
<%- partial('_partials/global/head') %>
<body>
    <div class="container">
        <%- partial('_partials/global/header') %>
        <%- body %>
        <%- partial('_partials/global/footer') %>
    </div>  
</body>
</html>

In the above, there are 3 references to partial views, we will break those down in a moment. If you are not familiar with this concept, I plan to do a quick post on partial views in the future. So stay tuned for that. There is also a reference to <%- body %>. This is where the content of the page is placed.

Breaking Down the Partials - Head

For the head partial, I created a new file layout/_partials/global/head.ejs. This is just the beginning of what should appear in the head of an HTML document. So I expect this to grow and change as the theme expands. For now, it is quite easy:

<head>
    <meta charset="utf-8"/>
    <title><%= config.title %></title>
</head>

Pretty simple head definition. The one thing to note is the value of the title. As we saw earlier in the series this is using the EJS annotation. Where <%= outputs an HTML escaped value. The value this is outputting comes from the sites _config.yml file, not to be confused with the _config.yml that was just created in the theme directory. There is a value on line 6 of the file title: Blog. This is where the dynamic nature of the static file generation is powerful. There can be one source of truth for values in the configuration of the project. This way we as developers are able to perform lookups of this single value in potentially many places throughout the site. If this value is to ever change, which it will, all we have to do is update the configuration, then regenerate the site. No more string searching throughout a project to replace values.

Breaking Down the Partials - Header

As with the head partial, I created a new file layout/_partials/global/header.ejs. At some point in time, this will contain the navigation, potentially search, as well as other features one would come to expect on global navigation. But for now, it's quick and simple.

<header>
    <h1><%= config.subtitle %></h1>
</header>

Again, same with the head partial view. This is merely referencing the subTitle value in the site

Breaking Down the Partials - Footer

Following suit, the footer is not much different than the previous 2 examples.

<footer>
    <span>&copy;<%= config.author %></span>
</footer>

Creating the landing page

If you are familiar with creating a static HTML site, this pattern will feel quite familiar and it is quite easy. All we have to do is create a new EJS file, layout/index.ejs. That is all. Any content we place in this file will be displayed on the landing page of our Hexo site. To meet the MVP criteria I set, my landing page should have a little more than static content. After all, this is a blog. So how about a list of all the blog posts? That should do it. For this, I opted to create another partial view, listing all the blog posts. Just in case I wanted to have list functionality on other pages in the future.

<%- partial('_partials/list') %>
Breaking Down the Partials - List

Now this one is a bit more interesting than the other partials. There is actually some JS in it for starters. Let's take a quick look at the partial view:

<% page.posts.each(function(post){ %>
    <%- partial('_partials/post-preview', { post: post }) %>
<% }) %>

notice the use of <% tags on lines 1 & 3? Those allow us to write JS without outputting anything here. In the case of line 1, each Hexo page has a list of variables. page is a global variable that can be referenced on any of the page types created. The home index page adds a set of variables that can be accessed. In the case of this partial, the one of interest is the array of post objects.

For each of the post objects, we execute a function. Passing the individual post into the partial view by adding , { post: post } after defining the partial view to be used.

Breaking Down the Partials - Post Preview

In this partial view, we find many of the same elements. With some minor differences. In the earlier partial views, the data to be presented came directly from the configuration. This data has to be from the post objects. Recall the JSON passed into the post-preview partial view, from the list partial view? Here we can reference that data. Specifically, the post property passed in. Lines 3, 5, and 8 do just that. In this partial view, we know that the type passed in is of type post, so we have access to all the properties available on that object to display.

<article style="width: 80%">
    <span>---------------------------------------------------------------------------------------------------------------------------------------------------------</span>
    <h2><%- post.title %></h2>
    <div>
        <%= post.date %>
    </div>
    <div>
        <%- post.content %>
    </div>
</article>

Is That It?

As I was rolling through this implementation, I was confident in answering the question with 'YES'. However, I was wrong. As I was executing hexo generate to test the changes, I was frequently getting errors of Cannot read property 'each' of undefined. To which I was quite confused. The other examples I have seen of templates all iterate through posts in a similar manner. As I compared and contrasted other templates. I found one missing thing a file layout/post.ejs that was the only difference. I quickly created an empty file, everything worked without issue. At this point, I can only speculate. However, I think it has to do with a post being a top-level entity in Hexo. As the site is being generated, the Hexo CLI does not have a post.ejs file. If that file is not present, the generator does not populate the page.posts property.

As I get deeper into Hexo and its inner workings, I hope to understand this more. For now, I am good with not knowing 100%. It seems likely that this will be a similar pattern for the other Hexo top-level entities (archive, category, tag, and page). I would also anticipate that the layout/post.ejs file will come in handy when I have a post detail page. But, for now, it will remain empty.