Well here we are; the first blog post on this blog. And it is about the blog itself – kinda self centered but I wanna write down why I did what I did here :)
In creating a blog I wanted to have the following features:
- Easier writing in a markup language other than HTML while keeping a rich featureset such as code blocks and linkable sections
- Automatic article detection from the filesystem
- Automatic generation of index pages that also allows article filtering and maybe even search
- A link scheme that is flexible and stable
Markup Language #
As a markup language, the choice was not too difficult. I have written markdown everywhere for quite some time so this was my preferred language. The implementation I found to be quite useful is python-markdown because the webserver for my homepage is written in python anyway and that library appears to be stable in addition to being quite flexible in terms of markdown extensions12. It is notably not CommonMark but that’s okay for me.
The API is also not too difficult to use as you can see by the example below.
I have also added some extensions for a richer feature-set such as footnotes, code highlighting, tables and other features I tend to use in other markdown environments.
The rendered body_html
content is then included in the individual article page via Jinja2.
@dataclass
class Article:
body: str
...
@property
def body_html(self) -> str:
return markdown.markdown(
text=self.body,
extensions=[ "fenced_code", "footnotes", "tables", "sane_lists", "smarty", "codehilite", BlogMdExt() ],
)
Custom Extensions
The rendering behavior of python-markdown
was not quite what I needed.
For this reason I created three custom extensions using the provided extension API3.
The first one is very basic.
It simply converts the footnote container generated by the builtin footnotes
extension from a <div>
tag to an <aside>
one.
The second and third extensions work together closely to wrap content into <section>
tags and make them linkable.
The little “#” symbols you see to the right of headers are generated by these extension.
Since this blogs source source is available, you can look at its implementation on github.com/ftsell/homepage:src/homepage/blog.py.
Frontmatter
As you can see at the top of this page, there is also some metadata associated with this post such as its creation and last edit date. This information is kept as part of the markdown file backing the content. Specifically, each markdown file contains a frontmatter section with machine-readable data. I extract that data at runtime using the python-frontmatter package so that it is available for sorting, filtering and rendering.
Article Organisation & Linking #
As mentioned in the introduction, I wanted a way to automatically detect articles I write without having to register them in the code of my webserver. This hyperlinks which are synthesized from this process should also follow a scheme that I can keep compatible for a long time regardless of changes to my website or blogging software4.
What I chose to do is this:
- Create a directory as part of this websites source code called
blog
. - Write articles in that directory, naming the files e.g.
001-creating-a-blog.md
. - At runtime, read that directories content to create an index of written articles.
The number contained at the start of the file name is considered an articles ID and the one globally stable identifier of that article.
Any characters contained afterwards (
-creating-a-blog.md
in this case) are just decoration so that I can recognize source files. - When receiving an HTTP request for an article e.g.
/blog/001-creating-a-blog.html
, the article ID is similarly extracted, looked up from the index and the rendered article content is returned.
The webserver also redirects requests to any article to a canonical url that is deterministically generated from an articles ID and title.
This way, articles are consistently identified only by a numeric ID and everything else is just there to help identifications. Those numeric IDs are very easy to work with and can hopefully be kept stable. If an articles title changes, the canonical URL does too but old URLs continue to work.
Automatic Index Pages #
For discoverability and navigation, the blog naturally also needs an index page. As of when this post was written, the index page is very bare bones and does not support any explicit filtering or sorting but that is planned.
It is implemented using a Jinja2 template that contains rules for rendering the article index described in Article Organisation & Linking. Before rendering though, the index is sorted by creation date of the contained articles.