An in-depth guide to customising lists with CSS - Piccalilli
Think you know about styling lists with CSS? Think again!
This is just a taste of the kind of in-depth knowledge that Rich will be beaming directly into our brains at Web Day Out…
Think you know about styling lists with CSS? Think again!
This is just a taste of the kind of in-depth knowledge that Rich will be beaming directly into our brains at Web Day Out…
Frameworks like React are often perceived as accelerators, or even as the only sensible way to do web development. There’s this notion that a more “modern” stack (read: JS-heavy, where the JS ends up running on the user’s browser) allows you to be more agile, release more often with fewer bugs, make code more maintainable, and ultimately launch better sites. In short, the claim is that this approach will offer huge improvements to developer experience, and that these DevEx benefits will trickle down to the user.
But over the years, this narrative has proven to be unrealistic, at best. In reality, for any decently sized JS-heavy project, you should expect that what you build will be slower than advertised, it will keep getting slower over time while it sees ongoing work, and it will take more effort to develop and especially to maintain than what you were led to believe, with as many bugs as any other approach.
Where it comes to performance, the important thing to note is that a JS-heavy approach (and particularly one based on React & friends) will most likely not be a good starting point; in fact, it will probably prove to be a performance minefield that you will need to keep revisiting, risking a detonation with every new commit.
This is an excellent one-stop shop of interface patterns:
This is an organic collection of common JS patterns that can be replaced with just HTML, CSS, and no, or very low, JS. As HTML and CSS continue to mature, this collection should expand.
There have been so many advances in HTML, CSS and browser support over the past few years. These are enabling phenomenal creativity and refinement in web typography, and I’ve got a mere 28 minutes to tell you all about it.
I’ve been talking to Rich about his Web Day Out talk, and let me tell you, you don’t want to miss it!
It’s gonna be a wild ride! Join me at Web Day Out in Brighton on 12 March 2026. Use JOIN_RICH to get 10% off and you’ll also get a free online ticket for State of the Browser.
Jemima runs through just some of the exciting new additions to CSS:
Replacing 150+ lines of JavaScript with just a few CSS features is genuinely wild. We’re able to achieve the same amount of complexity that we’ve always had, but now it’s a lot less work to do so.
And Jemima will be opening the show at Web Day Out in Brighton on the 12th of March if you want to hear more of this!
There’s a new meta tag on the block. This time it’s for allowing system-level text sizing to apply to your website.
This episode of the Shop Talk Show is the dictionary definition of “rambling” but I had a lot of fun rambling with Chris and Dave!
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.

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.
This is depressing.
Every millisecond you spend executing JavaScript is a millisecond the browser can’t spend responding to a click, updating a scroll position, or acknowledging that the user did just try to type something. When your code runs long, you’re not causing “jank” in some abstract technical sense; you’re ignoring someone who’s trying to talk to you.
This is a great way to think about client-side JavaScript!
Also:
Before your application code runs a single line, your framework has already spent some of the user’s main thread budget on initialization, hydration, and virtual DOM reconciliation.
Progressive enhancement is about building something robust, that works everywhere, and then making it better where possible.
You might not need (much) JavaScript for these common interface patterns.
While we all love the power and flexibility JS provides, we should also respect it, and our users, by limiting its use to only what it needs to do.
Yes! Client-side JavaScript should do what only client-side JavaScript can do.
Spot-on observations from Terence linking the fundamental nature of parsing in web browsers with the completely wrong-headed takes of some technologists who have built on top of the web.
The core idea of the event is to get you up to speed on the most powerful web platform features that you can use right now. I love that because it aligns perfectly with what I’ve been working on over the last couple of years: finding ways to break old habits to get the most out of CSS.
Eric Meyer and Brian Kardell chat with Jay Hoffmann and Jeremy Keith about Shadow DOM’s backstory and long origins
I enjoyed this chat, and it wasn’t just about Shadow DOM; it was about the history of chasing the dream of encapsulation on the web.
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.
Explore the platform. Challenge yourself to discover what the modern web can do natively. Pure HTML, CSS, and a bit of vanilla JS…
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:
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!
This is a superb way to deprecate a little JavaScript library. Now that you can just use HTML instead, the website for Pikaday has been turned into a guide to choosing the right design pattern for your needs. Bravo!
Pikaday is no longer a JavaScript date picker. Pikaday is now a friendly guide for front-end developers. I want to push developers away from the classic date picker entirely. Especially fat JavaScript libraries.
We’ve arrived at an industrialised process, one that’s like an assembly line for applications. Frameworks like React have become the machinery of that assembly line. They enable us to build efficiently, to build at scale, to build predictably. But they also constrain what we build.
But what aren’t we building? What new kinds of experiences, what new kinds of applications, what new kinds of interaction could we create if we were deeply exploring and engaging with the capabilities of the platform? I don’t know, because we’re not building them. We’re building what the frameworks enable us to build, what the assembly line can produce efficiently.
Collectively, as an industry and as a profession, consciously or not, we’ve chosen this maxima that we’re stuck on. We can build what React or Vue or Next or name your framework/library enables us to do.
I share John’s despair at this situation, but I don’t share his belief that large language models will save us.