:focus-visible is shipping in Safari/WebKit
This is the final report about the work Igalia has been doing to add support for :focus-visible
in WebKit. As you probably already know this work is part of the Open Prioritization campaign by Igalia that has been funded by different people and organizations. Big thanks to all of you!
If you’re curious and want to know all the details you can find the previous reports on this blog.
The main highlight for this blog post is that :focus-visible
has been enabled by default in WebKit (r286783). 🚀
This change was included in Safari Technology Preview 138, with its own post on the official WebKit blog. And finally reached a stable release in Safari 15.4. It’s also included in WebKitGTK 2.36 and WPE WebKit 2.36.
Open Prioritization #
Let’s start from the beginning, my colleague Brian Kardell had an idea to find more diverse ways to sponsor the development of the web platform, after some internal discussion that idea materialized into what we call Open Prioritization. In summer 2020 Igalia announced Open Prioritization that intially had six different features on the list:
- CSS
lab()
colors in Firefox :focus-visible
in WebKit/Safari- HTML
inert
in WebKit/Safari - Selector list arguments for
:not()
in Chrome - CSS Containment support in WebKit/Safari
- CSS
d
(SVG path) support in Firefox
By that time I wrote a blog post about this effort and CSS Containment in WebKit proposal and my colleagues did the same for the rest of the contenders:
- Brian Kardell on
inert
and:focus-visible
. - Frédéric Wang about CSS
lab()
colors and SVGd
path. - Oriol Brufau about CSS
not()
selector.
After some months :focus-visible
was the winner. By the end of 2020 we launched the Open Prioritization Collective to collect funds and we started our work on the implementation side.
Last year at TPAC, Eric Meyer gave an awesome talk called Adventures in Collective Implementation, explaining the Open Prioritization effort and the ideas behind it. This presentation also explains why there’s room for external investments (like this one) in the web platform, and that all open source projects (in particular the web browser engines) always have to make decisions regarding priorities. Investing on them will help to influence those priorities and speed up the development of features you’re interested in.
It’s been quite a while since we started all this, but now :focus-visible
is supported in WebKit/Safari, so we can consider that the first Open Prioritization experiment has been successful.
When :focus-visible
was first enabled by default in Safari Technology Preview early this year, there were lots of misunderstandings about how the development of this feature was funded. Happily Eric wrote a great blog post on the matter, explaining all the details and going over some of the ideas from his TPAC talk.
:focus-visble
is shipping in in WebKit, how that happened? #
In November last year, I gave a talk at CSS Conf Armenia about the status of things regarding :focus-visible
implementation in WebKit. In that presentation I explained some of the open issues and why :focus-visible
was not enabled by default yet in WebKit.
The main issue was that Apple was not convinced about not showing a focus indicator (focus ring) when clicking on a focusable element (like a <div tabindex="0">
). However this is one of the main goals of :focus-visible
itself, avoiding to get a focus indicator in such situations. As Chromium and Firefox were already doing it, and aiming to have a better interoperability between the different implementations, Apple finally accepted this behavioral change on WebKit.
Then Antti Koivisto reviewed the implementation, suggesting a few changes and spotting some issues (thanks about that). Those things were fixed and the feature was enabled by default in the codebase last December. As usual once a feature is enabled some more issues appear and they were fixed too. Including even a generic issue regarding accesskey
on focusable elements, which required to add support to test accesskey
on WebKit Web Platform Tests (WPT).
As part of all this work since my previous blog post we landed 9 more patches on WebKit, making a total of 36 patches for the whole feature, together with a few new WPT tests.
Buttons and :focus-visible
on Safari #
This topic has been mentioned in my previous posts and also in my talk. Buttons (and other form controls) are not mouse focusable in Safari (both in macOS and iOS), this means that when you click a button on Safari, the button is not focused. This behavior has the goal to match Apple platform conventions, where the focus doesn’t move when you click a button. However Safari implementation differs from the platform one, as the focus gets actually lost when you click on such elements. There are some very old issues in WebKit bugtracker about the topic (see #22261 from 2008 or #112968 from 2013 for example).
There’s a kind of coincidence related to this. Before :focus-visible
existed, buttons were never showing a focus indicator in Safari after mouse click, as they are not mouse focusable. This was different in other browsers where a focus ring was showed when clicking on buttons. So while :focus-visible
fixed this issue for other browsers, it didn’t change the default behavior for buttons in Safari.
However with :focus-visible
implementation we introduced a problem somehow related to this. Imagine a page that has an element and when you click it, the page moves the focus via script (using HTMLElement.focus()
) to a different element. Should the new focused element show a focus indicator? Or in other words, should it match :focus-visible
?
The answer varies depending on whether the element clicked is or not mouse focusable:
- If you click on a focusable element and the focus gets moved via script to a different element, the newly focused element does NOT show a focus indicator and thus it does NOT match
:focus-visible
. - If you click on a NON focusable element and the focus gets moved via script to a different element, the newly focused element shows a focus indicator and thus it matches
:focus-visible
.
All implementations agree on this, and Chromium and Firefox have been shipping this behavior for more than a year without known issues so far. But a problem appeared on Safari, because unlike the rest of browsers, buttons are not mouse focusable there. So when you click a button in Safari, you go to point 2) above, and end up showing a focus indicator in the newly focused element. Web authors don’t want to show a focus indicator on that situations, and that’s something that :focus-visible
is fixing through point 1) in the rest of browsers, but not in Safari (see bug #236782 for details).
We landed a workaround to fix this problem in Safari, that somehow adds an exception for buttons to follow point 1) even if they are not mouse focusable. Anyway this doesn’t look like the solution for the long term, and looking into making buttons mouse focusable on Safari might be the way to go in the future. That will also help to solve other interop issues.
And now what? #
The feature is complete and shipped, but as usual there are some other things that could be done as next steps:
- The
:focus-visible
specification is kind of vague and has no normative text related to when or not show a focus indicator. This was done on purpose to advance on this area and have flexibility to adapt to user needs. Anyway now that all 3 major web engines agree on the implementation, maybe there could be the chance to define this in some spec. We tried to write a PR for HTML spec when we started the work on this feature, at that time it was closed, probably it was not the right time anyway. But maybe something like that could be retaken at some point in the future. - WebKit Web Inspector (Dev Tools) don’t allow you to force
:focus-visible
yet. We sent a patch for forcing:focus-within
first but some UI refactoring is needed, once that’s done adding support for:focus-visible
too should be straight forward. - Coming back to the topic on buttons not being mouse focusable in Safari. The web platform provides a way to make elements not keyboard focusable via
tabindex="-1"
. Why not providing a way to mark an element as not mouse focusable? Maybe there could be a proposal for a new HTML attribute that allows making elements not mouse focusable, that way websites could mimic Apple platform conventions. There are nice use cases for this, for example when you’re editing an input and then you click on some button to show some contextual information, with something like this you could avoid losing the focus from the input to carry on with your editing.
Wrap-up #
So yeah after more than a year since Igalia started working on :focus-visible
in WebKit, we can now consider that this work has been complete. We can call the first Open Prioritization experiment a success, and we can celebrate together with all the people that have supported us during this achievement. 🎉
Thank you very much to all the people that sponsored this work. And also to all the people that helped reviewing patches, reporting bugs, discussing things, etc. during all this time. Without all your support we won’t be able to have made this happen. 🙏
Last but not least, we’d like to highlight how this work has helped the web platform as a whole. Now the major web browser engines have shipped :focus-visible
and are using it in the default UA stylesheet. This makes tweaking the focus indicator on websites easier than ever.