Safari :has(:empty) bug

Previous post

Next post

Conference organising in 2026 CSS Day final speakers

About this site and me

Blog homepage RSS feed Mastodon Bluesky About me

This blog uses a new RSS feed. Please update the old QuirksBlog feed you used to follow.

Tag archives

Archives 2 CSS 4 Conferences 5 Personal 2 Safari 1 Site 2 Thidrekssaga 1

Monthly archives

May 2026 4 April 2026 9 2004-2021 blog

This week I spent too much time on a Safari :has(:empty) bug — my first bug report in at least five years. I did it mostly to prove I could still do it. The Safari team already fixed the bug, so this report helped a bit.

A month ago I I created a sales page to sell about two-thirds of my board game collection. (If you're in Amsterdam and want to buy one of them, hey, let's talk!) I show sold games in a separate table until they’re actually collected — and I decided to hide that table if it contained no data.

main:has(tbody:empty){
	display: none;
}

Isn't CSS cool nowadays?

Except in Safari (Mac and iOS). No content at all was visible. Unless you resized the page. Vertically.

It says something shameful about my testing habits that it took me a month to notice. Then again, it was browser bug #32820 or so in my professional career.

(Meanwhile I removed the offending rule, so you can only see the bug on the bug report page.)

The bugs these days

The problem is, I'm just not very impressed by today’s browser bugs. The ones we had 25 years ago, when Netscape and IE battled it out, now those were the real deal! And they remained bugs for years. Nobody fixed bugs within 24 hours.

But today’s web developers start crying as soon as pretty much anything goes even slightly wrong, and I simply don’t take the silly yelling youngsters who encounter My First Very Minor Browser Cncompatibility seriously. And no, I’m not old.

This goes beyond a minor incompatibility, though. A selector that should work doesn’t work. On my page. Somebody really should do something.

Was that somebody going to be me? Did I feel like solving the bug, as I did in the good old days? Or would I just remove the offending selector and let it be?

To my surprise, I wanted to prove I could still solve a browser bug. I mean, I have this huge brain lobe dedicated to browser incompatibilities, but it kind of atrophied in the past ten years. Could it still handle a juicy bug?

It could. Actually, the process went fairly smoothly and was enjoyable, and I even found a partial workaround.

Findings

My findings, summarised:

From the fix it appears that the problem was simply that :has was not updated after the :empty state changed. Initially I assumed that that was the case, but the wealth of extra bits and pieces of bug I found made it appear it was much more complicated. (I’ve never been able to figure out which of these details are important and which ones merely obscure the truth, but I tend to err on the side of over-reporting.)

Workaround

Adding a non-:has selector such as the one below fixes the bug. It needs a CSS declaration, but that can just be a variable that you never use. It has to contain the :empty element and select your y element, like this:

x:empty ~ * {
	--bug: 'solved';
}

:has(x:empty) y {
	// works!
}

This may not always work. In particular, it doesn't work for my original use case since I can’t write a selector for main that also touches tbody:empty without using :has.

main tbody:empty {
	// doesn't work
	// selects tbody instead of main
}

main:has(tbody:empty){
	display: none;
}

In order to truly solve the problem you’d have to remove the :has(:target), I’m afraid — so that’s what I did on my page. All the better that the bug has been solved.

Time and money

The process took me about eight hours: six for the research, and two for the report writing interspersed with some research — for instance, the workaround came from an idea I had fairly late in the writing process. And that doesn’t count writing this blog post.

Although I don’t regret spending a working day on this bug, it’s not something I’m going to do a lot of. I mean, conference catering proposals don’t check themselves, and sponsors and speakers need gentle reminders every now and then. This week I did less conference work than I had planned.

And this sort of bug squashing doesn’t make you any money. That’s part of the problem — it always has been.

But still, it was fun. It’s good to be back.