The code snippets on this site are the only evidence of CSS on this site at present. For example:

JavaScriptconsole.log("Hello, world!")

This is thanks to the Eleventy plugin for syntax highlighting using PrismJS. Now, the plugin leaves it up to you to add the CSS for whichever theme you want. I added prism-base16-ateliersulphurpool.light.css as the default and prism-a11y-dark.css for dark mode. I didn’t want this CSS to be included on pages where there was no code to highlight, though.

Doing it manually

The first and simplest option is to add a value to the front matter of any entry that needs highlighting, e.g.:

title: "A code sample"
needsHighlighting: yes

Then you could check for this value in your template.

I didn’t like this approach. I’d much rather spend a week setting up complicated automation than spend an extra five seconds on each entry. It just makes more sense to me.

Automatically adding a per-entry value

Eleventy’s item data structure has no obvious extension points. Even assuming I rolled up my sleeves and dug into its API to replace the item with some sort of custom code, I would then need to set the property based on the rendered entry (and whether or not it contained any code elements). I could already see this getting far too complicated.

Handling it in the template

Thinking of the rendered entry made me realize there was one place where I had complete control over how it was postprocessed: the template. What I needed to do was take the content, check for any code elements inside it, and include the CSS only if required. I wasn’t about to parse HTML using regexes, so I went looking for a fast HTML parser. node-html-parser looked promising, but it failed to parse the snippets correctly and its selector engine wasn’t fully-featured enough to work around it. Continuing my search, I was reminded of my old, reliable friend cheerio. Of course, when I tried to use these packages directly in the Pug template, I got errors about require not being defined, so I had to create a new JavaScript data file exporting a single function:

JavaScript// data/hasCode.js

const cheerio = require("cheerio");

module.exports = () => {
  return (content) => {
    const $ = cheerio.load(`<div>${content}</div>`);
    return $("code").length > 0;

And now I could use this in the template:

    if hasCode(content)
      link(rel="stylesheet" href="/assets/css/prism-base16-ateliersulphurpool.light.css")
      link(rel="stylesheet" href="/assets/css/prism-a11y-dark.css" media="(prefers-color-scheme: dark)")

Et voilà ! The CSS would be included for pages that needed syntax highlighting (such as this one) but not for others (such as my introductory entry).

Future improvements

This works fine for one or two pages, and would probably scale very far beyond that, but it’s fundamentally wasteful to have Cheerio parse the entire contents of the page into a DOM structure only to see whether there’s a <code> element in there somewhere. Two possible avenues of exploration are streaming HTML parsers (which can return as soon as a <code> is encountered) and hooking into the Markdown processor itself to avoid needing to post-process the entry at all.

Next in series: (#3 in Colophon: Finding A Place For My Head)