Journal tags: end

404

sparkline

Brigid by Kim Curran

I enjoyed Kim Curran’s debut novel, The Morrigan, so when I saw a copy of her brand new book in the local library, I snapped it up.

Like The Morrigan, Brigid is modern retelling of Irish mythology, but in a very different time period. Whereas The Morrigan was set in a mythical time of the Fomorians and the Tuatha Dé Danann, Brigid is set in the relatively recent past of early Christian Ireland.

I was curious to see which Brigid this book would be about: the pagan goddess or the Christian saint?

Both, it turns out. The protagonist is the saint, but the narrator is the goddess. And they interact. It’s a clever framing device and for the most part, it works.

There are cameos a-plenty from the Christian pantheon like Patrick and Brendan the navigator but this is not the hagiography we learned in school. All the usual miracles are present and accounted for, but any supernatural powers aren’t ascribed to a Christian deity.

The world of Brigid isn’t so far removed from the world of The Morrigan after all.

Brigid isn’t a ground-breaking book, and it didn’t grab me as much as The Morrigan but it’s an enjoyable read nonetheless.

Buy this book

Finn Mac Cool by Morgan Llywelyn

After reading The Morrigan I was hungry for more retellings of Irish myths and legends. I tracked down the 1994 novel Finn Mac Cool by Morgan Llywelyn.

When I was devouring modern retellings of Greek myths, I commented on an interesting difference in the tellings:

The biggest difference I’ve noticed is the presence or absence of supernatural intervention. Some of these writers tell their stories with gods and goddesses front and centre. Others tell the very same stories as realistic accounts without any magic.

The Morrigan was dripping in magic. Finn Mac Cool is a more down-to-earth affair.

That’s not to say that magic doesn’t matter. For the characters in this book, their belief in magic is as real as their belief in the weather. But there are no supernatural powers here. If anything, Finn’s superpower is his ability to tell—and believe—tall tales involving supernatural intervention.

All the usual accounts of Finn Mac Cool’s prowess are retold as deeds that may have a basis in reality but then get exaggerated almost immediately.

It’s a framing device that works well. It’s all too easy to believe in the rise to power of a charismatic man skilled in controlling the narrative.

There’s plenty of Machievellian politics at play. There are no outright villains, or even heroes. There’s a pleasing messiness to the forces at work.

Sometimes the author’s research shows a bit too much. There are digressions into explanations of Brehon law that threaten to derail the narrative.

Overall though, this is an engaging and vivid retelling that just makes me want to spend more time in this world.

TinyStart

Sometimes I look back through my blogging archives and notice what’s changed over time.

For example, I used to write quite enthusiastically about the arrival of a new operating system from Apple. That is no longer the case, to put it mildly. I’m currently holed up on Sequioa, trying to resist all the nudgings to “upgrade” to the tacky design nightmare that is Tahoe. I feel like the protagonist of Pluribus.

I used to write about software I really liked. Sometimes it was software made by Apple. More often it was from some independent developer.

Like, I remember how much I loved a little application called Quicksilver. It just did one thing. You pressed control and space and then started typing the name of any programme installed on your computer. After a few characters Quicksilver would show you the match, you hit enter and the programme launched.

If that process sounds familiar, it’s because Apple ended up incorporating it into their own Spotlight feature. Quicksilver got sherlocked (ask your parents).

Recently though, Spotlight got worse and worse at doing its one job. It’s been laggy and inaccurate, even though I set my Spotlight indexing options to only index the Applications folder.

Then I found TinyStart. It’s like Quicksilver reborn!

A tiny launcher for macOS, fast and focused on the essentials.

Actually, it does double duty. As well as being an application launcher, it’s also an emoji picker. 👍

Best of all, not only is TinyStart a return to the focus and quality of software of yore, it’s also a return to the pricing model. You buy the software—for a measly €5—and that’s it. You own it now. There’s no subscription you have to pay every month.

I love everything about this.

Mistrust

Four years ago I wrote about something that has long puzzled me in the world of front-end development. Trust:

The mindset I’ve noticed is that many developers are suspicious of browser features but trusting of third-party libraries.

Developers are more likely to trust, say, Bootstrap than they are to trust CSS grid or custom properties. Developers are more likely to trust React than they are to trust web components.

That post got some thoughtful responses but I never really understood the imbalance of trust and suspicion:

I’m kind of confused by this prevalent mindset of trusting third-party code more than built-in browser features.

But something happened recently that helped me understand that mindset better.

I wrote a while back about how the datalist element on iOS has been completely fucked up. It’s worse than if Safari simply didn’t support it.

Breaking the web like that should be a five-alarm fire, but nobody is in any rush to fix it. I recall a similar lackadaisical attitude when Safari completely broke their implentation of IndexedDB.

I had it in my head that browser features followed a forward path generally. They’d be iterated on and improved on to iron out any glitches, but it was reasonable to expect things to get better with each new version of a browser.

Now I see that’s not necessarily the case.

Had I used an over-engineered JavaScript library instead of the datalist element, I wouldn’t be facing the current situation of having to use browser-sniffing to avoid sending a standard HTML element to any browser on iOS.

Sure, that third-party JavaScript would mean that users are downloading more code, and it probably wouldn’t work well with assistive technology, but as long as I didn’t touch it, it would continue to work. That should be true of web standards—I should be able to use them secure in the knowledge that they won’t suddenly shit the bed.

Perhaps I should be grateful to Apple for dispelling my naïveté. I now have much more empathy and understanding for web developers who are suspicious of web standards and prefer to use third-party libraries instead.

Good job, Apple. Happy anniversary.

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.

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!

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.

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.

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.

Magic

I don’t like magic.

I’m not talking about acts of prestidigitation and illusion. I mean the kind of magic that’s used to market technologies. It’s magic. It just works. Don’t think about it.

I’ve written about seamless and seamful design before. Seamlessness is often touted as the ultimate goal of UX—“don’t make me think!”—but it comes with a price. That price is the reduction of agency.

When it comes to front-end development, my distrust of magic tips over into being a complete control freak.

I don’t like using code that I haven’t written and understood myself. Sometimes its unavoidable. I use two JavaScript libraries on The Session. One for displaying interactive maps and another for generating sheet music. As dependencies go, they’re very good but I still don’t like the feeling of being dependant on anything I don’t fully understand.

I can’t stomach the idea of using npm to install client-side JavaScript (which then installs more JavaScript, which in turn is dependant on even more JavaScript). It gives me the heebie-jeebies. I’m kind of astonished that most front-end developers have normalised doing daily trust falls with their codebases.

While I’m mistrustful of libraries, I’m completely allergic to frameworks.

Often I don’t distinguish between libraries and frameworks but the distinction matters here. Libraries are bits of other people’s code that I call from my code. Frameworks are other people’s code that call bits of my code.

Think of React. In order to use it, you basically have to adopt its idioms, its approach, its syntax. It’s a deeper level of dependency than just dropping in a regular piece of JavaScript.

I’ve always avoided client-side React because of its direct harm to end users (over-engineered bloated sites that take way longer to load than they need to). But the truth is that I also really dislike the extra layer of abstraction it puts between me and the browser.

Now, whenever there’s any talk about abstractions someone inevitably points out that, when it comes to computers, there’s always some layer of abstraction. If you’re not writing in binary, you don’t get to complain about an extra layer of abstraction making you uncomfortable.

I get that. But I still draw a line. When it comes to front-end development, that line is for me to stay as close as I can to raw HTML, CSS, and JavaScript. After all, that’s what users are going to get in their browsers.

My control freakery is not typical. It’s also not a very commercial or pragmatic attitude.

Over the years, I’ve stopped doing front-end development for client projects at work. Partly that’s because I’m pretty slow; it makes more sense to give the work to a better, faster developer. But it’s also because of my aversion to React. Projects came in where usage of React was a foregone conclusion. I wouldn’t work on those projects.

I mention this to point out that you probably shouldn’t adopt my inflexible mistrustful attitude if you want a career in front-end development.

Fortunately for me, front-end development still exists outside of client work. I get to have fun with my own website and with The Session. Heck, they even let me build the occasional hand-crafted website for a Clearleft event. I get to do all that the long, hard stupid way.

Meanwhile in the real world, the abstractions are piling up. Developers can now use large language models to generate code. Sometimes the code is good. Sometimes its not. You should probably check it before using it. But some developers just YOLO it straight to production.

That gives me the heebie-jeebies, but then again, so did npm. Is it really all that different? With npm you dialled up other people’s code directly. With large language models, they first slurp up everyone’s code (like, the whole World Wide Web), run a computationally expensive process of tokenisation, and then give you the bit you need when you need it. In a way, large language model coding tools are like a turbo-charged npm with even more layers of abstraction.

It’s not for me but I absolutely understand why it can work in a pragmatic commercial environment. Like Alice said:

Knitting is the future of coding. Nobody knits because they want a quick or cheap jumper, they knit because they love the craft. This is the future of writing code by hand. You will do it because you find it satisfying but it will be neither the cheapest or quickest way to write software.

But as Dave points out:

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.

I shudder!

But again, this too is nothing new. We’ve all seen those codebases that contain mysterious arcane parts that nobody dares touch. coughWebpackcough. The issue isn’t with the code itself, but with the understanding of the code. If the understanding of the code was in one developer’s head, and that person has since left, the code is dangerous and best left untouched.

This, as you can imagine, is a maintenance nightmare. That’s where I’ve seen the real cost of abstractions. Abstractions often really do speed up production, but you pay the price in maintenance later on. If you want to understand the codebase, you must first understand the abstractions used in the codebase. That’s a lot to document, and let’s face it, documentation is the first casuality of almost every project.

So perhaps my aversion to abstraction in general—and large language models in particular—is because I tend to work on long-term projects. This website and The Session have lifespans measured in decades. For these kinds of projects, maintenance is a top priority.

Large language model coding tools truly are magic.

I don’t like magic.

The Morrigan by Kim Curran

Every culture has its myths and legends. Greece has its gods and warriors. England has its stories of Arthur. Ireland has the Tuatha Dé Danann, The Ulster Cycle, and more.

But while the Arthurian legends and the Greek myths have been retold many times, the stories of ancient Ireland have remained largely untouched.

Kim Curran’s book The Morrigan takes on this challenge.

The blurb for the book compares it Madeline Miller’s Circe, which is a bold comparison. The writing in The Morrigan isn’t in the same league as Circe, but then again, very little is.

Structurally, the comparison makes complete sense.

Circe starts with the titular nymph in the world of the gods of Olympus before moving on to more mortal affairs, coming to a head with the events of The Odyssey, when Odysseus’s story dominates.

The Morrigan starts with the titular goddess in the world of the gods of the Túatha Dé before moving on to more mortal affairs, coming to a head with the events of The Táin, when Cú Chulainn’s story dominates.

I took me a little while to adjust to the tone, but once I did, I thoroughly enjoyed this retelling. It manages to simultaneously capture the bloody, over-the-top feeling of The Táin while also having a distinctly modern twist. By the last third, I was completely engrossed.

After finishing Circe I went on a spree of reading many, many modern retellings of Greek myths. Now that I’ve finished The Morrigan I want to do the same for the Irish legends.

But I can’t. Apart from re-reading a translation of The Táin, there’s not much else out there for me.

Kim Curran does have another book that’s just been released; Brigid (the goddess? the saint? both?). If it’s anything like The Morrigan, it’s going to be a must-read.

I hope these books are the first of many.

Buy this book

The datalist element on iOS 26

The datalist element is all fucked up on iOS. Again.

I haven’t “upgraded” my iPhone to iOS 26 and I have no plans to. The whole Liquid Glass thing is literally offputting. So I wouldn’t have known about the latest regression in Safari if a friend hadn’t texted me about the problem.

He was trying to do a search on The Session. He was looking for the tune, The Road To Town. He started typing this into the form on the home page of the site. He got as far as “The Road To”. That’s when the entire input was obscured by a suggestion from the associated datalist.

A screenshot of The Session on an iPhone during a search on the homepage. The search input is completely obscured by the text: The Road To Lisdoonvarna.

This is incredibly annoying and seems to be a pattern of behaviour for Safari. Features are supported …technically. But the implementation is so buggy as to be unusable.

I’ll probably have to do some user-agent sniffing, which I hate. And it won’t be enough to just sniff for Safari on iOS 26. Remember that every browser on iOS is just Webkit in a trenchcoat.

Time to file a bug and then wait God knows how long for an update to get rolled out.

Update: I filed a bug, but in the meantime it looks like user-agent sniffing is going to be impossible.

3 + 4

Toward the end of 2021, I wrote about working a four-day week. It really suited me. So much so that I’ve gone one further. For the past year or so I’ve been working a three-day week.

I work on Tuesday, Wednesday, and Thursday. From Friday to Monday, my days are my own.

This really changes the dynamic of the week. It no longer feels like an extended weekend. What I mean is that usually we think about the working week as the default and the weekend as the exception. That’s been flipped on its head for me. The three days I spend working feel like the exception.

Once again, this decision meant earning less money. But I’ve decided that I value time more than money. I know that’s a privileged position to be in. Many people have to expend all their time in order to make just enough money.

I’ve made some choices along the way that certainly help. I don’t have children. I don’t have a car. I live in a modest flat and I’ve paid off the mortgage. I live in a country where healthcare is free.

So I don’t have too many expenses. My biggest expenses are travel-related; getting to the States to see family, or travelling to Irish music festivals wherever they may be.

But still, working a three-day week means I can make enough to cover my expenses and still put some money aside for the future.

Now, this wouldn’t work for everyone. My work tends to be the kind that doesn’t require much direct collaboration (which is also why I mostly work from home). I imagine it could get frustrating being on a team of people working different numbers of days.

I’m also really lucky to have the choice to do this. I know that many workplaces wouldn’t allow this kind of lifestyle. Clearleft is different.

In my last conference talk, I touched on this:

I think you could you could divide management into two categories like you can do with programming languages. There is a very imperative school of management where it’s all about measurements, it’s all about those performance reports, it’s all about metrics, time tracking. Maybe they install software on your machine to track how long you’ve been working. It’s all about measuring those outputs.

That’s one approach to management. Then there’s a more declarative approach, where you just care about the work getting done and you don’t care how people do it. So if they want to work from home, let them work from home. If they want to work strange hours, let them work strange hours. What do you care as long as the work gets done? This is more about giving people autonomy and trust.

I’m very happy that Clearleft takes the declarative approach.

And I can reiterate what I said when I stopped working on Fridays:

I haven’t experienced any reduction in productivity. Quite the opposite. There may be a corollary to Parkinson’s Law: work contracts to fill the time available.

Now that I don’t work on Mondays, bank holiday weekends don’t mean much to me anymore. Or to put it another way, every weekend is like a bank holiday weekend. If I want to travel somewhere on a Friday and come back on a Monday, I don’t need to book any time off. That’s really nice.

I’ve got four days in a row to do with as I wish. I had to fight the urge to immediately launch into some new project or side-hustle to fill the time. I’m savouring it instead.

I’ve got time to take care of The Session. I’ve got time to read. I’ve got time to cook. I’ve got time to spend learning Irish. Mostly I’ve got time to just be.

Installing web apps

Safari, Chrome, and Edge all allow you to install websites as though they’re apps.

On mobile Safari, this is done with the “Add to home screen” option that’s buried deep in the “share” menu, making it all but useless.

On the desktop, this is “Add to dock” in Safari, or “Install” in Chrome or Edge.

Firefox doesn’t offer this functionality, which as a shame. Firefox is my browser of choice but they decided a while back to completely abandon progressive web apps (though they might reverse that decision soon).

Anyway, being able to install websites as apps is fantastic! I’ve got a number of these “apps” in my dock: Mastodon, Bluesky, Instagram, The Session, Google Calendar, Google Meet. They all behave just like native apps. I can’t even tell which browser I used to initially install them.

If you’d like to prompt users to install your website as an app, there’s not much you can do other than show them how to do it. But that might be about to change…

I’ve been eagerly watching the proposal for a Web Install API. This would allow authors to put a button on a page that, when clicked, would trigger the installation process (the user would still need to confirm this, of course).

Right now it’s a JavaScript API called navigator.install, but there’s talk of having a declarative version too. Personally, I think this would be an ideal job for an invoker command. Making a whole new install element seems ludicrously over-engineered to me when button invoketarget="share" is right there.

Microsoft recently announced that they’d be testing the JavaScript API in an origin trial. I immediately signed up The Session for the trial. Then I updated the site to output the appropriate HTTP header.

You still need to mess around in the browser configs to test this locally. Go to edge://flags or chrome://flags/ and search for ‘Web App Installation API’, enable it and restart.

I’m now using this API on the homepage of The Session. Unsurprisingly, I’ve wrapped up the functionality into an HTML web component that I call button-install.

Here’s the code. You use it like this:

<button-install>
  <button>Install the app</button>
</button-install>

Use whatever text you like inside the button.

I wasn’t sure whether to keep the button element in the regular DOM or generate it in the Shadow DOM of the custom element. Seeing as the button requires JavaScript to do anything, the Shadow DOM option would make sense. As Tess put it, Shadow DOM is for hiding your shame—the bits of your interface that depend on JavaScript.

In the end I decided to stick with a regular button element within the custom element, but I take steps to remove it when it’s not necessary.

There’s a potential issue in having an element that could self-destruct if the browser doesn’t cut the mustard. There might be a flash of seeing the button before it gets removed. That could even cause a nasty layout shift.

So far I haven’t seen this problem myself but I should probably use something like Scott’s CSS in reverse: fade in the button with a little delay (during which time the button might end up getting removed anyway).

My connectedCallback method starts by finding the button nested in the custom element:

class ButtonInstall extends HTMLElement {
  connectedCallback () {
    this.button = this.querySelector('button');
    …
  }
customElements.define('button-install', ButtonInstall);

If the navigator.install method doesn’t exist, remove the button.

if (!navigator.install) {
  this.button.remove();
  return;
}

If the current display-mode is standalone, then the site has already been installed, so remove the button.

if (window.matchMedia('(display-mode: standalone)').matches) {
  this.button.remove();
  return;
}

As an extra measure, I could also use the display-mode media query in CSS to hide the button:

@media (display-mode: standalone) {
  button-install button {
    display: none;
  }
}

If the button has survived these tests, I can wire it up to the navigator.install method:

this.button.addEventListener('click', async (ev) => {
  await navigator.install();
});

That’s all I’m doing for now. I’m not doing any try/catch stuff to handle all the permutations of what might happen next. I just hand it over to the browser from there.

Feel free to use this code if you want. Adjust the code as needed. If your manifest file says display: fullscreen you’ll need to change the test in the JavaScript accordingly.

Oh, and make sure your site already has a manifest file that has an id field in it. That’s required for navigator.install to work.

The schedule for Web Day Out

Here’s the schedule for Web Day Out—what a fantastic collection of talks!

Web Day Out
10:00 – 10:30 I can’t believe it’s not JavaScript Jemima Abu
10:30 – 11:00 A pragmatic guide to browser support Rachel Andrew
11:30 – 12:00 Progressive web apps from the trenches Aleth Gueguen
12:00 – 12:30 Build for the web, build on the web, build with the web Harry Roberts
14:00 – 14:30 Breaking with habits Manuel Matuzovič
14:30 – 15:00 What’s new in web typography? Richard Rutter
15:30 – 16:00 Customisable <select> and the friends we made along the way Jake Archibald
16:00 – 16:30 The browser is the playground Lola Odelola

Seeing all of those talk titles in a row is getting me very, very excited for this day!

I hope that you’re excited too, and I hope you’ve got your ticket already.

If you need to convince your boss to send you (and your team) to Web Day Out I’ve put together some reasons to attend along with an email template that you can use as a starting point.

Also, if your company is sending a group of people anyway, consider sponsoring Web Day Out. You get a bunch of conference tickets as part of the sponsorship deal.

Hope to see you in Brighton on Thursday, 12 March 2026!

Why use React?

This isn’t a rhetorical question. I genuinely want to know why developers choose to build websites using React.

There are many possible reasons. Alas, none of them relate directly to user experience, other than a trickle-down justification: happy productive developers will make better websites. Citation needed.

It’s also worth mentioning that some people don’t choose to use React, but its use is mandated by their workplace (like some other more recent technologies I could mention). By my definition, this makes React enterprise software in this situation. My definition of enterprise software is any software that you use but that you yourself didn’t choose.

Inertia

By far the most common reason for choosing React today is inertia. If it’s what you’re comfortable with, you’d need a really compelling reason not to use it. That’s generally the reason behind usage mandates too. If we “standardise” on React, then it’ll make hiring more straightforward (though the reality isn’t quite so simple, as the React ecosystem has mutated and bifurcated over time).

And you know what? Inertia is a perfectly valid reason to choose a technology. If time is of the essence, and you know it’s going to take you time to learn a new technology, it makes sense to stick with what you know, even if it’s out of date. This isn’t just true of React, it’s true of any tech stack.

This would all be absolutely fine if React weren’t a framework that gets executed in browsers. Any client-side framework is a tax on the end user. They have to download, parse, and execute the framework in order for you to benefit.

But maybe React doesn’t need to run in the browser at all. That’s the promise of server-side rendering.

The front end

There used to be a fairly clear distinction between front-end development and back-end development. The front end consisted of HTML, CSS, and client-side JavaScript. The back end was anything you wanted as long as it could spit out those bits of the front end: PHP, Ruby, Python, or even just a plain web server with static files.

Then it became possible to write JavaScript on the back end. Great! Now you didn’t need to context-switch when you were scripting for the client or the server. But this blessing also turned out to be a bit of a curse.

When you’re writing code for the back end, some things matter more than others. File size, for example, isn’t really a concern. Your code can get really long and it probably won’t slow down the execution. And if it does, you can always buy your way out of the problem by getting a more powerful server.

On the front end, your code should have different priorities. File size matters, especially with JavaScript. The code won’t be executed on your server. It’s executed on all sorts of devices on all sorts of networks running all sorts of browsers. If things get slow, you can’t buy your way out of the problem because you can’t buy every single one of your users a new device and a new network plan.

Now that JavaScript can run on the server as well as the client, it’s tempting to just treat the code the same. It’s the same language after all. But the context really matters. Some JavaScript that’s perfectly fine to run on the server can be a resource hog on the client.

And this is where it gets interesting with React. Because most of the things people like about React still apply on the back end.

React developers

When React first appeared, it was touted as front-end tool. State management and a near-magical virtual DOM were the main selling points.

Over time, that’s changed. The claimed speed benefits of the virtual DOM turned out to be just plain false. That just left state management.

But by that time, the selling points had changed. The component-based architecture turned out to be really popular. Developers liked JSX. A lot. Once you got used to it, it was a neat way to encapsulate little bits of functionality into building blocks that can be combined in all sorts of ways.

For the longest time, I didn’t realise this had happened. I was still thinking of React as being a framework like jQuery. But React is a framework like Rails or Django. As a developer, it’s where you do all your work. Heck, it’s pretty much your identity.

But whereas Rails or Django run on the back end, React runs on the front end …except when it doesn’t.

JavaScript can run on the server, which means React can run on the server. It’s entirely possible to have your React cake and eat it. You can write all of your code in React without serving up a single line of React to your users.

That’s true in theory. The devil is in the tooling.

Priorities

Next.js allows you to write in React and do server-side rendering. But it really, really wants to output React to the client as well.

By default, you get the dreaded hydration pattern—do all the computing on the server in JavaScript (yay!), serve up HTML straight away (yay! yay!) …and then serve up all the same JavaScript that’s on the server anyway (ya—wait, what?).

It’s possible to get Next.js to skip that last step, but it’s not easy. You’ll be battling it every step of the way.

Astro takes a very different approach. It will do everything it can to keep the client-side JavaScript to a minimum. Developers get to keep their beloved JSX authoring environment without penalising users.

Alas, the collective inertia of the “modern” development community is bound up in the React/Next/Vercel ecosystem. That’s a shame, because Astro shows us that it doesn’t have to be this way.

Switching away from using React on the front end doesn’t mean you have to switch away from using React on the back end.

Why use React?

The titular question I asked is too broad and naïve. There are plenty of reasons to use React, just as there are plenty of reasons to use Wordpress, Eleventy, or any other technology that works on the back end. If it’s what you like or what you’re comfortable with, that’s reason enough.

All I really care about is the front end. I’m not going to pass judgment on anyone’s choice of server-side framework, as long as it doesn’t impact what you can do in the client. Like Harry says:

…if you’re going to use one, I shouldn’t be able to smell it.

Here’s the question I should be asking:

Why use React in the browser?

Because if the reason you’re using React is cultural—the whole team works in JSX, it makes hiring easier—then there’s probably no need to make your users download React.

If you’re making a single-page app, then …well, the first thing you should do is ask yourself if it really needs to be a single-page app. They should be the exception, not the default. But if you’re determined to make a single-page app, then I can see why state management becomes very important.

In that situation, try shipping Preact instead of React. As a developer, you’ll almost certainly notice no difference, but your users will appreciate the refreshing lack of bloat.

Mostly though, I’d encourage you to investigate what you can do with vanilla JavaScript in the browser. I totally get why you’d want to hold on to React as an authoring environment, but don’t let your framework limit what you can do on the front end. If you use React on the client, you’re not doing your users any favours.

You can continue to write in React. You can continue to use JSX. You can continue to hire React developers. But keep it on your machine. For your users, make the most of what web browsers can do.

Once you keep React on the server, then a whole world of possibilities opens up on the client. Web browsers have become incredibly powerful in what they offer you. Don’t let React-on-the-client hold you back.

And if you want to know more about what web browsers are capable of today, come to Web Day Out in Brighton on Thursday, 12th March 2026.

Manuel Matuzovič is speaking at Web Day Out

The line-up for Web Day Out is now complete! The final speaker to be added to the line-up is the one and only Manuel Matuzovič.

You may know Manuel from his superb Web Accessibility Cookbook (full disclosure: I had the honour of writing the foreword to that book). Or perhaps you’re familiar with the crimes against markup that he documents at HTMHell. But at Web Day Out, he’s going to be talking about CSS.

The past few years have seen a veritable explosion in CSS capabilities. It’s one thing to hear about all the new stuff in CSS, but how do you actually start using it?

You may need to unlearn what you have previously learned. That’s what Manuel’s talk will be covering:

Manuel built a new project from scratch with modern CSS and questioned every line of code he wrote.

In this talk, he presents what he has learned and encourages you to review your best practices.

You can see why I’m so excited about this—it’s perfect for the agenda of Web Day Out:

Do you feel like you’re missing out on some of the latest advances in HTML, CSS, and JavaScript APIs? Web Day Out is your chance to get up to speed on what matters.

There’ll be eight brilliant speakers for your entertainment:

  1. Jemima Abu
  2. Rachel Andrew
  3. Jake Archibald
  4. Aleth Gueguen
  5. Manuel Matuzovič
  6. Lola Odelola
  7. Harry Roberts
  8. Richard Rutter

You won’t want to miss this, so get your ticket now for the ludicrously reasonable price of just £225+VAT!

See you in Brighton on 12 March 2026!

Providers

If you’re building software, it’s generally a good idea to avoid the Not-Invented-Here syndrome. This is when you insist on writing absolutely everything from scratch even if it would make more sense to use a third-party provider.

Need your app to take payments? Don’t try to become your own payment provider—use an existing provider instead.

Need your app to send email? Don’t try to code all that up yourself—just use an existing service.

This same thinking seems to apply to JavaScript libraries too. If you don’t use a library or framework, you’ll just end up writing your own library or framework instead, right?

Except that’s not the way that JavaScript frameworks work. At least not any more.

There was a time when JavaScript libraries really did abstract away browser differences that you probably didn’t want to deal with yourself. In the early days of jQuery—before querySelector existed—trying to work with the DOM could be a real pain. Libraries like jQuery helped avoid that pain.

Maybe it was even true in the early days of Angular and React. If you were trying to handle navigations yourself, it probably made sense to use a framework.

But that’s not the case any more, and hasn’t been for quite a while.

These days, client-side JavaScript frameworks don’t abstract away the underlying platform, they instead try to be an alternative. In fact, if you attempt to use web platform features, your JavaScript framework will often get in the way. You have to wait until your framework of choice supports a feature like view transitions before you get to use it.

This is nuts. Developers are choosing to use tools that actively get in the way of the web platform.

I think that most developers have the mental model of JavaScript frameworks completely backwards. They believe that the framework saves them time and effort (just like a payment provider or an email service). Instead these frameworks are simply limiting the possibility space of what you can do in web browsers today.

When you use a JavaScript framework, that isn’t the end of your work, it’s just the beginning. You still have to write your own code that makes use of that framework. Except now your code is restricted to only what the framework can do.

And yet most developers still believe that using a JavaScript framework somehow enables them to do more.

Jim Nielsen has a great framing on this. JavaScript libraries aren’t like payment providers or email services. Rather, it’s the features built into web browsers today that are like these third-party providers. When you use these features, you’re benefiting from all the work that the browser makers have put into making them as efficient as possible:

Browser makers have teams of people who, day-in and day-out, are spending lots of time developing and optimizing new their offerings.

So if you leverage what they offer you, that gives you an advantage because you don’t have to build it yourself.

Want to do nifty page transitions? Don’t use a library. Use view transitions.

Want to animate parts of the page as the user scrolls? Don’t use a library. Use scroll-driven animations.

Want to make something happen when the user clicks? Don’t use a library. For the love of all that is holy, just use a button.

If you agree that using a button makes more sense than using a div, then I encourage you to apply the same thinking to everything else your app needs to do.

Take advantage of all the wonderful things you can do in web browsers today. If instead you decide to use a JavaScript framework, you’re basically inventing from scratch.

Except now all of your users pay the price because they’re the ones who have to download the JavaScript framework when they use your app.

Aleth Gueguen is speaking at Web Day Out

Almost two months ago, I put out the call for speaker suggestions for Web Day Out. I got some good responses—thank you to everyone who took the time to get in touch.

The response that really piqued my interest was from Aleth Gueguen. She proposed a talk on progressive web apps, backed up with plenty of experience. The more I thought about it, the more I realised how perfect it would be for Web Day Out.

So I’m very pleased to announce that Aleth will be speaking at Web Day Out about progressive web apps from the trenches:

Find out about the most important capabilities in progressive web apps and how to put them to work.

I’m really excited about this line-up! This is going to be a day out that you won’t want to miss. Get your ticket for a mere £225+VAT if you haven’t already!

See you in Brighton on 12 March, 2026!

Jake Archibald is speaking at Web Day Out

I’m very happy to announce that the one and only Jake Jaffa-The-Cake Archibald will be speaking at Web Day Out!

Given the agenda for this event, I think you’ll agree that Jake is a perfect fit. He’s been at the forefront of championing user-centred web standards, writing specs and shipping features in browsers.

Along the way he’s also created two valuable performance tools that I use all the time: SVGOMG and Squoosh, which has a permanent place in my dock—if you need to compress images, I highly recommend adding this progressive web app to your desktop.

He’s the man behind service workers and view transitions—two of the most important features for making websites first-class citizens on any device.

So what will he talk about at Web Day Out? Image formats? Offline functionality? Smooth animations? Something else entirely?

All will be revealed soon. In the meantime, grab yourself a ticket to Web Day Out—it’s just £225+VAT—and I’ll see you in Brighton on Thursday, 12 March 2026!