Dynamic Datalist: Autocomplete from an API :: Aaron Gustafson
Great minds think alike! I have a very similar HTML web component on the front page of The Session called input-autosuggest.
Great minds think alike! I have a very similar HTML web component on the front page of The Session called input-autosuggest.
AI has the Jeopardy Phenomenon too.
If you use it to generate code that is outside your expertise, you are likely to think it’s all well and good, especially if it seems to work at first pop. But if you’re intimately familiar with the technology or the code around the code it’s generating, there is a good chance you’ll be like hey! that’s not quite right!
Not just code. I’m astounded by the cognitive dissonance displayed by people who say “I asked an LLM about {topic I’m familiar with}, and here’s all the things it got wrong” who then proceed to say “It was really useful when I asked an LLM for advice on {topic I’m not familiar with, hence why I’m asking an LLM for advice}.”
Like, if you know that the results are super dodgy for your own area of expertise, why would you think they’d be any better for, I don’t know, restaurant recommendations in a city you’ve never been to?
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.
This is a spot-on analysis of how CSS-in-JS failed to deliver on any of its promises:
CSS-in-JS was born out of good intentions — modularity, predictability and componentization. But what we got was complexity disguised as progress.
Another clever use of clamp() and calc() for web typography, but this time it’s adjusting letter-spacing.
I find Brian Eno to be a fascinating chap. His music isn’t my cup of tea, but I really enjoy hearing his thoughts on art, creativity, and culture.
I’ve always loved this short piece he wrote about singing with other people. I’ve passed that link onto multiple people who have found a deep joy in singing with a choir:
Singing aloud leaves you with a sense of levity and contentedness. And then there are what I would call “civilizational benefits.” When you sing with a group of people, you learn how to subsume yourself into a group consciousness because a capella singing is all about the immersion of the self into the community. That’s one of the great feelings — to stop being me for a little while and to become us. That way lies empathy, the great social virtue.
Then there’s the whole Long Now thing, a phrase that originated with him:
I noticed that this very local attitude to space in New York paralleled a similarly limited attitude to time. Everything was exciting, fast, current, and temporary. Enormous buildings came and went, careers rose and crashed in weeks. You rarely got the feeling that anyone had the time to think two years ahead, let alone ten or a hundred. Everyone seemed to be passing through. It was undeniably lively, but the downside was that it seemed selfish, irresponsible and randomly dangerous. I came to think of this as “The Short Now”, and this suggested the possibility of its opposite - “The Long Now”.
I was listening to my Huffduffer feed recently, where I had saved yet another interview with Brian Eno. Sure enough, there was plenty of interesting food for thought, but the bit that stood out to me was relevant to, of all things, prototyping:
I have an architect friend called Rem Koolhaas. He’s a Dutch architect, and he uses this phrase, “the premature sheen.” In his architectural practice, when they first got computers and computers were first good enough to do proper renderings of things, he said everything looked amazing at first.
You could construct a building in half an hour on the computer, and you’d have this amazing-looking thing, but, he said, “It didn’t help us make good buildings. It helped us make things that looked like they might be good buildings.”
I went to visit him one day when they were working on a big new complex for some place in Texas, and they were using matchboxes and pens and packets of tissues. It was completely analog, and there was no sense at all that this had any relationship to what the final product would be, in terms of how it looked.
It meant that what you were thinking about was: How does it work? What do we want it to be like to be in that place? You started asking the important questions again, not: What kind of facing should we have on the building or what color should the stone be?
I keep thinking about that insight: “It didn’t help us make good buildings. It helped us make things that looked like they might be good buildings.”
Substitute the word “buildings” for whatever output is supposedly being revolutionised by generative models today. Websites. Articles. Public policy.
Can you ship AI-generated code without creating a maintenance nightmare six months from now? Can you debug it when it breaks? Can you modify it when requirements change? Can you onboard new engineers to a codebase they didn’t write and the AI barely explained?
Most teams haven’t realized this shift yet. They’re optimizing for code generation speed while comprehension debt silently accumulates in their repos.
One team I talked to spent 3 days fixing what should have been a 2-hour problem. They had “saved” time by having AI generate the initial implementation. But when it broke, they lost 70 hours trying to understand code they had never built themselves.
That’s comprehension debt compounding. The time you save upfront gets charged back with interest later.
An excellent example of an HTML web component from Eric:
Extend HTML to do things automatically!
He layers on the functionality and styling, considering potential gotchas at every stage. This is resilient web design in action.
A very, very deep dive into like-for-like comparison of JavaScript frameworks. The takeaway:
Nuxt demonstrates that established “big three” frameworks can achieve next-gen performance when properly configured. Vue’s architecture allows competitive mobile web performance while maintaining a mature ecosystem. React and Angular show no path to similar results.
And the real takeaway:
Mobile is the web. These measurements matter because mobile web is the primary internet for billions of people. If your app is accessible via URL, people will use it on phones with cellular connections. Optimizing for desktop and hoping mobile is good enough is backwards. The web is mobile. Build for that reality.
Every engineer eventually overbuilds something. You think you’re being smart. You’re thinking ahead, building for growth and before you know it, you’ve created a system ten times heavier than your actual problem. That’s the trap. We keep designing for imaginary futures for scale that may never come and call it engineering. But it’s not engineering. It’s over-engineering.
The industry rewards it too. Nobody gets promoted for keeping things small and sane. You get promoted for complexity.
When I set about writing this article, I intended it to be a strong argument for progressive rendering. But after digging into it, my feelings are less certain.
Suppose somebody is using a blade. Perhaps they’re in the bathroom, shaving. Or maybe they’re in the kitchen, preparing food.
Suppose they cut themselves with that blade. This might have happened because the blade was too sharp. Or perhaps the blade was too dull.
Either way, it’s going to be tricky to figure out the reason just by looking at the wound.
But if you talk to the person, not only will you find out the reason, you’ll also understand their pain.
I’m not the only one who’s amazed by how much you can do with just a little CSS these days.
I was messing about with some images on a website recently and while I was happy enough with the arrangement on large screens, I thought it would be better to have the images in a kind of carousel on smaller screens—a swipable gallery.
My old brain immediately thought this would be fairly complicated to do, but actually it’s ludicrously straightforward. Just stick this bit of CSS on the containing element inside a media query (or better yet, a container query):
display: flex;
overflow-x: auto;
That’s it.
Oh, and you can swap out overflow-x for overflow-inline if, like me, you’re a fan of logical properties. But support for that only just landed in Safari so I’d probably wait a little while before removing the old syntax.
Here’s an example using pictures of some of the lovely people who will be speaking at Web Day Out:
While you’re at it, add this:
overscroll-behavior-inline: contain;
Thats prevents the user accidentally triggering a backwards/forwards navigation when they’re swiping.
You could add some more little niceties like this, but you don’t have to:
scroll-snap-type: inline mandatory;
scroll-behavior: smooth;
And maybe this on the individual items:
scroll-snap-align: center;
You could progressively enhance even more with the new pseudo-elements like ::scroll-button() and ::scroll-marker for Chromium browsers.
Apart from that last bit, none of this is particularly new or groundbreaking. But it was a pleasant reminder for me that interactions that used to be complicated to implement are now very straightforward indeed.
Here’s another example that Ana Tudor brought up yesterday:
You have a
sectionwith apon the left & animgon the right. How do you make theimgheight always be determined by thepwith the tiniest bit of CSS? 😼No changing the HTML structure in any way, no pseudos, no background declarations, no JS. Just a tiny bit of #CSS.
Old me would’ve said it can’t be done. But with a little bit of investigating, I found a nice straightforward solution:
section > img {
contain: size;
place-self: stretch;
object-fit: cover;
}
That’ll work whether the section has its display set to flex or grid.
There’s something very, very satisfying in finding a simple solution to something you thought would be complicated.
Honestly, I feel like web developers are constantly being gaslit into thinking that complex over-engineered solutions are the only option. When the discourse is being dominated by people invested in frameworks and libraries, all our default thinking will involve frameworks and libraries. That’s not good for users, and I don’t think it’s good for us either.
Of course, the trick is knowing that the simpler solution exists. The information probably isn’t going to fall in your lap—especially when the discourse is dominated by overly-complex JavaScript.
So get yourself a ticket for Web Day Out. It’s on Thursday, March 12th, 2026 right here in Brighton.
I guarantee you’ll hear about some magnificent techniques that will allow you to rip out plenty of complex code in favour of letting the browser do the work.
When I talk about large language models, I make sure to call them large language models, not “AI”. I know it’s a lost battle, but the terminology matters to me.
The term “AI” can encompass everything from a series of if/else statements right up to Skynet and HAL 9000. I’ve written about this naming collision before.
It’s not just that the term “AI” isn’t useful, it’s so broad as to be actively duplicitous. While talking about one thing—like, say, large language models—you can point to a completely different thing—like, say, machine learning or computer vision—and claim that they’re basically the same because they’re both labelled “AI”.
If a news outlet runs a story about machine learning in the context of disease prevention or archeology, the headline will inevitably contain the phrase “AI”. That story will then gleefully be used by slopagandists looking to inflate the usefulness of large language models.
Conflating these different technologies is the fallacy at the heart of Robin Sloan’s faulty logic:
If these machines churn through all media, and then, in their deployment, discover several superconductors and cure all cancers, I’d say, okay … we’re good.
“AI” is mostly a marketing phrase for a bunch of different processes and tools which in a different era would have been called “machine learning” or “neural networks” or something else now horribly unsexy.
But I’ve noticed something recently. More than once I’ve seen genuinely-useful services refer to their technology as “traditional machine learning”.
First off, I find that endearing. Like machine learning is akin to organic farming or hand-crafted furniture.
Secondly, perhaps it points to a severing of the ways between machine learning and large language models.
Up until now it may have been mutually benificial for them to share the same marketing term, but with the bubble about to burst, anything to do with large language models might become toxic by association, including the term “AI”. Hence the desire to shake the large-language model grifters from the coattails of machine learning and computer vision.
I’m almost certainly preaching to the choir here because I bet you’re reading these very words in a feed reader, but what Molly White has written here is too good not to share:
RSS offers readers and writers a path away from unreliable, manipulative, and hostile platforms and intermediaries. In a media landscape dominated by algorithmic feeds that aim to manipulate and extract, sometimes the most radical thing you can do is choose to read what you want, when you want, without anyone watching over your shoulder.
- Building HTML pages is easy
- Pure HTML is evergreen
- Bloated web pages are too slow
- I can host it anywhere, often for free
- Accessibility and SEO benefits are automatic
- It won’t need security patches
- There are no build steps
SPAs were a clever solution to a temporary limitation. But that limitation no longer exists.
Use modern server rendering. Use actual pages. Animate with CSS. Preload with intent. Ship less JavaScript.
- Install Stuff Indiscriminately From npm
- Pick a Framework Before You Know You Need One
- Always, Always Require a Compilation Step