How to use Harp.js to generate your resume

Jan 15, 2017

* * *

I had to update my resume this week in preparation for the big job hunt, and I was pretty happy with the way it turned out. It also got me thinking about the evolution of my resume over the years.

In the beginning there was nothing…

Everyone starts off with a Microsoft Word resume. Even as a high schooler applying for Craigslist summer gigs, I was already fiddling with the formatting and modifying templates to make my resume my resume.[1] It was exhausting to do any custom styling because I would have to highlight and change the formatting phrase-by-phrase.

Fast-forward a couple years, and I discovered the Data Merge function in Adobe InDesign. From this, I accidentally learned a pretty useful concept: separate content from style.

Content from style

By separating content from style, it’s easier to manage both. A word processor like Office mixes the two: I wrote my content and then manually selected a chunk of text to change the styling from a bunch of drop-down menus. Then I repeat this for every chunk.

Instead, why not label your content so that your computer can figure out which chunks are which? Then, you set some rules and your computer does what it was invented to do: save you from doing repetitive boring things.

A resume is just a bunch of repeating components. For example, each work experience should have:

From an object-oriented programming standpoint, “work experience“ could be considered a class, with these required properties. If this was a programming assignment, I could write a constructor function and pass the necessary arguments for each work experience I wanted to list. Once again, the computer would then do the heavy lifting for formatting.

But this was before I even knew what CS was.[2]

Instead, I divided these fields into separate columns in a CSV file (one row per work experience) to import into InDesign. From there, I could use Paragraph Styles to change fonts and sizes that would apply to all corresponding components.

InDesign drawbacks

This was great, but I still had to arrange textboxes manually to a grid of my design after importing. I didn’t think it was possible to preserve bulleted lists in a CSV, so I also had to re-create those for each job description after I imported into InDesign.

In addition, I used a feature in InDesign known as Create Outlines, which converted text into vector shapes. It was easier to manipulate size this way (I could just use the scale tool instead of fiddling with decimals in the “font size” box), but I wouldn’t be able to edit the text in the future since they were now simply outlines.

Finally, although I could fiddle with all these elements and the spacing between them with precision, it was tedious, to say the least.

As I fired up InDesign this week and tried to remember where the Data Merge window was, I thought: There has to be a better way. Ideally, I could put my words into a black box and it would spit out a formatted one-page resume for me.

The black box

Enter Harp.js! It’s the static site generator I use (as of this post) for my personal site. At this point, I realized that separation of content and style was exactly what Markdown and HTML + CSS were designed for.[3] By considering my resume as a one-page website, I could set my formatting rules, input my text, and the computer would do the rest for me — no more manually tweaking!

A couple hours later, I was the proud father of a Harp-generated resume.

Design philosophy

The project only requires three files:

I want to be efficient with page real estate, so I put title, company, and duration on the same row. To do this in Markdown, I use two-column tables and right-align text in the second column. I assumed I would have to apply a custom class for each second column to do so, but turns out there’s a CSS property called nth-child. Neat!

It would be tedious and less readable in Markdown to apply classes for every single component, so I use the em and strong tags and tweak them in SCSS to fit my needs. With SCSS and Jade, I can use variables, which is nice if I decide to change font families or color palettes (I currently just match with my main website).

Plus, I can version control using Git, so I don’t have a bunch of old versions of my resume cluttering up my hard drive now.

Harp.js drawbacks

Although my current method is definitely a step up from InDesign, there are a few small things that could be improved.

As I mentioned, my ideal black box spits out a perfectly formatted one-page resume. Formatting is largely handled automatically with Harp now, but I still had to do some trial-and-error fiddling with the base font-size and line-height CSS properties in order to avoid spillover to a second page. There’s an upper limit to how much text can fit on a page before hampering readability, so we should be Gucci moving forward now that it’s been set.

Additionally, the Markdown parser that comes with Harp.js requires tables to have headers. I only need a single regular table row for each work experience, so in the Markdown file, each row currently has a redundant crown of pipes and hyphens. Alternatively, I could add SCSS styling to th tags so that they look the same as td tags, but either way is a hacky workaround. There’s probably a way to install a different Markdown parser in Harp’s dependencies, but the pain-to-gain ratio seems unfavorable.

The main annoyance is that, in the end, my Harp-generated resume is just a webpage. It doesn’t render automatically as a PDF, and recruiters want PDFs that they can save locally and share. So to host my resume online, I would still have to do the grunt work of saving the page as a PDF and uploading updated versions. I’ve been using Google Chrome’s “Save as PDF“ option from the print dialog, but I just realized that I could save a step by using a HTML to PDF Terminal tool to pipe straight from a harp compile command and string it with an && cp command to copy it from its directory to the proper place in my personal website directory. In fact, I could probably put that all in an NPM or shell script.

Ideally though, the only step I would need to take is update the text in index.md and let the black box take care of the rest…Actually, the ideal black box would just know the history of my career trajectory and upload individualized resumes into every recruiter’s brain. But one step at a time.

The takeaway

Overall, I’m satisfied with the tool I was able to create in a couple hours of obsessive geekiness. You can check out the Github repo here.

Turns out there are even geekier methods too. As always, there’s more to learn.


  1. I’ve since learned that standing out with an unorthodox resume style isn’t necessarily a good thing because it annoys recruiters if they have to break their resume-scanning flow to find vital information.  ↩

  2. You can read about that little story here  ↩

  3. On a higher, more general level, the black box I was looking for was declarative programming  ↩


drop me a line at blog@kevinzhai.com!