Typst and a CV template

Sun, Apr 23, 2023 tags: [ Typst Programming ]

Updated 2023-08-30

If you are interested in typesetting outside the normal options (Word, LibreOffice), and have had a gnawing feeling that LaTeX is, after all, a bit annoying to use (and I say that as a maybe not power user, but having used it for the past ten years, including all important documents), Typst may be for you.

It’s a new document language, conceptually somewhere between LaTeX and Markdown, has been hyped a bit recently – and that hype is fully deserved. Having had no experience in it, I rewrote my CV on a Sunday morning within less than two hours, and it looks better than the prior LaTeX version. Written in Rust, it is easily installable through cargo, as a standalone binary, or usable on the web. I went the first route (as a matter of pride).

With that said, I don’t want to give a full introduction, because the documentation is amazing as well.

What I will leave here, is my little black-and-white CV template. It is useful enough for a real CV while being not very complicated to layout. Unfortunately, syntax highlighting doesn’t exist yet in my website generator :-)

Example document

Colors and spacings can be adjusted directly in the header. If you use it or make some modifications – it is admittedly quite bare-bones right now – I’d love to hear from you!

// example.typ
#import("cv.typ"): *

/* CV begins below here: */
/* ========================================================== */

#cv_title([Your Name])

#cv_h2()[
Personal Information
]

#grid(
    columns: (20%, 80%),
    rows: 16pt,
 
    [_Address_], [123 Wonder lane, Example town, XY 81444],
    [_Born_], [X city; Month 17, 19xy],
    [_Phone_], [+1 234 567 89]
)

#cv_h2()[Experience]

#cv_h3[Work]

#cv_table(
    [Year 2], [
        *Great achievement*

        Saving the world (full-time).
    ],
    [Year 1], [
        *General specialist*

        Expert in everything.
    ],
)

#cv_h3[Education]

#cv_table(
    [2003-2013], [*Degree*, Physics (4.0 GPA)],
    [1998-2002], [High school, not worth talking about]
)

#cv_h2[Skills]

#cv_h3[General]

- Bullshitting (pro-level)
- Mansplaining (world-class)
- Complaining (if needed)

#cv_h3[Computers]

- Turning off and on again
- Screaming at
- Clicking _print_ three times instead of once

And this is the template package:

// cv.typ

#let cv_title(t) = [
    #set align(center)
    #text(t, font : "Fontin", weight : "bold", size : 26pt)
]
#let cv_h2(t) = [
    #v(3pt)
    #line(length: 100%, stroke: gray)
    #smallcaps(text(t, font: "Fontin", size: 18pt, weight: "regular"))
]
#let cv_h3(t) = [
    #smallcaps(underline(text(t, font: "Fontin", size: 14pt, weight: "regular"), offset: 5pt, stroke: gray))
]

/* Standard link, in blue. */
#let cv_link(t, lab) = text(link(t, lab), fill: blue)

/* Standard layout constants. */
#let cv_citysep = " "
#let cv_table_cell_width = 35em

/* Indents a block slightly to offset it from surrounding content. */
#let cv_inset(t) = par(hanging-indent: 1em, first-line-indent: 1em, t)

/* Indicates a station within an overall experience:

    #cv_station([Apr 2019 - Apr 2020], [Taste University], [Flavortown, USA])
*/
#let cv_station(time, inst, loc) = [
    *#time* #h(1em) *#inst* (#loc)
]

/* Not for external use: use cv_table() instead. Feel free to adjust colors etc. */
#let cv_table_cell(t, width: cv_table_cell_width) = block(t, fill: luma(250),
    width: width, outset: 5pt, stroke: (left: black + 1pt))

/* Use like this:

    #cv_table([2024\ 2020], [Some awesome job])
*/
#let cv_table(width1: 7em, row-spacing: 20pt, ..cellpairs) = [
    #grid(
        columns: (width1, cv_table_cell_width - width1 + 7em),
        rows: auto,
        row-gutter: row-spacing,

        ..(cellpairs.pos().enumerate().map(p => {
                let (i, b) = p
                if calc.even(i) [
                    #text(weight: "bold", b)
                ] else [
                    #cv_table_cell(b, width: cv_table_cell_width - width1 + 7em)
                ]
            })
        )
    )
]