Reading List
The most recent articles from a list of feeds I subscribe to.
The negotiation cycle.
On the work, and being endlessly clever.
On popover accessibility: what the browser does and doesn’t do
One of the premises of the new popover attribute is that it comes with general accessibility considerations “built in”. What does “built in accessibility” actually mean for browsers that support popover?
See also: Scott's post Popping preconceived popover ponderings and Hidde's talk on popovers, and other posts about popover semantics, positioning popovers and the difference with dialogs and other components.
About this post
NOTE: except for this note, this whole post was co-written with Scott O’Hara (thanks Scott!). See also Scott's post, popover accessibility and his post Popping preconceived popover ponderings. Whether you're a developer, designer or accessibility specialist, hearing “accessibility is built in” probably makes you want to know what exactly is built-in. For popover, this actually changed quite a bit over time, after discussions at Open UI and with WHATWG. At first, the plan was to introduce a popup element with built-in roles. Later, an attribute ended up making more sense (more on that in the post). For that attribute, and thanks to the great effort of Scott and others, some “accessibility guardrails” have now emerged. And they shipped in most browsers. I hope this post helps you understand better what accessibility is “built-in” when you use popover, and what is not.
In this post
- Accessibility semantics
- What browsers do (aria-expanded, aria-details, group, keyboard accessibility)
- What browsers don't do
- Conclusion
Accessibility semantics
The “built-in” accessibility of popover is in the addition of guardrails: browsers try to improve accessibility where they can. These guardrails exist mostly in the form of browsers augmenting accessibility semantics. Before we get into what those guardrails are, let's clarify what that term means.
Many features of HTML have some amount of accessibility semantics associated with them - e.g., roles, states and properties. This is information that a web page exposes, which browsers then pass on to platform accessibility APIs. They do this, so that assistive technologies can build UIs around them (see: How accessibility trees inform assistive tech). These semantics are sometimes baked into native HTML elements. For instance, headings and lists have implicit roles (heading and list, respectively). Other elements, like the checkbox input type, have an implicit role as well as additional states and properties. Developers can use HTML elements with such “built-in” semantics. But they can also set, overwrite and augment accessibility semantics more directly in their HTML structure, using WAI-ARIA.
The thing with the popover attribute is that it doesn’t have a built-in role. After all, it’s not an element. Its purpose is to only add “popover behaviour”, as discussed in Popover semantics. In that sense, popover is a bit like tabindex or contenteditable. These attributes also add behaviour: tabability and editability behaviours, respectively.
A major reason for this choice is that there are a number of components that exhibit popover behaviours. Examples include menus, “toast” messages, sub-navigation lists of links and tooltips. You can use popover on a specific element, then it will get that element's role. Or you can use it with a generic element, and add a role that best matches what you are building.
So, while the default role is ‘generally’ not handled by the attribute (more on that later), there are other semantics (properties and states) that the attribute will expose. Browsers can take care of those with some degree of confidence.
What browsers do
There are two semantics that the browser should take care of when you use popover, and its associated popovertarget attribute. Additionally, there is some keyboard focus behaviour that may also be handled automatically, depending on the type of popover you are using.
The aria-expanded state
First, aria-expanded. This state is exposed on the element that invokes the popover, currently limited to buttons (for a variety of reasons that would require a whole other article to talk about - so this is all you get right now). When a popover is invoked by / associated with a button with the popovertarget attribute, the browser will automatically convey whether the popover is in the expanded (rendered) state, or if it is in the collapsed (hidden) state. This is implemented in Edge, Chrome, Firefox and Safari.
For the following example, the ‘heyo’ button will automatically convey whether its associated popover list is in the expanded or collapsed state, based on whether the popover list is invoked as a popover.
<button popovertarget=p>
Heyo
</button>
…
<ul
aria-label="Heyo subpages"
id=p
popover
></ul>
Note: the state won’t be applied if script, rather than the declarative attribute, does the opening on click of any button (or any other element). Needless to say: it also doesn’t work if there isn’t an invoking button, for instance, and script invokes this popover (because in that case, there isn’t any expanding going on). Additionally, if you force open your popover using CSS display block, then it will not be rendered as a popover - and thus the button will still communicate that the “popover” is in the collapsed state. Also, if you’re doing that - forcing your popover open with CSS - maybe you have some things you need to reconsider with your UI.
The aria-details relationship
When the popover doesn’t immediately follow its invoking button in the accessibility tree, browsers are supposed to create an aria-details relationship on the popover’s invoking button with its associated popover. At the time of writing, this is implemented in Chrome, Edge and Firefox.
For instance, in the following markup snippet an implicit aria-details relationship will be made with the button that invokes the popover, because the button and the popover are not immediate siblings in the accessibility tree.
<button popovertarget=foo>something</button>
<p>...</p>
<div role=whatever popover id=foo>...</div>
Similarly, an aria-details relationship will be made with the next markup snippet too, because even though the popover and its invoking button are siblings, the popover is a previous sibling to the invoking element, and it might not be understood which element is the popover, because it doesn’t immediately follow the element that invoked it.
<div role=whatever popover id=foo>...</div>
<button popovertarget=foo>something</button>
In contrast, the next two examples have no aria-details association because that would be unnecessary. For the first, the popover is the immediate next sibling in the accessibility tree (note divs are generic and often ignored when they do not provide information important to accessibility). For the second, the button is a descendant of the popover, so browsers do not need to tell users that the button they are interacting with is about the context they are within. That’d be silly.
<!--
example 1:
popover immediate sibling in acc tree
-->
<button popovertarget=m>something</button>
<div class=presentational-styles-only>
…
<div role=menu popover id=m>...</div>
</div>
<!--
example 2:
button descendant of popoover
-->
<dialog popover id=d>
<button popovertarget=d>close</button>
…
</dialog>
For more information on how aria-details works, check out the details in the ARIA spec.
Note: aria-details is often confused with aria-describedby. That makes sense, “details” and “descriptions” are very similar. However, these two properties expose different information. aria-describedby takes the associated element’s content, flattens it into a single text string, and exposes that as the ‘description’ or ‘hint’ of the element which the attribute is specified. In contrast, aria-details only informs a user that there is additional information about the current element. That might not seem useful, until you know that screen readers largely provide quick-keys to navigate to and from that associated element which provides the details.
At the time of writing, navigating into the referenced content using quick-keys is supported by JAWS and NVDA (release notes), but not VoiceOver.
Here’s a quick demo of that with JAWS 2023 in Edge 124. JAWS lets us jump to the details content if we press Alt + Ins + D:
In NVDA (2023.3.4), tested with Edge 124, it works slightly differently: when you press the shortcut (NVDA + D), we don't jump to the details content, but it is read out after the shortcut is pressed:
(see demo on CodePen; note: announcements and shortcuts depend on the user's settings, versions, etc)
In the following demo, you can see how the aria-details relationship works between popovers and their invokers (in JAWS 2023 with Edge 124):
(video contains screenshot of code, see demo on CodePen)
In summary: the aria-details association is not announced by JAWS when we focus the invoking button for the first time. This is because the corresponding popover is hidden, so the association isn't made yet. After we open the popover, JAWS announces the button's “has details” association while it is open, to hear it we navigate away and back. This is also how it works in NVDA, which, in addition, also requires you to switch to forms mode to actually hear the relationship announced.
Warning: even if the aria-details association is implemented, it may not be completely ironed out in how the UX behaves for people. For instance, there isn't currently a way for users to find out about the details relationship once it is established, like when the popover opened. It requires for the user to move away from the button and return to it, at which point the relationship is announced. Maybe it would be helpful if the browser would fire some kind of event, to let AT like JAWS know that an element representing details has appeared.
We mention this not to deter you from using popover or to indicate that anyone is doing something “wrong” here. Rather, this is a relatively new feature that people still need to figure out some of the UX kinks around. Feedback is welcome, and to help ensure the best UX is provided, please reach out to the necessary browsers / AT with your thoughts.
The group role
As mentioned above, popover can be used on any element, including elements that don’t have a built-in role, like div. But even without a role, it’s likely that the contents of a popover form some kind of meaningful whole. This is why in Chrome, Edge and Firefox, a role of group is automatically added to popovers if they would otherwise have no role, or a role of generic (for instance, divs and spans).
The group role is added, so that assistive technology can have the option to expose the boundaries of the popover that is being displayed. This can be important to users, because a popover is a behavior and visual treatment of its content. How is one to know where such content begins or ends if it doesn’t have boundaries to expose?
It’s important to know that an unnamed group is often ignored by screen readers. This is because otherwise the Internet would be riddled with unhelpful “group” announcements. (See also why Webkit made the decision to remove list semantics from lists that have been styled to not look like lists. These topics are related). Here though, it again comes down to what assistive technology wants to do. By exposing the group role for the popover, now the element can be named by authors, which will force the group role to be exposed in most cases. Then, if AT decided they want to do something special for popover groups, they now have the opportunity to do so.
Keyboard accessibility
One more aspect of “built-in accessibility” that browsers do for your popover, is take care of some keyboard behaviors.
Moving focus back to invoking element
Edge/Chrome, Firefox and Safari will all return focus to the invoking element when you close the popover (only if you are inside of it). This is useful, because if focus was on an element inside the popover, the default would be to return focus to the start of the document. Various groups of users would get lost, increasingly so on pages with a lot of content. Moving focus back to the invoking element helps ensure people can quickly return to what they were doing, rather than spending time having to re-navigate to where they think they were last.
Popover content in tab order
Desktop browsers also do something else: they add the popover content into the tab order just after the invoking button. Even if that’s not where the popover is in the DOM.
Imagine this DOM structure:
<button popovertarget=p>Open popover</button>
<p>content… content… <a href="#">link 1</a></p>
<p>content… content… <a href="#">link 2</a></p>
<div popover id="p"><a href="#">link 3</a></div>
When the popover opens, and you press Tab, you might think you’d jump to “link 1”, the next interactive element in the DOM. Except, in desktop browsers, you will jump to “link 3” instead. The browser basically moves the popover content’s position in tab order to just after its invoking button. This takes it out of its expected position in the tab order. That improves the user experience, because it is likely that upon opening the popover, users will want to interact with its contents.
Keep in mind: browsers adjust the Tab order for instances like this, but they don't adjust the placement of the content in the accessibility tree. This is why the aria-details association was implemented. This special Tab order behavior helps ensure logical focus order for keyboard accessibility. However, we should still strive to make sure our popovers come after the invoking element in the DOM.
But since there will be times where the exact location of the popover in the DOM may be out of one’s control, this behavior is still quite welcome. For instance, if the popover happens to be far away in the DOM, having to go through the rest of the document before reaching the popover would be a nuisance. It would be highly unexpected and unwanted to have to navigate through all other focusable elements in the DOM, prior to the popover one just opened. WCAG 2.4.3 Focus Order requires focusable elements to receive focus in an order that “preserves meaning and operability”. This special browser Tab restructuring helps ensure that requirement can be met.
What browsers don’t do
We can keep this one short: the browser will not do anything apart from the behaviours listed above. Browsers will not add behaviors based on which elements you use or which role you add to them. The popover attribute is merely a first step for us to build new components.
Conclusion
The popover attribute provides a starting point for building popover-like interactions on the web. Browsers don't magically make your components accessible, that's not a thing. But there are some specific keyboard behaviours included with popover, as well as these semantics:
- In specific cases, browsers set the
aria-expandedstate and/or set thearia-detailsrelationship on the invoker. - Browsers apply the
grouprole to thepopovercontent if it doesn’t have a role of its own, so that assistive technologies can expose their boundaries.
Browser support note: at the time of writing, Safari only sets aria-expanded, not aria-details. It also doesn't add a group role fallback.
Originally posted as On popover accessibility: what the browser does and doesn’t do on Hidde's blog.
Why does an extraneous build step make my Zig app 10x faster?
For the past few months, I’ve been curious about two technologies: the Zig programming language and Ethereum cryptocurrency. To learn more about both, I’ve been using Zig to write a bytecode interpreter for the Ethereum Virtual Machine.
Zig is a great language for performance optimization, as it gives you fine-grained control over memory and control flow. To motivate myself, I’ve been benchmarking my Ethereum implementation against the official Go implementation.
TinyPilot: Month 44
New here?
Hi, I’m Michael. I’m a software developer and the founder of TinyPilot, an independent computer hardware company. I started the company in 2020, and it now earns $80-100k/month in revenue and employs six other people.
Every month, I publish a retrospective like this one to share how things are going with my business and my professional life overall.
Highlights
- We completed the first-ever TinyPilot release where I didn’t perform any release task directly.
- Publishing a release through delegation helped identify many undocumented or poorly conceived steps in our release process.
- I’m continuing to enjoy writing a bytecode interpreter in Zig.
Goal grades
At the start of each month, I declare what I’d like to accomplish. Here’s how I did against those goals:
Reflections from IndieWebCamp Brighton
The other weekend, I attended IndieWebCamp Brighton and had a wonderful time! I've been working from home for four years, and most of my friends have left London, so events like this excite me to see internet buddies! Like Jeremy said, I, too, feel like it was my RSS feed coming to life!
Notes from the event were masterfully collected in IndieWeb's wiki, but here are my write-up and thoughts on the sessions I attended. It was incredibly hard to pick which sessions to attend, as all the topics were interesting.
Day one - Unconference
Energy Efficiency
I started to follow this topic a few years ago when the carbon calculator came out. It impacted how I added certain things to my website, which also positively impacted its performance. In 2022, at FFConf, I saw a talk by Natalia about a greener web that mentioned how streams are a source of pollution. Frankly, it had never occurred to me and has been bothering me a lot. It was one of the decisions that led me to delete my Spotify account and made me think about what other waste I was making.
As captured in the notes, one of my comments was about how many automated processes are running in the background of running our personal websites. I could generate the HTML pages of my website locally and manually upload the files to my hosting. Instead, I have it on GitHub, which gets my commits and then triggers a build on Netlify and, later, a deployment. For all this cool tech to work, there are servers, energy consumption, data stored, etc.
On the other hand, I promote the idea that your personal website should be a place where you experiment and learn. In the grand scheme of things, my personal website does not impact the environment, the same way that deleting my Spotify account hasn't improved the world, but it makes part of me give up taking space on the web.
I've wondered in the past if I could create some videos or even stream, but not only do I have privacy concerns, but I also know I would feel guilty if I didn't feel as sustainable as I could be.
I really enjoyed the session, but I have a hang-up on feeling like I am doing too much digital waste instead of rightfully blaming the billionaires and their stupid private jets.
Site death
I love how I just carried on choosing slightly less happy themes. I loved that this topic was suggested, especially since I recently dealt with my dad's digital life.
My mind was in two places during this session: how would I build a dead man's switch on my personal website, and what do I even want to happen if I suddenly die.
I still don't have an answer. My tech content will undoubtedly be useless, and my personal notes will only prove that I existed and had thoughts and opinions.
Do I want a banner at the top explaining that I died? Maybe I should renew my domains for ten more years? Should I print my personal posts? I am still trying to figure out what I want.
I keep thinking about the 80L plastic box at my mum's house with my dad's belongings that we picked that we wanted to keep. I wonder what he would have picked.
It also made me realise that because of digital cameras, only a few photos of me were printed from the age of 11 or 12 until my wedding. If I suddenly died, I don't think anyone would know about my teenage years. I wondered what my daughter would like to have from me. It is a digital dark age. Before worrying about my website, I should worry about my backups and create physical copies. See? More waste.
Hosting
I attended this session because I was keen to help if anyone had questions. I am trying to remember where I said this before, but deciding where to host your website in 2024 is awful. The search results are full of ads. Someone who wants their personal website to exist will struggle with this. No wonder websites like Squarespace have an extensive consumer base since they solve build and hosting. But their pricing is ridiculous.
I made a note that I would like to create a blog post where I try out and list free hosting services for anyone to host their HTML pages. Hopefully, this would help someone.
NFC
I really enjoyed this session with Terrence! I've seen people sharing cool experiments like having your oyster card on your nails, but I never thought about how it works. I suggested having an NFC tag on my cat and pointing to her website. As soon as I got home, I planned to put the NFC sticker on her AirTag case. And I did it! Except her AirTag is also an NFC, so they clashed when I scanned it with my phone. I will get back to this—there must be a way of having a tiny one glued to her collar at a distance from the Airtag.
But the question was: how would you apply this to the IndieWeb? We thought of the dead man's switch! Maybe an API/smart URL on the other side of the NFC tag that, when run, would update the website so that it knows I am still alive? Maybe an NFC tag that opens a URL that tells your website to deploy a type of webmention (like a check-in). That would be fun!
I noticed that my library books have NFC tags! I can't stop seeing them everywhere I go now!
Personal Website Pain Points
Maggie initially suggested this topic, and I suggested another topic loosely titled "tech debt" on your personal website. I proposed merging the topics into one session to make it easier to organise the unconference, and it worked out well.
Maggie was curious to know people's pain points on their websites because they are working on building a digital garden tool (which sounds incredible).
I shared how I have so many regrets about decisions I made on this website: URLs, asset locations, organisation, very old code that still hasn't been refactored, etc.
It was amusing to realise that the way I organise my blog posts' URLs, which I regret, is how many people wanted to do on their websites.
I've recently made better mental efforts to avoid allowing tech debt to paralyse me and make me feel like my blog codebase has to be perfect.
Day two - Hands-on
My day started by sitting down with Paul and trying out IndieKit. I've been dreaming of creating my micropub endpoint, but lack of time and confidence has stalled me. We got it working! It turns out that having sat down together to try to run it allowed us to spot some bugs and gave Paul a different perspective on how another person could use their tool. I loved it! Now, I need to work on my plug-in to ensure the templates are created for my specific needs. After that, I will be unstoppable!
This took a chunk of the morning, so I needed an achievable task after it. I decided to add a privacy page to my blog. A while back, through my analytics, I saw someone having thoughts and opinions on my analytics usage and the way it was worded made it sound like I used trackers of the invasive kind. It was time to add a page that clarified my analytics usage.
The day was wrapped up with impressive demos from everyone.
Thank you so much, Paul and Mark, for organising it! I had a fantastic time and hope it will become a frequent event.
More people wrote about their day, and you should check it out!