You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[selectors-4] Introduce :popover-open pseudo class #8637
We recently resolved to add general-purpose :open and :closed pseudo classes, which apply to "things that can open and close". While not defined in CSSWG, the stated intention was to include things like <details>, <select>, <dialog>, and Popover.
@jakearchibald recently raised the issue that in some cases, this is ambiguous, because there are multiple "ways" that something can be open or closed, and both might apply at the same time. Take, as an example, <details popover>, which can be "open" as a popover, but "closed" as a details, or vice versa. There's no way for these to be disambiguated on the basis of :open or :closed alone.
It is a bit unclear whether fullscreen elements should be included as "something that can open and close", but it's possible. That allows the possibility of a fullscreen <details> element which can be both open and closed. But fullscreen elements have a dedicated pseudo, :fullscreen, which allows one to disambiguate that state of openness vs. others.
It would seem that we need another such pseudo for popovers, because any element can also be a popover, and can therefore run into this dual-states problem. We discussed this in OpenUI, and proposed :popover as a pseudo class that applies only to popovers that are open (as a popover). That would allow developers to do:
details[popover]:closed:popover {
/* styles for a non-expanded details that is open as a popover */
}
One thing I suggested in the OpenUI discussion is that :open and :closed could be for anything where the open/closed state is related to the element semantics, but that things like popover and fullscreen (which can be applied to any element) should have separate pseudo-classes to distinguish them and avoid this problem of unexpected intersections.
One thing I suggested in the OpenUI discussion is that :open and :closed could be for anything where the open/closed state is related to the element semantics, but that things like popover and fullscreen (which can be applied to any element) should have separate pseudo-classes to distinguish them and avoid this problem of unexpected intersections.
This also seems like a reasonable suggestion. @jh3y do you know of any popover use cases that would be harmed by not having access to a :closed pseudo class? It seems like :not(:popover) would generally work instead, but just checking.
Generally up until now you'd likely do something like [popover]:not(:open) so don't see an issue. I remember a while back it was not(:top-layer) in a demo 👀
:open and :closed could be for anything where the open/closed state is related to the element semantics, but that things like popover and fullscreen (which can be applied to any element) should have separate pseudo-classes to distinguish them
That distinction seems pretty reasonable to me, yeah.
@dbaron@tabatkins should :open apply to popovers as well as:popover, or is :popoverinstead of:open?
I think I'm leaning towards :opennot applying to popover, as it also avoids difficult questions around :open and :closed applying at the same time, due to multiple definitions of open/closed.
So if an element type can open/close, then it uses :open and :closed, whereas concepts that apply to multiple element types, such as popovers and fullscreen, get their own pseudoclass.
My understanding was that :popover replaces :open to detect whether a popover is in the top layer.
The only issue I see is "Does :popover communicate that state well?". But, then I think it's likely fine as it kinda falls in with :modal/:fullscreen. Hopefully, it won't cause confusion.
Could all elements that are in the top-layer have a :top-layer pseudo? That would be applicable to dialogs, modals, and popovers, right? I suppose one counterpoint is that it does not clear up the ambiguity between an open dialog and an open dialog popover?
So we might consider :overlay as a pseudo class for things in the top layer, given that we decided to call the CSS property overlay. But I think that's a question for a different issue.
I'm in favor of a) adding :popover for (only) popovers in the open state, and b) removing support for :open and :closed for popovers entirely. It feels like the definition for :open and :closed really refers specifically to specific elements that can open and closed, and not any element that can be made to be open or closed through a separate attribute or Javascript method. So that rules out popover and fullscreen.
I still find :overlay to be an interesting choice that potentially gets muddled up with ::backdrop.
Sometimes I wonder if the appropriate pseudo is one we discussed before with :show/n/ing. Something like details is opened/closed. But, we have popovers that can be shown or not shown (:shown, :not(:shown)).
Sometimes I wonder if the appropriate pseudo is one we discussed before with :show/n/ing. Something like details is opened/closed. But, we have popovers that can be shown or not shown (:shown, :not(:shown)).
I think the problem is that, due to the issues we've discussed above, it needs to be specific to the API. So it should have "popover" in the name. And akin to :fullscreen for the "fullscreen" API, it seems most parallel to use :popover. Another alternative is something like :popover-open but that's verbose and seemingly unnecessary. If the popover is "closed" but made visible via display:block or something, I'd say it really isn't a popover any longer, it's just an element, so it still makes sense for that not to match :popover.
I have a working scenario where I need this pseudo class and is also related to your comment about the toggle events not being cancelable when hiding popovers.
I created a React component to encapsulate a popover and I need it to be descriptively controlled instead of imperatively.
My Popover component receives an open property, renders a
and shows (dialogRef.showPopover()) or hides (dialogRef.hidePopover()) the popover based on the open property.
I cannot intercept and cancel (event.preventDefault()) the toggle event to rely on the consumer of my component changing the open property; the most I can do is let the consumer know that the popover was closed.
Popovers can be light-dismissed by a user action (clicking outside or pressing the Escape key), so I need to first check if the popover is still visible before attempting to call .hidePopover();, because trying to hide an already-hidden popover generates a warning.
I can easily achieve this with const isVisible = dialogRef.matches(':popover-open');, and it works like a charm, but testing environments still don't recognize this :popover-open pseudo class so I'm forced to check the display value instead:
I cannot intercept and cancel (event.preventDefault()) the toggle event to rely on the consumer of my component changing the open property; the most I can do is let the consumer know that the popover was closed.
Right, this is by design, to avoid a slew of footguns. Sounds like you have a way to make your application work, which is good.
Popovers can be light-dismissed by a user action (clicking outside or pressing the Escape key), so I need to first check if the popover is still visible before attempting to call .hidePopover();, because trying to hide an already-hidden popover generates a warning.
You could also use the toggle event to keep track of the state, if you like.
I can easily achieve this with const isVisible = dialogRef.matches(':popover-open');, and it works like a charm, but testing environments still don't recognize this :popover-open pseudo class
It's landed in browsers, so I'm not sure why testing environments wouldn't have it.
Activity
dbaron commentedon Mar 23, 2023
One thing I suggested in the OpenUI discussion is that
:openand:closedcould be for anything where the open/closed state is related to the element semantics, but that things like popover and fullscreen (which can be applied to any element) should have separate pseudo-classes to distinguish them and avoid this problem of unexpected intersections.mfreed7 commentedon Mar 23, 2023
This also seems like a reasonable suggestion. @jh3y do you know of any popover use cases that would be harmed by not having access to a
:closedpseudo class? It seems like:not(:popover)would generally work instead, but just checking.jh3y commentedon Mar 23, 2023
Generally up until now you'd likely do something like
[popover]:not(:open)so don't see an issue. I remember a while back it wasnot(:top-layer)in a demo 👀tabatkins commentedon Mar 23, 2023
That distinction seems pretty reasonable to me, yeah.
jakearchibald commentedon Mar 27, 2023
@dbaron @tabatkins should
:openapply to popovers as well as:popover, or is:popoverinstead of:open?I think I'm leaning towards
:opennot applying to popover, as it also avoids difficult questions around:openand:closedapplying at the same time, due to multiple definitions of open/closed.So if an element type can open/close, then it uses
:openand:closed, whereas concepts that apply to multiple element types, such as popovers and fullscreen, get their own pseudoclass.jh3y commentedon Mar 27, 2023
My understanding was that
:popoverreplaces:opento detect whether a popover is in the top layer.The only issue I see is "Does
:popovercommunicate that state well?". But, then I think it's likely fine as it kinda falls in with:modal/:fullscreen. Hopefully, it won't cause confusion.keithamus commentedon Mar 27, 2023
Could all elements that are in the top-layer have a
:top-layerpseudo? That would be applicable to dialogs, modals, and popovers, right? I suppose one counterpoint is that it does not clear up the ambiguity between an opendialogand an opendialog popover?jh3y commentedon Mar 27, 2023
That's what it was at one point last year. There was a
:top-layerpseudo for detecting when apopoverwas "popped" 😅But, I think it was removed for naming reasons.
mfreed7 commentedon Mar 27, 2023
So we might consider
:overlayas a pseudo class for things in the top layer, given that we decided to call the CSS propertyoverlay. But I think that's a question for a different issue.I'm in favor of a) adding
:popoverfor (only) popovers in the open state, and b) removing support for:openand:closedfor popovers entirely. It feels like the definition for:openand:closedreally refers specifically to specific elements that can open and closed, and not any element that can be made to be open or closed through a separate attribute or Javascript method. So that rules out popover and fullscreen.Convert `:open` to `:popover` for popovers
jh3y commentedon Mar 27, 2023
I still find
:overlayto be an interesting choice that potentially gets muddled up with::backdrop.Sometimes I wonder if the appropriate pseudo is one we discussed before with
:show/n/ing. Something likedetailsis opened/closed. But, we have popovers that can be shown or not shown (:shown, :not(:shown)).mfreed7 commentedon Mar 27, 2023
I think the problem is that, due to the issues we've discussed above, it needs to be specific to the API. So it should have "popover" in the name. And akin to
:fullscreenfor the "fullscreen" API, it seems most parallel to use:popover. Another alternative is something like:popover-openbut that's verbose and seemingly unnecessary. If the popover is "closed" but made visible viadisplay:blockor something, I'd say it really isn't a popover any longer, it's just an element, so it still makes sense for that not to match:popover.Rename :open to :popover and remove :closed
13 remaining items
Convert `:open` to `:popover-open` for popovers
Rename :open to :popover-open and remove :closed
annevk commentedon Apr 3, 2023
HTML side of this landed: whatwg/html#9077.
Convert `:open` to `:popover-open` for popovers
Bug 1824795 [wpt PR 39222] - Convert `:open` to `:popover-open` for p…
destradaxcheckapp commentedon Apr 2, 2024
Hi @mfreed7
I have a working scenario where I need this pseudo class and is also related to your comment about the toggle events not being cancelable when hiding popovers.
I created a React component to encapsulate a popover and I need it to be descriptively controlled instead of imperatively.
My Popover component receives an
openproperty, renders aand shows (
dialogRef.showPopover()) or hides (dialogRef.hidePopover()) the popover based on theopenproperty.I cannot intercept and cancel (
event.preventDefault()) the toggle event to rely on the consumer of my component changing theopenproperty; the most I can do is let the consumer know that the popover was closed.Popovers can be light-dismissed by a user action (clicking outside or pressing the Escape key), so I need to first check if the popover is still visible before attempting to call
.hidePopover();, because trying to hide an already-hidden popover generates a warning.I can easily achieve this with
const isVisible = dialogRef.matches(':popover-open');, and it works like a charm, but testing environments still don't recognize this:popover-openpseudo class so I'm forced to check the display value instead:I see that, as @annevk mentioned, this was already merged to the HTML side, but I can't find if there's been any update on this side
mfreed7 commentedon Apr 5, 2024
Right, this is by design, to avoid a slew of footguns. Sounds like you have a way to make your application work, which is good.
You could also use the
toggleevent to keep track of the state, if you like.It's landed in browsers, so I'm not sure why testing environments wouldn't have it.
Note that this is a bit brittle, since you can set
display:whateveron the dialog manually.Right, this is part of the HTML spec, but doesn't appear yet in the selectors spec. @tabatkins @fantasai is it easy (and appropriate) to get
:popover-openadded to the CSS spec?[-][selectors-4] Introduce `:popover` pseudo class[/-][+][selectors-4] Introduce `:popover-open` pseudo class[/+][selector-4] Add :popover-open pseudo-class.
[selectors-4] Add :popover-open pseudo-class.
[selectors-4] Add :popover-open pseudo-class. (w3c#11362)