Table of contents
History of development
I have been a software developer for a long time already, but I haven’t always had my own website. A year ago, when I had developed half of my projects already (lcalc, FCDL and stm32-morse-device), I recognised a problem: nobody knew about my achievements, my projects nor my knowledge. I simply didn’t have familiar people that were as competent in computers and development as me. Besides, my projects always could use a little bit more of popularity.
That’s when the idea to develop my own website was formed. First of all, I just wanted to write down my skills and projects in this website, just so people would know that a genius programmer’s behind all this. Also, I thought it would be nice to aggregate all of my projects and contact info in one place, in my own little corner of the Internet.
And so, initial version of lch361.net was published. It is not being hosted today, because new version replaced it, but here is an old source code, still available for building and viewing for those who are interested. In my opinion, that website had some major drawbacks:
Navigating projects was hard. They were strictly divided by 5 categories, there was no total list of every project, so it was harder to find specific projects by their name.
Also, because projects were only divided by categories, it was hard to expand for the new projects. What happens if I start exploring and boldly coding in entirely new area that was never tried by me? I would have to draw a new icon, write new description for new category, and then put there my first project.
In general, I wasn’t really satisfied with the information presented on my website. I always felt like I have far more stories to tell, more knowledge to share. Aggregating the projects is simple, but I always knew about every project a little bit more than shown in their respective homepages or READMEs. So why not try to write proper articles next time?
The style of that website wasn’t bad, but I recognized that I could do it better.
With these reasons in mind, the idea to develop lch361.net 2.0 was formed. This time, I wanted to not only present my projects and skills, but also write articles about advanced topics, sharing with visitors the most valuable thing in software development — knowledge. Along the way, I also was experimenting with style and interface, improving it for readability, navigation and overall making it more interesting.
This is how lch361.net in its current state was published. There is no stable release for this website, because it is constantly improving, just like me. Today, I am very proud of this work and I’m looking forward to maintain this website for a long time.
Architecture
Website is divided into 5 sections, each of them consists of either one page or a list of pages.
Home — main page of the website. Briefly describes this website’s idea, tells about me and mentions every other section.
FAQ (new) — trivial questions that someone might want to ask me.
Projects — collection of all of my developments, with various links. Migrated from old website, enhanced with detailed descriptions for every listed project.
Articles (new) — technical writings that are not tied to one of my project, although articles and project descriptions can depend on each other.
Presentations (todo) — in the future, when I’ll be making technical presentations and speaking on conferences, then I’ll probably make a new section where these presentations will be archived.
Together, they form the following net:
My website is also designed with one core principle: no JavaScript. There is no interactivity on my websites, just plain HTML & CSS. I don’t think JavaScript is a good idea in general, my website doesn’t need interactivity, and worst of all — I wasn’t going to have performance issues on my website. Every page should load near–instantly (so no JavaScript UI frameworks) and should not be CPU–heavy when viewing (no JavaScript/SVG animations either).
Websites shouldn’t be complicated. They were designed to be simple. Although the majority of the Internet websites nowadays might not agree with me.
Rewriting from Hugo to Zola
Old and new versions have one crucial difference: old website was written with Hugo, new website is being developed with Zola.
First of all, Hugo and Zola are pretty similar:
both are command line programs;
both are static site generators (further, SSG);
both have extremely fast times (around a few milliseconds) to build a website and extensively use caching;
both can serve the website while rebuilding it on any change;
both are using Markdown with shortcodes for content and Sass for stylesheets;
However, there are also some crucial differences between these. Feel free to check Hugo and Zola documentation to see the differences and choose your favourite flavor of SSG12. You can also look into the new and old source code of my website to see practical differences in implementing the website with both SSGs. The table below is based on the documentation of SSGs paired with my experience of development two versions of the website.Criteria Hugo Zola Inserting template instructions in HTML Uses {{
and }}
to insert both template expressions and statements, making the syntax simpler.Uses {{
and }}
to insert template expressions and {%
and %}
to insert statements, visually separating template instructions that produce content from those that do not.Control structures Has if and for loops. Also has a very convenient with
keyword, allowing to evaluate an expression with certain context with a very compact syntax.Has if and for loops. Doesn’t have a with
keyword, forcing the usage of if
instructions, which makes the syntax simpler.Implicitness Many things in Hugo’s functionality are implicit. For example: layouts for every page follow a strict order of lookup; context .
can have different value in different parts of the program; etc.Many things in Zola are explicit. For example: templates for every page can be set explicitly in the frontmatter; template context never changes when evaluating the template; etc. Amount of functionality Can do a ridiculous amount of things not only related to website building: everything that Zola can + Git, other markup languages and many other things. Can do a little amount of things just needed for creating a website: Markdown, HTML, CSS/Sass and static assets. Documentation Extremely large due to numerous features Hugo has. It’s better to use a built–in search index if you need to find something, especially if you want to develop the website quickly. Small and simple, just as Zola itself. The search index is also present, but you can easily scroll through every chapter in table of contents and figure out how the entire SSG works. Site configuration Very complex. The documentation for simple hugo.toml
file consists of numerous chapters.Simple. The documentation for simple config.toml
file consists of few chapters.
In summary, I personally prefer Zola due to its minimalistic and explicit nature. It was far more intuitive to develop the website with Zola, and I do not regret a single line of HTML written in Zola instead of Hugo. Unless…
Extending the minimalistic functionality of Zola
As I was saying, Zola is a minimalistic SSG. It has just enough features to develop a website with static content, and nothing more. However, there are some interesting ways to add new functionality to site generation process, which the next subsections are all about.
If Zola doesn’t have the piece of functionality you want, it’s not a reason to give up on your project; it’s the reason to get creative.
Collecting Git information
Zola doesn’t have, for example, GitInfo
like in Hugo — an object that has an information about last Git commit that modified a certain page1. Unfortunate, because it would make implementation of Git commits for all pages on my website straightforward.
On the other hand, Zola has one mighty function — load_data
. That’s right, Zola is capable of reading external input from files, and even supports some common data formats, like JSON.
Lastly, we can always resort to generate the input for load_data
via scripts or external programs. In my project, I used Python scripts all the way, because it’s still my favourite scripting language for general purpose. My task was to write a script that would present Git commit info in a JSON.
Thus, if we define script.py
as our hypothetical script for this task, and data.json
as its output, data should flow in our build process like this:
That’s how the update_times.py
was developed. On execution, it puts its output into a file inside the resources
folder — a folder generated specifically for temporary input/output between Zola and Python scripts. According to the data flow diagram above, update_times.py
runs before zola build
.
There weren’t any further modifications to my build system. When developing, I was simply executing update_times.py
before every zola serve
command and it was enough to implement Git info for my pages and to develop further content comfortly. Until…
Custom markup language for math formulas
There is one difficult task in generating webpages: rendering math formulas. I am very unlucky, considering that I needed to write math formulas on my first article.
If you try to search the Internet for a way to render math in HTML, chances are you’ll find MathJax. The good part is that it is:
Beautiful and accessible math in all browsers
The bad part is:
A JavaScript display engine for mathematics that works in all browsers.
JavaScript is forbidden in my website, so I didn’t want to use MathJax and similar technologies anymore.
Reading the web documentation, I found MathML3. Now this was a very promising feature of the web. MathML is also an XML–based markup language, and in this regard, you can compare it to SVG: MathML to mathematical formulas is the same as SVG to images.
So I immediately started using MathML in my first article. However, I recognized that it was not a good idea to write MathML by myself:
memmove ( k )
=
k * 2 m
s
The HTML markup above will be rendered in the following formula:
Just like it’s usually not a good idea to write SVG by hand, MathML also shouldn’t be written, but rather generated from some sort of markup language.
When I was looking for alternative markup languages for math notation, my first choice was Typst’s math mode4. At that time I was writing a lot of documents in Typst, and it was my favourite technology for typography ever. Particularly, I enjoyed how easy and intuitive it was to write formulas in Typst, so I immediately got an idea to try to implement that part of Typst in my website.
To do that, I was planning to use the same power of Python as with Git info, but the implementation would heavily differ: Git info could be calculated for all files ahead of compilation pretty easily, while formulas are engraved inside the content pages, and parsing them out and calculating every formula would be far beyond reasonable difficulty. For the first time, I needed to generate content externally not before, but along the Zola build process.
And so, I consulted the Zola documentation once more2. Looked again at the load_data
documentation and noticed that, aside from the path
parameter, this function has a url
parameter. That’s right, this amazing function can read data from external URLs, and for one URL, a response may be generated dynamically (contrary to static content, where all data is on the disk before site compilation). This is excellent, because it allows us to transform pieces of content found while parsing Markdown pages. On this principle, I started implementing a solution for substituting Typst formulas inside every math
shortcode to MathML.
I implemented a second Python script — an HTTP server that for every GET request responses with converting URL path into MathML. For conversion itself, Pandoc was used — a very powerful utility for converting documents between a myriad of formats. My server processes every GET request in the following fashion:
The only problem left to be dealt with was connecting to the HTTP server while evaluating Zola templates.
HTTP server listens on a random free port (binding to a port 0 gives this effect), so we can’t predict the URL for server ahead of compilation for Zola to use.
Solution: on server initialization, its URL with a port number is written out into a file that Zola will pick up with a good old
load_data
function.Sending arbitrary content taken from Markdown pages via URL paths was deemed unsafe, so URL–safe base64 is used for transferring the data5.
Solution: Zola actually has
base64_encode
function that will help us in this task2. I didn’t see any notes in the documentation stating that it is a URL–safe base64, but in any case, we can alwaysreplace
+
with-
, and/
with_
6.
All problems have been solved, so the server and zola build
just need to be launched in the following fashion:
Wrapping Zola in Python extensions
When I developed the first script, building my project was still fairly easy: just run one script, and then zola build
.
When I made the second script, building the website got a little cumbersome: I needed to always run first script, then open a separate terminal specifically to run Python HTTP server, and then run zola build
.
This is why I made a Python wrapper around Zola: to make the build process once again as simple as calling zola
itself while still having my extensions of functionality.
It goes without saying that this idea works well, but it of course has major drawback for my project: performance degrades. Remember how I said that Zola can build a website in milliseconds? This is no longer the case, because, at least on my machine, the build time for my website increased to 1.5s (when using zola serve
, subsequent rebuilds take ~30ms though due to caching). Probably, this is because:
- Python is not the fastest interpreted language;
- calling
pandoc
on every Typst conversion is kinda expensive; - connecting to a server and exchanging data via HTTP also is an overhead.
I think this problem can be solved by forking Zola and extending its Tera, the template engine. But I don’t really want to do that, because it would take away my beloved minimalism from Zola.
Conclusion
Developing lch361.net was an extremely interesting and experimental process, just as I like to develop software.
This article shows an unusual, creative and even quite fast (in terms of development time) way to extend the functionality of a compiled software with the power of networking and a couple of lines of Python. Extending functionality this way is the main reason why I like minimal and composable programs.