How To Make A Blog
Blog posts should typically be about something, but forgive me while I learn how to create this blog in public
In most cases I imagine that my blog posts will be meaningful to me, and if not I hope I don't make them. In this case however my intent is going to be primarily around the idea of how to create a blog from a technincal perspective. It is not a post I expect you to read! It's more of a record for myself of how I decided to create the thing you are currently looking at as I built it. Pretty meta... It is also not intended to be the world's best technical specimen. I want a blog that allows me to share my thoughts not one whose internals will impress nerds.
How will blog posts be written?
So how should we make a blog? How about we start with a series of markdown files which can be easily created and edited locally in MarkText and since markdown is directly translatable to HTML we have a simple means of converting our human editable content to internet distributable human readable content.
As to why I'm not just writing in a code editor or an online editor. It's because writing itself is a craft. A code editor is a great tool for editing code and while you can write markdown in any text editor there is a value to having a clean readable preview as you create and not getting bogged down by the syntax of markdown regardless of how much more pleasant it is than raw HTML. As to why I prefer an offline expereince it's simple: writing is not something that should require the trappings of a life lived in an "always online" state (ads, internet connection issues, ads, local to remote syncing, ads). By using MarkText I am making a few claims about what's important to me as a writer:
-
The act of writing should be in itself a pleasant expereince.
-
An offline and ad-free writing experience is important to me.
-
Markdown is powerful enough for anything I would want to do and I actually like that's its feature set is somewhat narrow.
How will blog posts be made web ready?
Ok, so we have the first and most important aspect settled! That's great, but we do want to share this blog so how are we going to do that?
Note: This post is from an imagined future. I'm writing this as I discover how I want to do these things so they are subject to change.
I like Go as a language. I like using Justfiles for managing tasks. I like Markdown for writing content and I like Less CSS for managing styles, but please not too much CSS 😫 Let's see if we can combine these items in a smart and fun way.
I think we should start simply with a Go HTML template that has a little spot for some content baked into it:
1<body>
2 <main>{{.Content}}</main>
3</body>
So our blog posts will be written in Markdown and compiled to an HTML string and placed into a <main> element in an HTML page. Feels logical enough. How will we compile the markdown? Let's take a look at Goldmark a library for converting markdown to HTML in Go:
1// filePath is the path to our markdown file "/path/to/post.md"
2var md = goldmark.New()
3var buf bytes.Buffer
4data, _ := os.ReadFile(filePath)
5md.Convert(data, &buf)
6content := buf.String()
Once converted from markdown to a safe HTML string I'll put the resulting data into a Post struct and apply that struct to the template we made above above:
1func NewPost(entry.Name(), postHTML string) Post {
2 return Post {
3 FileName: fmt.Sprintf("%s.html", cleanName),
4 Title: strings.Join(strings.Split(cleanName, "-"), " "),
5 Content: template.HTML(content),
6 }
7}
8
9createPostHTML(
10 blogTemplate,
11 destinationDir,
12 NewPost(entry.Name(), postHTML),
13)
Ok, so we make a template, compile the template, convert our markdown to HTML and inject that HTML as "content" into our template. Feels reasonable enough, and this should get us all the way to having something we can use to display blog posts on the internet. Awesome!
For our styles using Less CSS we can use the Less.go compiler:
1import less "github.com/toakleaf/less.go/less"
2lessContents, _ := os.ReadFile(filepath.Join("src", "styles", "main.less"))
3result, _ := less.Compile(string(lessContents), nil)
4css := []byte(result.CSS)
5os.WriteFile(filepath.Join(DIST, "styles", "main.css"), css, 0644)
Finally, for our static assets (images, video, and audio) since we aren't worried about having an optimization step since simplicity trumps a few miliseconds of load time we will just copy a shared assets folder over from a known spot to our distribution location.
1import cp "github.com/otiai10/copy"
2assetsSrc := filepath.Join(blogDirectory, "assets")
3assetsDest := filepath.Join(DIST, "assets")
4cp.Copy(assetsSrc, assetsDest)
This is far from the whole story, but the core of what makes a blog post deliverable is covered here. We have a way of going from writing markdown files locally to having them collected as HTML files with styles and static assets colocated for simple distribution. The only thing we are missing is a way to index our posts. Some sort of index.html file. We can collect our posts as we compile them and simply place them into a list in a second template file. For our index template we could use something like this:
1<section class="post-list">
2 {{range .}}
3 <article><a href="{{.URL}}">{{.Title}}</a></article>
4 {{end}}
5</section>
Then as we compile our posts we can collect them and once we have finished processing all of our posts we can compile them into our index template:
1posts := []Post{}
2post := NewPost(entry.Name(), postHTML)
3posts = append(posts, post)
4indexTemplate.Execute(file, posts)
As mentioned above this post is not really intended for the average reader and as such the code here is provided as fragments rather than as a cohesive program, but for my purposes of recalling how this blog was created it's very well suited.
How will the blog be served to readers?
So now we have a blog that we can view as a website on our own machine. How do we take that fully functional static website and get it on the internet so that it can be loved by the whole wide world?
Well we have to use a "server" to "serve" our website. There are several means of serving a simple static website, but I like the idea of having complete ownership of my blog and so I intend to self host it. To do this I am going to use a very friendly Proxy Server called Caddy. The Caddy server comes with the ability to create a Caddyfile which uses a specfic syntax to make things like static file serving very simple. Let's try it out.
1:9090 {
2 root * ./dist
3 file_server {index index.html}
4 @stripExtensions path_regexp strip (.*)\.(html)
5 redir @stripExtensions {re.strip.1} 301
6 try_files {path} {path}/ {path}.html
7}
Since the syntax of the Caddyfiles can be a bit odd we can break down this block piece by piece:
-
We create a site block for http://localhost:9090 with the shortcut syntax
:9090. Within a site block we will configure Caddy on how to behave when requests that are aimed at this site are recieved. -
The
rootdirective will "match"*which means anything in the dist directory. -
The
fileserverdirective which takes an index option indicating that the index for the site will be the root level index.html -
The final three lines are all a helper to make URLs that end in ".html" work with just their file names without thr extension. So for example: blog.com/post.html could be accessed as just blog.com/post.
-
We start with a "named matcher" which matches two portions of a URL the first portion is anything before a ".html" and the second is just the "html" portion of the path.
-
We then perform a redirect on anything which matches out matcher. Note the matcher will only return true if the requested URL ends in .html.
-
Finally, we have a
try_filesdirective which behaves kind of cleverly when you request an image from the site, say assets/pics/pic.png the{path}variable is that value and this will be attempted and the picture if it exists will be returned. Next, if you request a directory using the{path}/syntax the same is true. Finally, the "trick" if no other match is found becuase let's say we redirected a request for /post/blog-1.html to just /post/blog-1 then Caddy will "try" /post/blog-1.html for the{path}/post/blog-1.
-
This Caddyfile will only serve local traffic, but apart from changing the site block from locahost to some real site this is all the configuration that's required. This post will not be going over how to host this blog. It is enough for now to know how to make a proper webserver. If you wanted a quick description for exposing this site to the internet it would go something like this.
-
Change your site block to run the server on port
:80 -
Configure your router to point incoming requests to itself to port
:80of the machine running the server -
Visit your router's IP address from anywhere in the world
-
Enjoy being your own cloud provider
Like I said though we aren't going to to into that topic here.
What other features might be added?
As I mentioned at the beginning of this post. This post is virtually just a discssion I had with myself about trying to create a blog in a very friendly way. The things I recall wanting were as follows:
-
I didn't want to pay any money to anyone to get my ideas online (self-hosted)
-
I wanted to enjoy my wiriting experience (MarkText)
-
I wanted to have a nice looking blog with very little technical overhead (Go templates, Goldmark, and Less.go + Concrete CSS)
With these requirements as I was able to make an MVP of a blog that I wouldn't mind using for a long time to come and wouldn't go stale in my brain if I walkled away from it for a year or two. If you reread this post future-Matt I bet you feel like you get how it all works and is delivered. Love you dawg! That being said things that I might add in the future include:
-
Tagging and being able to search posts
-
Comments maybe...
I think the goal of the post has been accomplished. If you read it you know the basics of creating your own blog.