Comments on an nth Redesign and a Modern Static Web Stack
One of the changes
I
suggested might be forthcoming was, of course, a redesign. I realize that this
is essentially impossible to see if you haven’t visited the site before (and
although there is the
Wayback
Machine, it does not appear to have pulled the old css file, which makes sense
given that I used to append a ?<date-time-slug> to it because of the way the
old server cached that file).
The previous design was all monospace font, all the same size, with a color for
links and a color for <code> and underlines for emphasis. Not really pretty,
but it was… a decision that I had made.
Now, though still — frankly — austere, it at least is hopefully more legible due to the more readable font and multiply-sized headings, actual italics for emphasis, and so on. How did we get here.
No Theme for Me
As noted in the colophon, this is a Jekyll-based static site. Typically, Jekyll sites use themes that you install via a Ruby Gem. You do, it turns out, need a theme, for when I tried to remove the default theme Gem, Jekyll would no longer build. But because I am grumpy I override the default theme more or less entirely, and as such, I more or less design the actual semantic layout (i.e., the bones of the thing) and the styles (the window-dressing) "from scratch." This is not to say that this is impressive by any means, but only to say that, as someone who is not really a designer in any sense of the word, I am opening myself up to a mountain of small and large decisions when I design, or redesign, this little blog of mine.
The semantic layout I feel very good about; I work in publishing, spend (too much) time thinking about Semantic HTML, and generally feel rather on top of modern HTML(5).[1]
The design, less so. That is probably why it is minimal, in addition to my preferences for designs that highlight the text(s) themselves (at least when, as here, the text is more or less the entire point). If it is minimal I have to make fewer decisions. This is good. Because although, as below, I did, and still do, have a reasonable handle on formerly-modern and currently-modern CSS,[2] what exactly to do with all that knowledge (or at least with the many tabs open to the Mozilla Developer Docs) is a separate matter entirely.
Modern CSS, Kind Of
I recently completed an "engineering mentorship" at work on React, which is a fancy-pants web framework that, according to a friend, "gives you a lot of rope with which to hang yourself," and is, alas, an industry standard. But that’s somewhat neither here nor there; what is relevant here is that my old friend Sass, a CSS preprocessor,[3] may no longer be needed! Basically with react you can inject CSS right into the JavaScript, and some of the things that you used to need something like Sass to do (variables, nesting, combining multiple smaller files into one file that gets sent over the wire). You still can’t do the file combination natively (at least not without some other system like React to do that for you), but we now have things like variables, nesting, and light-dark themes out of the box, and so I was pretty existed to use them! But.
Jekyll comes with Sass baked into it, so, like, you may as well use it.
To get into the weeds, beyond being able to combine files via @import
directives, I personally find the variable syntax much nicer. To wit:
// sass
$light-color: #282c34;
$light-bg: #fafafa;
// modern css
:root {
--light-color: #282c34;
--light-bg: #fafafa;
}
Of course really it’s in the calling code where it’s nicer:
// sass
body {
background-color: $bg-color;
color: $text-color;
}
// modern css
body {
background-color: var(--bg-color);
color: var(--text-color);
}
Is the difference slight? Yes. It is less characters to type? Also yes. And it makes it somewhat easier to do convoluted things like the following:
// light mode
$primary-light: #0d45bf;
$secondary-light: #a626a4;
// dark mode
$primary-dark: #90b3ff;
$secondary-dark: #fc9cfa;
// just call the one variable instead of light-dark-ing all the time
$primary: light-dark($primary-light, $primary-dark);
$secondary: light-dark($secondary-light, $secondary-dark);
So I used some modern CSS in the redesign, but still fell back largely on Sass, which I feel perfectly fine about.
Jekyll Metadata, Or: I Do Not Keep My Frameworks Sufficiently Up-To-Date
There was a fun bug I ran into when trying to get the sidebar over there to display categories, as well as the new filters on the posts archive page:
Liquid Exception: undefined method `each' for "bullshit, code":String p.data[post_attr]&.each { |t| hash[t] << p } ^^^^^^ in posts/index.html
Error: undefined method `each' for "bullshit, code":String p.data[post_attr]&.each { |t| hash[t] << p } ^^^^^^
Error: Run jekyll build --trace for more information.
After much frustrated googling, I turned to Gemini (the free version, for I am
both cheap and not interested in spending money on LLMs), which
informed me that although the
Jekyll documentation seems
to indicate that categories: bullshit, code should work, a change to the
template processor (called "Liquid") means that it instead needs to be formatted
as an actual array, i.e., categories: [bullshit, code], or in asciidoc, as in
this post, :page-categories: [bullshit, code].
Minor thing, but the point is that as a part of this "redesign" I upgraded the
version of Jekyll I was running on to the current version, which at the time of
writing is 4.2.2. Cool, fine. The earliest commit I can find has me starting
at 3.8.6. But I’m pretty sure I moved the repo at some point. Based on when I
actually started the blog in May 2017, it would have been version 3.4.3. So at
least one major version upgrade. No wonder things that used to work simply
stopped working.
Anyway. Now the liquid to make the little categories stuff work, works:
{% for category in post.categories %}
<li>
<a href="{{site.baseurl}}/posts/?filter={{ category }}">{{category}}</a>
</li>
{% endfor %}
Beautiful.
Lights, Vanilla JS
Now as mentioned, too many times, this is a static site, which means that it is just text files (HTML files) and a couple images on a "dumb" server that just serves them up; there is no back-end logic doing anything at all. So any interactivity I may want I need to implement in the client side, i.e., in your browser with JavaScript.[4] There are essentially two things that I thought were worth adding: a way to choose a light or dark mode, and a better way to filter the posts archive, now that I may or may not be using "categories" more appropriately.[5]
And while, yes, it would be nice to use some fancy JS framework like React, the great thing about the modern web is that, not unlike CSS, "vanilla" or plain JavaScript does so much more than it used to, and is, generally, nearly even pleasant to work with (although I’m still probably partial to TypeScript for larger and more complex projects, even though that itself can be a pain in the ass).
So what did we do.
Lights
This ended up being simple enough, sort of. The actual code that does the switching is very straightforward:
const lightsToggle = document.querySelector("#lights-toggle"); // (1)
lightsToggle.addEventListener('click', () => { // (2)
const isDark = getComputedStyle(document.documentElement).colorScheme === 'dark';
const nextTheme = isDark ? 'light' : 'dark'; // (3)
document.documentElement.setAttribute('data-theme', nextTheme);
localStorage.setItem('theme', nextTheme); (4)
})
-
We get the
idof the link up yonder in the header there. -
We add an event listener for when that link is clicked.
-
We use a fancy ternary operator to switch the "next theme.
-
Here’s where the trouble starts.
So, I think it’s cool that we can use local storage now. It’s simple, easy, and a good way to remember what "mode" you want when you visit a site again (because you’ll come back to read more, right? Right?).
The problem is that, sticking the code that checks the localStorage for any
prior preferences where you’d normally stick a script, i.e., at the end of a
page (or maybe this is too "old web," and we don’t do that anymore? I didn’t
bother to check), you get a weird flash, because your browser tries to render
things as it reads them, i.e., it’ll show the page as it gets it, not once it’s
gotten all the content, so if the script is at the end, the page is shown before
it knows whether to keep the lights on or not.
The very dumb, I-didn’t-bother-thinking-of-a-"right"-way-to-do-this solution was
to just stick that code in the <head> tag. I won’t show you that because it’s
ugly and all in one string to make the page load just that much faster, but
you can "view source" if you must. So anyway, there is a light and dark mode now
that you can switch at will.[6]
Filters
This is equally simple as the lights, thing, so I’ll just show and annotate the code:
const queryString = window.location.search; // (1)
const urlParams = new URLSearchParams(queryString);
const queryFilter = urlParams.get("filter"); // (2)
const posts = document.querySelectorAll('li[data-categories]'); // (3)
if (queryFilter) { // (4)
filterPosts(queryFilter)
}
function filterPosts(category) {
posts.forEach(post => { // (5)
const categories = post.dataset.categories.split(" ");
if (categories.includes(category) || category === "all") { // (6)
post.style.display = "block"
} else { // (7)
post.style.display = "none"
}
})
}
-
We get the URL to see if there were any query strings. We send you to the same
posts/page when you click on, for example,codeover there in this page’s "categories", but with a?filter=codequery string. Otherwise that won’t be present. -
But, you know, if it is present, we want to know what we’re filtering for, in the case mentioned above, that would be
"code". -
Here we’re gathering all the posts that have a
data-categoriesattribute. All posts have adata-categoriesattribute. -
If there is a filter, we run the
filterPostsfunction to filter for that filter. -
Onto the function itself, we basically just iterate through the list of posts we collected up in
(3)above. -
If they have the category we’re filtering for, we set the display style to
block(the default for an<li>element). -
If they do not have the relevant category in their
data-categoriesattribute, we set the display style tonone, thus making it "disappear" off the page as seen from your browser.
Simple enough.
Does It Look Any Better?
Beauty is, they say, in the eye of the beholder. I think, at least, it does not look worse.
Partially it was a good exercise for myself to think about things like what makes a blog something one wants to read, what kinds of functions do I like, how does one these days marry a small amount of interactivity with static sites in a way that is both nice while nevertheless keeping to the minimalist ethos of a static site?
As with all side projects, the work was a little hurried and harried, but I’m pretty happy with the result in the end, and feel fine about it, just fine.
I mean: the point is the writing anyway, right? Right?