Popovers are commonly positioned relative to their invoker (if they have one). When we use the popover attribute, anchoring is tricky, as these popovers are in the top layer, away from the context of their invoker. What options do we have?
See also: Hidde's talk on popovers, and other posts about popover accessibility, positioning popovers and the difference with dialogs and other components.
Basically, there are two ways to position popovers: one you can use today and one that will be available in the future. I'll detail them below, but first, let's look at why we can't use absolute positioning relative to a container shared by the invoker and the popover.
Not all popovers are anchored, but I expect anchored popovers to be among the most common ones. For popovers that are not anchored, such as toast-like elements, “bottom sheets” or keyboard-triggered command palettes, these positioning constraints do not apply.
Examples of anchored popovers: map toggletip (Extinction Rebellion), date picker (European Sleeper), colour picker (Microsoft Word web app)
See also my other posts on popovers:
- Dialogs and popovers seem similar. How are they different
- Semantics and the popover attribute: what to use when?
- On popover accessibility: what the browser does and doesn't do (with Scott O'Hara)
Top layer elements lose their positioning context#heading-1
One of the unique characteristics of popovers (again, the ones made with the popover attribute, not just any popover from a design system), is that they get upgraded to the top layer. The top layer is a feature drafted in CSS Positioning, Level 4. The top layer is a layer adjacent to the main document, basically like a bit like a sibling of <html>
.
Some specifics on the top layer:
- It's above all
z-index
es in your document, top layer elements can't usez-index
. Instead, elements are stacked in the order they are added to the top layer. - As developers, we can't put elements in the top layer directly, as it is browser controlled. We can only use certain elements and APIs that then trigger the browser to move an element to the top layer: the Full Screen API,
<dialog>
s withshowModal()
andpopover
'ed elements, currently. - Top layer elements, quoting the specification, “don't lay out normally based on their position in the document”.
When I positioned my first popover, I tried (and failed): I put both the popover and its invoking element in one element with position: relative
. Then I applied position: absolute
to the popover, which I hoped would let me position relative to the container. It didn't, and I think the last item above explains why.
In summary, elements lose their position context when they are upgraded to the top layer. And that's okay, we have other options.
Option 1: position yourself (manually or with a library)#heading-2
The first option is to position the popover yourself, with script. Because the fact that the top layer element doesn't know about the non-top layer element's position in CSS, doesn't mean you can't store the invoker's position and calculate a position for the popover itself.
There are some specifics to keep in mind, just like with popovers that are built without the popover
attribute: what happens when there's no space or when the popover is near the window? Numerous libraries can help with this, such as Floating UI, an evolution of the infamous Popper library.
Let's look at a minimal example using Floating UI. It assumes you have a popover in your HTML that is connected to a button using popovertarget
:
<button popovertarget="p">Toggle popover</button>
<div id="p" popover>… popover contents go here</div>
By default, browsers show the open popover in the center of the viewport:
The popover is centered
The reason that this happens is that the UA stylesheet applies margin: auto
to popovers. This will reassign any whitespace around the popover equally to all sides as margins. That checks out: if there's the same amount of whitespace left and right, it element will effectively be in the center horizontally (same for top and bottom, but vertically).
For anchored popovers, we want the popover to be near the button that invoked it, not in the center. Let's look at a minimal code example.
In your JavaScript, first import the computePosition
function from @floating-ui
:
import { computePosition } from '@floating-ui/dom';
Then, find the popover:
const popover = document.querySelector('[popover]');
Popovers have a toggle
event, just like the <details>
element, which we'll listen to:
popover.addEventListener('toggle', positionPopover);
In our positionPopover
function, we'll find the invoker, and then, if the newState
property of the event is open
, we'll run the computePosition
function and set the results of its computation as inline styles.
function positionPopover(event) {
const invoker = document.querySelector(`[popovertarget="${popover.getAttribute('id')}"`);
if (event.newState === 'open') {
computePosition(invoker, popover).then(({x, y}) => {
Object.assign(popover.style, {
left: `${x}px`,
top: `${y}px`,
});
});
}
}
To make this work, I also applied these two style declarations to the popover:
margin: 0
, because the UA's auto margin's whitespace gets included in the calculation, with0
we remove that whitespaceposition: absolute
, because popovers getposition: fixed
from the user agent stylesheet and I don't want that on popovers that are anchored to a button
It then looks something like this:

See it in action: Codepen: Positioning a popover with Floating UI.
In the Codepen, I also use some Floating UI config to position the popover from the left. In reality, you probably want to use more of Floating UI's features, to deal with things like resizing (see their tutorial).
Option 2: with Anchor Positioning#heading-3
To make all of this a whole lot easier (and leave the maths to the browser), a new CSS specification is on the way: Anchor Positioning, Level 1. It exists so that:
a positioned element can size and position itself relative to one or more "anchor elements" elsewhere on the page
This, as they say, is rad, because it will let the browser do your sizing and positioning maths (even automatically- update 4 May 2024: looks like automatic anchoring was removed). It is also exciting, because it doesn't care where your elements are. They can be anywhere in your DOM. And, important for popovers, it also works across the top layer and root element.
Though popovers would get implicit anchoring, you can connect a popover with its invoker via CSS. To find out how all of this works in practice, I recommend Jhey Tompkins's great explainer on Chrome Developers (but note it's currently somewhat outdated, the editor's draft spec changed since that post, and has new editors). Roman Komarov covers his experiments and some interesting use cases in Future CSS: Anchor Positioning, and also wrote Anchor Positioning on 12 days of web.
The Anchor Positioning spec was recently updated, and is currently in the process of being implemented in browsers, hence the Option 1 in this article. But, excitingly, it is in the works. Chromium has already issued an intent to ship anchor positioning, and so did Mozilla/Gecko. The recent updates are still pending TAG review.
Wrapping up#heading-4
So, in summary: if your popover needs to be anchored to something, like a button or a form field, you can't “just” use absolute positioning. Instead, you can use JavaScript (today), or, excitingly, anchor positioning (in the near-ish future, an Editor's Draft in CSS was published last year and a new version of that with new editors was released in April 2024.
List of updates
- 6 May 2024: Added that Gecko intents to ship anchor positioning.
- 4 May 2024: Reworded to reflect that the editor's draft of the anchor positioning spec was updated (as editor's drafts are), is now different and not yet passed TAG review.
Comments, likes & shares (94)
sim, Derek P. Collins, Ash, Michelle Barker, Roel Groeneveld, Nicolas Chevobbe, Mike Aparicio, James Basoo, Roma Komarov, Wolfr, Mikhail Shornikov, Thomas Broyer, carmya, Martín Baldassarre, Paul Mason, Heather Buchel, dovyden, Curtis Wilcox, Florian Geierstanger, Philip Zastrow, Cory :prami_pride_demi:, Vincent Valentin, Scott Kellum :typetura:, Jason Lawton :wordpress:, Nic, Maïa ????, s:mon, Patrick Grey, Andy Davies, Konnor Rogers, Masataka Yakura, Ollie Boermans, Zacky Ma :favicon:, Olliew, Henry, Baldur Bjarnason, Bruce B, Sonia P., Simon St.Laurent, Vale, Karpour, Matt Wilcox, Dennisn't, sidasa, micha, mariuz, Kaare Larsen, carlfeberhard@mastodon.social, Angela "Ge" Ricci and Max liked this
Jeroen Zwartepoorte, Manuel Matuzović, ~/j4v1, Nicolas Chevobbe, Roma Komarov, Wolfr, Bramus, Thomas Broyer, Bhupesh Singh, Ted M. Young, Ryan Trimble, Konnor Rogers, Henry, Olliew, dasplan, Axel Rauschmayer, The gallant knight, mgiraldo, GENKI and Jens Tangermann reposted this
With the new
popover
attribute in HTML, we can put elements in the top layer and allow them to disappear with ‘light dismiss’. This attribute adds behaviour, not semantics: you're supposed to add your own role when it makes sense. In this post, we'll look at different roles that could make sense for your popover-behaved elements.See also: Hidde's talk on popovers, and other posts about popover accessibility, positioning popovers and the difference with dialogs and other components.
Semantics?
Accessibility semantics are roles, states and properties that are exposed by by browsers for many HTML features, and then passed on to assistive technologies.
The ‘role’ of an element establishes what kind of element it is. Roles are built-in (‘implicit’) to some elements: a
h1
has theheading
role, ana
has thelink
role and so forth. Roles can also be added with arole
attribute explicitly. For some roles, that is the only way: there exists no corresponding element. If there's an element and a value for ‘role’, it doesn't really matter for end users which you use, but generally you don't want to overwrite implicit role. As mentioned, your user's browser or assistive technology may use the role to provide a UI. For instance, a screenreader may generate a list of links or headings, a reader mode may render list items with bullets.Popovers have no default role
Whenever we add the
popover
attribute to an element, it continues to be that element semantically, just with some specific behaviours. Menus remain menus, dialogs remain dialogs, and so on. The popover attribute does not change an element's role. It's a bit like thecontenteditable
attribute in that sense. In addition to choosing that you want the popover behaviour, you need to decide if you add a role and, if so, which role.The most basic example of a popover:
This is how it works:
div
will be invisible on page load, because it has apopover
attribute and popovers are closed on page load by defaultdiv
will also be toggleable via the button, as the button points to thediv
's ID in itspopovertarget
attributePotential roles for your popover
Let's now look at common roles for popovers: menu, dialog and listbox, and consider what to do about tooltips.
Menus: the
menu
roleLet's start with menus. The
menu
role is what you'd use when your component offers a list of choices to the user, specifically choices that are actions. (Note:menu
is not for a list of links, like a navigation, it is only for a list of actions).A menu with popover behaviour can be built with a
menu
role:In a menu widget, there are also some keyboard and focus expectations. For instance, that users can use their arrow keys to cycle through the different buttons. As a developer, this is something you'd add with JavaScript yourself. The first button is focused when it opens (hence
autofocus
), the second and after would get focused moved to them when they're the next one and an arrow key is pressed (hencetabindex="-1"
: this takes the buttons out of tab order, because you make them reachable with arrow keys instead).(Note: The
menu
role is not to be confused with themenu
element, which has alist
role and is “a semantic alternative to<ul>
”)Examples of when you would use
role="menu"
:menu
for each author with some actions (each action has amenuitem
role)menuitem
s._See also: Marco Zehe on the
menu
role and “Menu control type” in Windows Accessibility Features documentationDialogs: the
dialog
roleA dialog role is what you add when an element is like a smaller window on top of the main web page. It can block interaction with the rest of the page or leave the rest of the page as it is, either way it is somewhat separate from the page, both in purpose and visually.
The
<dialog>
element implicitly has adialog
role, and comes with dialog methods and behaviours (like you can runelement.showModal()
to show it as a modal). You can also add the dialog role manually withrole="dialog"
, but then you have to add the behaviours manually too.A dialog with popover behaviour can be built like this:
You see, there's no explicit role attribute, because the
dialog
role comes with the<dialog>
element.If not using a button with
popovertarget
, you could open this dialog with script using theshowPopover()
method that works on any element that is a popover (by having apopover
attribute present).Note: because this specific popover example uses the
<dialog>
element, two other methods are also available (through the HTMLDialogElement):show()
andshowModal()
. They have slightly different behaviours thanshowPopover()
would. I recommend against using these two methods on dialogs that are popovers. In other words, if you're inclined to use them, you probably don't want thepopover
attribute, as that attribute's purpose would basically be defeated byshow()
/showModal()
(also, in some cases you might get a console error if you try to runshowModal()
on a popover). Popover is really for non-modal dialogs; see also my post on dialogs vs popovers).Other examples of elements that could have popover behaviour and a
dialog
role are:role="menu"
, a navigation with links is semantically different from a menu with buttons)Listboxes / autocompletes: the
listbox
roleA listbox is for elements where the user gets to choose from one or more options, like a
<select>
. They can exist as single select (user can select one option) or multi select (user can select multiple options).Listboxes are often part of an autocomplete or combobox widget, they are the part that contains the actual options. Like in this example:
For instance, in the following example, there is a component that pops over the page's content. It contains filter and sorting buttons, as well as a listbox with actual options. The element with
popover
is probably a dialog (and you could give it adialog
role), while the element that contains options would need a role oflistbox
:Tooltips/toggletips:
tooltip
(with caveats) ordialog
In their simplest form, tooltips are like the
title
element in HTML, that browers display on hover. These browser built-in tooltips are problematic in many ways, including that in most browsers, there is no way to get to the contents oftitle
with just a keyboard. Let's call them “plain text tooltips”. They are often customised by developers, for instance to change their visual styles (currently from scratch, maybe via CSS in the future).Sometimes they are also found underneath input fields, to explain what that input does or what is expected, like some of Scott O'Hara's custom tooltips examples.
These custom “plain text tooltips” are what the
tooltip
role seems to be meant for. Note thatrole="tooltip"
doesn't do much in terms of screen reader announcements as Sarah Higley explains in Tooltips in the time of WCAG 2.1, though there are cases where ARIA-provided labels and descriptions don't work across browsers and assistive technologies without the role (if they aren't interactive,iframe
orimg
elements and also don't have a landmark or widget role). What is useful for accessibility of that kind of tooltip, going beyond roles for a moment: usearia-describedby
to link up a tooltip that describes a thing with that thing, and never place essential content in them. Also ensure that the tooltip (1) stays visible when its content is hovered, (2) is dismissable (with Escape) and (3) persists until hover/focus removed, dismissed or irrelevant (all required to meet WCAG 1.4.13).My advice would be that whenever tooltips contain more than just plain text, a non-modal
dialog
would be more appropriate (even if elements withtooltip
role were apparently meant to also allow for interactive elements). Non-modal dialog tooltips could contain semantic elements (like a heading) or interactive elements (like a link or a button). In most cases it would be best to display them on click instead of hover + focus, in which case they are really “toggletips”. Of course, if there is interactive content, that also means you'll want to consider focus order.Conclusion
In this post, we've covered some of the most common semantics you could choose to use with the
List of updatespopover
behaviour:menu
,dialog
andlistbox
, plus looked at usingtooltip
for plain text tooltips ordialog
for tooltips that contain anything more than plain text. Are you building components that don't really fall into any of these categories? I'm curious to learn more, slide in my DMs or email!- 30 April 2024: Reworded the bit about semantics a bit to explain roles as an example of various accessibility semantics.
- 28 April 2024: Removed note on browser support, as popover is now supported in latest versions of all major browsers.
- 17 May 2023: Explained attributes in menu example
- 16 May 2023: Changed example that used <menu> to use role=menu
Thanks to Eric Eggert, Steve Faulkner and Mason Freed for feedback on earlier drafts and Adrian Roselli for pointing out a mistake in an earlier version.@hdv Are there any kind of polyfills for popovers that you would recommend? We could really use them right now, but browser coverage isn't good enough unfortunately.
@bastianallgeier Yes, Oddbird made this one https://github.com/oddbird/popover-polyfill
(there are some caveats listed, and it won't put it actually in the top layer as that can't be faked)
GitHub - oddbird/popover-polyfill@hdv Damn, that would have been my only requirement. But I think it would only be possible to polyfill this with a dialog somehow, right? There's no other top layer element so far afaik. The thing that is getting us in trouble here are container queries. As much as I love them, their stacking context is giving us a really hard time.
@bastianallgeier yes, the only way would be a modal <dialog>, but then it's a modal dialog (while popover is ~ for nonmodal dialogs) (or full screen, but then it's, ehm… full screen)
DevOps is Bullsh*t – Some thoughts on why the practice hasn’t lived up to its potential.
Do Clients Need To Know How Their Website Works? – Tips for focusing on the most important aspects of a client’s website.
Negative-Space Typography – Controlling the space between text styles is as important as differentiating the styles themselves.
An Introduction to
@scope
in CSS – Explore the benefits and potential use cases of this CSS specification.Four Exclusive Demos: Slideshows & Typographic Animations – Demos that range from image slideshows to on-scroll typography and hover effects.
BentoGrids – Check out this curated collection of tile-based grid layouts.
The Bright Side of an Increasingly Homogeneous Web – A look at the positive effects of a more consolidated web.
Comic Mono – Download a copy of this legible monospaced font.
The Gotchas of CSS Nesting – Some common problems that browsers have with the practice.
Stack Overflow Developer Survey – Developers share how they learn and level up, which tools they’re using, and which ones they want.
The 30 Best Branding Identity Mockup Templates – There’s something for every project you’re passionate about in this beautiful collection.
Kablammo – An extraordinary variable font from…outer space?
The Continuing Tragedy of CSS – Has CSS become too complicated?
2023 Logo Trend Report – Taking a look at a year of ideas, symbols, and AI.
Why Niche WordPress Plugins May Be the Best Option – When it comes to plugins, quality is more important than quantity.
Positioning Anchored Popovers – Review the available options for positioning popover elements.
48 Laws, Rules, and Principles of Web Development – A handy list of laws, rules, and principles related to web and software development.
HeadstartWP – Check out this free, open-source tool for creating headless WordPress websites.
Automattic Donates €20,000 to Fund Next Phase of Drupal Gutenberg Development – Using the Block Editor in Drupal gets a boost.
Source link
Related
Michelle Barker delivered a really fun & inspiring presentation called Modern CSS Layout is Awesome! She covers all the great innovations of the last few years, and shows how we can use them IRL. When she gets into the container queries demo, things get really fun… :-)
Chris Coyier continues the trend by writing about Modern CSS in Real Life. Chris goes beyond just “what can you do”, but “how you can do it in your day-to-day work”.
And the great Kevin Powell covers a bunch of CSS features with better browser support than you might have thought, including such faves as Custom Properties, Object Fit, Intrinsic and Extrinsic Sizing, Conic Gradients, Logical Properties and much, much more!
Sticking with Kevin for a second, he shares a freaking awesome approach to animating
height
from0
toauto
, with no JS… The first example is a little simple (as a good first demo should be), but the accordion example really shines!And Una Kravits shares another common animation example, transitioning from
display: none; opacity: 0;
todisplay: block; opacity: 1;
! (This one might still be behind a flag for now, but if it still is, it is coming!)Consider this a Public Service Announcement from @StackBlitz, reminding us that we can customize form element highlights to match our site’s branding with one simple line of CSS:
Zoran Jambor offers a very nice, easy-to-follow Advanced Positioning in CSS Grid tutorial video, including some useful tips for setting up DevTools for debugging.
Zoran also shows how to find all the changes you made while tinkering with a layout or design in DevTools. So handy!
And speaking of DevTools, Patrick Brosset offers DevTools Tips. Currently boasting 150 tips to help with your debugging!
And speaking of Patrick, he also presented Style Recalculation Secrets They Don’t Want You To Know at CSS Day 2023. Always fun to get “insider info”! ;-)
Geoff Graham writes about Writing CSS In 2023: Is It Any Different Than A Few Years Ago?. It’s no secret that CSS has changed a tremendous amount in recent years, and there is much more on the immediate horizon. What are some of your fave changes in how you write CSS?
Joan León shares a collection of Web Perf Snippets that you can use either as part of your CI/CD pipeline or from within DevTools. Handy stuff!
Hidde de Vries helps us understand Positioning anchored popovers. Like the
You might also want to have a glance at Hidde’s related Dialogs and popovers seem similar. How are they different and Semantics and the popover attribute: what to use when? articles.dialog
element, I love using these semantic elements & attributes!Ahmad Shadeed provides some really great examples of creating Conditional CSS with
:has
and:nth-last-child
including dynamic layouts based on the number children, modal actions, timelines and more.And finally, for anyone that has had the brutal experience of coding HTML emails, you will finally find comfort knowing that
table
s are no longer required… There are, of course, still some buggaboos, but nestingtable
s withintable
s withintable
s is no longer one of them…Happy reading,
Atg
Top⇪
Related
@hdv @tylersticka
Mostly, about how to banush them to the. It field in webpages.
@sil Yay, happy to read that! Anchor positioning support situation is about to improve I think