Static draft posts with Harp

I have a lot of bad ideas. Once—well, more than once—I excitedly wore this gaudy, lime green t-shirt, sporting some kind of mesh racing stripe through the middle. Tucking it into the matching shorts was what really made the outfit. Or, what about when I stayed up so late the night before school, I fell asleep on someone on the bus the next day? (Fine, that one happened again yesterday.)

I could go on—an incident involving rm -rf at work may come up—but I’m realising that it’s also a bad idea to keep sharing these kinds of things with you. In short, I have a lot of bad ideas. At least with my writing, I can now contain some of them to draft posts.

Whether I’m writing about creating a statically generated blog with Harp, the static web server with built-in preprocessing, or about how terrible I look in shorts, I still want somewhere to put my poorly worded, unfinished ideas, before they get deleted—I mean published.

Start a blog with Harp

In Start a blog with Harp, I covered how you can install and built a basic static blog using Harp. If you already have Node.js, you can install or upgrade Harp with:

sudo npm install -g harp

Now, you can start giving your collection of Markdown posts some more complex features. If you want to follow along, building on the example site from the previous post, its source is available on GitHub.

Drafts as partials

While using Harp, prefixing a folder’s name with an underscore will make all files inside into partials; they are no longer served directly by Harp, but could be brought into other EJS or Jade files.

When I moved my blog to Harp, I put all my work-in-progress posts into a _drafts/ folder; this is the fastest and most boring way to give your static blog draft posts.

Usually, partials are used for pieces of your site that aren’t useful on their own, like the the header or footer of your site. These blog posts are likely Markdown files, and they are supposed to represent entire pages, so this isn’t the most versatile approach.

Drafts through metadata

Instead, add a new Markdown post and add its metadata inside the _data.json file. Now, with this post and the others, specify which posts should shouldn’t be published.

{
  "a-simple-article": {
    "title": "A Simple Article"
  },
  "start-a-blog-with-harp": {
    "title": "Start a blog with Harp"
  },
  "an-unfinished-article": {
    "title": "Still working on a title",
    "published": false
  }
}

Setting "published": false won’t do anything yet. It’s just like the title, or any other piece of metadata in Harp: flexible, you define and work with it. You could even use "draft": true instead of "published": false if you prefer.

Using your flexible metadata

In Start a blog with Harp, I covered how to iterate over your posts’ metadata using Jade:

ul
  each article, slug in public.articles._data
    li
      a(href="/articles/#{ slug }") #{ article.title }

You could also do the same in EJS if you prefer:

<ul>
  <% for (slug in public.articles._data) { %>
    <% var article = public._data[slug] %>
    <li>
      <a href="/articles/<% slug %>"><%= article.title %></a>
    </li>
  <% } %>
</ul>

From there, you can make Harp determine whether or not draft posts should appear in the loop:

ul
  each article, slug in public.articles._data
    if article.published!==false
      li
        a(href="/articles/#{ slug }") #{ article.title }

Or, using EJS:

<ul>
  <% for (slug in public.articles._data) { %>
  <% var article = public._data[slug] %>
    <% if(published!==false) { %>
      <li>
        <a href="/articles/<% slug %>"><%= article.title %></a>
      </li>
    <% } %>
  <% } %>
</ul>

This is an improvement. You are now specifying the state of the post through metadata rather than renaming or moving files, but if you’re like me, you’ll still want to see those draft posts when you’re working locally.

Plus, if you were to stop here, your individual posts would still be accessible via their URL. Though they aren’t appearing in the index, I could still visit /articles/an-unfinished-article and your post would be there.

Ask Sintaxi

I mentioned my problem to Brock, the author of Harp, as was packing up for the evening. He had just dropped his computer into his messenger bag. “It would be great if I could template conditionally if I was developing locally or if my site was published,” I said. “Some function or something,”

“Yeah, some kind of variable so you can know if in the Harp app is in development or production?” he said, smiling a little too knowingly.

It turned out, Harp already has a variable called environment. It’s set to the string development or production as appropriate.

This opens up many possibilities: your blog can behave one way locally, and another when it’s served in production mode or published on the Harp Platform. Additionally, harp compile, which flattens your blog into static HTML, CSS & JavaScript, is considered to be a production environment. This means anything indented for production will still occur even when you’re hosting static files.

Learning the environment

With the environment variable, you can make pieces of your static blog behave differently in different contexts.

ul
  each article, slug in public.articles._data
    if article.published!==false
      li
        a(href="/articles/#{ slug }") #{ article.title }

Continuing to use the environment variable, you can update your _layout file to check if the yield—where the posts’ content is actually appearing—is set to be published:

if published!==false || environment=='development'
  != yield

Now, the yield still won’t be rendered if published is false, but you’re running the blog, you’ll still be able to view your post:

This screenshot shows the posts is still visible in development environments.

If published is false, and you’re not running the blog locally, that means a visitor has stumbled upon a page you aren’t yet ready for them to see. It’s up to you what to show in this situation, instead of showing draft’s content: “Hey, you’re here early. This post isn’t published yet, but you can follow me on Twitter here,” for example. Alternatively, you can use the partial function to include your 404 page to make the situation even less conspicuous:

if published!==false || environment=='development'
  != yield
else
  != partial("404")

Now, in the admittedly unlikely situation a visitor lands on the page for an unpublished post, they’ll simply see your 404 Not Found page.

The 404 page is shown when viewing the example site with Harp in production mode.

Styling drafts locally

Now that the drafts are work as you’d like, you can make the appear as you’d like. When I run the Harp blog locally, for example, I can easily see what posts have yet to be published:

This screenshot shows harpjs.com in development and production modes—in one, the draft posts are visible, but they are not in the other.

On the left, the unpublished posts are visible, while in production, they have disappeared. This is done with a conditionally added CSS class. To do something similar in your static blog, update the post loop in index.jade:

ul
  each article, slug in public.articles._data
    if article.published!==false || environment=='development'
      li
        a(href="/articles/#{ slug }" class="#{ article.published==false ? 'unpublished' : '' }") #{ article.title }

Now, the anchor tag will have the class .unpublished when "published": false is set in articles/_data.json. This means with a couple lines of Sass, which is also built into Harp, you can style the .unpublished posts differently:

.unpublished {
  color: lighten($blue, 20%);
  &:after {
    content: " (unpublished)";
  }
}
An example of taking the Harp simple blog and adding draft posts.

Exploring the environment

Now that you’ve used the environment variable to add contextual draft posts to your static blog, you’re ready to write! You can also view the example blog’s source on GitHub, take a look at the Harp documentation for more ideas of what’s possible.

Static site is a misnomer when the possibilities become this targeted and dynamic.

    Start a blog with HarpWeb Type West