#Random Notes — My blog rewritten using Blogatto

1 messages · Page 1 of 1 (latest)

sacred cargo
#

https://blog.nytsoi.net

I'm an avid blog engine writer and occasional blogger as well. Ever since ditching Wordpess many years ago, I've had a bunch of self-written engines in JS, Elixir, and finally in Gleam with https://discord.com/channels/768594524158427167/1231492933220569139. But I have so many projects and only so much time, so I decided to rewrite it with #1475820252939030539, since it seems to have some momentum behind it, and already has more features than Scriptorium, not to mention being more approachable. So, thanks to @gloomy dew for it!

My blog follows some conventions back from the times it was on WP, so I had to do some custom stuff. For example tags now link to a long page listing all of them, linked to the tag in question, where you can find more posts. Date based archives are gone, but I'm pretty sure no one uses those. Pagination is also gone, all the posts are just listed on the front page now, having less detail when you get closer to the end.

The style is similar to my previous one, but I switched the font to New Computer Modern, which might be familiar to any TeX users here. To make the download small, I split the font to multiple Unicode ranges and the browser will only pick the ones it needs. I'll need to write a post about that.

The codebase is currently private since all my posts and drafts are also in the same repo, but I should split them at some point so that others can pick off any good ideas. Before that, I'll try to put some code snippets below in case they would be useful.

#

How I generate the feed because I wanted full post contents there instead of an excerpt:

let rss =
    feed.new("Random Notes", site_url, "")
    |> feed.language("en")
    |> feed.generator("Blogatto")
    |> feed.output("./feed.xml")
    |> feed.serialize(fn(metadata) {
      feed.FeedItem(
        title: metadata.post.title,
        description: "<![CDATA["
          <> element.to_string(element.fragment(metadata.post.contents))
          <> "]]>",
        link: option.Some(metadata.url),
        author: option.None,
        comments: option.None,
        source: option.None,
        pub_date: option.Some(metadata.post.date),
        categories: [],
        enclosure: option.None,
        guid: option.Some(metadata.url),
      )
    })
#

My meta tags for social media embeds

let author = "..."

list.flatten([
  [
    meta_utils.name_meta("author", author),
    meta_utils.name_meta("description", post.description),
    // Schema.org
    meta_utils.itemprop_meta("name", post.title),
    meta_utils.itemprop_meta("author", author),
    meta_utils.itemprop_meta("headline", post.title),
    meta_utils.itemprop_meta("description", post.description),
    // Twitter/X card
    meta_utils.name_meta("twitter:card", "summary_large_image"),
    meta_utils.name_meta("twitter:title", post.title),
    meta_utils.name_meta("twitter:description", post.description),
    meta_utils.name_meta("twitter:creator", ""),
    // Open Graph
    meta_utils.property_meta("og:title", post.title),
    meta_utils.property_meta("og:type", "article"),
    meta_utils.property_meta("og:description", post.description),
    meta_utils.property_meta("og:site_name", "Random Notes"),
  ],
  {
    let timestamp = post_metadata.true_timestamp(post)
    let headers_dict = post.extras
    let image = post.featured_image
    let image_alt = case image {
      option.Some(_) ->
        headers_dict |> dict.get("image_alt") |> option.from_result()
      option.None -> option.None
    }
    [
      meta_utils.property_meta("article:published_time", timestamp),
      meta_utils.itemprop_meta("datePublished", timestamp),
      meta_utils.property_meta("og:url", post.url),
      ..case image {
        option.Some(image) -> [
          meta_utils.name_meta("twitter:image", image),
          meta_utils.property_meta("og:image", image),
          meta_utils.itemprop_meta("image", image),
          ..case image_alt {
            option.Some(image_alt) -> [
              meta_utils.name_meta("twitter:image:alt", image_alt),
              meta_utils.property_meta("og:image:alt", image_alt),
            ]
            option.None -> []
          }
        ]
        option.None -> []
      }
    ]
   },
])
#

plus of course these for fedi:

      h.link([a.rel("me"), a.href("https://masto.ahlcode.fi/@nicd")]),
      h.meta([a.name("fediverse:creator"), a.content("@[email protected]")]),
#

Styling is Tailwind for the base layout and some overrides for the HTML rendered from Markdown. I have this script for building the blog with styles

#!/usr/bin/env zsh

tailwindcss -i tailwind.css -o ./static/tailwind.css

gleam run

and then in the dev module I have this

|> dev.build_command("./build-dev.sh")
#

Syntax highlighting is still on the todo list

gloomy dew
#

If you want glailglind provides tailwindcss installation and conversion directly inside of Gleam

sturdy wadi
#

looks very nice!

sacred cargo
#

Thanks, I love the font personally. 🙂 It has very fancy italics too.

sacred cargo
sacred cargo
#

lol I posted that in the future because I forgot that Blogatto takes times as UTC 😅

warped quartz
#

That's very useful thank you!

sacred cargo
#

one random tip: since time immemorial I've had my posts split with a <!-- SPLIT --> so that only the first part is shown in the post list and the rest is shown only on the post page. this allows me to have the post list less cluttered as the posts could be long. so when rendering my posts I have this little hack:

const post_split = "<!-- SPLIT -->"

// later
  let full_contents = post.contents |> element.fragment() |> element.to_string()
  let contents = case string.split_once(full_contents, post_split) {
    Ok(#(start, _)) -> [
      element.unsafe_raw_html("", "div", [], start <> "</div>"),
      h.a([a.href(post.url)], [h.text("Read more…")]),
    ]
    Error(_) -> post.contents
  }

so in case the comment was found (it's passed through by the markdown parser), just cut the rendered HTML at that point. a </div> is inserted because the markdown parser turns an empty line with the comment into <div><!-- SPLIT --></div>, so after this replacement it'll just be <div></div>.

I don't use the excerpt feature of Blogatto because it seemed to cut off all HTML and just take the equivalent of .innerText. this allows me to have HTML and whatever markdown I want in the first part.

another banger by Nicd for y'all.