Building Static Websites
Last Modified 2024-09-03
This website is static, which means that it consists of a fixed set of HTML, CSS, and JavaScript files. Writing those files by hand can be tedious, so in these notes, I’ll describe the tools I’ve created to automate the process of building dkzhang.com.
There are lots of popular and well-tested tools for creating static websites, like Jekyll and Hugo. I chose to design my own static site generator for a few reasons:
-
I want to understand how my website is built from the ground up. I don’t want to serve a single HTML tag or CSS rule that I can’t explain the purpose of.
-
I’m very particular about typesetting and mathematical typography. I want to precisely control how text and math are rendered on both the screen and the printed page. (Websites that print well on paper are exceedingly rare.)
-
I like programming, and building a static site generator is something of a rite of passage for programming enthusiasts.
All of the tools I’ve developed for building dkzhang.com are open-source and freely available on GitHub in the dzhang314/my-markdown-docs repository. If you’d like to create a similar website, please feel free to use my files as a starting point — but be aware that they are highly tailored to my specific preferences, and you will likely want to adapt them to suit your own taste.
Overview
Every page on my website is generated from a Markdown file in dzhang314/my-markdown-docs. These Markdown files, together with a few HTML/CSS templates and a JS build script, are the only manually-written files I use to create dkzhang.com.
Each page has the same basic structure outlined in dkzhang-template.html. This template has four fields that build.mjs fills in to generate the final HTML file for each page.
{{{STYLE}}}
is replaced with the contents of dkzhang-style.css.{{{NAVBAR}}}
is replaced with the contents of dkzhang-navbar.html.{{{TITLE}}}
is replaced with the title supplied tocompilePage
.{{{CONTENT}}}
is replaced with HTML rendered from the Markdown file supplied tocompilePage
.
Components
Most of the content on this website is built from basic HTML elements, like <h1>
titles, <h2>
headings, <p>
paragraphs, and <ul>
lists. In addition, I use two special components from Bootstrap 5.3. First, a fixed-top
Bootstrap Navbar provides a navigation interface that sticks to the top of the page as you scroll. I apply navbar-expand-md
to collapse the navbar into a hamburger menu on smaller screens and d-print-none
to hide the navbar when printing. Second, I use Bootstrap cards to call attention to important information and set mathematical definitions and proofs apart from surrounding descriptive text.
The structure of the navbar is defined in dkzhang-navbar.html, which I manually edit every time I add a new page. I might automate the process of generating the navbar in the future, but I don’t have enough pages to necessitate that yet.
Style
The style of this website is based on the Bootswatch Litera theme with a few manual tweaks in dkzhang-style.css to adjust navbar and card padding. I apply a max-width
constraint to the main container-lg
to improve legibility and ensure consistent printing. A width of 19cm
fits A4 paper with 1cm margins and US Letter paper with 0.5" margins.
I display text using the STIX fonts, which are optimized for legibility on both screen and print media and are conveniently hosted by Google Fonts. To match the STIX glyphs, I display code using Fira Code at font-size: 1.0rem;
and slightly enlarge the KaTeX math font with .katex { font-size: 1.05em; }
.
Rendering
Every page on my website is written in Markdown, a lightweight markup language which is easier to write by hand than HTML. Using markdown-it, an extensible Markdown-to-HTML renderer, I developed my own dialect of Markdown with two extensions:
-
TeX-style math typesetting with KaTeX, featuring both inline
$...$
\(...\)
and display$$...$$
\[...\]
environments, implemented with custom parsing rules. -
Block syntax for Bootstrap cards using markdown-it-container, which turns this:
:::::: card ::: card-header **Quadratic Formula** ::: ::: card-body **Theorem**: Let $a, b, c \in \C$ be real or complex numbers with $a \ne 0$. For any $x \in \C$, the quadratic equation $ax^2 + bx + c = 0$ is satisfied if and only if \[ x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}. \] ::: ::::::
into this:
Quadratic Formula
Theorem: Let be real or complex numbers with . For any , the quadratic equation is satisfied if and only if
I use katex.renderToString
to render math to static HTML elements that display correctly even when JavaScript is disabled. I also maintain a collection of TeX macros with helpful abbreviations that I’ve developed for real-time note-taking during lectures and seminars.
Writing
My markdown-it rendering pipeline is fast enough to run on every keystroke as I type. I use VSCode with the Live Preview and Run on Save extensions to automatically run build.mjs and view the final rendered webpage in real time while I write. This is especially helpful for typesetting complex equations and fiddling with line breaks.
You can try out my writing setup at dkzhang.com/ZhangEdit, a live in-browser Markdown editor that uses the same markdown-it extensions and CSS styles as my website.
Hosting
This website is hosted on GitHub Pages, which provides free hosting for static websites. When you visit dkzhang.com, GitHub’s servers send you the bundle of generated HTML files that I’ve uploaded to this GitHub repository (which is different from my-markdown-docs).
Since my GitHub username is dzhang314, this website would normally be hosted at the URL dzhang314.github.io. To make it appear at dkzhang.com, I’ve configured a custom domain that I purchased through Squarespace Domains (formerly Google Domains) for $12/year.