How I Made This Blog Stunwin dot com

How I Made This Blog

CSS is not fun, but Gleam is

How I Made This Blog

Doing things the hard way

A great man once said: "why do something easily, when you can do it poorly?"

I don't know who said that. Perhaps it's he's lost to time. I know it's a he. Be honest.

I wanted to build this site in Gleam because I really genuinely enjoy writing Gleam code, and the Lustre framework for building html pages is really a treat to work with. Interestingly enough, I suspect that my enthusiasm for Lustre is a product of the fact that I have zero professional experience with frontend development. I showed some of the code to a very senior frontend friend of mine and he was horrified. More's the pity for him I suppose. I think this is nice:

pub fn view(post: Post) {
  h.div([a.class("app-root")], [
    h.div([a.class("page-container")], [
      shared.head_tags(
        title: post.title,
        description: post.preview,
        image: post.image,
        url: post.slug,
      ),
      shared.header_row(),
      shared.header_card(post),
      h.div([a.class("tagcloud")], tagcloud(post)),
      h.div(
        [a.class("blog")],
        djot.render(post.content, djot.default_renderer()),
      ),
      shared.footer(),
    ]),
  ])
}


It's just… satisfying? Tactile? Whatever, my post about why I think Gleam is a good language for beginners will come later. Today we're just talking about how I set up a static site generator.

The source code is right here, by the way

Ok, so where do we start? Well we're using the Lustre SSG library, which basically handles all of the actual file creation and routing. So let's take blog posts for example. First, you need to actually go though your folder of markdown files, and package them up in a way that the library can work with them. That meand mapping over all the files in the posts folder with something like this that extracts the frontmatter, and builds everyting into a Post() type.

pub fn build_post(path: String) -> #(String, Post) {
  echo path
  let assert Ok(file) = simplifile.read(path)
  let assert Ok(toml) = djot.metadata(file)

  let meta_fields =
    ["title", "slug", "image", "preview", "time"]
    |> list.map(fn(field) {
      echo field
      let assert Ok(val) = tom.get_string(toml, [field])
      #(field, val)
    })

  let assert Ok(tags) = tom.get_array(toml, ["tags"])
...

et cetera. Like those echo keywords? Print debugging is for winners who don't have time to plumb through result types at the moment.