Jeremy Keith's Blog, page 6
May 8, 2025
CSS snippets
I���ve been thinking about the kind of CSS I write by default when I start a new project.
Some of it is habitual. I now use logical properties automatically. It took me a while to rewire my brain, but now seeing left or top in a style sheet looks wrong to me.
When I mentioned this recently, I had some pushback from people wondering why you���d bother using logical properites if you never planned to translate the website into a language with a different writing system. I pointed out that even if you don���t plan to translate a web page, a user may still choose to. Using logical properties helps them. From that perspective, it���s kind of like using user preference queries.
That���s something else I use by default now. If I���ve got any animations or transitions in my CSS, I wrap them in prefers-reduced-motion: no-preference query.
For instance, I���m a huge fan of view transitions and I enable them by default on every new project, but I do it like this:
@media (prefers-reduced-motion: no-preference) { @view-transition { navigation: auto; }}I���ll usually have a prefers-color-scheme query for dark mode too. This is often quite straightforward if I���m using custom properties for colours, something else I���m doing habitually. And now I���m starting to use OKLCH for those colours, even if they start as hexadecimal values.
Custom properties are something else I reach for a lot, though I try to avoid premature optimisation. Generally I wait until I spot a value I���m using more than two or three times in a stylesheet; then I convert it to a custom property.
I make full use of clamp() for text sizing. Sometimes I���ll just set a fluid width on the html element and then size everything else with ems or rems. More often, I���ll use Utopia to flow between different type scales.
Okay, those are all features of CSS���logical properties, preference queries, view transitions, custom properties, fluid type���but what about actual snippets of CSS that I re-use from project to project?
I���m not talking about a CSS reset, which usually involves zeroing out the initial values provided by the browser. I���m talking about tiny little enhancements just one level up from those user-agent styles.
Here���s one I picked up from Eric that I apply to the figcaption element:
figcaption { max-inline-size: max-content; margin-inline: auto;}That will centre-align the text until it wraps onto more than one line, at which point it���s no longer centred. Neat!
Here���s another one I start with on every project:
a:focus-visible { outline-offset: 0.25em; outline-width: 0.25em; outline-color: currentColor;}That puts a nice chunky focus ring on links when they���re tabbed to. Personally, I like having the focus ring relative to the font size of the link but I know other people prefer to use a pixel size. You do you. Using the currentColor of the focused is usually a good starting point, thought I might end up over-riding this with a different hightlight colour.
Then there���s typography. Rich has a veritable cornucopia of starting styles you can use to improve typography in CSS.
Something I���m reaching for now is the text-wrap property with its new values of pretty and balance:
ul,ol,dl,dt,dd,p,figure,blockquote { hanging-punctuation: first last; text-wrap: pretty;}And maybe this for headings, if they���re being centred:
h1,h2,h3,h4,h5,h6 { text-align: center; text-wrap: balance;}All of these little snippets should be easily over-writable so I tend to wrap them in a :where() selector to reduce their specificity:
:where(figcaption) { max-inline-size: max-content; margin-inline: auto;}:where(a:focus-visible) { outline-offset: 0.25em; outline-width: 0.25em; outline-color: currentColor;}:where(ul,ol,dl,dt,dd,p,figure,blockquote) { hanging-punctuation: first last; text-wrap: pretty;}But if I really want them to be easily over-writable, then the galaxy-brain move would be to put them in their own cascade layer. That���s what Manu does with his CSS boilerplate:
@layer core, third-party, components, utility;Then I could put those snippets in the core layer, making sure they could be overwritten by the CSS in any of the other layers:
@layer core { figcaption { max-inline-size: max-content; margin-inline: auto; } a:focus-visible { outline-offset: 0.25em; outline-width: 0.25em; outline-color: currentColor; } ul,ol,dl,dt,dd,p,figure,blockquote { hanging-punctuation: first last; text-wrap: pretty; }}For now I���m just using :where() but I think I should start using cascade layers.
I also want to start training myself to use the lh value (line-height) for block spacing.
And although I���m using the :has() selector, I don���t think I���ve yet trained my brain to reach for it by default.
CSS has sooooo much to offer today���I want to make sure I���m taking full advantage of it.
April 30, 2025
Codewashing
I have little understanding for people using large language models to generate slop; words and images that nobody asked for.
I have more understanding for people using large language models to generate code. Code isn���t the thing in the same way that words or images are; code is the thing that gets you to the thing.
And if a large language model hallucinates some code, you���ll find out soon enough:
With code you get a powerful form of fact checking for free. Run the code, see if it works.
But I want to push back on one justification I see repeatedly about using large language models to write code. Here���s Craig:
There are many moral and ethical issues with using LLMs, but building software feels like one of the few truly ethically ���clean���(er) uses (trained on open source code, etc.)
That���s not how this works. Yes, the large language models are trained on lots of code (most of it open source), but they���re not only trained on that. That���s on top of everything else; all the stolen books, all the unpaid creative work of others.
Even Robin Sloan, who first says:
I think the case of code is especially clear, and, for me, basically settled.
���goes on to acknowledge:
But, again, it���s important to say: the code only works because of Everything. Take that data away, train a model using GitHub alone, and you���ll get a far less useful tool.
When large language models are trained on domain-specific data, it���s always in addition to the mahoosive amount of content they���ve already stolen. It���s that mohoosive amount of content���not the domain-specific data���that enables them to parse your instructions.
(Note that I���m being very delibarate in saying ���parse���, not ���understand.��� Though make no mistake, I���m astonished at how good these tools are at parsing instructions. I say that as someone who tried to write natural language parsers for text-only adventure games back in the 1980s.)
So, sure, go ahead and use large language models to write code. But don���t fool yourself into thinking that it���s somehow ethical.
What I said here applies to code too:
If you���re going to use generative tools powered by large language models, don���t pretend you don���t know how your sausage is made.
April 29, 2025
UX London flash sale
In exactly six weeks time, UX London is happening!
I am ridiculously excited about this year���s line-up���I can���t wait to see the talks and get hands-on in the workshops.
If you haven���t yet got your ticket, now is the time. There���s a flash sale this week: use the discount code FLASH20 to get a whopping 20% of any ticket. Do it before the end of Friday!
Whether you���re coming for all three days or choosing one focused day, you���re in for a treat.
Day one on Tuesday, 10 June is discovery day.Day two on Wednesday, 11 June is design day.Day three on Thursday, 12 June is deliver day.Head on over to the website to get all the details and then get your discounted ticket.
See you there!
April 16, 2025
OKLCH()
I was at the State Of The Browser event recently, which was great as always.
Manu gave a great talk about colour in CSS. A lot of it focused on OKLCH. I was already convinced of the benefits of this colour space after seeing a terrific talk by Anton Lovchikov a while back.
After Manu���s talk, someone mentioned that even though OKLCH is well supported in browsers now, it���s a shame that it isn���t (yet) in design tools like Figma. So designers are still handing over mock-ups with hex values.
I get the frustration, but in my experience it���s not that big a deal in practice. Here���s why: oklch() isn���t just a way of defining colours with lightness, chroma, and hue in CSS. It���s also a function. You can use the magical from keyword in this function to convert hex colours to l, c, and h:
--page-colour: oklch(from #49498D l c h);So even if you���re being handed hex colour values, you can still use OKLCH in your CSS. Once you���re doing that, you can use all of the good stuff that comes with having those three values separated out���something that was theoretically possible with hsl, but problematic in practice.
Let���s say you want to encode something into your CSS like this: ���if the user has specified that they prefer higher contrast, the background colour should be four times darker.���
@media (prefers-contrast: more) { --page-colour: oklch(from from #49498D calc(l / 4) c h);}It���s really handy that you can use calc() within oklch()���functions within functions. And I haven���t even touched on the color-mix() function.
Hmmm ���y���know, this is starting to sound an awful lot like functional programming:
Functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that map values to other values.
April 13, 2025
Paying it forward
For the past couple of years, myself and Jessica have been going to the Belfast Tradfest in the Summer. It���s an excellent event with great workshops, sessions, and concerts. And it helps that Belfast is such a lovely city to spend a week in.
What struck me the first time we were participating in workshops thre was the great mix of age ranges. It always warms my heart to see young people getting really into the music.
Then I found out about their bursary sponsorship scheme:
For many young musicians, financial barriers stand in the way of this invaluable experience.��Your support can make a real difference��by sponsoring a bursary that covers the cost of tuition for a deserving student.
Last year, I decided to forego one month���s worth of donations to The Session���the contributions that help cover the costs of hosting, newsletters, geocoding, and so on. Instead the money went towards bursary sponsorships for Belfast Tradfest.
It was a great success that managed to cover places for quite a few young musicians.
Normally, I wouldn���t mention the ins-and-outs of TheSession.org over here on adactio.com but I thought you might like to partake in this year���s fund drive:
For the month of April 2025, any donations made to The Session will go towards bursary sponsorships for young musicians to attend workshops at this year���s Belfast Trad Fest:
Maybe you���ve liked something I���ve written here. Maybe you enjoyed Resilient Web Design, the free book I published online. You can also read HTML5 For Web Designers and Going Offline for free now too.
I���ve never asked for any recompense for my online ramblings, but if you���ve ever wanted to drop me some money to thank me for something I���ve put out there, now���s your chance.
Any contribution you make will go towards fostering the next generation of traditional Irish musicians, something that���s very dear to my heart.
April 10, 2025
Salter Cane unplugged in Lewes
This Saturday, April 12th, is Record Store Day. To mark the occasion, Salter Cane will be playing a stripped-down set at Union Music Store in Lewes. That���s the place that Jamie used to run, so it has a special place in our hearts.
Liza Lo will be playing at midday.
Salter Cane are on at 1pm.
If you���re in the neighbourbood, please swing by! We���re really looking forward to playing a mix of some old songs and some brand new songs from our brand new album.
Deep Black Water by Salter CaneApril 7, 2025
Denial
The Wikimedia Foundation, stewards of the finest projects on the web, have written about the hammering their servers are taking from the scraping bots that feed large language models.
Our infrastructure is built to sustain sudden traffic spikes from humans during high-interest events, but the amount of traffic generated by scraper bots is unprecedented and presents growing risks and costs.
Drew DeVault puts it more bluntly, saying Please stop externalizing your costs directly into my face:
Over the past few months, instead of working on our priorities at SourceHut, I have spent anywhere from 20-100% of my time in any given week mitigating hyper-aggressive LLM crawlers at scale.
And no, a robots.txt file doesn���t help.
If you think these crawlers respect robots.txt then you are several assumptions of good faith removed from reality. These bots crawl everything they can find, robots.txt be damned.
Free and open source projects are particularly vulnerable. FOSS infrastructure is under attack by AI companies:
LLM scrapers are taking down FOSS projects’ infrastructure, and it’s getting worse.
You try to do the right thing by making knowledge and tools freely available. This is how you get repaid. AI bots are destroying Open Access:
There’s a war going on on the Internet. AI companies with billions to burn are hard at work destroying the websites of libraries, archives, non-profit organizations, and scholarly publishers, anyone who is working to make quality information universally available on the internet.
My own experience with The Session bears this out.
Ars Technica has a piece on this: Open source devs say AI crawlers dominate traffic, forcing blocks on entire countries.
So does MIT Technology Review: AI crawler wars threaten to make the web more closed for everyone.
When we talk about the unfair practices and harm done by training large language models, we usually talk about it in the past tense: how they were trained on other people���s creative work without permission. But this is an ongoing problem that���s just getting worse.
The worst of the internet is continuously attacking the best of the internet. This is a distributed denial of service attack on the good parts of the World Wide Web.
If you���re using the products powered by these attacks, you���re part of the problem. Don���t pretend it���s cute to ask ChatGPT for something. Don���t pretend it���s somehow being technologically open-minded to continuously search for nails to hit with the latest ���AI��� hammers.
If you���re going to use generative tools powered by large language models, don���t pretend you don���t know how your sausage is made.
March 23, 2025
Five years
My favourite bit of the archive on this site is the link that says ���on this day���. It���s of no interest to anyone except me, but I love going through this little time tunnel.
Using that link this month gives me a flashback to March five years ago when The Situation was unfolding.
I remember the build-up at the end of February. We were in Galway for a birthday weekend getaway. One morning in the hotel I saw the papers were running a story that seemed so Irish to me: because of this emerging virus, people were no longer to give the ���sign of peace��� at mass (that���s the bit where you awkwardly shake hands with the people around you). I chuckled. Nervously.
Then we were leaving Ireland, in the taxi to the airport in Dublin the radio was on. A medical professional was urging the cancellation of the St. Patrick���s Day parade because a grand total of 2 or 3 people in the country had this virus. The DJ reacted with incredulity. It sounded like a pretty far-fetched idea to me too, because St. Patrick���s Day was just over two weeks away.
The St. Patrick���s Day parade was cancelled.
Throughout The Situation I was keeping track of things in Ireland. It was like seeing an A/B test unfolding. Everything that England was doing wrong, Ireland was doing the opposite. It wasn���t quite New Zealand, but they put scientists front and centre of their decision-making precision. Whereas here, policy was driven by wishful thinking.
I was writing about it all here on my website. I also started recording a tune every day for 200 days. Here���s the first one. See how fresh-faced I am? I decided to stop shaving during lockdown. After six weeks, I looked like this.
But to really recall what that time was like, I recommend reading Jessica���s account of 2020. The first entry is called A Journal of the Plague Week and it was published five years ago. The final entry was A Journal of the Plague Week 52 a year later.
March 20, 2025
Command and control
I���ve been banging on for a while now about how much I���d like a declarative option for the Web Share API. I was thinking that the type attribute on the button element would be a good candidate for this (there’s prior art in the way we extended the type attribute on the input element for HTML5).
I wrote about the reason for a share button type as well as creating a polyfill. I also wrote about how this idea would work for other button types: fullscreen, print, copy to clipboard, that sort of thing.
Since then, I���ve been very interested in the idea of ���invokers��� being pursued by the Open UI group. Rather than extending the type attribute, they’ve been looking at adding a new attribute. Initially it was called invoketarget (so something like button invoketarget="share").
Things have been rolling along and invoketarget has now become the command attribute (there���s also a new commandfor attribute that you can point to an element with an ID). Here���s a list of potential values for the command attribute on a button element.
Right now they���re focusing on providing declarative options for launching dialogs and other popovers. That���s already shipping.
The next step is to use command and commandfor for controlling audio and video, as well as some form controls. I very much approve! I love the idea of being able to build and style a fully-featured media player without any JavaScript.
I���m hoping that after that we���ll see the command attribute get expanded to cover JavaScript APIs that require a user interaction. These seem like the ideal candidates:
button command="share"button command="fullscreen"button command="print"button command="copy"button command="install"There’s also scope for declarative options for navigating the browser’s history stack:
button command="back"button command="forward"button command="refresh"Whatever happens next, I’m very glad to see that so much thinking is being applied to declarative solutions for common interface patterns.
March 19, 2025
Style legend
There���s a new proposal for giving developers more control over styling form controls. I like it.
It���s clearly based on the fantastic work being done by the Open UI group on the select element. The proposal suggests that authors can opt-in to the new styling possibilities by declaring:
appearance: base;So basically the developer is saying ���I know what I���m doing���I���m taking the controls.��� But browsers can continue to ship their default form styles. No existing content will break.
The idea is that once the developer has opted in, they can then style a number of pseudo-elements.
This proposal would apply to pretty much all the form controls you can think of: all the input types, along with select, progress, meter, buttons and more.
But there���s one element more that I wish were on the list:
legend
I know, technically it���s not a form control but legend and fieldset are only ever used within forms.
The legend element is notoriously annoying to style. So a lot of people just don���t bother using it, which is a real shame. It���s like we���re punishing people for doing the right thing.
Wouldn���t it be great if you, as a developer, had the option of saying ���I know what I���m doing���I���m taking the controls���:
legend { appearance: base;}Imagine if that nuked the browser���s weird default styles, effectively turning the element into a span or div as far as styling is concerned. Then you could style it however you wanted. But crucially, if browsers shipped this, no existing content would break.
The shitty styling situation for legend (and its parent fieldset) is one of those long-standing annoyances that seems to have fallen down the back of the sofa of browser vendors. No one���s going to spend time working on it when there are more important newer features to ship. That���s why I���d love to see it sneak in to this new proposal for styling form controls.
I was in Amsterdam last week. Just like last year I was there to help out Vasilis���s students with a form-based assignment:
They���re given a PDF inheritance-tax form and told to convert it for the web.
Yes, all the excitement of taxes combined with the thrilling world of web forms.
(Side note: this time they were told to style it using the design system from the Dutch railway because the tax office was getting worried that they were making phishing sites.)
I saw a lot of the same challenges again. I saw how students wished they could specify a past date or a future date in a date picker without using JavaScript. And I saw them lamenting the time they spent styling legends that worked across all browsers.
Right now, Mason Freed has an open issue on the new proposal with his suggestion to add some more elements to consider. Both legend and fieldset are included. That gets a thumbs-up from me.
Jeremy Keith's Blog
- Jeremy Keith's profile
- 56 followers

