Tags: ties

199

sparkline

Wednesday, April 1st, 2026

CSS or BS?

We show you a CSS property name. You tell us if it’s real or if we made it up. That’s it. It starts easy. It does not stay easy.

Monday, March 9th, 2026

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.

Wednesday, August 13th, 2025

Style your underlines

We shouldn’t rely on colour alone to indicate that something is interactive.

Take links, for example. Sure, you can give them a different colour to the surrounding text, but you shouldn’t stop there. Make sure there’s something else that distinguishes them. You could make them bold. Or you could stick with the well-understood convention of underlying links.

This is where some designers bristle. If there are a lot of links on a page, it could look awfully cluttered with underlines. That’s why some designers would rather remove the underline completely.

As Manu observed:

I’ve done a lot of audits in the first half of this year and at this point a believe that designing links without underlines is a kink. The idea that users don’t understand that links are clickable arouses some designers. I can’t explain it any other way.

But underlining links isn’t the binary decision it once was. You can use CSS to style those underlines just as you’d style any other part of your design language.

Here’s a regular underlined link.

For a start, you can adjust the distance of the underline from the text using text-underline-offset. If you’re using a generous line-height, use a generous distance here too.

Here’s a link with an offset underline.

If you’d rather have a thinner or thicker underline, use text-decoration-thickness.

Here’s a link with a thin underline.

The colour of the underline and the colour of the link don’t need to be the same. Use text-decoration-color to make them completely different colours or make the underline a lighter shade of the link colour.

Here’s a link with a translucent underline.

That’s quite a difference with just a few CSS declarations:

text-underline-offset: 0.2em;
text-decoration-thickness: 1px;
text-decoration-color: oklch(from currentColor l c h / 50%);

If that still isn’t subtle enough for you, you could even use text-decoration-style to make the underline dotted or dashed, but that might be a step too far.

Here’s a link with a dotted underline.

Whatever you decide, I hope you’ll see that underlines aren’t the enemy of good design. They’re an opportunity.

You should use underlines to keep your links accessible. But you should also use CSS to make those underlines beautiful.

Tuesday, August 5th, 2025

How to Make Websites That Will Require Lots of Your Time and Energy - Jim Nielsen’s Blog

  1. Install Stuff Indiscriminately From npm
  2. Pick a Framework Before You Know You Need One
  3. Always, Always Require a Compilation Step

Tuesday, June 17th, 2025

The Recurring Cycle of ‘Developer Replacement’ Hype

Here’s what the “AI will replace developers” crowd fundamentally misunderstands: code is not an asset—it’s a liability. Every line must be maintained, debugged, secured, and eventually replaced. The real asset is the business capability that code enables.

If AI makes writing code faster and cheaper, it’s really making it easier to create liability. When you can generate liability at unprecedented speed, the ability to manage and minimize that liability strategically becomes exponentially more valuable.

This is particularly true because AI excels at local optimization but fails at global design. It can optimize individual functions but can’t determine whether a service should exist in the first place, or how it should interact with the broader system. When implementation speed increases dramatically, architectural mistakes get baked in before you realize they’re mistakes.

Thursday, May 8th, 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.

Sunday, April 13th, 2025

Friday, March 21st, 2025

The Blowtorch Theory: A New Model for Structure Formation in the Universe

Make yourself a nice cup of tea and settle in with Julian Gough’s magnum opus:

How early, sustained, supermassive black hole jets carved out cosmic voids, shaped filaments, and generated magnetic fields

Saturday, March 1st, 2025

The future of the internet is likely smaller communities, with a focus on curated experiences | The Verge

Good news for the fediverse, the indie web, and community sites like The Session:

People are abandoning massive platforms in favor of tight-knit groups where trust and shared values flourish and content is at the core. The future of community building is in going back to the basics.

Anchor position tool

This is a great little helper in understanding anchor positioning in CSS.

Chrome-only for now.

Sunday, February 16th, 2025

The hardest working font in Manhattan – Aresluna

This is absolutely wonderful!

There’s deep dives and then there’s Marcin’s deeeeeeep dives. Sit back and enjoy this wholesome detective work, all beautifully presented with lovely interactive elements.

This is what the web is for!

Sunday, February 9th, 2025

Wednesday, January 29th, 2025

6 CSS Snippets Every Front-End Developer Should Know In 2025 · 19 January 2025

  • Springy easing with linear()
  • Typed custom properties
  • View transitions for page navigation
  • Transition animation for dialog and popover
  • Transition animation for details
  • Animated adaptive gradient text

Thursday, January 9th, 2025

Hixie’s Natural Log: When complaints are a good sign

This is a very smart way to handle feedback about a product.

Sunday, December 15th, 2024

Progressively enhancing maps

The Session has been online for over 20 years. When you maintain a site for that long, you don’t want to be relying on third parties—it’s only a matter of time until they’re no longer around.

Some third party APIs are unavoidable. The Session has maps for sessions and other events. When people add a new entry, they provide the address but then I need to get the latitude and longitude. So I have to use a third-party geocoding API.

My code is like a lesson in paranoia: I’ve built in the option to switch between multiple geocoding providers. When one of them inevitably starts enshittifying their service, I can quickly move on to another. It’s like having a “go bag” for geocoding.

Things are better on the client side. I’m using other people’s JavaScript libraries—like the brilliant abcjs—but at least I can self-host them.

I’m using Leaflet for embedding maps. It’s a great little library built on top of Open Street Map data.

A little while back I linked to a new project called OpenFreeMap. It’s a mapping provider where you even have the option of hosting the tiles yourself!

For now, I’m not self-hosting my map tiles (yet!), but I did want to switch to OpenFreeMap’s tiles. They’re vector-based rather than bitmap, so they’re lovely and crisp.

But there’s an issue.

I can use OpenFreeMap with Leaflet, but to do that I also have to use the MapLibre GL library. But whereas Leaflet is 148K of JavaScript, MapLibre GL is 800K! Yowzers!

That’s mahoosive by the standards of The Session’s performance budget. I’m not sure the loveliness of the vector maps is worth increasing the JavaScript payload by so much.

But this doesn’t have to be an either/or decision. I can use progressive enhancement to get the best of both worlds.

If you land straight on a map page on The Session for the first time, you’ll get the old-fashioned bitmap map tiles. There’s no MapLibre code.

But if you browse around The Session and then arrive on a map page, you’ll get the lovely vector maps.

Here’s what’s happening…

The maps are embedded using an HTML web component called embed-map. The fallback is a static image between the opening and closing tags. The web component then loads up Leaflet.

Here’s where the enhancement comes in. When the web component is initiated (in its connectedCallback method), it uses the Cache API to see if MapLibre has been stored in a cache. If it has, it loads that library:

caches.match('/path/to/maplibre-gl.js')
.then( responseFromCache => {
    if (responseFromCache) {
        // load maplibre-gl.js
    }
});

Then when it comes to drawing the map, I can check for the existence of the maplibreGL object. If it exists, I can use OpenFreeMap tiles. Otherwise I use the old Leaflet tiles.

But how does the MapLibre library end up in a cache? That’s thanks to the service worker script.

During the service worker’s install event, I give it a list of static files to cache: CSS, JavaScript, and so on. That includes third-party libraries like abcjs, Leaflet, and now MapLibre GL.

Crucially this caching happens off the main thread. It happens in the background and it won’t slow down the loading of whatever page is currently being displayed.

That’s it. If the service worker installation works as planned, you’ll get the nice new vector maps. If anything goes wrong, you’ll get the older version.

By the way, it’s always a good idea to use a service worker and the Cache API to store your JavaScript files. As you know, JavaScript is unduly expensive to performance; not only does the JavaScript file have to be downloaded, it then has to be parsed and compiled. But JavaScript stored in a cache during a service worker’s install event is already parsed and compiled.

Sunday, November 3rd, 2024

New CSS that can actually be used in 2024 | Thomasorus

Logical properties, container queries, :has, :is, :where, min(), max(), clamp(), nesting, cascade layers, subgrid, and more.

Thursday, October 31st, 2024

My Modern CSS Reset | jakelazaroff.com

I like the approach here: logical properties and sensible default type and spacing.

Monday, September 30th, 2024

The Unraveling of Space-Time | Quanta Magazine

This special in-depth edition of Quanta is fascinating and very nicely put together.

Thursday, July 18th, 2024

Tuesday, March 26th, 2024

Who knows?

I love it when I come across some bit of CSS I’ve never heard of before.

Take this article on the text-emphasis property.

“The what property?”, I hear you ask. That was my reaction too. But look, it’s totally a thing.

Or take this article by David Bushell called CSS Button Styles You Might Not Know.

Sure enough, halfway through the article David starts talking about styling the button in an input type="file” using the ::file-selector-button pseudo-element:

All modern browsers support it. I had no idea myself until recently.

He’s right!

Then I remembered that I’ve got a file upload input in the form I use for posting my notes here on adactio.com (in case I want to add a photo). I immediately opened up my style sheet, eager to use this new-to-me bit of CSS.

I found the bit where I style buttons and this is the selector I saw:

button,
input[type="submit"],
::file-selector-button

Huh. I guess I did know about that pseudo-element after all. Clearly the knowledge exited my brain shortly afterwards.

There’s that tautological cryptic saying, “You don’t know what you don’t know.” But I don’t even know what I do know!