The Blogroll

From: Adactio

Project Hail Mary by Andy Weir

I was in the library the weekend before last when I spotted something on the shelf of recently-returned books. Project Hail Mary by Andy Weir.

I knew the film adaptation was coming out later that week. Ideally, I’d like to read the book before seeing the film. It would be a race against time! The film would be out in days, and the book is over 450 pages long. Could this nerdy white guy rise to challenge and overcome the odds?

As it turned out, it wasn’t all that arduous. Project Hail Mary is a real page-turner, just like Andy Weir’s previous book, The Martian.

But his books are worryingly regressive. The so-called golden age of science fiction featured plenty of plucky white science guys saving the day with their brainpower in books written by white science guys. Andy Weir’s books have a similar outlook.

On the other hand, they’re undeniably fun. And who knows? Maybe his next book will feature a protaganist that isn’t an aw-shucks white guy.

(Update: multiple people have pointed out that I completely missed that Andy Weir’s other book, Artemis, features a refreshingly different kind of protaganist—phew!)

Project Hail Mary is packed with plenty of plausible-sounding science. Perhaps too much. After a while it felt like elements were being added to the story to showcase the author’s smarts rather than to propel the plot.

Over all, the book is good entertaining fun but a bit baggy and could’ve been edited down somewhat.

I was interested to see how the film would translate the science from the written page to the screen. Very commendably, as it turns out.

The film does a great job of avoiding expositional blackboard sequences or explanatory dialogue. Wherever possible, it shows rather than tells. It helps that it doesn’t underestimate what the audience can handle.

Above all, it’s entertaining. Popcorn was invented for this kind of film. Ryan Gosling does his usual entertaining shtick, though I kept thinking that Sam Rockwell would’ve really delivered the goods.

The film trims the book down to its essentials. I didn’t miss any of the elements they chose to cut. I did spot one glaring mistake, but that was continuity error rather than anything to do with the science.

Project Hail Mary the film is better than Project Hail Mary the book. Go see it. And if it leaves you wishing for more, then you can always read the book.

Buy this book

From: Zeldman

Dine ’n em-dash

The best defense is to write humanly.

The post Dine ’n em-dash appeared first on Jeffrey Zeldman Presents.

From: Rachel Andrew

Do you need AI for that?

My social feed has divided mostly into two camps—those who can now only talk about how excited they are about AI, and those who are refusing to use it at all. I’m somewhat bemused by both of these positions, I see LLMs as a useful tool, in the way that I see spreadsheets as a […]

From: Zeldman

RSS creator on Bluesky & AT Proto

Bluesky can't abandon the developers who made a bet on AT Proto, so they should give the protocol to a standards body while catching up on UX.—Dave Winer

The post RSS creator on Bluesky & AT Proto appeared first on Jeffrey Zeldman Presents.

From: Dave Rupert

People are not friction

The Gell-Mann Amnesia Effect of AI is a pretty well documented phenomenon:

The Gell-Mann amnesia effect is a cognitive bias describing the tendency of individuals to critically assess media reports in a domain they are knowledgeable about, yet continue to trust reporting in other areas despite recognizing similar potential inaccuracies.

Summarizing, AI sounds like an incredible genius synthesizing the world’s knowledge right up until you ask it about the thing you know about, then it’s an idiot. Even knowing about this phenomenon and having experienced it countless times, LLMs have an intoxicating quality to them.

If there’s one thing LLMs do well, it’s delivering an enormous blast of dopamine if they do a good job on the first try. That “if” does a lot of heavy lifting, but when it happens you start to believe the idea that the multi-modal chat box could make you faster and infinitely capable. A hop and a skip and hundreds of dollars in tokens later, you made something that would take lifetimes of learning to create.

The marketing around Generative AI is that every person can be their own Designer, Developer, Researcher, Content Writer, Video Producer, and Podcast Editor. With the right skills.md files everything will fall into place. We will automate all automatable tasks. The experience will be “frictionless”.

You’ll be able to automate away the jobs you don’t enjoy…
And the jobs you don’t know how to do…
And the people who do those jobs…
The people you don’t enjoy…

You won’t need to talk to or wait on anyone. You can automate away anything or anyone that stands in your way. You will reduce costs. You will be richer. It will be frictionless.

Sometimes I feel like there’s a palpable tension in the air as if we’re waiting to see whether AI will replace designers or engineers first. Designers empowered by AI might feel those pesky nay-saying, opinionated engineers aren’t needed anymore. Engineers empowered with AI might feel like AI creates designs that are good enough for most situations. Backend engineers feel like frontend engineering is a solved problem. Frontend engineers know scaffolding a CRUD app or an entire backend API is simple fodder for the agent. Meanwhile, management cackles in their leather chairs saying “Let them fight…”

I think it’s a dangerous place to be when we start to consider people as friction.

That’s because we know Gell-Mann is real. We know there’s an optimism bias the size of the sun blinding us to the actual quality of what the machine is producing. We need knowledgeable people to share what they know to improve the quality of our work, generated or otherwise. We even need ignorant people to make sure we can break ideas down into their simplest form that everyone, agents or human, understand. People can have bad attitudes, be shitty, and have wrong opinions… but people are not friction. An LLM may be able to autocorrect its way into a plausible human response, but it’s not people. It doesn’t care if it’s right or wrong. The money and hype surrounding it acts as a shield to its reputation. LLMs make up answers if it doesn’t have enough context and they fall over if it has too much context. It amplifies good patterns the same as bad patterns. And it glazes you with flattering language the entire time so that chart go up.

People, the context-bearers, have experience and capabilities that machines might never understand encoded in our muscles and memory. I’m on record saying I despise nuance –and I do– but it’s more important than ever to be able to connect to our fellow humans over this nuance so our world is not paved over by contextless opinions from ill-informed robots. Empower and believe people over machines.

From: Rachel Andrew

Look into the future of the web platform

Last week I spoke at the very lovely Web Day Out in Brighton. My talk was about browser support, based on the work I’ve done over the past almost five years on Baseline. I ran through the various things you need to consider when deciding whether to use features that don’t meet your Baseline target. […]

From: Adactio

Early-bird tickets for UX London

You should come to UX London in the first week of June. Why? Because it’s going to be awesome, that’s why!

You probably knew that already. You probably already decided to get a ticket because you’re smart like that.

But don’t dilly-dally! Early-bird tickets are available now but in just over one week, they won’t be.

So get your ticket by Friday, March 27th. If you get your ticket now, it’s a win for everyone. You get a cheaper ticket. We know for sure that you’re coming.

Every time someone buys a conference ticket in plenty of time, the conference organiser sleeps a little better at night.

If you need to convince your boss, you can give them these reasons to attend. I even made an email template you can use a starting point for making the case.

You could come for all three days of UX London, or you can pick just one day.

Tuesday, June 2nd is discovery day with a focus on user research. You’ll hear from great speakers like Melin Edomwonyi and Maria Isachenko as well as getting workshops from Natasha den Dekker and Feyikemi Akinwolemiwa.

Wednesday, June 3rd is design day where it’s all about the nitty-gritty details. Not only will there be great talks from Andrea Grigsby, Julia Petretta, and Hidde de Vries, there’s going to be the best-named workshop ever from my colleague Chris How: Yippee IA!

Thursday, June 4th is delivery with a focus on design systems and collaboration. Alex Edwards, Lucy Blackwell, Rachel Ilan Simpson and Ben Callahan will all be giving talks (and Ben’s doing a workshop too).

That’s not even close to the final line-up. I’m confirming more speakers right now and getting very, very excited about how it’s all shaping up.

You know you don’t want to miss this one. So get your early-bird ticket now while you still can.

A black and white profil…g woman with long hair. A woman with curly hair …g and tilting her head. Portrait of a woman dres… with her hair tied up. A smiling young woman wi…n front of neon lights. A smiling young woman wi…inst a blue background. A black and white portra…k shoulder-length hair. A smiling man with short… shirt under his jumper A smiling man with curly…wearing a purple shirt. A smiling young woman wi…air wearing a dark top. A woman wearing glasses …colourful floral shirt. A woman with short hair …st a pastel background. A shaven-headed man with…d slightly to one side.

From: Chris Coyier

Meets Style Sheets

I’ve accepted an invitation to speak at Smashing’s (Online) Conference Meets Style Sheets. It’s free on Wednesday, May 6th. I named my talk In-N-Out Styling. Long time CSS evangelist Chris Coyier will talk about how you can style things on their way into view on a webpage, and on their way out. Of course, with […]

From: Adactio

A Fisherman Of The Inland Sea by Ursula K. Le Guin

When I was summing up my reading habits in 2022 I said:

I think the lesson this year is: you can’t go wrong with Octavia E. Butler or Ursula K. Le Guin.

I stand by that. But maybe I’d recommend some Ursula K. Le Guin books more than others.

A Fisherman Of The Inland Sea is a good collection of short stories. But it’s not a great collection of short stories. If you’re looking for a great collection of short stories, read The Unreal and the Real.

When it comes to Ursula K. Le Guin, the standard is always going to be high so even when the stories aren’t her best, they’re still better than the output of most other sci-fi writers.

My slight disappointment with A Fisherman Of The Inland Sea isn’t so much with the stories themselves but with the collection.

To begin with, there are four unconnected short stories. That’s fine. It’s a short story collection after all.

But then after that there are three interconnected short stories from the Hainish cycle. They’re the best part of this book. That just makes the preceding stories look like filler.

If those three stories had been released as little collection, it would be a miniature classic. As it stands, you get more of a mixed bag.

But still, it’s worth reading this collection for those three stories alone.

Buy this book

From: Adactio

That was Web Day Out

On March 12th, 1989, Tim Berners-Lee submitted Information Management: A Proposal. This would form the basis of what became the World Wide Web.

On March 12th, 2026, Web Day Out happened in Brighton.

Coincidence?

Yes. Yes, it is a coincidence. But it’s a pretty nice coincidence, you must admit.

It was a day dedicated to the World Wide Web. Not just the foundational languages of the web—HTML, CSS, and JavaScript—but also the foundational ideas of the web.

“Share what you know!” That was the original motto of the World Wide Web project. That was the motto of Web Day Out too.

Look, I’m biased because I put the line-up together but honestly, all of the speakers were superb! So much knowledge delivered in such entertaining fashion.

I had a blast. And I’ll give myself a little pat on the back for how I grouped the talks into rhyming couplets:

Browsers: Jemima talked about what you can do with just HTML and CSS these days, and Rachel followed up with how to come up with your own browser support strategy.

Performance: Aleth made the case for multi-page progressive web apps that work under any network conditions, and Harry followed up with an impassioned rant about how much time and energy has been wasted on over-engineered single-page apps that ignore what browsers can do.

Styling: Manuel walked us through a whole new approach to writing modern CSS, and Rich followed up with a whirlwind tour of all the great typographic possibilities in CSS.

Standards Jake took us on the standards journey to customisable select elements, including anchor positioning and popovers, and then Lola showed us exactly what it takes to add a new feature to a web browser.

Everything flowed together really nicely.

I was a little apprehensive going into Web Day Out that it would just be preaching to the converted. And sure, there were plenty of veteran devs there who already knew the value of progressive enhancement and making the most of web standards. But I was gratified to also see lots of younger faces in the crowd.

I was talking to one young developer afterwards and she told me what an eye-opening experience it was. Whereas before she would have defaulted to a framework-driven single-page app for everything, now she’s got the knowledge to make an appropriate architectural choice.

Mission accomplished!

If you couldn’t make it to Web Day Out and you want to experience some RAMO, here’s the chatter on Bluesky and Mastodon, lovely photos by Marc, a post by Dave, and a lovely post by Amber.

Thank you so much to everyone who came. I think you’ll agree it was a most excellent day out.

From: Zeldman

Too Many Notes

Lately, in work conversations, I find myself fighting a lifelong tendency to provide way more context than is absolutely required. If you ask me to okay your work, for example, I may respond with an essay on what delighted me about it. The teaching gene, plus the exuberance of writing and thinking clearly, compel me […]

The post Too Many Notes appeared first on Jeffrey Zeldman Presents.

From: Chris Coyier

Kermit Roosevelt

I was at a school function the other day where the 2nd graders performed a bunch of Aesop’s Fabels and it was great. It was a double-header with 3rd graders who then read prepared reports on famous people. It was cross-disciplinary thing as the kids brought props from design class, costumes from performing arts, and […]

From: Chris Coyier

AI is my CMS

I mean… it’s not really, of course. I just thought such a thing would start to trickle out to people’s minds as agentic workflows start to take hold. AI agents are already up in your codebase fingerbanging whole batches of files on command. What’s the difference between a CMS taking some content and smashing it […]

From: Zeldman

A die-cut above

Cover art for the 1971 prog-rock LP “Fearless,” by British band Family features a distinctive, die-cut cover design depicting the five band members gradually morphing into a single entity combining features of them all. Tom Brigham, a high school student and friend of mine the year the LP was released, had not yet invented morphing […]

The post A die-cut above appeared first on Jeffrey Zeldman Presents.

From: Boagworld

Generative UI: useful, or just chaos with better branding?

It can personalize and reduce friction. It can also wreck consistency and muscle memory. Here is my take on the latest buzzword.

From: Chris Coyier

Jerod Santo: After 13 years, 1042 podcasts, 452 newsletters, and countless friends made along the way… it’s time to say goodbye to The Changelog. I shipped my final News last Monday and Adam shipped our Friends finale yesterday. Huge congrats Jerod! Heck of a legacy.

March 11, 2026, 2:22 pm >>

From: Adactio

A web font strategy

The Session has been online in some form since the late 1990s. That’s long before web fonts existed.

To begin with, Times New Roman was the only game in town if you wanted serif type on a website. When Microsoft introduced Georgia it was a godsend. A beautiful typeface designed by Matthew Carter for the screen. I put it right at the start of my font stack for The Session.

Later, web fonts came along. Boy, does that short sentence belie the drama! There were very heated discussions about whether web browsers should provide this ability at all, and what it would mean for type foundries.

Microsoft led the way with their prorietary EOT format. Then everyone agreed on WOFF. Finally we got WOFF2, Electric Boogaloo.

Perhaps more important than that, we got intermediaries. Typekit, Fontdeck, and then the big daddy, Google Fonts.

That’s pretty much the state of play today. Oh yeah, and we’ve got variable fonts now.

I remember Nick Sherman presenting the idea of variable fonts at an Ampersand event years ago. I remember thinking “great idea, but it’ll never happen.” Pure science fiction. I thought the same thing when I first saw a conference presentation about a miraculous image format called Scalable Vector Graphics.

Sometimes I like to stop and take stock of what we take for granted in web browsers now. Web fonts. Variable web fonts. SVG. Flexbox. Grid. Media queries. Container queries. Fluid typography. And I haven’t even mentioned how we were once limited to just 216 colours on the web.

Georgia

Given all the advances in web typography, you might be wondering how my font strategy for The Session changed over the years.

It didn’t.

I mean, sure, I added fluid typography. That was a natural extension of my love for liquid layouts and, later, responsive design. But the font stack itself? That was still Georgia all the way.

Y’see, performance has always been a top priority for The Session. If I was going to replace a system font with a web font that the user had to download, it really needed to be worth it.

Over the years I dabbled with different typefaces but none of them felt quite right to me. And I still think Georgia is a beautiful typeface.

“But your website will look like lots of other websites!” some may cry. That used to be true when all we had was system fonts. But now that web fonts have become the norm, it’s actually pretty unusual to see Georgia in the wild.

Lora

Recently I found a font I liked. Part of why I like it is that it shares a lot of qualities with Georgia. It’s Lora by Olga Karpushina and Alexei Vanyashin.

I started to dabble with it and began seriously contemplating using it on The Session.

It’s a variable font, which is great. But actually, I’m not using that many weights on The Session. I could potentially just use a non-variable variety. It comes in fixed weights of regular, medium, semibold, and bold.

Alas, the regular weight (400) is a bit too light and the medium weight (500) is a bit too heavy. My goldilocks font weight is more like 450.

Okay, so the variable font it is. That also allows me to play around with some subtle variations in weights. As the font size gets bigger for headings, the font weight can reduce ever so slightly. And I can adjust the overall font weight down in dark mode (there’s no grading feature in this font, alas).

Subsetting

Lora supports a lot of alphabets, which is great—quite a few alphabets turn up on The Session occasionally. But this means that the font file size is quite large. 84K.

Subsetting to the rescue!

I created a subset of Lora that has everything except Cyrillic, Greek, and Latin Extended-B. I created another subset that only has Cyrillic, Greek, and Latin Extended-B. Now I’ve got two separate font files that are 48K and 41K in size.

I wrote two @font-face declarations for the two files. They’ve got the same font-family (Lora), the same font-weight (400 700), and the same font-style (normal) but they’ve got different values for unicode-range. That way, browsers know to only use appropriate file when characters on the page actually match the unicode range.

The first file is definitely going to be used. The second one might not even be needed on most pages.

I want to prioritise the loading of that first subsetted font file so it gets referenced in a link element with rel="preload".

The switcheroo

As well as file size, my other concern was how the swapping from Georgia to Lora would be perceived, especially on a slow connection. I wanted to avoid any visible rejiggering of the content.

This is where size-adjust comes in, along with its compadres ascent-override and descent-override.

Rather than adjusting the default size of Lora to match that of Georgia, I want to do it the other way around; adjust the fallback font to match the web font.

Here’s how I’m doing it:

@font-face {
    font-family: 'Fallback for Lora';
    src: local('Georgia');
    size-adjust: 105.77%;
    ascent-override: 95.11%;
    descent-override: 25.9%;
}

And then my font stack is:

font-family: Lora, 'Fallback for Lora', Georgia, serif;

It’s highly unlikely that any device out there has a system font called “Fallback for Lora” so I can be pretty confident that the @font-face adjustment rules will only get applied to browsers that have the right local font, Georgia.

But where did those magic numbers come from for size-adjust, ascent-override, and descent-override?

They came from Katie Hempenius. As well as maintaing a repo of font metrics, she provides the formula needed to calculate all three values. Or you could use this handy tool to eyeball it.

With that, Georgia gets swapped out for Lora with a minimum of layout shift.

First-timers and repeat visitors

Even with the layout shift taken care of, do I want to serve up web fonts to someone on a slow connection?

It depends. Specifically, it depends on whether it’s their first time visiting.

The Session already treats first time visitors differently to repeat visitors. The first time you visit the site, critical CSS is embedded in the head of the HTML page instead of being referenced in an external style sheet. Only once the page has loaded does the full style sheet also get downloaded and cached.

I decided that my @font-face rules pointing to the web fonts are not critical CSS. If it’s your first time visiting, those CSS rules only get downloaded after the page is done loading.

And unless you’re on a fast connection, you won’t see Georgia get swapped out for Lora. That’s because I’ve gone with a font-display value of “optional”.

Most people use “swap”. Some people use “fallback”. You’ve got to be pretty hardcore to use “optional”.

But the next page you go to, or the next time you come to the site, you more than likely will see Lora straight away. That’s because of the service worker I’ve got quietly putting static assets into the Cache API: CSS, JavaScript, and now web fonts.

So even though I’m prioritising snappy performance over visual consistency, it’s a trade-off that only really comes into play for first visits.

Next

I’m pretty happy with the overall strategy. Still, I’m not going to just set it and forget it. I’ll be monitoring the CRUX data for The Session keeping a particular eye on cumulative layout shift.

Before adding web fonts, the cumulative layout shift on The Session was zero. I think I’ve taken all the necessary steps to keep it nice and low, but if I’m wrong I’ll need to revisit my strategy.

Update: Big thanks to Roel Nieskens—of Wakamai Fondue fame—who managed to get the file size of my main subsetted font down even further; bedankt!

From: Chris Coyier

Claude is an Electron App

Juicy intro from Nikita Prokopov:

From: Chris Coyier

The chances of macOS actually running an automatic update on my machines is super low. Even if I OK the “Update Tonight?” prompt, there is no way it actually goes through, because I’ll have apps open that prevent the restart. iTerm is a big one, that one won’t just quit on command. But even tabs […]

March 9, 2026, 3:54 pm >>

From: Adactio

Testing browser support for `focusgroup`

In my previous post, I mentioned that I’ve used the web install API in production. Specifically, I’ve used it on The Session. In order to do that, I had to register for the origin trial.

I’ve just signed up for another origin trial. This time it’s for the proposed focusgroup attribute:

The focusgroup HTML attribute is a proposed declarative way to add keyboard arrow-key navigation to composite widgets such as toolbars, tablists, menus, listboxes, etc. without writing any roving-tabindex JavaScript. One attribute replaces hundreds of lines of boilerplate.

I’ve got an HTML web component on The Session called tab-controls. And yes, there’s a bunch of code in there to listen for keyboard events and respond appropriately. I would very much like to rip that code out.

So now that I’ve opted into the origin trial, I’ve added this to my HTML:

<tab-controls role="tablist" focusgroup="tablist">

If this focusgroup attribute takes off, I’ll be able to remove the role attribute but for now, it’s very much needed.

In the JavaScript for my tab-controls custom element, I need to be able to detect support for focusgroup. Here’s how I’m doing it:

if (!this.focusgroup) {
// do all my key handling stuff here
}

Here’s the important thing: don’t use getAttribute('focusgroup') to test for browser support. That will return true if the attribute is in the HTML. But the attribute will only get converted into a property if the browser understands it.

Jake has a lot more detail on the differences between attributes and properties.

Anyway, I figured I’d share that little snippet in case you too were interested in trying out the focusgroup proposal using progressive enhancement.

From: Adactio

Installing web apps

I have websites in my dock on my computer. I have websites on the home screen of my phone. When I open these websites from the dock or from the home screen, they behave just like native apps. It’s brilliant!

But knowing that you can add a website to the dock or to the home screen remains arcane knowledge. If you don’t know it’s possible, most web browsers aren’t going to tell you it’s an option. As a site owner, you pretty much have to explain to your users what they can do.

Lately it feels like there’s been some movement to change this situation. Or, at the very least, there’s been some discussion.

As a site owner, what you want is a way for someone visiting your site to press a button to initiate the process of adding the site to the dock or the home screen.

From what I can see from the discussion, there are two contenders for how to do this: BeforeInstallPromptEvent versus navigator.install.

I’ve used both APIs in production, so I’d like to offer my balanced feedback on both:

  • BeforeInstallPromptEvent sucks.
  • navigator.install rocks.

To add more detail…

The BeforeInstallPromptEvent API relies on you capturing and delaying an event that may or not fire at all.

Based on some arbitrary heuristics, Chrome—for example—will prompt the user to install the current website. This easily-dismissable prompt looks indistinguishable from a prompt to sign up to a newsletter or grant permission for cookies, so most people dismiss it. The idea with BeforeInstallPromptEvent is that you capture that prompt, prevent it from prompting, and then release it when you think it’s an appropriate time.

If you think it takes mental gymnastics to understand that, just imagine what it’s like trying to implement it!

The whole thing rests on this flawed idea of an install prompt being shown when certain conditions are met. Other browser vendors rightly point out that users should be able to install any website they want. Ideally it should have a manifest file. But making a service worker a requirement is a step too far (and I say that as someone who literally wrote a book about service workers).

Contrast that with the Web Install API, AKA navigator.install.

Based on a user interaction—like a click on a button—the browser initiates the installation process. The user still has to confirm they want to do this, of course. You know how geolocation or web notifactions work? It’s like that. You can’t trigger any of those APIs without the user’s permission.

That’s it. No contest.

It would be absolutely wonderful if more browsers supported navigator.install. It would be a pain in the ass if they decided to support BeforeInstallPromptEvent instead.

From: Chris Coyier

Mad CSS

Round One of Mad CSS is out on YouTube!

From: Chris Coyier

Lars Mensel: … the only way out of the delusion that AI can replace all of us is to do good meaningful work, and to appreciate good work when we see it.

March 6, 2026, 6:41 pm >>

From: Stuff & Nonsense

Unfinished Business #141: One Footer Unfinished Troika: Music crossover Special

Three podcasts for the price of one. One Footer in the Grave, Troika, and Unfinished Business. In this special crossover episode, Rich and I are joined by Jon Hicks and Marcus Lillington to discuss which three tracks we’d want with us if we were marooned on a deserted planet.


Listen to all tracks from this episode on Apple Music.

Sponsor Unfinished Business

The best way to promote your product or service to the Unfinished Business audience of creative professionals, designers, and developers. Sponsors get one exclusive two-minute ad per episode, live read by me or Rich, which feels personal and relatable. We can use your script or ad-lib, and you’ll also receive a link on each episode’s post, as well as a thank-you on our Bluesky and Mastodon accounts. Interested? We’d love to hear from you.

Support Unfinished Business on Patreon

We also have three monthly membership plans so you can support Unfinished Business on Patreon. $5, $10, and $20 if you’re feeling especially generous. All money goes towards podcast editing and hosting. Support Unfinished Business on Patreon.

From: Zeldman

My brother, the rhythmic conceptualist

Remembrance of beats passed.

The post My brother, the rhythmic conceptualist appeared first on Jeffrey Zeldman Presents.

From: Boagworld

Can AI Design a UI Yet? (And Why Designers Are Still Needed)

AI can design something adequate. It still struggles with context, trade-offs, and taste. Here is what changes, and what does not.

From: Stuff & Nonsense

Unfinished Business #140: Orangutan covered in coconuts

In this episode of Unfinished Business, Rich and I talk about how AI coding tools fit into our workflows at ClearLeft and Stuff & Nonsense. Then, I explain how I used AI to help me build Sho!io, a new app for sharing designs.

Sponsor Unfinished Business

The best way to promote your product or service to the Unfinished Business audience of creative professionals, designers, and developers. Sponsors get one exclusive two-minute ad per episode, live read by me or Rich, which feels personal and relatable. We can use your script or ad-lib, and you’ll also receive a link on each episode’s post, as well as a thank-you on our Bluesky and Mastodon accounts. Interested? We’d love to hear from you.

Support Unfinished Business on Patreon

We also have three monthly membership plans so you can support Unfinished Business on Patreon. $5, $10, and $20 if you’re feeling especially generous. All money goes towards podcast editing and hosting. Support Unfinished Business on Patreon.

From: Adactio

Feedback

If you wanted to make a really crude approximation of project management, you could say there are two main styles: waterfall and agile.

It’s not as simple as that by any means. And the two aren’t really separate things; agile came about as a response to the failures of waterfall. But if we’re going to stick with crude approximations, here we go:

  • In a waterfall process, you define everything up front and then execute.
  • In an agile process, you start executing and then adjust based on what you learn.

So crude! Much approximation!

It only recently struck me that the agile approach is basically a cybernetic system.

Cybernetics is pretty much anything that involves feedback. If it’s got inputs and outputs that are connected in some way, it’s probably cybernetic. Politics. Finance. Your YouTube recommendations. Every video game you’ve ever played. You. Every living thing on the planet. That’s cybernetics.

Fun fact: early on in the history of cybernetics, a bunch of folks wanted to get together at an event to geek about this stuff. But they knew that if they used the word “cybernetics” to describe the event, Norbert Wiener would show up and completely dominate proceedings. So they invented a new alias for the same thing. They coined the term “artificial intelligence”, or AI for short.

Yes, ironically the term “AI” was invented in order to repel a Reply Guy. Now it’s Reply Guy catnip. In today’s AI world, everyone’s a Norbert Wiener.

The thing that has the Wieners really excited right now in the world of programming is the idea of agentic AI. In this set-up, you don’t do any of the actual coding. Instead you specify everything up front and then have a team of artificial agents execute your plan.

That’s right; it’s a return to waterfall. But that’s not as crazy as it sounds. Waterfall was wasteful because execution was expensive and time-consuming. Now that execution is relatively cheap (you pay a bit of money to line the pockets of the worst people in exchange for literal tokens), you can afford to throw some spaghetti at the wall and see if it sticks.

But you lose the learning. The idea of a cybernetic system like, say, agile development, is that you try something, learn from it, and adjust accordingly. You remember what worked. You remember what didn’t. That’s learning.

Outsourcing execution to machines makes a lot of sense.

I’m not so sure it makes sense to outsource learning.

From: Zeldman

What a year that was.

Know your web design history.

The post What a year that was. appeared first on Jeffrey Zeldman Presents.

From: Adactio

Will There Ever Be Another You by Patricia Lockwood

Patricia Lockwood’s No One Is Talking About This knocked me for six when I read it back in 2022:

It’s like a slow-building sucker punch.

Like my other favourite book of that year—A Ghost In The Throat by Doireann Ní Ghríofa—it’s hard to classify. I think it’s autofiction. Not quite autobiography. Not quite fiction.

Will There Ever Be Another You is also autofiction. I think. It might also be poetry (which shouldn’t be surprising as Patricia Lockwood is a poet after all).

I can’t say that this one had the same emotional impact of No One Is Talking About This for me but then again, very little could.

The writing feels very impressionistic, with each chapter trying on a different mode. It’s kinda Joycean …if James Joyce was stuck indoors during a global pandemic.

The narrative—such as it is—revolves around The Situation from 2020 onwards. That was a surreal bizarre time so it makes sense that this is a surreal bizarre book.

I think I liked it. I can’t quite tell. I just let the language wash over me.

Buy this book

From: Chris Coyier

Me Trying To Take a Selfie in 2001

From: Chris Coyier

FOREVERGREEN

In the first few minutes, Ruby says to me, “This is like The Giving Tree“, and by the end, I was like, “OK, you’re right.”

From: Chris Coyier

I like how Nolan paints this two-sided picture of AI coding. The worst fact about these tools is that they work. They can write code better than you or I can, and if you don’t believe me, wait six months. You could abstain out of moral principle. And that’s fine, especially if you’re at the […]

March 2, 2026, 3:26 pm >>

From: Zeldman

Advice for job seekers

Pitching isn’t bragging.

The post Advice for job seekers appeared first on Jeffrey Zeldman Presents.

From: Adactio

The state of State Of The Browser

I went to State Of The Browser in London on the weekend. It was great!

I mean, it’s always great but this year the standard felt really high. All the talks were top quality. I’ve been at events with ticket prices a literal order of magnitude greater but with quality nowhere near this level.

Bramus got the ball rolling with an excellent presentation on CSS anchor positioning. Cassie closed the day with a great fun talk, making a game in the browser. In between we had accessibility, progressive enhancement, and other favourite topics of mine.

State Of The Browser isn’t just about the talks though. It’s very much a community event. For me, it’s like an annual get-together with some lovely people that I only get to see once a year.

But it’s not just a bunch of people who already know each other. Dave got a show of hands from people attending for the first time and it looked to me like around half the audience. That’s what you want at an event—a mix of the old and the new, the familiar and the exciting.

A personal highlight for me was spending lunchtime talking in Irish with my friend Paul from Ti.to. Bhain mé an-taitneamh as an deis Gaeilge a labhairt!

Dave handed over MC duties to Jake this year but he did do the opening and closing remarks. He’s always really, really supportive of other community events and encouraged everyone to go to Web Day Out.

He also pleads with people to buy their conference tickets early (it really does help us conference organisers sleep better) but if you’ve left it this late, you’re lucky that tickets are still available.

If you liked State Of The Browser, you’re going to like Web Day Out. And if you missed State Of The Browser and you wished you could’ve been there, you can make up for it by coming to Web Day Out.

The two events have a lot in common. Great talks, great people, and no mention of large language models.

I don’t know if it was a deliberate policy by Dave, but it felt so good to spend a day at a technology conference that wasn’t dominated by The Hype.

There were a few bits of slop in the slides of the first two talks (which always makes me cringe and wince—I crince) and Cassie threw some subtly hilarious shade during her presentation, but apart from that, the day was gloriously free of the A and the I.

No doubt some people will think that’s little more than sticking our collective head in the sand, but when the sand is this lovely, I’m okay with it.

Tickets for State Of The Browser 2027 are already on sale. Do what Uncle Dave says and get your ticket nice and early.

From: Chris Coyier

Cool little aside in Marcin Wichary’s How to shoot a screen using a board of keys: Acorn 8, a graphic app, has a delightful screenshotting feature parked under ⌘⇧7 that does something incredible: it takes a screenshot, but does so in a way where windows are separate layers, grouped by app. It’s amazing; you can […]

February 28, 2026, 4:24 pm >>

From: Boagworld

Is your website copy faceless?

Is your website copy invisible? Three quick tests to find out if your headlines are too generic to differentiate you, and how to fix them.

From: Dave Rupert

Smaller and dumber

If I can make it smaller, I should.

If I can make it dumber, I should.

Smaller, dumber things have more applications, go more places, and require less maintenance.

From: Dave Rupert

Priority of idle hands

I had a small, intrusive realization the other day that computers and the internet are probably bad for me. I mean that beyond the general advice to touch grass. From an ADHD and generalized anxiety perspective, computers and the internet have become an endless supply of poison pills for my brain; feeds full of constant dopamine hits with doom at every turn.

This is hard to accept because a lot of my work, hobbies, education, entertainment, news, communities, and curiosities are all on the internet. I love the internet, it’s a big part of who I am today, but I understand how its incentive structures harm me. I’m not planning to unplug and go off-grid yet, but it did inspire me to come up with a “priority of constituencies” for my idle hands and downtime:

Instruments over pads of paper over laptop over tablet over phone.

From: Zeldman

American healthcare

Cooling my heels at the drugstore.

The post American healthcare appeared first on Jeffrey Zeldman Presents.

From: Boagworld

It’s all interconnected

Why CRO, UX design, and design leadership aren't separate disciplines. They're three interconnected pieces that make each other more effective.

From: Rachel Andrew

Generative AI has broken the subject matter expert/editor relationship

Some thoughts about managing a publishing pipeline in a world of generative AI.

From: Zeldman

The salad bar theory of UX professionalism

Less, but better? Not this week.

The post The salad bar theory of UX professionalism appeared first on Jeffrey Zeldman Presents.

From: Stuff & Nonsense

I made sho!io. Share what you’re working on without the social noise

I’ve been quietly working on a side project called sho!io, and I’m opening it up to a few people. It’s meant to be a quiet place where you can share one thing you’re making each day, without the usual social noise. It’s early and very much still in progress, but I’d really value your honest feedback if you’re up for trying it.

sho!io timeline in grid view

Many design platforms started out as places to share work with friends. Then they became feeds. Then they became popularity contests. Then they became businesses built on attention. Along the way, the joy went missing. I want to bring it back.

Post one piece of work every 24 hours

I wanted somewhere where I could share what I’m working on every day, not a pseudo-portfolio, or a place to attract work. So, on sho!io, you post one thing a day. It can be an illustration, a layout, a sketch, or an unfinished work in progress. It doesn’t have to be perfect. It just has to be yours.

sho!io timeline in list view

You can follow people, and they can follow you back, but no one else can see who you follow or who follows you. Likewise, you can like someone’s post, and they can like yours, but no one else can see how many likes a post received.

sho!io post detail

There are no public follower counts, visible popularity contests, and no pressure to perform. You can keep your profile private and share only with people you trust. Or it can be public. It’s your work, so your decision.

Building an app from scratch isn’t something I’ve done before, so building sho!io was a real journey into unexplored territory, as I had to create a Postgres database in Supabase and set it up using SQL queries. But, with a little help here and there, I managed it, and the scripts which enable membership, following, liking, and posting.

sho!io account settings

I know it can be improved massively, so if more people want to use it, I plan to find a developer who can help it scale.

Right now, I’m ready to open up sho!io to a few people. It’s early and very much still in progress, but I’d really value your feedback if you’re up for trying sho!io. Add your name to the wait list, and I’ll send 25 invites a week for the next few weeks.

sho!io wait list

Join the wait list

From: Boagworld

Why I’m Not Worried About My AI Dependency

Should we worry about over-relying on AI? After becoming completely dependent on it, I think we're asking the wrong question.

From: Stuff & Nonsense

Unfinished Business #139: I like red and I like hats

This week on Unfinished Business, Rich and I are joined by developer and organizer of London Web Standards and State of the Browser, Dave Letorey.


Sponsored by Contract Killer

Most web design contract templates are stuffed with boilerplate content your clients will never read. Contract Killer covers everything—payments, revisions, scope, and timelines—in plain, human language. Trusted by designers for over a decade. Peer-reviewed, fully customisable, and built to keep projects on track, and your invoices paid.

Buy Contract Killer


Also available on YouTube

For anyone who can’t get enough Unfinished Business, we publish the show in video format on YouTube.

Watch “Unfinished Business” on YouTube

Sponsor Unfinished Business

The best way to promote your product or service to the Unfinished Business audience of creative professionals, designers, and developers. Sponsors get one exclusive two-minute ad per episode, live read by me or Rich, which feels personal and relatable. We can use your script or ad-lib, and you’ll also receive a link on each episode’s post, as well as a thank-you on our Bluesky and Mastodon accounts. Interested? We’d love to hear from you.

Support Unfinished Business on Patreon

We also have three monthly membership plans so you can support Unfinished Business on Patreon. $5, $10, and $20 if you’re feeling especially generous. All money goes towards podcast editing and hosting. Support Unfinished Business on Patreon.

From: Zeldman

Works in Progress

New tunes from an old maker.

The post Works in Progress appeared first on Jeffrey Zeldman Presents.

From: Dave Rupert

Magic Words

Skills are the newest hype commodity in the world of agentic AI. Skills are text files that optionally get stapled onto the context window by the agent. You can have skills like “frontend design” or “design tokens” and if the LLM “thinks” it needs more context about that topic, it can import the contents of those files into the context to help generate a response.

Generally speaking, skills do an okay job at providing on-demand context. Assuming the AI model is always 12-to-18 months behind in its training data, a skill could potentially backfill any recent framework updates. A skill could potentially undo some training data biases. A skill could potentially apply some of your sensibilities to the output. I’ve seen some impressive results with design guidance skills… but I’ve also seen tons of mediocre results from the same skills. That’s why I deliberately use the word “potentially”. When skills can be optionally included, it’s hard to understand the when and why behind how they get applied.

In that way, skills remind me a bit of magic numbers.

In programming “magic numbers” are a pattern you typically try to avoid. They’re a code smell that you haven’t actually solved the problem, but found a workaround that only works in a particular context. They’re a flashing light that you have brittle logic somewhere in your system. “We don’t know why, but setting the value to 42 appears to have fixed the issue” is a phrase that should send shivers down the spine.

And so now we have these “magic words” in our codebases. Spells, essentially. Spells that work sometimes. Spells that we cast with no practical way to measure their effectiveness. They are prayers as much as they are instructions.

Were we to sit next to each other and cast the same spell from the same book with the same wand; one of us could have a graceful floating feather and the other could have avada kedavra’d their guts out onto the floor. That unstable magic is by design. That element of randomness –to which the models depend– still gives me apprehension.

There’s an opaqueness to it all. I understand how listing skills in an AGENTS.md gives the agent context on where to find more context. But how do you know if those words are the right words? If I cut the amount of words (read: “tokens”) in a skill in half, does it still work? If I double the amount of words, does it work better? Those questions matter when too little context is not enough context and too much context causes context rot. It also matters when you’re charged per-token and more tokens is more time on the GPU. How do you determine the “Minimum Viable Context” needed to get quality out of the machines?

That sort of quality variance is uncomfortable for me from a tooling perspective. Tooling should be highly consistent and this has a “works on my machine” vibe to it. I suppose all my discomfort goes away if I quit caring about the outputs. If I embrace the cognitive dissonance and switch to a “ZOMG the future is amazeballs” hype mode, my job becomes a lot easier. But my brain has been unsuccessful in doing that thus far. I like magic and mystery, but hope- or luck-based development has its challenges for me.

Looking ahead, I expect these types of errant conjurations will come under more scrutiny when the free money subsidies run out and consumers inherit the full cost of the models’ mistakes. Supply chain constraints around memory and GPUs are already making compute a scarce resource, but our Gas Towns plunder onward. When the cost of wrong answers goes up and more and more people spend all their monthly credits on hallucinations, that will be a lot of dissatisfied users.

Anyways, all this changes so much. Today it’s skills, before that MCP, before that PRDs, before that prompt engineering… what is it going to be next quarter? And aren’t those all flavors of the same managing context puzzle? Churn, churn, churn, I suppose.

File under: non-determinism

From: Zeldman

Claude Code for Designers

FIRST, the disclaimers: Some of my favorite writers—folks who are as anti-fascist and pro-democracy as they come—publish on Substack, but I read and recommend their work less and less frequently, because Substack has a Nazi problem. To wit: Awkward: Substack’s Nazi Problem Substack call themselves a platform rather than a publication, a classic web conundrum. […]

The post Claude Code for Designers appeared first on Jeffrey Zeldman Presents.

From: Boagworld

Stuck in a Website Fixing Loop? Try This.

Fixing a broken website means inheriting all the reasons it broke. Try starting from nothing instead.

From: Zeldman

“A streamlined newspaper for a streamlined era”

Posted today for no particular reason.

The post “A streamlined newspaper for a streamlined era” appeared first on Jeffrey Zeldman Presents.

From: Dave Rupert

Write about the future you want

There’s a lot that’s not going well; politics, tech bubbles, the economy, and so on. I spend most of my day reading angry tweets and blog posts. There’s a lot to be upset about, so that’s understandable. But in the interest of fostering better discourse, I’d like to offer a challenge that I think the world desperately needs right now: It’s cheap and easy to complain and say “[Thing] is bad”, but it’s also free to share what you think would be better.

If complaining worked, we would have won the culture war already. We’d have a reformed Elon and the White House wouldn’t be committing crimes against humanity. But that’s not the world we live in. The one we live in is much worse. If you hate the here and now, write about what would be a better future. Write about what’s good and why more of that good would be good.

If you believe the current trend in tech is exploitative, write about tech that isn’t. What impresses you? What compromises did you make? Are you happy? Does giving money to a small bootstrapped company feel ten thousand times better than a large venture-backed company like I imagine? Can you list some of those out for me? Show me where I can throw my dollars.

If you detest content theft at a massive scale, describe a world where copyright matters. Tell me how you’re only going to watch TikToks with royalty free music from now on and not use an ad-blocker. Tell me how you’re going to delete that hard-drive of pirated content. No? Then invent a better copyright system! Talk about the commons and how we should tax people who abuse it by taking more than they give. What does that system look like?

If you detest the ecological impact of AI data centers, propose an alternative piece of technology that drives shareholder value. Or advocate for a broader, more equitable definition of Responsible AI. Don’t let the Effective Altruists define the terms. Itemize the problems and then dream up a version that doesn’t have those. List demands. Find levers. Or make the case for how small, local, private, and less energy-intensive models are probably “good enough” for most use cases and we should stop burning barrels of crude and use those instead. If we must throw it away, point people to organizations and political groups working towards that end.

If you believe billionaires are the problem, talk about a world without them. Share how we redistribute a billionaire tax equitably and talk about the impact it has on society. Make a spreadsheet or a chart. Get into numbers. Talk about degrowth. I know that “Line Goes Up” has problems and won’t work forever, but how are my family and I more okay if we undo centuries of continued economic growth and technical acceleration.

I think history is wrought with examples of this working; Dr. King’s “I have a Dream” speech, the Federalist Papers, David Hasselhoff singing at the Berlin Wall during the height of the Cold War… the list goes on. Few will rise to the level of Ambassador Hasselhoff, but I don’t have to look far to find people around me who have inspired me by writing about the future they want.

After repeated mass layoffs, Ethan Marcotte identified a problem with the wealth and labor dynamics in the tech industry. While Ethan could have been perfectly happy bitching about it on Twitter, he didn’t do that. Instead, he put hands to keyboard and made a talk, which turned into a book, about what he feels is the time-tested solution to our predicament: Unions. Truthfully, I’m not particularly predispositioned to be pro-union (Texan, etc), but Ethan and I agree on the problem. Seeing Ethan spend time to communicate the problem and explore the solution changed my opinion on unions. And while I certainly still have nuanced questions, Ethan convinced me that collective action is the best leverage workers have against abuses of labor. That wouldn’t have happened without Ethan’s dedication to highlighting the problem and the solution over many years.

Often people need you to show, not tell the alternative. You need to paint a picture. Not a full complete picture, but one where a person can paint themselves in it. When people want change and bad change is happening all around them, that’s a hopeless place. Build a raft of opportunity that people can latch onto in the rough open waters, instead of hitting them with spears.

From: Stuff & Nonsense

Panzera Flieger 47

I bought my first mechanical watch back when we were living in Australia for those couple of years. It was a Flieger 47 from the Australian brand Panzera, and it sparked my love of pilot watches.

I’m not sure how I found out about Panzera, but I suppose it was an ad on social media, most likely Instagram. They’re based in Sydney—which is where I was—and design their own watches using Swiss movements. The Flieger 47 was an homage to the WW2 pilot watches made for the Luftwaffe and Allied air forces by brands like IWC.

Panzera Flieger 47
Left: Panzera Flieger 47. Right: My IWC Big Pilot 43.

It has all the hallmarks of a Big Pilot, with an oversized onion crown and a large, clear display. From a reasonable distance, it looked very similar to an IWC Big Pilot and coffee shop watch nerds commented on it more than once.

I bought one in a sale for around AUD $400 and loved it. That was until the crystal mysteriously cracked during some very hot weather, and it’s been in a drawer ever since.

I really must send it back to Panzera for repair, although I suspect that will cost me more than what I paid for the watch when I bought it.

From: Zeldman

Mark your calendar: Local News Day is 9 April

It’s no secret that newspapers across the country exist in a fragile ecosystem. Automattic has long supported journalism and local media with investments in publications and platforms like Longreads, The Atavist, and Newspack. We believe that local news still matters—and now more than ever. That’s why we’re sponsoring Local News Day on April 9, 2026. — Support Journalism with […]

The post Mark your calendar: Local News Day is 9 April appeared first on Jeffrey Zeldman Presents.

From: Boagworld

Why Moving Buttons Won’t Fix Your Conversion Rate

CRO agencies promise quick UI fixes, but real conversion improvements need content overhauls and organizational change too.

From: Zeldman

We named them after the humans they were replacing.

“The word ‘computer’ only really slid over to mean ‘a machine’ in the late 19th and early 20th centuries, once we started building mechanical and then electronic devices to do that work instead [of people]. We did not name the machines after some abstract idea. We named them after the humans they were replacing.”

The post We named them after the humans they were replacing. appeared first on Jeffrey Zeldman Presents.

From: Dave Rupert

I'm swearing off APIs entirely

I got a lot of ideas for side projects rattling around in the old tin can. As part of my “No new projects” initiative, I’m trying to jump on building prototypes so I can decide if I want to explore ideas more or call it quits. A handful of my ideas are riffs or twists on existing app categories:

  • A tennis ranking app… but modern and performant
  • A nearby historical marker app… but with CarPlay support
  • A nearby real estate listing app… but with CarPlay support

All three of those have ended unceremoniously at the same dead end: no API access. USTA denied my application for tennis rankings. The aptly named Historical Marker Database site doesn’t have a public API. And you have to be an MLS® Realtor® or Broker to get access to the MLS® listings. Womp womp.

Scraping the data is always an option… but I don’t like the ethics of that and worry about the brittleness of that dependency.

Ugh. I wish I could build these little apps so that tens of people could enjoy them. I’d even be willing to pay a small API access fee ($10/mo?) and run these at a loss but whatever happened to free APIs. When I survey the land of public APIs it feels like we’ve lost a lot since the Web 2.0 days where API access was almost a God-given right.

To prevent this time loop of disappointment from happening again, I’m swearing off APIs entirely. That’s a hard stance, but I need a backstop at the idea phase to prevent me from wasting limited life force. If I don’t have the data, or can’t generate the data, or it’s not an open protocol… it’s not worth building or even thinking about.

OAuth apps are a good option and generally the best way to exfiltrate data because it’s tied to a user’s account…. but you still might run into call limits, incomplete endpoints, user-scope limitations, and so on. History also shows us what the future holds. There’s a Tweetbot-style risk when building on a someone else’s platform. Even if your app drives activity to the parent application, your access might get cut because it competes or doesn’t drive stakeholder value. And if the idea isn’t big enough, being “a feature, not a product” is also a bad position to be in, lest you get Sherlock’d.

Where’s that leave me and my pile of side project ideas? Thankfully… in a good place. I can close out these project tabs and free up some much needed Brain RAM. It sounds strange but “No more APIs” makes “Making video games” jump up in the viability rankings for side projects too, because games have closed ecosystems. Or I could spend more time writing shitty sci-fi. Write a serial. Print some zines. Who knows.

If the goal of “No new projects” is to finish more projects than I start, then I have to accept that part of figuring out which ideas to explore means “Nope” is a potential answer. It’s also not a total loss, I’m learning along the way. For example, CarPlay only lets you choose from eight pre-approved templates. There’s also pre-defined app categories and diverting in the slightest would almost guarantee App Store rejection. That sucks the fun out it… but ayyyy, I’ll probably try again. But now I know the limitation for future projects and its in the limitations where play begins.

From: Dave Rupert

Waiting for the power to go out

It’s a secret to everyone! This post is for RSS subscribers only. Read more about RSS Club.

It’s expected to freeze this evening in Austin and we may even see snow, which is exciting and novel for us Texans. But as we’ve learned in 2021 and 2023, cold snaps can lead to disaster. In those years the ice dealt untold damages as foot-thick tree limbs fell from the canopy like enormous glass Cadillacs. The crackle, pop, followed by crystalline shatter in the cold dead air is a sound I won’t forget. With the falling trees came down power lines and the lights went out.

It got worse. The disconnected Texas power grid failed. As homes and empty office buildings required more heating in the “extreme” cold, the rolling blackouts began to shed load. Except… they didn’t roll. Homes next to facilities (hospitals, fire stations, and water treatment plants) deemed critical were fine, but the rest of us were off-grid in the cold for nearly a week. The energy companies could have met capacity, but quit making energy because they would have had to sell it at a loss. Greed until the state promised them a windfall.

The infrastructure failed. Pipes burst, homes flooded, and trauma increased. While our senator and his banker wife fled to Mexico to stay in a Ritz Carlton, Texans burned their IKEA furniture and cedar fences to stay warm.

Building on past lessons, we’ve made all the preparations for today; external faucets capped, plants covered, and I built a cabinet out of foam board insulation for our external hot water heater. And now we wait… we wait to see if the Libertarian Capitalist systems my state has put into place will hold up. Waiting to see if the power goes out. Waiting for the impending disaster.

If you’ve ever had a child, you might know this feeling. You’ve taken the classes, assembled the crib, child-proofed the outlets, and then you wait… you wait for your life to change. While the miracle of childbirth is a beautiful event, any mother can tell you it’s not without some drawbacks. From prolonged labor, painful contractions, episiotomies, and beyond… every birth is a dice-roll of potentially life-threatening complications. A friend of mine who had three kids described it well; “Having a baby is like knowing you’ll be in a car accident.”

That feeling of pregnant, expectant waiting dominates a lot of my life right now as I wait for all the larger meta narratives in our country to unravel. I’m waiting for a mid-term election, one where I feel the fabric of democracy hangs by a thread. I’m waiting for an opposition party that has a fucking plan, but I don’t see one yet. I’m waiting for a particular person to leave office… or (preferred) die of natural causes. I’m waiting for the full release of the Epstein files, so the victims of billionaire pedophilia and human trafficking can have justice. I’m waiting for the government to stop its reign of cruelty on its own people, but masked fascists killed another innocent person in broad daylight today. I’m waiting for the economy to work for most Americans, not only the wealthy. I’m waiting for either the tech bubble to pop or for the robots to take my job… either way I’d like this story line to wrap up soon but I must ask, is there a third option? I’m waiting for the next big thing and praying it’s something that makes the world more loving and kind, but a new global religion would be a lot to deal with right now.

From: Boagworld

Generative Imagery: Stop Settling for Stock

Stop settling for 'almost right' stock photos. A practical guide to generative imagery tools, when to use them, and how to get consistent results.

From: Dave Rupert

The best version of my site so far...

You might have noticed that I did a big design refresh on my entire site… unless you’re on RSS I guess. I’ll talk about aspects in detail, but at a high level there’s been three big changes:

  1. A monospace font
  2. Named CSS grid lines
  3. Juicier multi-page view transitions

But my most favorite part is that today (when you set html { --hue-rotate: 15 }), my light theme looks like a stick of butter. And that brings me joy.

What didn’t change

Before I talk about what changed, here’s a quick list of the parts of my site that didn’t change:

Why change the parts you’re happy with, eh? Knowing what you want to change versus what you don’t want to change makes the whole process go faster.

All-in on a monospace font

My type system always falls apart. I start with a good setup and I’m guaranteed to ruin it over time. Whenever I read a site with a nice typeface and reasonable CPL, I started to feel like my system-ui based body text styles were letting me down. It didn’t feel cozy.

Then I saw Robin Rendle’s latest redesign (which is wonderful, btw) I wondered… Could I be a monospace font guy? Is that me? Do I identify as that? Is it too emo? Does it come off as a bad developer pun like bleep blorp I'm a programmer and using a computer bleep blorp?

All good questions to ask myself and exposes some insecurity I have, but I couldn’t shake the idea so I went for it.

The entire site is now set in Cascadia Mono which is a variant of Microsoft’s Cascadia Code. That decision is unintentionally on-brand for my current stage of life. Initially I wanted to use Inconsolata by Raph Levien, but it didn’t have a “numero” (№) symbol – which is a common missing glyph in a lot of indie fonts – and is an unfortunate piece of critical typographical infrastructure with my vibechecks.

In general I love the change. It feels very “me” right now. I worry about the readability of monospace fonts some, but I don’t think I was winning the Legibility Wars before.

Naming my grid lines

Improving the grid on my site is something I’ve wanted to do for nearly two years. The max-width container I had was fine before, but I relied a lot on margin overrides to change a container that felt like a hack that in my experience don’t scale.

Now I have something better with some sweet tech from Stephanie Eckles that I saw on Kevin Powell’s YouTube called “Named Grid Lines”. A not-so-well-known feature of grid is you can name the gutters in your grid template. The result is a grid that looks like this:

Full
Margin
Wide
Outdent
Content (Default)

See how all the grids collapse into one line that’s offset from the edge? That’s what I want. And then to use it, you specify the *-start and *-end line you tweak your container with a modifier class and make all the children start at a different grid line.

.foo.container > * {
	grid-column: wide-start / wide-end; 
  /* or grid-column: wide */
}

This gives me a lot of scalable control for my layout at the template level. For example, the layout of my homepage looks like this:

Header
Logo
Latest Posts
Projects
Footer

But when you click through to a single post, the template shifts to this:

Header
Title Goes Here
Content
Media
Content
Content
Media
Content
Content
Footer

I don’t have this setup, but I could write out a system of utility classes to make all this super flexible.

.full-start
.margin-start
.wide-start
.outdent-start
Default Content
.outdent-end
.wide-end
.margin-end
.full-end

That’s a lot of different layouts to craft. I look forward to more sustainable art direction for years to come. No more hacky negative margins or fixed positioned items… right?

More Multipage View Transitions

I’ve had multi-page view transitions on my site for years now. The original was a title transition from the index template to the post template where the post title would do a subtle swoop up and morph from an H3 to display-sized H1. This was great until I changed the font for my H1 (see above) and I obliterated the slight of hand magic I had before. Oops.

Now the flagship animation on my site is not typography dependent. If you navigate through my site, you’ll see that the “Dave” SVG on the homepage transitions into the main navigation on subpages. As a bonus, I get a little bit of much-needed branding on subpages. I didn’t do anything special other than naming the view transition and beefing up the stroke when the logo is in its smaller form.

The main nav is out-dented a bit by default, but using my new named columns I made the navigation go wider to match wide pages like my Bookshelf. Flexbox’s justify-content: space-between and view-transitions is doing all the heavy lifting on that animation there but I’m pleased with the effect and it feels natural (aka not synthetic and cheap).

I’m not a motion expert like Cyd Stumpel, but it feels good to have big, bold, un-ignorable view transitions on my site.

Slimmed down CSS

A redesign is always a great opportunity to clean house. I did the thing where I deleted my entire CSS and started from a naked page. Bit-by-bit I pulled back in the pieces I still needed.

I have about 40% less CSS which manifests in ~150 less lines of code to maintain and up to ~0.8 KB when compressed over the wire.

LoC Raw Min+Gzip Min+Brotli
Before 557 10.3 KB 2.76 KB 2.43 KB
After 391 5.36 KB 1.97 KB 1.76 KB

Now 0.8 KB isn’t much but because I inline my critical CSS the <head> on every page, this number matters. My entire homepage document is ~5.5 KB (Brotli), so that’s a 10%~20% performance improvement from cleaning up my CSS. How did I get these gains, you ask? Well…

One reason my CSS smaller is because I’m using modern CSS (nesting, :has(), :is(), :where(), :not(), etc). Those features seem like minor syntactic sugar but you also get more efficient selectors. For example my out-denting that I do in posts would previous be something like:

.post iframe, 
.post video, 
.post img,
.post table { }

In modern CSS that super-conjoined selector becomes something more expressive and elegant:

.post :is(iframe, video, img, table) {}

Those small improvements add up, but another reason the CSS is smaller is because its doing a lot less. Other than my inverse-sized H1s in posts, I’m not managing type styles at all beyond setting the body font-size. I think this is what Andy Bell calls “Be the Browser’s Mentor, Not Its Micromanager”. My strategy here is: Don’t futz with it until you can think of something way better than what the browser already does. That seems very Dao or Web’s Grain’y.

Onward to more art direction!

This redesign opens up a bright future for my site. I’ve already started on some of this work, but I’ll share that in a future post. I mentioned above but the named grid lines alone give me a lot of easy-to-use levers for expression. I touched up some old art-directed posts and they are benefitting from the changes already.

“Make it easy to play” seems like a good ethos for a personal site and I feel like I have that in the current manifestation.

From: Boagworld

Be a contributor, not a lurker

Stop lurking. Start contributing. Simple ways to build reputation and relationships (without turning into a LinkedIn gremlin).

From: Stuff & Nonsense

Smashing Animations Part 8: Theming Animations Using CSS Relative Colour

Yours truly over at the Smashing Magazine: “CSS relative colour values are now widely supported. In this article, pioneering author and web designer Andy Clarke shares practical techniques for using them to theme and animate SVG graphics.”

Read Smashing Animations Part 8: Theming Animations Using CSS Relative Colour

From: Dave Rupert

Focus rings with nested contrast-color()?

As I was playing around with contrast-color(), I got a wild idea that you could use contrast-color() to invert its return value by nesting it: contrast-color(contrast-color(var(--some-color)). When would this be useful? Uh… Good question. I couldn’t come up with an example right away but after a bit I found one sitting right under my nose….

four buttons in one line. a secondary button, an accented primary button with a focus state, a subtle button and a transparent button

Our focus-rings in Fluent use a 1px inset white highlight and a 2px offset black focus-ring. It’s a smidge chonkier than the Chromium default. The reason we do this is to guarantee contrast against the focused-element, in the above example, a blue button. Without the addition of the white stroke, the black outline wouldn’t “pop” with enough contrast to the blue background.

To make this work, we have a --focus-inner-ring token and a --focus-outer-ring token themed for both light and dark modes.

*:focus-visible {
  box-shadow: 0 0 1px 0 var(--focus-inner-ring);
  outline: 2px solid var(--focus-outer-ring);
  outline-offset: 1px;
}

How would this change with nested contrast-color()?

*:focus-visible {
  outline: 2px solid contrast-color(var(--page-bg));
  outline-offset: 1px;
  box-shadow: 0 0 1px 0 contrast-color(contrast-color(var(--page-bg)));
}

All you would need is one token you probably already have (--page-bg) vs two tokens. Neat.

One consideration… your focus-ring isn’t always on a --page-bg. Sometimes it shows up on a --card-bg and this little trick might fall apart. As always, your mileage may vary.

Aside: This got me thinking it’d be nice to have a currentBackgroundColor like we have currentColor in CSS today. I’m not sure how much career I’ve got left to wait for that, but who knows.

On second thought…

Mulling this over a bit more… you might be better off using color-scheme and light-dark() for this instead.

 :root {
	 color-scheme: light dark;
 }
 
 *:focus-visible {
   box-shadow: 0 0 1px 0 light-dark(white, black);
   outline: 2px solid light-dark(black, white);
   outline-offset: 1px;
}

Yeah… I’d probably do that. You dodge the spotty contrast-color() support and have a singular function instead of a nested situation. It keeps it simple and readable.

If you converted this to tokens, you’d probably need four tokens to fill out the inner/outer and light/dark matrix. But I’d consider not using custom color tokens for focus-rings at all and embrace the higher contrast… or limit tokens to the outer-ring.

From: Dave Rupert

Interpolate contrast-color() to manipulate lightness

In my first post on contrast-color() I demo’d using color-mix() to change a background-color on hover, but I will be honest… mixing black and white isn’t always what you want. It would be cool and helpful to coerce contrast-color() to return either 1 or -1 so that we could adjust lightness in a color function on hover instead of only mixing white and black.

Building on the inline CSS if() statements in my last post, we can use the same trick to interpolate the result of contrast-color() into a number.

Disclaimer: All caveats from the previous post about browser support, caching quirks, and expected syntax changes still apply.

See the Pen contrast-color() powered design system colors by Dave Rupert (@davatron5000) on CodePen.

Ahh… feel that? Now our states maintains its harmonious color palette where mixing in white or black gets us a bit muddier results. We’re picking another note on the scale of the color’s lightness ramp.

The relevant CSS to make this trick work goes like this:

/* Needed for if() statement */
@property --captured-color {
  syntax: "<color>";
  inherits: true;
  initial-value: white;
}

/* https://lea.verou.me/blog/2024/contrast-color/ */
@function --contrast-color(--bg-color) {
  --l: clamp(0, (l / var(--l-threshold, 0.623) - 1) * -infinity, 1);
  result: oklch(from var(--bg) var(--l) 0 0);
}

button {
  background-color: var(--ds-button-bg);
	--captured-color: --contrast-color(var(--ds-button-bg));
  --lighter-or-darker: if(
    style(--captured-color: oklch(1 0 0)): 2.5; /* go extra lighter */
    else: -1; /* go darker */
  );  
  ...
  
	&:hover, &:focus {
		background-color: oklch(
			from var(--ds-button-bg) 
			calc(l + (0.1 * var(--lighter-or-darker))) c h 
		);
  }
}

For comparison’s sake, I web-inspected up a little apples-to-apples, side-by-side of the adjusting lightness way and the color-mixing way of algorithmic hover states where it’s 10% lightened/darkened versus 10% mix of white/black.

a side by side look at adjusting lightness vs mixing in white and black. the rest for all the buttons is on top and the hover state for all the buttons is on the bottom. The hover states for the buttons on the adjusting lightness demo are a bit warmer, but probably hard to notice to the average person.

The difference is almost imperceptible, but the hover states from the “Adjust Lightness” method feel a tad bit warmer, particularly on the bottom row with the green, blue, and purple buttons. The difference becomes more obvious if the step is greater than 10%.

Unless your customers are a bunch of color dorks, they probably won’t see it the care you put into this. But I’m willing to wager that even if they don’t see the difference, they will be able to feel the difference.

We also get a lot more control with this if() statement route. For example, I can set the lighten amount to 2.5 (+25%) instead of 1 (+10%) because that’s what felt better. If you’re algorithmically generating your color palettes, it should be easy to find the ideal values; either a step-up or step-down, perhaps. And we’re also not just limited to lightness! You could mess with chroma or whatever the b in lab() is. Find what makes sense for your system.

A part of me wants to take this even further to get more control. For example, if the color is super dark (e.g. black) and the lightness value is below 0.1, lighten by 25%, otherwise lighten by 10%. That might be possible with an if() inside the oklch() or a sin() function… but that sounds like a lot of Math and probably hurts readability. More experiments to do though.

It’s fun to embark on this new world of algorithmic color schemes in vanilla CSS. While I’m excited to play, I’m more excited to see what your beautiful brains come up with.

From: Dave Rupert

Using your design system colors with contrast-color()

One predictable pain point with contrast-color() is that it only returns black and white named colors. From a design systems perspective, that’s not ideal because you want your colors. You want your harmonious brand and the colors you and your team spent thousands of man hours in meetings deciding on. Those colors.

In fact, an earlier version of Safari had color-contrast() (confusing I know, naming is hard) which allowed you to pass in a list of best candidates to choose from. I beleive that proposal got mired in standards discussions, color contrast algorithms, and competing proposals; and contrast-color() is what survived which got simplified down to a binary result.

In the future though, we can use contrast-color() and if() together to help pick the value we want. Alas, at the time of writing no browser supports both if() and contrast-color(). But we can use Lea Verou’s --contrast-color() workaround to experiment with how this will work in Chromium today.

See the Pen contrast-color() powered design system colors by Dave Rupert (@davatron5000) on CodePen.

The CSS to get this working looks something like this:

@property --captured-color {
  syntax: "<color>";
  inherits: true;
  initial-value: white;
}

/* https://lea.verou.me/blog/2024/contrast-color/ */
@function --contrast-color(--bg-color) {
  --l: clamp(0, (l / var(--l-threshold, 0.623) - 1) * -infinity, 1);
  result: oklch(from var(--bg) var(--l) 0 0);
}

:root {
  --ds-text-white: wheat;
  --ds-text-black: darkslategray;
}

button {
  background-color: var(--ds-button-bg);
  --captured-color: --contrast-color(var(--ds-button-bg));
  
  color: if(
     style(--captured-color: oklch(1 0 0)): var(--ds-text-white);
     else: var(--ds-text-black);
  );
}

And that’s it! Using (a form of) contrast-color() you can select the proper tokens from your design system. Cool.

One quirky bit needed to make it work is defining a type for --captured-color using CSS @property, a trick I learned from Roma Komarov. To be honest I don’t fully understand the why behind Registered Custom Properties and the Computed Value Time Behavior superpower, but my simple brain created a rule “If you’re going to compare a variable to a <color> in a CSS if() statement, make sure to register the variable as a <color>.”

If/when any of the browsers start supporting both features, I expect we’ll have to update oklch(1 0 0) in the style query to white or rgb(255 255 255). At least, I hope it works that way.

One unexpected challenge that I encountered with this demo was that if I abstracted out the if statement out into its own custom function, it would break. I’m not 100% sure why but I think it’s based on how CSS functions cache results. I’ll have to dig into this more, but I’m happy the inline if statement works. Hopefully someone smarter can figure it out.

Anyways, exiting times. And can we pause for a moment and marvel at how this is all vanilla 2026 CSS?! What a world.

From: Stuff & Nonsense

The Timex x Pan Am Waterbury Automatic Ace

Talk to my family and friends, and they’ll tell you I never bloody stop talking about the IWC Big Pilot 43 watch that I bought to celebrate my 60th in November. Mechanical watches are all over my social media feeds, and the other day I did a double-take when the algorithm suggested I might like a new design from, of all companies, Timex.

The Timex x Pan Am Waterbury Automatic Ace is a 41mm brushed stainless-steel watch from a collaboration between Timex and the nostalgic Pan Am brand. I immediately thought there was something familiar in this watch’s design. But I couldn’t somehow put my finger on it.

Left: IWC Big Pilot 43. Right: Timex x Pan Am Waterbury. (Images from IWC and Timex.)

The Timex x Pan Am Waterbury bears more than a passing resemblance to the latest addition to IWC’s iconic Big Pilot line, the Big Pilot 43, which is on my wrist now. But it most closely resembles IWC’s Pilot’s Watch Mark XX. You can see just how closely Timex followed IWC when the two dials are scaled to match.

Left: Timex x Pan Am Waterbury. Right: IWC Pilot’s Watch Mark XX. (Images from IWC and Timex.)

The Timex’s hour and second hands match the IWC Mark XX, while the minute hand is straighter. Its date window is the same size, although I’m unsure why the dot—between it and the marker in the 3 o’clock position—was necessary. Placing the word “Automatic” under the 6 is neat and leaves room for the Pan Am logo above. I actually prefer this placement to IWC’s, where it’s beneath the Mark XX type. The size relationship between the words “Timex” and “The Waterbury” looks identical to that between “IWC” and “Schaffhausen.”

Close-up of the Timex x Pan Am Waterbury dial. (Image from Timex.)

Timex did a nice job of placing the Pan Am logo on the crown and using its signature blue on the inside of the strap.

Pan Am logo on the crown. (Images from Timex.)

On the dial, the line between that famous logo and the plane icon at the 12 o’clock position comes straight from Pan Am’s design history.

Linework on the Pan Am logo and Timex dial. (Images from Pan Am and Timex.)

I don’t know which typeface Timex chose for their numerals on the Waterbury dial, but it certainly isn’t the Helvetica, which Pan Am briefly switched to between 1970 and 1973.

Helvetica and the short-lived Pan Am brand redesign. (Image from Timex.)

Oh, and if you’re curious how Helvetica looks on a watch dial, IWC used it for the numerals on its 1993-released Mark XII.

Helvetica on the IWC Mark XII. (Image from Chrono24.)

I suppose that, being a collaboration with Pan Am, the question to answer is whether this is a successful pilot’s watch design? I’d say it is. It’s not overly large, but it is legible. The skinny minute hand isn’t as obvious as IWC’s, and there’s that inexplicable dot next to the date window, but as an homage to the pilot’s watch, it’s nice. Is it more than an homage to IWC’s iconic Big Pilot design? I’d say so.

But I like it. Hell, I might just buy one for fun to wear places and times when I wouldn’t be comfortable wearing the real thing.

From: Dave Rupert

Algorithmic hover states with contrast-color()

Firefox 146 added support for contrast-color() joining Safari 26 in the First Implementor’s Club. For those unfamiliar, contrast-color(<color>) is a new CSS function that will take a <color> as input and returns either white or black depending on which has the most contrast.

The quintessential example is choosing a foreground text color with the best contrast.

button {
	--button-bg: red;
	background: var(--button-bg);
	color: contrast-color(var(--button-bg)); 
	/* @returns black (5.25:1 WCAG AA Pass) 
			not white (3.99:1 WCAG AA Fail) */
}

If someone changes --button-bg to purple, the foreground color automatically resolves to the either white or black, whichever has more contrast. It avoids having to set an extra token for color and takes the guess work out of picking an accessible foreground color.

I think this is going to be an incredible boon to design systems where I don’t control what --button-bg is, but I do care about providing accessible experiences. And I think that’s the goal of this feature; to have “smart defaults” that lead to more accessible websites, easier algorithmically-driven color systems, and better “theme a whole website from a single color picker” demos.

Sure contrast-color() can do foreground colors, but what about backgrounds?

We’re having conversations at work about algorithmically driven rest/hover/active states for Buttons. In the current Baseline you can use color-mix() to lighten/darken colors on :hover

button {
	background-color: var(--button-bg);
	color: contrast-color(var(--button-bg));

	&:hover, &:focus {
		background-color: color-mix(
			in srgb,
			var(--button-bg) 90%,
			black 10%
		)
	}
}

The code above will dim your button on :hover 10% by mixing in black. But what if our Buttons are already dark (black, navy, etc)? In that situation we want to lighten the background-color instead of dimming. We can glue on new classes like button.lighten-on-hover or button.invert-hover and that works… until we get to light and dark theme modes of our Button where you probably want to lighten/darken oppositely depending on the mode…

Ugh. In that situation you’d have @media (prefers-color-scheme: dark), [data-theme="dark"] styles in your Button styles and people are mad now because the Button styles are too complex. There’s got to be a better way!

Well, do I have good news for you…

See the Pen contrast-color() powered lighten/darken bg on hover by Dave Rupert (@davatron5000) on CodePen.

Per my previous conversations, I wondered if we could use contrast-color() to programmatically lighten/darken an button based on its current background-color. If the Button is black, and the contrast-color is white, let’s mix in white (and vice versa):

:root {
	color-scheme: light dark;
	--button-bg: light-dark(navyblue, lightpurple);
}

button {
	background-color: var(--button-bg);
	color: color-contrast(var(--button-bg));

	&:hover, &:focus {
		background-color: color-mix(
			in srgb,
			var(--button-bg) 75%, 
			contrast-color(var(--button-bg)) 10%
		)
	}
}

Huzzah! Now our Button’s hover states go in the desired direction and our foreground color is intrinsically styled based on its own background-color. Nice. From a design systems perspective I’m pretty excited about the possibility to remove a bunch of state-based tokens from our collection.

Now available in… everywhere?

This approach only works in Safari and Firefox. However, if I use Lea Verou’s method of polyfilling contrast-color(), we can pop in a custom @function --contrast-color() that works in Chromium 139+. The final working solution looks like this:

/* @function supported in Chromium */
@function --contrast-color(--bg-color) {
  --l: clamp(0, (l / var(--l-threshold, 0.623) - 1) * -infinity, 1);
  result: oklch(from var(--bg-color) var(--l) 0 h);
}

button {
  /* contrast-color() supported in Safari & Firefox */
  --button-fg: contrast-color(var(--button-bg));
  
  @supports not (color: contrast-color(red)) {
    --button-fg: --contrast-color(var(--button-bg));
  }
  
  background: var(--button-bg);
  color: var(--button-fg);
  
  &:hover,
  &:focus{
    background-color: color-mix(
      in srgb,
      var(--button-bg) 75%,
      var(--button-fg) 10%
    );
  }
}

Depending on your browser matrix, this may work for you. It’s probably a smidge too new for us to roll out to customers, right now but for personal sites heck yeah.

This is interesting tech and I’m excited to dig in more. And spoiler alert, this is the first post in a small little series I have already written up for you.

From: Boagworld

Stop Lurking. Start Getting Known.

A practical, low-effort system for building your reputation (without turning it into a second job).

From: Stuff & Nonsense

Snow mode has just gotten even chillier

Although we’ve had a smattering of snow here in the Rhineland—where I’m basing myself for a couple of months—at home, an “arctic blast” means snow is threatening. Last week, I added a snow mode to my websites, but my pioneer characters still looked toasty and warm, and unaffected by the wintery conditions. So, it was time for them to get chilly.

My blog page animated graphic without snow.
Graphic with snow but characters still look warm.

I’ve been writing about CSS relative colour properties for Smashing Magazine, but until those articles land, here’s the short version.

Instead of specifying my characters’ skin tones in the SVG using a fill value:

<path fill="#F7F1ED" class="skin-highlight" d="[…]"/>
<path fill="#F7BEA1" class="skin-mid" d="[…]"/>
<path fill="#BA7E5E" d="[…]" class="skin-dark" />

And changing the colours via those class attributes, I removed the fill attributes altogether and replaced them with inline styles, which include custom properties:

<path style="fill: var(--skin-mid);" d="[…]"/>
<path style="fill: var(--skin-mid);" d="[…]"/>
<path style="fill: var(--skin-dark);" d="[…]"/>

Now, before you rush to your keyboard to tell me that inline CSS is bad, when used with custom properties, it’s very, very good indeed. As for those custom properties, they’re not just plain ol’ colour values; they’re a colour system. To start with, yes, I have those normal skin colours:

--skin-highlight-base: #F7F1ED;
--skin-mid-base: #F7BEA1;
--skin-dark-base: #BA7E5E;

I used them to turn Hex into OKLCH so I could manipulate each channel separately:

--skin-highlight: oklch(from var(--skin-highlight-base)
calc(l + var(--env-l))
calc(c * var(--env-c))
calc(h + var(--env-h))
/ calc(alpha * var(--env-a)) );

Those --env-* variables define how much each colour channel shifts. These are my placeholders, which leave the original colours untouched:

.outlaw {
--env-l: 0;
--env-c: 1;
--env-h: 0;
--env-a: 1; }

Snow mode is a toggle, so I then defined a new set of variables just for when it’s active:

.snow-mode .outlaw {
--chilly-l: -0.04;
--chilly-c: 0.35;
--chilly-h: -38;
--chilly-a: 1; }

Rather than changing the base environment values, I introduced a second set specifically for cold conditions. Then swapped the environment variables for chilly ones:

--skin-highlight: oklch(
from var(--skin-highlight-base) calc(l + var(--chilly-l)) calc(c * var(--chilly-c)) calc(h + var(--chilly-h)) /
calc(alpha * var(--chilly-a))
);

Then, I registered those properties to make them animatable:

@property --chilly-l {
syntax: "";
inherits: true;
initial-value: 0; }

@property --chilly-c {
syntax: "";
inherits: true;
initial-value: 1; }

@property --chilly-h {
syntax: "";
inherits: true;
initial-value: 0; }

@property --chilly-a {
syntax: "";
inherits: true;
initial-value: 1; }

And added a transition so the characters start off looking warm and then get progressively colder:

@media (prefers-reduced-motion: no-preference) {
.outlaw {
transition:
--chilly-l 15s ease,
--chilly-c 10s ease,
--chilly-h 10s ease,
--chilly-a 10s ease; }
}
Characters getting chilly.

I love adding surprise behaviours—especially ones that reinforce the idea that these characters are reacting to their environment. So now, when someone hovers over a chilly character, they also shiver:

@media (prefers-reduced-motion: no-preference) {
.snow-mode .outlaw:hover {
--shiver-duration: 0.5s;
--shiver-x: 1.5px;
--shiver-y: 0.4px;
animation: shiver var(--shiver-duration) ease-in-out infinite;
transform-origin: center bottom; }
Graphic with snow and characters looking chillier over time.

These are subtle changes, but they add to the fun. Most importantly, by changing the way I set up colour for my animated graphics, I can have much more fun more easily in the future.

Check out the new chilly characters on my home and blog pages.

From: Rachel Andrew

2025 in review

I concluded my 2024 review post by saying that I hoped to make the move back to the North of England in 2025. The event that defined 2025 (other than my 50th birthday!) is that I managed to do just that, and I’m writing this post from a little town in Northumberland, where I’ve been […]

From: Zeldman

Cold Storage

Good UX is what companies do when they have to. A company that has your stuff locked away doesn’t have to.

The post Cold Storage appeared first on Jeffrey Zeldman Presents.

From: Dave Rupert

Twenty Twenty-Five

2025 was… a year. And I made it to the end of that year. If you’re reading this, I imagine you did too and let’s celebrate that. But also not one without loss; of loved ones, of health, of relationships, of jobs, of liberties, of pursuits of happiness. Let’s mourn those.

My year was mundane by most accounts. I worked, I family’d, and with the remaining life force I focused on myself. A couple failed side projects and half-baked game ideas in there but welcomingly “unproductive” relative to what I normally subject myself to. On a handful of fronts this year has been about scaling back in what I burden myself with, in material posessions, in finances, in obligations, and elsewhere. Ideally, I can clear the plate of duties that demand my limited attention. To be so bored that I read a magazine.

Resolutions resolved

I set some slightly different goals last year. Let’s check on last year’s goals:

  • 🟢 Seize work opportunities - I’ll call this a success. Released a design system and token system. An executive-level mandate caused us to pivot the entire design language using the tokens. Success. Other big products are beginning to use our team’s work, we’re recognized for the accessibility of our product… which is great.
  • 🔴 Hunker down and be creative - In the interest of binaries, I’ll say no. I think I gravitated to work and mind-numbing recovery, not creativity. I wasn’t very productive beyond work and family life. I think given “the situation” in the homeland, that’s okay.
  • 🟠 Slow down to 1x mode - I still watch and listen way too much in 2x but at a certain point it does feel natural. Certain content gets the 1x treatment, but not enough. I did slow down though, generally speaking. Less books, less blog posts, less side projects. And that’s okay.
  • 🔴 Join a club - I made attempts to join two different writing clubs, but it didn’t work out. One met on an inconvenient night and I guess I failed the application for the other one. I joined a Discord for a Gunpla group… but never went. Closest I got to joining a club is a dad band that plays after our kids’ band practices.
  • 🟢 Understand myself - I’m progressing on my health journey to untangle the knot of anxiety, weight, and ADHD. I’ve done all the important appointments and follow-ups. I’ve done the diet. I’ve done the exercise. I’ve got new medications all lined up. Waiting for something to change.

A month-by-month breakdown

  • Jan
    • New pain meds
    • New ADHD meds
  • Feb
    • Launched internal design system
    • Shipped cross-product/platform token system
  • Mar
    • Landscaping project
    • Trip to Arizona
    • Renaissance Faire
  • Apr
    • Cheer competition in New Orleans
    • New car
    • 45th Birthday
  • May
    • New ADHD meds
  • Jun
    • Trip to Grand Canyon
  • Jul
    • Shipped design system redesign
    • Trip to San Diego
  • Aug
    • Back to school
  • Sept
    • Volunteered at Austin-Oita 夏祭り
    • New BP meds
  • Oct
    • Trip to Santa Barbara
  • Nov
    • The Beths
    • Rainer Maria and Cap’n Jazz
    • Launched Grid-Paper
  • Dec
    • New BP meds
    • New GLP-1 meds
    • Christmas in Nebraska

The school rush and kid activities fill most non-working hours. It’s a lot of work right now but we signed up for it by having children. Speaking of children, my daughter’s cheer team won a national championship, which is an incredible accomplishment and experience for her. And my son started playing guitar this year which makes the whole family happy. Both kids are 10+ now which feels like we’re entering a new era in parenting; more autonomy, different problems, deeper conversations, movies with curse words, and more attitude. It’s new territory.

An average amount of books

I had a sub-goal of reading less this year and I succeeded! That feels good.

I encountered a lot of mediocre books this year but there were some bangers in there like The Dawn of Everything, The Wrong Stuff, and The Sirens’ Call. It’s possible I’ve maxed out on books though. My appetite to seek out new books to read in the last two months has been super low. They’re all starting to blur into a large mush. I have a backlog of manga to read… perhaps next year is the year I get back into comics?

An average amount of blogging

I published 53 posts in 2025 which averages to a post-a-week, but there’s some hot streaks in there skewing the stats. It’s less than the year before which (again) I’ll count as a win. Behind the scenes there’s dozens of half-finished posts, but it takes so much effort to finish a technical post about CSS or web components who knows when those will see the light of day.

Here’s a smattering of posts that either made the rounds or I’m proud of:

I’m sure I left something out but no need to overthink it. You can peruse the archive if you want.

Living in the zone of shit

American politics drove a lot of my daily vibes this year. Day-after-day of bullshit executive orders, tariffs, lies, DOGE, fuckups, coverups, corruption, kidnappings, gulags, military occupation of democratic US cities under false pretenses, murders at sea, billionaires, institutional collapse, and everything else is exhausting. The economic anxiety weighed heavily on me; we even panic bought a car! But watching groceries get more expensive, budgets get tighter, all while the President and his cronies launch crypto grifts to launder money from billionaires and foreign governments in an open influence peddling scheme is infuriating.

I had to step back from news a bit for my own mental health, but also it’s getting repetitive. Regime does stupid/illegal thing, PERSON gets DESTROYED by THING (but never does), consequences never happen, the world is unjust. I know the strategy is to exhaust, but I hit peak outrage years ago and this feels like a long, unshakable hangover of bad decisions. My trust in any form of government erodes like sand cliffs.

Mix in a highly speculative tech hype cycle which makes up a disproportionate amount of the economy and stock market valuation… and it’s hard to shake off a sense of dread.

Focusing on my physical and mental health

All my extra spoons went to wrangling my physical and mental health. Sometimes that manifested as staring at an iPad on my recliner after long day at work, but it also involved a lot of doctors visits. This has been a year and a half long journey (since good healthcare kicked in) of prioritizing myself. And while the news isn’t all great, I’m getting a clearer picture of the brain and body I’m dealing with. There’s a long road ahead and I don’t actually know when/if it gets better, but having a pathway forward means a lot right now.

I was unconsolably grumpy for nearly two months straight. It could be burnout. It could be cheese-moving. It could be depression manifesting differently. What I learned from going through that spell was good people and great music are the secret to being happy. At least for me. And I need to make sure I’m building that into the regular rhythms of my life.

2026: No New Projects

Next year has the potential to be good. If a certain elderly statesman, for example, passed away from age-related illnesses… that might change the mood considerably. But speaking to what’s within my sphere of control, I think there’s capacity for goodness.

For 2026, I’m adopting a mantra of “No new projects.” There will be new projects of course; they fit all the interest, novelty, challenge, urgency, and passion (INCUP) requirements that motivate ADHD brains. The spirit of “No new projects” is to finish more projects than I start. In the last month or so I’ve been aggressive on closing out old lingering to-dos; home improvements, organizing, taxes, managing devices, etc. and I want to continue that work of clearing out all those nagging unfinished bits. I want to buy back precious Brain RAM so I feel less overwhelmed all the time.

“No new projects” helps me be more present and realize when I’m creating work for myself. When I buy a new device, or an instrument, or think about building my own home server array… “No new projects” puts a hurdle in the way of that decision so that I don’t DDoS myself with a backlog of small jobs pursuing an impulse.

I tend to externalize, but 2026 also feels highly dependent on what happens in tech in the next year. A change in my job? A robot taking my job? A mass layoff? A bubble bursting? An industry collapse? A retreat to a smaller web? A new and unexpected technological breakthrough? As I hinted earlier, it feels like the entirety of tech has hitched itself to a highly speculative bet and we all have to wait and see what happens. The lack of clarity and concrete vision for the future of human-centered computing and labor creates a feeling of insecurity for me. A trillion dollar hurricane. Displacement.

But we can write. And we can dream. And we can imagine a future where humans connect.

From: Stuff & Nonsense

A new gold mine graphic animation

Of all the “pioneering” graphic animations I’ve made this year, the one I disliked the most was on my contact page. That was one of the first ones I’d produced, and it was before I’d really got into the process. Anyway, it’s gone, replaced with a new gold mine graphic.

This time, I set my Magnificent 7 pioneers in the entrance to a gold mine. There’s dust in the air, gold shimmering, and oil lamps glowing and swinging.

New gold mine graphic with characters
New gold mine graphic with characters

I started with a loose pencil sketch.

Pencil sketch of my gold mine
Pencil sketch of my gold mine

I moved the artwork into Sketch to make my vectors. I kept the elements I knew I wanted to animate separate so I could add them to my graphic more easily later:

  • Background buckets swaying
  • Dust particles in the air
  • Lamps glowing and swinging
  • Pile of gold shimmering

CSS animations make the buckets sway, and lamps swing, then a little vanilla JS spawns and animates the dust particles and gold shimmers. Here’s the gold mine graphic on its own, so you can see all the components.

New gold mine graphic without characters
New gold mine graphic without characters

As I said before, I’ve been having way too much fun with my website since I implemented the new pioneer characters, so I doubt this will be the last time I add new features. In fact, I guarantee it won’t be.

From: Stuff & Nonsense

Let it snow

Winter’s definitely arrived, and there’s a chill wind blowing. We haven’t had snow in the village yet, but it’s falling on my website, courtesy of a new ‘snow mode.’

My winter blog page graphic animation

Visit my home page or blog to see that winter’s set in. On the home page, snow now covers the cacti and craggy rocks. On my blog, it’s settled on the buildings. And, unless animations have been turned off, snowflakes drift down from the sky. Brrrrr.

Adding snow to my existing animated graphics was surprisingly easy. I added three layers to my SVGs:

  1. Falling snow
  2. Settled snow
  3. Chilliness overlay
Settled snow layer
Chilliness overlay layer

That overlay lies on top of the background elements. It has a blue fill colour and a blending mode that cools whatever’s behind it. I think it’s incredible how much of a difference a colour blend can make to how the artwork feels. Then, alongside the button which starts/stops all animations, I added another which toggles the snow.

Snow mode on
Snow mode off

Toggle snow mode on and off to see the effect of these layers.


As you might’ve noticed, I’ve been having way too much fun with my website since I implemented the new pioneer characters, and in my mind, there’s nowt wrong with that.