I did a bunch of more updates to this website, including adding a dark mode. Most of the other changes are either invisible or barely noticeable. That's OK. My several visitors will appreciate them. Or at least I'll appreciate them.
Dark mode and syntax highlighting
I added a dark mode using CSS, with
prefers-color-scheme
(widely availabe as of 2020). I switched syntax highlighting (using
Pygments) from inline styles to CSS classes, which use
different theme colors for light and dark modes. This change will cause the RSS
feed to not have syntax highlighting (since RSS readers shouldn't interpret CSS
classes), whereas before some RSS readers may have allowed the style elements.
I think RSS feeds aren't supposed to be styled, so that's probably OK.
RSS feed
I reduced the size of the RSS feed by only including summaries for old posts, rather than the full content. Also, if I write too many more posts, the RSS feed will stop including the oldest posts. I really prefer when RSS feeds include the full content with each post, but I think that's less useful for very old posts.
Markdown renderer
Many of these pages are generated from Markdown. I added support for it in 2014 using Python-Markdown, which was probably the obvious choice. Since then, some folks standardized Markdown as CommonMark, which is used widely in VS Code and GitHub (GitHub Flavored Markdown extends CommonMark), among others. The differences between Python-Markdown and CommonMark are small but annoying, so I've switched to markdown-it-py. Since this website is generated as static pages, it was relatively easy to diff the HTML and RSS across this change for manual review.
Mako parsing conflicts
This website contains both HTML and Markdown pages and templates. The Markdown pages are processed through the Mako templating engine and then the Markdown renderer. The HTML pages are just processed through Mako. Most of what I use Mako for is basic variable substitution, loops, and if statements. It also allows running full Python code inside templates, which I like. (I write the templates, so I can live with myself abusing them. I wouldn't want this in a larger team project.)
Mako's syntax is usually not in conflict with Markdown or HTML, but I've found three exceptions where there are ambiguities:
${variable}
Mako interprets the occasional ${variable}
in a shell
script (or this paragraph) as a Mako variable substitution. Sometimes I want
one behavior (Mako) and sometimes I want the other (literal). Fortunately, when
this happens, it's unlikely that the variable exists in Mako's scope, so it
usually causes a build error.
I haven't found an easy way to escape ${variable}
locally.
The best approach is to use
<%text>
to opt out of Mako for an entire section. Otherwise, using $
is an
option in HTML, but not within a `code block`
in Markdown
(see CommonMark example).
Another approach is to define a Mako variable called dollar
with a value of
$
, then write ${dollar}{variable}
.
Trailing backslash
Mako consumes a trailing backslash at the end of a line and merges the line
below it. Many programming languages use this syntax too. It's easy to forget
about this and get poor rendering of code blocks. Similar to the dollar issue,
<%text>
is a good way to disable it, or you can use \
in HTML but not Markdown, or you can define a backslash variable.
I found that I was only using the trailing backslash feature of Mako in one
place, so I wanted to turn it off and have a default that won't keep biting me.
Unfortunately, substituting \\
for \
at the end of the line doesn't help,
as consuming the second backslash is
baked into Mako's lexer.
Instead, I added a workaround that replaces a trailing \
with a unique string
before Mako runs and then replaces that string back to a \
after Mako runs. I
think that should in most contexts, including code and non-code and
Mako-enabled and Mako-disabled regions. This does completely prevent using a
trailing backslash in Mako and Mako Python blocks
(<% ... %>
), but those are usually unnecessary. If I forget
and try to use a trailing backslash in Mako, it's likely to cause a build
error, which is the behavior I want.
Leading hashes
Mako interprets ##
at the start of a line as a comment, which Markdown uses
for <h2>
. I found out about this one almost immediately after starting to use
Markdown on this site. I don't need that style of Mako comments, so I wanted to
disable them.
I've had a workaround in place for a while that pre-processed the input and
injected <h2>
tags before Mako ran. That worked well for me, but in theory it
would break ##
if used in code blocks or in Mako Python blocks
(<% ... %>
).
I've updated my workaround to replace a leading ##
with #
followed by a
unique string before Mako runs, and then replace it back after Mako runs. I
think that'll work in all contexts.
Social sharing metadata
I added some meta tags for social media. The
Open Graph Protocol
metadata is used by Meta,
LinkedIn,
and others, but that site is
archived on GitHub.
Facebook's page is
another resource. Twitter has
its own metadata
but will largely use Open Graph metadata if available.
fediverse:creator
is used on Mastodon.
Note: there's a technical distinction between:
<meta name="..." content="...">
and
<meta property="..." content="...">
where some fields want one or the other.
This metadata required some code generator changes. Similar to the page title,
the metadata is set above where the main page content is rendered. That
information has to be "pushed up" to be available outside the main content.
Also, the pages had to be made aware of their URLs for og:url
.
The OpenGraph Protocol requires og:image
to be set, yet most of my pages
don't have an image. I created a larger version of the
favicon as a default.
Here's an example of a link to this blog post on Mastodon:
Favicon
Since I redrew the favicon as an SVG
for the social sharing image, I also added the SVG for browsers that support
it. I created a dark mode variant, but I don't think mainstream browsers
support that yet (see the links from
this issue). Firefox seems to
ignore the prefers-color-scheme
and take the last SVG. Other browsers may
default to the first SVG, so I have sandwiched the dark favicon declaration in
between two light ones:
<link rel="icon" type="image/svg+xml" sizes="any"
media="(prefers-color-scheme: light)" href="favicon-light.svg" />
<link rel="icon" type="image/svg+xml" sizes="any"
media="(prefers-color-scheme: dark)" href="favicon-dark.svg" />
<link rel="icon" type="image/svg+xml" sizes="any"
media="(prefers-color-scheme: light)" href="favicon-light.svg" />
<link rel="icon" type="image/png" sizes="16x16"
href="favicon.png" />
Hopefully dark mode favicons will start working in more browsers over the next few years.
Heading links
I added links to many headings to make sharing URLs that jump to a place on the
page easier. On mobile, you can see an example #
link above this paragraph.
On desktop, hover over the heading to see it.
I chose not to make the entire heading a link because sometimes headings do
legitimately link to things, and that could get confusing. I like that the #
is hidden until hovering on desktop, but hovering is not really an available
gesture on touch-screens.
I didn't want the RSS feed to include these links because they might not work,
so the <a>
tags have no content in the HTML, and CSS fills in the #
. Since
RSS feeds don't use the CSS, they will just get an invisible empty link.
I added these links manually to a bunch of headings. I didn't see a good way to
automate that, since the value of the id
attribute is worth customizing and
the placement would not be obvious to an algorithm. I ended up adding a bunch
of
<section>
elements
(2015) for this.
HTML and CSS updates
I updated and modernized some HTML and CSS, including use of these newer features:
var()
and custom properties (2017) are useful for defining theme colors for light/dark modes.:has()
(2023) was useful in styling just the<pre>
elements that contain<code>
.:is()
(2021) helped save some duplication for styles related toh1
-h6
.:not()
(2021) helped keep some styles self-contained.box-sizing: border-box
(2015) is old news but I started this website in 2007.
For testing dark mode and the styling updates, it was convenient to have the blog index page render the entire contents of the blog on one page that I could scroll through quickly.
I tried to keep the website usable with older phones. For example, I assume iOS users have Safari 15 but not necessarily Safari 16 or newer yet. These are some CSS features that look nice but that I've avoided for now until they're more widely available:
@scope
(not available yet in Firefox) would be helpful to create a namespace for Pygments' classes.- nesting (2023) and
&
nesting selector (2023) would improve readability significantly. - Range comparisons in
@media
queries (2023) would be a little easier to read. light-dark
(2024) might avoid some variable definitions for dark mode.