HTML resume generator
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.

Spin generates HTML résumés and CVs from reusable content and writes each file to an output directory.

$ spin /path/to/profile [/path/to/other_profile [...]] \

This project solves the problem of needing many promotional documents tailored to a different audience. If you need document conversion, use pandoc.


Profiles are scripts used to generate targeted promotions.

$ spin software_engineer tupperware_massager ...

Each profile script provided writes a file of the same name with the extension '.html' to --out-dir.

Profiles are written in Markdown, with the added feature of @ directives used to add assets and define content in key places.

Look in the examples directory for profile scripts promoting a fictitious person.


Directives are instructions that appear among Markdown that give instructions to Spin. Like Linux shells, Spin reserves spaces as separators for words, and requires quotes to treat multiple words as single arguments.

You can think of this as calling a function like foo('a', 'b', 'c')

@foo a b c 

...and this as calling as a function like foo('a', 'b c')

@foo a "b c"

Documentation for each directive follows. [] denotes optional arguments.

@import relpath

Imports an asset.

If relpath is a URL or has a .css extension, the asset will appear as a <link> to a stylesheet. Spin will not verify the existence of static assets. The generated HTML file will contain the paths you specify exactly as written.

If relpath lacks an extension, it is assumed to point to a .spin-partial file that will be loaded where the directive appears. In this case, paths may be absolute, or relative to the profile script where the @import directive appears.

@import styles.css 

@import shared/retail_work

In the above example, the first two @imports create links to stylesheets in the output HTML. The third import loads a shared/retail_work.spin-partial relative to the profile script in which the import appears.

@section [name]

Starts a new named promo section. If you specify a name blank, the section will begin with a top-level heading containing that name.

@section Summary

My name is Spencer and I am on the **fast-track to working with the W3C!**

@section Work

@experience "Asst. Manager"

@experience CTO

@experience name

Start documenting an experience inside of a section.

@section Education
@experience "Bachelor of Science"

@organization name

Names an organization associated with an experience.

@experience Roofer
@organization "Rough Roofers, Inc."

@location "address"

Specifies a location associated with an experience.

@experience "System Engineer"
@location "Marshall Space Flight Center, Huntsville, AL"

@dates startdate [enddate]

Sets the start and end dates for an experience. Raises an error if used outside the scope of experience.

Spin will not impose any formatting rules on dates and will not sort your content chronologically.

If an end date is not specified, it will appear as "Present".

@experience "Window Washer"
@organization "Wells Fargo"
@dates "Oct 2008" "Dec 2010"


Output documents use BEM classes. The selectors are closely related to directives.

  • section
    • section__name: The string given to @section
    • section__content: Formerly Markdown content
  • experience
    • experience__name: The string given to @experience
    • experience__location: @location content
    • experience__start-date: See @dates
    • experience__end-date: See @dates
    • experience__date-dash: The en-dash between the start and end dates.
    • experience__dates: Contains all aforementioned date content
    • experience__content: Formerly Markdown content