html
html copied to clipboard
Fix user-agent style rules for top layer transitions
With the new css-position spec and overlay CSS property, it is possible for popovers, dialogs, and their ::backdrops to be in the top layer while transitioning from showing to hidden. During this time, :popover-open and :modal don't apply, but we still want the user-agent styles to apply as if these elements were still showing in the top layer. This PR accomplishes this by moving these styles from selectors with :modal or :popover-open to a definition which says that the element is in the top layer. This is implemented in chromium with an internal pseudo-selector.
These rules can't be accomplished with existing selectors and pseudo-selectors because there is no way to select an element which is transitioning to closed but is still being rendered in the top layer.
- [ ] At least two implementers are interested (and none opposed):
- Chrome
- …
- [x] Tests are written and can be reviewed and commented upon at:
- https://github.com/web-platform-tests/wpt/pull/40369
- [ ] Implementation bugs are filed:
- Chromium: https://chromium-review.googlesource.com/c/chromium/src/+/4585724
- Gecko: …
- WebKit: …
- [ ] MDN issue is filed: …
(See WHATWG Working Mode: Changes for more details.)
/infrastructure.html ( diff ) /rendering.html ( diff )
@josepharhar Can you solve the issue by just re-writing the UA styles to be this?
/* common to fullscreen, dialog, popover */
::backdrop {
display: block;
position: fixed;
inset: 0;
}
/* fullscreen */
:not(:root):fullscreen::backdrop {
background: black;
}
/* modal dialogs */
dialog:not([popover])::backdrop,
dialog[popover]:modal::backdrop {
background: rgba(0, 0, 0, 0.1);
}
/* popovers that aren't fullscreen or modal dialogs */
[popover]:not(:modal)::backdrop {
pointer-events: none;
}
I'm defining the modal pseudo class here: https://github.com/whatwg/html/pull/9395
cc @mfreed7 @lilles
@josepharhar Can you solve the issue by just re-writing the UA styles to be this?
/* common to fullscreen, dialog, popover */ ::backdrop { display: block; position: fixed; inset: 0; } /* fullscreen */ :not(:root):fullscreen::backdrop { background: black; } /* modal dialogs */ dialog:not([popover])::backdrop, dialog[popover]:modal::backdrop { background: rgba(0, 0, 0, 0.1); } /* popovers that aren't fullscreen or modal dialogs */ [popover]:not(:modal)::backdrop { pointer-events: none; }
I think that works. Curious though, can't the dialog
rules just be this:
dialog:modal::backdrop {
background: rgba(0, 0, 0, 0.1);
}
It can only be :modal
, even if it matches [popover]
, if it got there via showModal()
. Right?
I think that works. Curious though, can't the
dialog
rules just be this:dialog:modal::backdrop { background: rgba(0, 0, 0, 0.1); }
It can only be
:modal
, even if it matches[popover]
, if it got there viashowModal()
. Right?
I mostly wrote it that way in case you want the rule to match during a transition, but yeah dialog:modal::backdrop
works too if that doesn't matter too much.
Quickly looking at the implementation in Chrome, :modal does not match after close() going through the transition.
Quickly looking at the implementation in Chrome, :modal does not match after close() going through the transition.
dialog:not([popover])::backdrop,
dialog[popover]:modal::backdrop {
background: rgba(0, 0, 0, 0.1);
}
would cover this case.
dialog:not([popover])::backdrop,
Oh, clever, that's why you wrote it that way. I think there's still a problem though: what if you have <dialog popover>
and you do dialog.showModal()
. And you transition the closing of the dialog. Then during dialog.close()
, neither of those selectors will match. (The top one won't because it does have [popover]
but it just isn't being used at the moment.)
I think you do actually need a pseudo state for "is a popover in the top layer", which is what this PR tries to do.
Oh, clever, that's why you wrote it that way. I think there's still a problem though: what if you have
Calling showModal()
on a <dialog popover>
is a pattern I'd be ok not supporting tbh. It already leads to weird behavior when you try to light dismiss the popover modal dialog since the page would remain inert.
In any case, I think it would be worth looking into cleaning up the UA styles a bit.
~~[popover]:not(:modal)::backdrop
and [popover]:popover-open::backdrop
aren't working for the overlay-transition-backdrop-entry.html
test I'm adding:
https://github.com/web-platform-tests/wpt/pull/40369/files#diff-9dc3657353b3d04b1a244272b418d008cc6773665b6e7ff0e096dd35543446b3~~
~~We need to stop matching the user-agent styles to ::backdrop when the popover is showing but its entrance into the top layer has been delayed with overlay
. This does not have any interactions with dialog elements.~~
Nevermind, that test is a work in progress and was not working with the changes for this PR anyways
Calling
showModal()
on a<dialog popover>
is a pattern I'd be ok not supporting tbh. It already leads to weird behavior when you try to light dismiss the popover modal dialog since the page would remain inert.
I'd be supportive of that also, but it unfortunately doesn't solve the problem. You could do showModal()
and then add [popover]
after that. And in that case the UA style rules are still broken. So I don't think we should prohibit this case, since it doesn't solve anything.
We used to have a "transitioning" state explicitly in the spec, but it was unfortunately removed due to pushback. But what we really have here is a state that represents "transitioning". The UA style rules need to apply during transitions. To do that, without exposing the "transitioning" state to JS, we need an internal pseudo class and the equivalent in spec text (i.e., this PR).
/* common to fullscreen, dialog, popover */
::backdrop {
display: block;
position: fixed;
inset: 0;
}
:not(:root):fullscreen::backdrop {
background: black;
}
dialog:modal::backdrop {
background: rgba(0, 0, 0, 0.1);
}
[popover]:popover-open::backdrop {
pointer-events: none;
}
@mfreed7 @josepharhar Here's another idea, it sounded like the Chromium bug report was about the popover & dialog ::backdrop styles not having the same lifespan wrt transitions.
The styles above would make it as short for both dialog & popover ^
If the author wants to preserve the ::backdrop styles during the transition, they can add a transition to the backdrop or unconditionally add the background there.
Quickly looking at the implementation in Chrome, :modal does not match after close() going through the transition.
dialog:not([popover])::backdrop, dialog[popover]:modal::backdrop { background: rgba(0, 0, 0, 0.1); }
would cover this case.
This could fix the backdrop, but the user-agent styles for the dialog element itself which keep it in the center of the screen also need to stay applied while animating out, during which time the dialog does not match :modal
: https://bugs.chromium.org/p/chromium/issues/detail?id=1451910
I suppose that in order to fix this, we could move the positioning related styles from the dialog:modal
selector to the dialog
selector, which would also make it match while the dialog is closed... or add an internal pseudo selector which matches when an element is in the top layer and use that instead.
Edit: Moving the whole thing from dialog:modal to dialog won't work since then it would also match non-modal dialogs
I pushed some commits for similar fixes for the dialog element, which I learned about and am fixing here: https://bugs.chromium.org/p/chromium/issues/detail?id=1451910
@nt1m What do you think of the current state of this PR? I'd like to get multi-implementer interest
Sorry for coming in late, but if the existing pseudoclasses aren't sufficient for UA usage, does that indicate they'll similarly be insufficient for author usage? I'm not sure I see why an author would be okay with their :modal
styles no longer applying while the element is still acting modal-ish. Am I missing something that makes these phases okay to only be targetable by the UA itself?
If not, the correct path would be to fix the definitions of the pseudoclasses, to match what they actually need to.
Yeah, we could make the internal pseudoclass a real pseudoclass for authors too. It just didn't seem like an important use case to provide for authors
The UA style rules need to apply during transitions.
A different way to make that happen is to call out the relevant properties in transition-property
, and give them a step-end
easing function--basically handling them the same way that was proposed for the overlay
property itself.
The main awkwardness with this that authors need to remember what properties are involved--but we could solve that by providing a keyword that functions just like a shorthand, expanding out to the relevant set of properties. For example, something like ua-modal-dialog
would expand out to overlay
, overflow
, inset
, etc.
The author can then easily control these properties together as one unit; or split out individual ones by listing them individually later in the list, just like for regular shorthands (e.g. transition: border ease, border-style step-start
).
(We could maybe even go a step further and add a generic keyword that computes to the correct set of properties depending on the element involved.)
Note: Another solution would be to invent additive cascade and have the UA default stylesheet include these properties together with a step-end
easing function, so that authors can just add to the list; but that's a much larger project than I suspect we want to tackle here. :)
I see how are are kind of requiring authors to transition overlay, but I'd rather not add even more stuff for authors to include to make the exit animation reasonable.
If we end up deprecating and removing non-modal dialog.show(), then we could make these user-agent styles also apply when the dialog is in any state and then none of this stuff would be needed anymore. I'm not sure if this would also work with popover but it might - I'm going to go test it out.
EDIT: I tested it out and I think its fine except that <dialog popover>
s can't differentiate between whether they should have popover styles or dialog styles during the transition out since :modal
and :popover-open
are both gone. Maybe this is fine though? Or we could have an internal selector to make this differentiation.
I think for web developers it would not be that much more work. Instead of overlay
you'd either use overlay-dialog
or overlay-popover
and those would expand to the relevant properties. The upside of that is that we don't end up with magical unexplained states only the user agent gets to style.
What is the current status on this? I haven't read all of the above, but just trying to ask some general questions to get things moving:
@josepharhar, do you believe the current PR draft represents the best proposal at this point, in light of the above discussions? If so, @annevk @tabatkins @nt1m what do you think of it?
Sorry for coming in late, but if the existing pseudoclasses aren't sufficient for UA usage, does that indicate they'll similarly be insufficient for author usage? I'm not sure I see why an author would be okay with their :modal styles no longer applying while the element is still acting modal-ish. Am I missing something that makes these phases okay to only be targetable by the UA itself?
If not, the correct path would be to fix the definitions of the pseudoclasses, to match what they actually need to.
Yeah I suppose that we could try making :modal
match while the dialog is in the top layer rather than matching while its modal flag is true. I haven't worked on this in some time but I imagine that could be all we need. Does that sound ok to everyone?
Yeah I suppose that we could try making
:modal
match while the dialog is in the top layer rather than matching while its modal flag is true. I haven't worked on this in some time but I imagine that could be all we need. Does that sound ok to everyone?
I don't have strong opinions, but one thing to note: the "modal" nature of the dialog goes away immediately. The only thing that is preserved during an animation is the top layer status. In that sense, :modal
not matching immediately sounds technically correct.
How does that allow you to style the transition? I'm not seeing it. I still think we need something akin to what @fantasai wrote above and ideally the CSS WG does a thorough review of it first.
I created a csswg issue to discuss: https://github.com/w3c/csswg-drafts/issues/9912
@josepharhar @mfreed7 I actually wonder if the UA styles could be transition-property: overlay, overflow, inset, etc.
by default, but with the transition-duration
set to 0. On the author level, they would just need to override duration
if they want.
We could also have an additive CSS solution transition-property: revert-layer, additional-properties-to-transition
I actually wonder if the UA styles could be transition-property: overlay, overflow, inset, etc. by default, but with the transition-duration set to 0. On the author level, they would just need to override duration if they want.
If the author uses transition
or transition-property
it would override these, right? So If I wanted to transition opacity for 1 second, then all of these UA styles wouldn't apply anymore and I'd have to also add overflow inset etc. again right?
I actually wonder if the UA styles could be transition-property: overlay, overflow, inset, etc. by default, but with the transition-duration set to 0. On the author level, they would just need to override duration if they want.
If the author uses
transition
ortransition-property
it would override these, right? So If I wanted to transition opacity for 1 second, then all of these UA styles wouldn't apply anymore and I'd have to also add overflow inset etc. again right?
Yeah hence the suggestion for additive CSS: transition: revert-layer 0.3s, translate 0.2s
, which would also apply the properties from the UA sheet.
The revert-layer bit would need to be a CSSWG addition