-
More cache busting
I wrote a little bit, a while ago, about my cache busting strategy for this website. The basic idea is to generate a unique identifier at the time of deploy, and then use that unique identifier as a query param for requests to static assets. So that if I haven't deployed in a while, and on the off chance that I have a repeat visitor to this website, that visitor gets the cached version of my (admittedly minimal) CSS and JavaScript. When I re-deploy the website, that visitor's browser will determine that something has changed and reload the files.
Previously, when this website was redeployed using GitHub Actions, I used the ID of the action's run as the unique identifier—on the basis of that identifier being incremented on every deploy.
Now that I deploy manually, I no longer have that identifier. So I use a random hexadecimal string instead:
VERSION=$(openssl rand -hex 4) grep -rl '{|{VERSION}|}' ./templates | xargs sed -i "s/{|{VERSION}|}/$VERSION/g"
This finds all files in my
templates
directory that have the{|{VERSION}|}
string in them (just a distinctive string that I likely won't be using anywhere else)—and then usessed
to replace that string, inline with my random identifier.I suppose I could be doing something clever like hashing each of the files, so the ID doesn't change unless the file does—but this works, and works well for a website of this scale.
-
No build step
Jim Nielsen on the cost of avoiding annoyance, which is itself a commentary on why HTMX (the framework for JS-in-HTML) doesn't have a build step.
A resounding yes! here: if you're using boring technology for your backend and progressively enhancing your frontend, modern browsers have made it extremely easy to skip out on the build step, both for CSS and JavaScript. This website has no build step, and it's got light/dark mode, color/bw mode, a dynamic component for pulling my recent tracks from Last.fm, and a search k-bar.
Heck, my brother's website doesn't even import stylesheets, it just uses Twig templates to dump a template called style.css into a
<style>
tag.Next time you're spinning up a (let's be serious) side project, reconsider whether you even need a build step.
-
Search
Somehow, over the past ~3 years, my life on the computer has become increasingly command-palettet-ised. It started with Sublime Text, back when I was first casting out on my wild software development experiment, and from them, and soon expanded to Alfred and VS Code, and then in latter years onwards to Raycast and any other site or application that will give me a handy autocomplete context menu triggered by a keyboard shortcut. Usually on the web this is
Ctrl/Cmd+K
. One of my greatest computer regrets is that I don’t have the expertise to build an extension for command-palette-ising Mail.app.My website, however: a different story. I’m the slinger of code round here and now I’ve got a handy Ctrl/Cmd+K shortcut for searching the site. I was mostly motivated by the series of poor experiments in listing archived content that I’ve iterated through over the past few months. I haven’t yet found a really good way to return to historical posts, but leveraging CraftCMS’s search has been the least-bad solution so far.
The core of the search is actually pretty simple, testament to the practical minds behind CraftCMS:
Entry::find() ->section("not projects") ->search('"' . $query . '"') ->all()
This code is exposed by an API endpoint that I hit with plain old JavaScript.
Meanwhile, on the frontend, the search field is a
<dialog>
element. The<dialog>
element probably isn’t quite ready for production yet; Safari, in particular, took their sweet time implementing it, so users on Safari 15.3 and lower (which, admittedly, is a minuscule proportion of the web-surfing public) won’t get search. This isn’t a huge concern for me, since my website’s core audience is me, and I’m on Safari 16.3. The upside is that I don’t have to think about trapping focus and keybindings; the downside is that the page in the background is still scrollable.The blob of JSON that the backend returns is then parsed into a
<template>
element and each result appended to the<dialog>
. I do a little manual keybinding to allow users to select items from the list using a keyboard alone. I could probably take a lesson or two from popular accessible autocomplete libraries, but it works for me so far.If you’re using Craft
Remember to refresh your search index; if you make a field (like post body) searchable after those posts have already been written, they won’t just become searchable. You have to bulk resave them to add them to the search index:
php craft resave/entries --update-search-index
Next up
I’m in the evaluation phase at the minute, trying to find the sharp edges of search on my site (and trying to produce more CoNtEnT to feed to it), but a couple of ideas for where to go next with search:
- Testing with VoiceOver is probably the next priority. I think I’ve checked the basic boxes on the accessibility front but I need to do some actual usability tests.
- At the minute, the search is just full-text. Searching by tag might be helpful, but I haven’t run into a search that would have come up in the tags but that hasn’t come up in full-text yet.
- Maybe Google-style keywords could be a fun addition?
-
A couple more words on CSS nesting
So nesting has landed in Safari Technology Preview—well done to the WebKit team and hallelujah!
Except CSS nesting feels like a distinctly 2010s approach to writing styles. I've written a little bit about how I feel about CSS nesting before, but I've got a couple more objections:
It unnecessarily couples components. Consider this (hopefully) representative example:
.card { box-shadow: 0px 2px 4px rgba(0,0,0,0.15); padding: 1rem; & .image { display: block; margin: -1rem; } & .content { margin-top: 1rem; } }
Why is the style of
.image
and.content
coupled to.card
? The.card
element should be responsible for the display of its children; an.image
shouldn’t care about the context in which it’s displayed. This won’t be the only usage of.image
in your stylesheet. Skip the nesting here and keep all of your.image
styles co-located and context-agnostic..card { box-shadow: 0 2px 4px rgba(0,0,0,0.15); } .image { display: block; } .content { padding: 1rem; }
If you do need context-dependent styles, then you’re probably already using something like BEM. Speaking of which: nesting makes it difficult to search for styles in your codebase, especially if you’re using a methodology like BEM.
.card { &__image {} &__content {} }
Developers looking for
.card__image
won’t find what they’re looking for. Prefer instead:.card {} .card__image {}
That’s not to say that I’m not going to use nesting. Of course I’m going to use it: more tools in the toolbelt is always nice. But better approaches to tools, and more _selective use of tools, is always going to trump the brute force of numbers.
Further reading
-
Server-sent events
Ryan Florence posted this tweet the other day, showing real-time updates in Remix, and I thought at first that he'd built some slick websockets stuff into Remix. Alright, that's pretty cool. But it's not websockets.
It's server-sent events.
Websockets are terrific for opening and persisting a connection with a server that you're going to regularly communicate with. You send data up the line, and data comes back down the line to you. It's two-way.
Server-sent events, on the other hand, only send data down to the client (the browser). As the name implies, the server can't receive events—only send them, over an open HTTP connection. But for 80% of use cases, that's fine[1].
The benefit of this is that you don't have to spool up a whole nother server just to handle the websocket connections. It's all just HTTP, using some clever
Content-Type
headers. This makes client-side interactivity based on server-side events trivial to implement—another way that traditional "multipage" applications are closing the gap to single-page applications.
Archive
Posts Stream Books Walks • Clear filters
2022
November 2022
September 2022
August 2022
Currently showing latest 20 posts