How This Site Is (Currently) Made - 2026-02-19

Goodmorning, this is not a tech-blog. But… idk, this might be of interest if you're on here to begin with?

Instead of steadily writing posts here the last week or so, I instead experienced what we call "executive dysfunction". The upside is that now I have a script to automatically put-together my home-page, (and one to generate my posts as a bonus).

This isn't really a tutorial, and no knowledge is assumed, I'm mainly just going to yap.

Baseline, what are we working with?

The Tools

The Site

.
├── drafts
│   ├── how-this-site-is-made.org
│   └── init.org
├── index.html
├── intro.org
├── journal.org
├── posts
│   └── init.html
├── project-todo.org
├── scripts
│   ├── bb.edn
│   ├── create-homepage.bb
│   └── create-post.bb
├── style.css
└── templates
    ├── base.html.tmpl
    └── home-page.html.tmpl

5 directories, 13 files

I'd like to point out a few different items here:

Templating our pages

So, we have these two little template files, one for our homepage (home-page.html.tmpl), and one for our posts (post.html.tmpl).

Here's the latter, I'll try to explain what we're looking at:

Posts

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{title}}</title>
    <link href="/style.css" rel="stylesheet" type="text/css" media="all">
  </head>
  <body>{{content}}</body>
</html>

The points of interest here are those {{title}} and {{content}} bits.

To put it briefly: {{content}} will be replaced by an HTML-ized draft, and {{title}} will be replaced by the first header in that draft- this is ultimately handled by create-post.bb, which we'll look at… now!

(ns create-post
  (:require
   [clojure.string :as str]
   [selmer.parser :as selmer]
   [babashka.cli :as cli]
   [clojure.java.io :as io]
   [babashka.process :as p :refer [shell]]))

(def templatefile "<my-project-path>/neocity/templates/post.html.tmpl")
(def cli-opts {:fname {:coerce :string}})

(defn file->title [f]
  (with-open [rdr (io/reader f)]
    (-> rdr (line-seq) (first) (str/split #"#|\*" 2) (last) (str/trim))))

(defn -main [{:keys [fname destdir]}]
  (let [title (file->title fname)
        content (:out (shell {:out :string} "pandoc" fname))]
    (println
      (selmer/render (slurp templatefile)
        {:title title :content [:safe content]}))))

(when (= *file* (System/getProperty "babashka.file"))
  (-main (cli/parse-opts *command-line-args* {:spec cli-opts})))

Ok, I don't expect you to understand any of this. Just gonna give some really broad bullet-points:

I run this script like so:

bb create-post.bb :fname [path-to-my-draft] > [path-to-our-new-post]

And a new little post is born unto the world.

Next up…

The Homepage

Lets start with our template, it's a little more complicated than the last.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>The web site of dotdotok</title>
    <link href="/style.css" rel="stylesheet" type="text/css" media="all">
  </head>
  <body>
    {{intro}}
    <h2 id="posts">Posts</h2>
    <ul>
    {% for post in posts %}
      <li><a href={{post.path}}>{{post.title}}</a></li>
    {% endfor %}
    </ul>
    <h2 id="friends">Friends</h2>
    <ul>
    {% for friend in friends %}
      <li><a href={{friend.link}}>{{friend.name}}</a></li>
    {% endfor %}
    </ul>
    <br>
    <hr />
    {{running-journal}}
  </body>
</html>

Theres 3 distinct parts to my homepage, which I'll name: "Intro", "Links", and "Running Journal".

Our template is a little more interesting, and our script has to do a little more as a result, here's the corresponding create-homepage.bb file:

(ns create-homepage
  (:require
   [clojure.string :as str]
   [selmer.parser :as selmer]
   [babashka.fs :as fs]
   [babashka.cli :as cli]
   [clojure.java.io :as io]
   [hickory.core :as hickory]
   [babashka.process :as p :refer [shell]]
   [com.rpl.specter :as s :refer :all]))

(def templatefile "../templates/home-page.html.tmpl")
(defn pandoc [fname & args]
  (->> args
    (apply shell {:out :string} "pandoc" fname)
    :out))
(def intro (pandoc "../intro.org"))
(def running-journal (pandoc "../journal.org" "--shift-heading-level-by" "1"))

(def post-list
  (->> (fs/list-dir "../posts/")
    (map (fn [x]
           {:path x :creation-time (fs/creation-time x) :last-updated (fs/last-modified-time x)}))))

(defn file->title [f]
  (->> f
    hickory/parse
    hickory/as-hickory
    (select-one (walker #(-> % :tag (= :title))))
    :content
    (first)))

(def post-data
  (sort-by :creation-time
    (map (fn [{:keys [path creation-time last-updated]}]
          {:path (-> path str (str/split #"/" 2) (last))
           :title (file->title path)
           :creation-time creation-time
           :last-updated last-updated})
      post-list)))

(spit "../index.html"
  (selmer/render (slurp templatefile)
    {:intro [:safe intro]
     :posts post-data
     :friends [{:link "https://hope42.neocities.org"
                :name "hope42"}
               {:link "https://iantimony.neocities.org"
                :name "iantimony"}]
     :running-journal [:safe running-journal]}))

A few more lines in this script, but maybe some are already a little familiar.

This one I can just run via:

bb create-homepage.bb

And my homepage is updated and fresh.

Why not just use one of the countless static blog generators out there?

  1. Yeah.
  2. Neurodivergence.
  3. I wanted to DIY this at least once, before trying to use a generator, and it's in-theme with the rest of the whole "keep it simple/haphazard" mission-statement here.