Kitchen Sink: Every Element This Site Can Render

One page that uses every renderer in the pipeline: KaTeX math, highlighted code, footnotes, tables, and the whole prose scale. Handy for eyeballing typography after touching the CSS.

This page exists to be looked at, not read. It puts one of everything the templates know how to render on a single screen, so that after a change to the type scale, the colour tokens, or the font loading, there is one URL that will show the damage.

Everything below is live output from the same pipeline that renders real posts: Goldmark for Markdown, Chroma for syntax highlighting, and KaTeX rendered at build time — no math JavaScript is shipped to your browser.1

Prose and inline marks

Running text sets in Lora at 17px across a 720px measure. The face carries italics, bold, and bold italics, plus inline code, strikethrough, and an internal link alongside an external one — the latter picks up target="_blank" automatically from the link render hook.

The fonts ship as two unicode-range slices, and most accents live in the first one: the latin file covers U+0000–00FF plus a few strays, so Gödel, Poincaré, naïve, Ångström and Œuvre all come from the same file as the rest of this sentence. Only a handful of characters fall through to the latin-ext file — Erdős (ő, U+0151) and Łukasiewicz (Ł, U+0141) are the ones to watch. If those two render in a visibly different face from the names beside them, the extended subset failed to load; the others would not tell you anything either way.

A blockquote marks the left edge with the brand colour and drops the text to the muted foreground. It should still read as part of the article, not as a callout box bolted onto it.

Three kinds of list, one after another:

  • Unordered, first item
  • Second item, with a nested level below it
    • Nested item, which sets one step tighter
    • Another nested item
  • Third item
  1. Ordered lists number in the prose face
  2. Second step
  3. Third step

Term-style lists work through plain paragraphs and bold leads:

Blocking period — how long the browser hides text while it waits for a webfont. Swap period — how long it will still replace the fallback once the real face arrives.


Mathematics

Inline math sits on the text baseline: the golden ratio satisfies , and for any we have where .

Display math gets its own block and centres:

Multi-line alignment, sums, and products:

Matrices, cases, and the calligraphic and blackboard faces — these pull in KaTeX’s AMS and Caligraphic files, which are not preloaded, so they are the slowest glyphs on the page:

A softmax over logits, the one formula every ML post eventually needs:

Code

Python, with a language chip in the corner and a copy button on hover:

from dataclasses import dataclass


@dataclass(frozen=True)
class FontFace:
    family: str
    weight: tuple[int, int]
    display: str = "swap"

    def css(self) -> str:
        lo, hi = self.weight
        return (
            f"@font-face{{font-family:{self.family!r};"
            f"font-weight:{lo} {hi};font-display:{self.display}}}"
        )


faces = [FontFace("Lora", (400, 700)), FontFace("Raleway", (100, 900))]
print("\n".join(f.css() for f in faces))

Go, to check a second lexer’s colour assignments:

package main

import "fmt"

// preload reports whether a face is worth a top-priority hint: only if the
// page actually paints glyphs in it.
func preload(face string, used map[string]int) bool {
    return used[face] > 0
}

func main() {
    used := map[string]int{"Lora": 3926, "Raleway": 0}
    for _, face := range []string{"Lora", "Raleway"} {
        fmt.Printf("%-8s used=%-5d preload=%v\n", face, used[face], preload(face, used))
    }
}

A shell session, where the prompt and comments carry most of the colour:

# Rebuild and check what the page actually shipped
hugo --gc --minify --baseURL "https://hxiang-sun.com/"
find .hugo/public -name '*.woff2' -exec ls -la {} \; | awk '{print $5/1024 "KB", $9}'

And a short config sample, since JSON and YAML land in posts often enough:

{
  "fonts": { "lora": "400 700", "raleway": "100 900" },
  "preload": ["lora-latin.woff2"],
  "display": "swap"
}

guessSyntax: true is set, so a fence with no language does not stay unlabelled — Chroma guesses, and the corner chip ends up reading whatever it guessed. Tag the fence text when the block is output rather than source:

$ curl -sI https://hxiang-sun.com/ | grep -i cache-control
cache-control: public, max-age=0, must-revalidate, s-maxage=600

Tables

Tables stretch to the measure and rule every cell:

FaceSubsetAxisSizePreloaded
Loralatinwght 400–70037 KBon posts
Loralatin-extwght 400–70020 KBno
Lora italiclatinwght 400–70040 KBno
Ralewaylatinwght 100–90042 KBoff posts
Ralewaylatin-extwght 100–90026 KBno

Numbers set with lining figures, so a column of them stays even: 1234567890.

Raw HTML

unsafe: true is on in the Goldmark renderer, so hand-written HTML passes through — useful for figures that need a caption:

CSS 480mspaint 540msLora 565ms
A post load on throttled 4G. The gap between first paint and the webfont arriving is the flash — about 25ms here, which is short enough not to be seen.

Heading depth

The table of contents indexes h2 only, so the levels below exist to be looked at rather than navigated.

Third level

Sets smaller than an h2 but keeps the serif face and the same tight leading. Body copy under it stays at the normal measure.

Fourth level

Smaller again, still a heading rather than emphasised prose.

Fifth level becomes a quiet label

Small caps and letter-spacing take over at this depth, which is why it reads as a label instead of a title.

Sixth level matches it

The last rung of the ladder.

Footnotes and the end

Footnotes collect at the bottom with a rule above them and a back-reference arrow on each.2 They renumber automatically, so the order they are written in does not matter.3


  1. The render-passthrough hook calls transform.ToMath during hugo, so the finished HTML already contains the formula markup. Only KaTeX’s stylesheet and its woff2 faces load at runtime, and only on pages that set math: true in front matter. ↩︎

  2. A second note, to check that the list markers survive the global <ol> reset that strips numbering everywhere else on the site. ↩︎

  3. A third, with inline code and italics inside it, since footnote text sets one step down and the smaller size is where a bad fallback shows first. ↩︎