Archive for July, 2022


Friday, July 29th, 2022

I got them in white.

KEF LEX II are a pair of bookshelf-sized speakers that I recently bought. Spendy! But nice.

I’ll get to them, but I’m long winded so I gotta do a bunch of blogwords first to set the scene.

I moved to the Mac Studio not long ago. I love it. It’s a great machine. Feels very weird to have gone to a desktop machine, after (counts fingers) all those years. Seems like it is all but assumed developers work from laptops these days. But I was ready. A desktop means to me that I can come to work, sit down, and have a machine 100% ready to go. No plugging anything in whatsoever. Everything is exactly as I left it. I already had a nice monitor that permanently sits on my work desk, after all.

Plus I can commute back and forth to work, usually on a bike, carrying nothing but what fits in my pockets: keys, wallet, and phone.

I still have my “main” Macbook Pro laptop, I still have it at home for computering and for travel. If I had to choose only one, I’d have to choose the laptop.

The Mac Studio has quite a few ports:

That’s a generous amount of ports, coming from a laptop.

So many that I no longer needed my (beloved) CalDigit TS3 Plus Dock. It was almost the perfect amount of ports.

That’s just the back too, there are two more USB-C’s and a SD card slot on the front. Jeez.

Notice no speakers.

I briefly mentioned recently that I was using Apple HomePod Mini’s for computer speakers. I liked the idea at first! You can “pair” two of them for proper stereo sound and connect them as a proper pair. My favorite part is that I could put them anywhere I pleased and there needn’t be any cable directly to the computer. And I could select them like any other speakers, even name them how I liked.

But alas it was just a dream. Two annoying problems:

  1. There was a big delay in selecting them. They never seemed to stay the default speakers. Extra weird on a desktop machine that never moves, right? Re-selecting them would take sometimes 30-seconds to several minutes to connect, and sometimes outright fail. Not tenable.
  2. Even when they are connected, pausing and re-starting sound had a 2-3 second delay. Just a bad feeling to that.

So I got back to desiring a regular set of plugged in speakers.

The Mac Studio does have a little speaker in it, but it is hilariously bad. I actually have a couple of sets of Audioengine A2’s that I very love and considered using here. I’ve used them for various things over the years, including stints as computer speakers. This would have been particularly easy here because of the Aux jack — no dongle or anything needed.

But, as a completely bougie bastard who often makes his desk is 17th hobby, I really didn’t wanna use Aux. If you plug anything into Aux, the sound menu in the main menu bar of macOS will say “External Headphones” instead of “Mac Studio Speakers”. I’m afraid, in my vanity, I could not allow my desktop speakers to be named “headphones”.

So that’s what lead me to the KEF LSX II‘s. Not only are the lovely looking and work well size-wise on my desk, but I watched some pretty glowing reviews. I believe one of the distinguishing features of the model II’s is that it offers USB-C connection, which is exactly what I was looking for. Not a ton of speakers have USB-C ports, in my searching around. By using USB-C…

I think I might have a port fetish.

They also have a million other features I’ll probably never use. Bluetooth of course (ughk), but they’ll take Aux too in case it comes to that, HDMI, Optical, and even Ethernet (not sure how that works).

But the features sprawl from there, including having an iOS app through which you “stream” music to it from the likes of TIDAL, Spotify, and Amazon Music. That’s a big yawn from me. I very much do not want to open your weird little app in order to use my main music service. I’d better be able to connect to the speaker via from the main music service app (which of course you can).

I had a few hiccups worth documenting:

All in all, I’m pretty stoked to welcome them to Team Chris’ Work Desk:

Can’t click on tmux panes in iTerm?

Thursday, July 28th, 2022

We use tmux at CodePen. It’s essentially so we can all run a single terminal command and it’ll run all the scripts and whathaveyou to fire up our dev environment. Then we have a UI for all the different services and whatnot we run. We can and do update it regularly to help us do our jobs.

I wouldn’t say I love it. The designer part of me wishes I could design something that does what it does with some more appealing pixels. But whatever, it’s a decent system for scripting and automating all our stuff.

To change panels in there, you can literally click on them. That’s usually how I roll because I’m a mouse-loving freak. But one day it stopped working and I couldn’t figure out why. Turns out this checkbox in iTerm got unchecked.

Enable Mouse Reporting option in Profiles > Terminal

So yeah if it happens to you it’s probably that.

How to Build a Grayscale Color Palette

Wednesday, July 27th, 2022

If you wanted to be 100% mathematical and consistently even about crafting a grayscale color palette, you could create the colors in a math-friendly color function like HSL, like so:

hsl(0 0% 0%);
hsl(0 0% 10%);
hsl(0 0% 20%);
hsl(0 0% 30%);
hsl(0 0% 40%);
hsl(0 0% 50%);
hsl(0 0% 60%);
hsl(0 0% 70%);
hsl(0 0% 80%);
hsl(0 0% 90%);
hsl(0 0% 100%);

Here’s an example of using those colors as backgrounds:

If you wanted to DRY that up in SCSS, you could do something like:

@for $i from 0 through 10 {
  div:nth-child(#{$i}) {
    background: hsl(0 0 ($i * 10%));

In actuality, you’d probably run that loop just to create variables dynamically then use those variables. We could do that as Sass variables, but you could adapt the above to do that if you wish. Let’s move on to something more useful.

CSS Custom Properties are probably the way to go with color variables, so maybe you’d do:

@for $i from 0 through 10 {
  html {
    --color-#{$i}: hsl(0 0% #{$i * 10%});
  div:nth-child(#{$i}) {
    background: var(--color-#{$i});

That duplicates a bunch of html selectors, but whatever, text compression is good and browsers are fast. Refactor as you please.

If you wanted to sprinkle in a little hue, like have all your grays be just a little green too, because that’s so hot hot hot right now, you could just dial the hue where you want it and turn the saturation up a tiny smidge…

hsl(85 3% 0%)
hsl(85 3% 10%)
hsl(85 3% 20%)

For good measure, RGB makes gray colors when all three of the thingers are the same. So you could do like:

rgb(0 0 0)
rgb(25 25 25)
rgb(50 50 50)
rgb(75 75 75)

I don’t know how you’d mess with Hue in that world, but here’s gray-only anyway:

Here’s the thing though: I don’t think any of that is all that useful really.

A “real world” grayscale system probably…

Tools that are maybe more useful

Grayscale design is a nice tool that checks off some of my boxes for a better grayscale palette:

  1. As many/few values as you want.
  2. Adjust the positions to make groupings as needed. Each value doesn’t have to be equidistant. Allows you to add colors too to make hue variations or entire color-based palettes with the same math.

Color Designer is similar in how you can make as many steps as you want, has a neat feature where the steps are between any two colors, and can interpolate in new-school color spaces like LCH and LAB.

Primer Prism is very cool, and probably my favorite, in how it visualizes the hue, lightness, and saturation. Clearly, lightness changes dramatically, but it’s a great look to make saturation dip in the middle and change hue ever so slightly in each step 😍. That’s the kind of choice a talented designer might make and it’s so interesting to see it visually expressed like this, with the ability to tweak.

Moving on with Font Management

Wednesday, July 27th, 2022

I recently saw Editorial New by Pangram Pangram.

It’s got some freebie downloads, which I’m a sucker for. If I end up using it for something, I’ll be back for the rest. The system works.

Normally I’d download what I can and file them away in my FontExplorer X software. But alas, I can’t invest any more time into that. I said I was going to try Typeface 3, and I’ve pulled the trigger.

One thing I was surprised about: upon opening Typeface 3, all my fonts were just… already in there.

It even did a pretty good job, seemingly instantaneously, of tagging them with the basics like serif, sans-serif, and monospace. That alone is pretty darn useful. It’s easy to activate/deactivate them and create my own taxonomies. Fantastic. I’ll have to recreate my other taxonomies, but whatever, cleaning house can be fun.

It’s quick to find other really neat features in there. LOOK AT THIS ONE. How cool is it that I can DRAG to adjust font-size and line-height?

Get out of here. Those example layouts are icing on the typographical cake. There are more cool features than I have the time to cover here, so I’m suitably impressed and will likely stick with it.

One thing tripped me up a little. With FontExplorer X, I could just drop the font onto the application and it would pick up the font files and move them into an organized place of my choosing. For me, a folder in Dropbox, so that all my fonts are synced with my other computers and backed up.

Typeface 3 doesn’t really do that. It doesn’t move your font files, it just links up their current location. At first I was like oh no, how am I gonna do this? But it turns out the answer is fairly simple. While it doesn’t officially support “sync”, it kinda does. You just use the default “Import Fonts from Folder” function and point it to a folder (for me, the Dropbox > Fonts folder). That not only will import all the fonts from there, it will keep it updated.

Typeface can keep your library up to date if you import font directories instead of individual fonts. These linked locations are scanned for new fonts automatically on launch, so you don’t have to import fonts manually.

Very nice. Effectively the same thing, minus the automatic organization. So as long as I import that same folder on multiple machines, all I do is drop fonts in there and they’ll be synced.

CascadiaJS 2022

Tuesday, July 26th, 2022

CascadiaJS (August 30th – September 2nd, Sunriver, Oregon) is open to everyone from anywhere, and I’ll be speaking at it this year. Come one come all. I figured I’d blog it (in addition to it being on my speaking page) in particular because I’d hate it if any of my fellow Bendites or anybody generally local didn’t know about it. I’ve never spoke at it before, but I hear good things, and it’s a good opportunity as the organizers are going on a bit of a hiatus after this one. I’d be there no matter what as I couldn’t never pass up this spot-on of an industry event coming to a town just down the road from me, here in the not-exactly-ultra-populus high desert of the Pacific Northwest.

All the good speakers.

My plan is to revamp my “Websites are good now.” talk with the latest and greatest info. It is where I attempt to make the case that the web is as good as it’s ever been. There are all sorts of things that, either used to be complicated, or are still complicated, but the tooling and tech have gotten so good that it’s nearly a solved problem. Almost like, gulp, the industry is maturing.

Here’s the best discount code I have, good for $100 off:

CampanaCeramics on Etsy

Monday, July 25th, 2022

Jeff took a 6-year hiatus on Etsy, and now is back with a new line of work:

All work here is designed by me on the computer, 3d printed, then I make a mold of that, and cast from my own recipe porcelain and glaze.

I remember telling him when I first saw the work that aside from the more obvious old-school digital aesthetic, it reminded me of basalt columns.

I saw some in Idaho that were like that too. Don’t know what it’s called but in my head I named it “pixel canyon”

Pizza Pit

Monday, July 25th, 2022

Pizza Pit was the pizza of my youth. Fresh, Fast, and Hot Delivery, yo.

The boxes were a super bright almost neon yellow and I love them.

They were pulling up to my childhood home on the weekly. My folks got pepperoni and onion, which always annoyed me because the onions were weird soggy squiggles and I hated them. I even like onions now as an “adult” and I still won’t order that specific combo. They had to be in a pretty good mood to order pepperoni and half onion.

The last time I was in Wisconsin, I was sad to see one of the local ones gone. It feels like it’s been a dying local chain for a long time, but I’m pleased to see ~10 locations holding strong. We ordered it one of the nights we were there and it didn’t disappoint. I think of it as just like… regular pizza. It ain’t think, it ain’t thin. They don’t make huge ones, they don’t make tiny ones. They don’t have fancy ingredients. It’s just a gosh darned pizza mmkay. And the logo rules.


Friday, July 22nd, 2022

The ol’ anniversary today. Technically we went to the courthouse a few days before July 22nd to be legally married, but July 22nd is when we had the big wedding party so it feels more like our real anniversary.

It’s the “wood” anniversary, traditionally. We both got each other cool hand-crafted wooden boxes, without any coordination at all.

Easy Asset Cache Busting with Buddy

Thursday, July 21st, 2022

I’m quite chuffed with this! I’ve been solving this in different ways for a heck of a lot of years, in situations where asset cache busting isn’t part of whatever site building situation I’m in provides so I need to do it myself. But it always requires a little more technology than I would like, like setting up Gulp or the like.

This is all it is:

Find & Replace. That’s all we are doing here, and Buddy offers that feature.

The Setup

You’ve got a stylesheet on a website. So in the HTML:

<link rel="stylesheet" href="style.css">

I’ve got my server/CDN set up to cache the living bejesus out of that file. That’s a no-brainer day-one kind of performance requirement. So if I check the response headers on a file like that, I’ll see something like:

cache-control: public, max-age=31536000

So when I need to change that file, I need to break that cache, otherwise, users’ browsers that have already been to the site will hang onto their old version of file. That’s worse than a failed deployment, it’s almost like a half/broken deployment because it’s likely HTML has also changed that no longer correctly corresponds to the updated CSS. That’s bad.

A classic easy way to break cache is to add a query parameter:

<link rel="stylesheet" href="style.css?version=2">

Query parameters are fine¹.

The code we’re looking at? That’s HTML. So the job is:

Automatically update the version number in the HTML when a new deployment is made that changes the contents of that file.

One possible system: update the query parameters by hand. It’s not that hard of a task, but it’s easy to forget, and the consequences of forgetting, as I mentioned above, suck pretty hard.

Aren’t there lots of ways to do this?

There are (checks notes) a million ways to do this. It’s such a known need that it’s built into all sorts of software. The Rails asset pipeline does this. Bundlers like Rollup, Parcel, Vite, etc will happily do this as well. The assumption in all of these is that it’s processing your HTML and producing a post-processed version of the HTML that is deployed.

Most commonly, I reach for Gulp.
// For example, I'll set up a watch task for scripts, doScripts);
// Then I'll do a bunch of stuff to them when they change.
function doScripts(done) {
  return gulp.series(
    done => {
      cacheBust("./parts/footer-scripts.php", "./parts/");
// At the end, I'll do a Find & Replace on the cache busting string in the PHP file.
function cacheBust(src, dest) {
  var cbString = new Date().getTime();
  return gulp
      replace(/cache_bust=\d+/g, function() {
        return "cache_bust=" + cbString;

To me, assuming that HTML (via PHP or not) will be processed is a fairly big assumption. I have worked on lots of websites that don’t have anything processing the HTML. Or even if there is some kind of HTML processor in place, it doesn’t have an auto-incrementing cache buster automatically in place. Like a basic SSG or whatnot.

So when do we need this?

But here’s my #1 use case: WordPress websites.

A WordPress website produces HTML from PHP templates. There is no processing there other than PHP itself². I tend not to have a build process for WordPress sites³.

Using Buddy for the Find & Replace

So here’s my actual line of code in my header.php file on a WordPress site.

<link rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>?v={{{version}}}">

Note the {{{version}}} in there. That’s just an invented syntax. Just something fairly unique that I’ll have Buddy replace.

Now here’s an example Buddy pipeline. A lot of times, I’ll have one action: just deploy the dang site. But here we’ll add a Find & Replace setup first.

The purge cache at the bottom there is for Cloudflare. It’s just for good measure, but weirdly, not that relevant here, as changing the HTML is enough to break the cache we’re talking about. I do recommend the Cloudflare plugin though and paying for the WordPress-specific optimizations they do though as they are pretty rad.

Within the settings for Find & Replace, we get to tell it what file to run it on, what to look for, and what to replace it with. Miraculously, there are magical variables you have access to (like your ENV vars) including one called $BUDDY_EXECUTION_ID which is a unique ID per deployment, so it’ll work great for cache busting.

In order for this to work, you need to have the Upload files action set to use Pipeline Filesystem and not GitHub. It’ll still pull from GitHub, but apparently, this gives it permission to alter the files before uploading or something. This is crucial and tripped me up for quite a while setting this up. If you don’t do it, it seems like it is all working, but it just doesn’t work, the file uploads unchanged.

You could run the Find & Replace on every deployment, breaking the cache every time, but I’ve added a conditional here to only do it when the actual stylesheet has changed:

Seeing it Run

You can see the Find & Replace execute and succeed in the pipeline logs.

That’ll do it. I can use this on like 3-4 WordPress websites right now, including this one, so I’m happy to have found this simple solution.

  1. There used to be one odd-ball server a million years ago that didn’t respect that and required you to change the name of the file, but let’s not worry about that.
  2. This kinda makes me think that we could do the asset versioning by outputting filemtime($filename) at the end of them, which is the last changed time for the file. I’d just want to make sure good HTML caching is in place then so the server isn’t like hitting the disk for that information on every page view. Probably a can of worms there.
  3. If I work on it alone, I tend to use CodeKit just for a little easy Sass action. It has a cache buster too, but only for HTML that is processed, and probably wouldn’t work for PHP.

The World’s Most Satisfying Checkbox

Wednesday, July 20th, 2022

As a proprietor of CodePen, I’ve seen, definitely, thousands of customized checkboxes, some classy and modern, some classy from trends gone by, some fun, some (whimsically) frustrating, and some clever twists.

So I was a bit skeptical of Andy Allen’s headline. But I’ve heard nothing but good things about the (Not Boring) Apps, and I wanted to check them out.

I started with (Not Boring) Habits and, as expected, I love it.


Let me strip off the sugarcoating and share a little secret about habit tracker apps: they’re little more than a glorified checkbox. The interaction is simple: every day you open the app and hit the checkbox to record a completed habit.

So it’s an app with a daily checkbox.

I made my habit “Sweat”. Something I want to do every day. But I kept it vague. I’m back on my CrossFit thing so maybe it’s that. Maybe it’s a long walk in the summer sun. Maybe it’s getting on a Peloton thing. Whatever. I just wanna sweat. And so:

Hot damn, that is a satisfying checkbox. The world’s most, perhaps.

The app also comes with a widget that looks great.

Hierarchical Custom Post Types in WordPress

Tuesday, July 19th, 2022

You need to set both of these things to make it work correctly:

register_post_type( 'docs',
    'labels'        => array(
      'name'          => __( 'Documentation' ),
      'singular_name' => __( 'Documentation Page' ),
      'add_new'       => __( 'Add Documentation Page' ),
      'add_new_item'  => __( 'Add New Documentation Page' ),
      'edit_item'     => __( 'Edit Documentation Page' ),
    'public'       => true,
    'has_archive'  => true,
    'rewrite'      => array( 'slug' => 'docs' ),
    'show_in_rest' => true,
    'hierarchical' => true,
    'supports'     => array( 'title', 'editor', 'thumbnail', 'excerpt', 'page-attributes' )

Now when you are on the Create/Edit screen for the new Post Type, you’ll see the Page Attributes area that allows you to select the Parent you’d like.


Monday, July 18th, 2022

Lovely weather this weekend. Took Miranda’s Chevy Tahoe over to the car wash for cleaning. Ruby and I toweled off the wet bits after the wash. After we were done, I went to start the car, click-click-click-click. Light internet research suggested the battery was dead.

Miranda was able to drive over with the jumper cables we keep in the garage. We should probably keep jumpers in (both) vehicles, but honestly, neither of them have good storage for stuff like that, which seems silly since both my Tundra and her Tahoe are essentially rolling castles. We got a good connection on the jumper cables, but it didn’t seem to provide enough juice. We could get the Tahoe to turn over once or twice, but then it was dead again.

Soooo what now, we thought, are we AAA members? We have been in the past. Maybe multiple times? I tried scouring email and 1Password and such for info, but nothing very recent. Doesn’t look like it.

I was going to call a local towing company or something, but I figured we might as well just sign up for AAA right then and there because 1) I want to have it anyway 2) maybe they’ll just help us right away. Turns out, yep!, they’ll just help you immediately. It’s not like insurance where you can’t wait for a tree to fall on your house before you sign up for it. That’s a good thing everyone should know: it’s probably cheaper, easier, and faster to get help by signing up for AAA on the spot when you need help. It was basically a hundred bucks to get help immediately. A very helpful agent helped us on the phone, and less than 15 minutes later a very helpful local fella was on the scene helping.

I was thinking we’d get them to tow us over to the dealership, but Miranda rightfully suggested we just have them do battery repair right there, and that’s exactly what they did. First, they had a little jumper machine that immediately started the engine.

That little thing is 3,000 AMP while jumping from another battery apparently only provides 750. I should get one, it’s easier than cables anyway.

Then they tested the battery, and it was their opinion that the battery was in good shape and as long as we run the car for a while and charge it back up, we’d be fine, no need to replace it.

The Tahoe behaved well the rest of the day with a few on/off starts. But the next morning? Dead again. This time a traditional jump did the trick. We clearly need a new battery.

But, consistently, that morning we were dropping the Tahoe off at the Chevrolet dealership to sell it back to them anyway. The new Tesla (!) arrives in the next few months and Chevrolet were giving us a better cash offer than Tesla would on a trade-in. We’re just gonna rock one truck (and electric bikes) until it comes, which is extra fine while it’s summer and bikes are more fun anyway.

« Older Entries