-
Notifications
You must be signed in to change notification settings - Fork 715
[css-view-transitions-2]: Nested ::view-transition-group should have a container pseudo. #11926
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
To give people an idea of how not trivial this is with the current implementation in which the nested groups are direct children of the outer group: when using nested View Transitions that uses a border, you don’t want a nested snapshot to appear over the border. You can see this problem in the “Nested View Transitions (::v-t-group in ::v-t-group)” section of the playground and also in the following screenshot: ![]() To mitigate authors need to apply the border onto the ::view-transition-group(clipper) {
/* Add the border + border-radius */
border: var(--border-width) solid;
border-radius: var(--border-radius);
/* Make sure the width/height is not affected by adding the border */
box-sizing: border-box;
/* Clip the contents */
overflow: clip;
}
::view-transition-new(clipper),
::view-transition-old(clipper) {
/* Because the content-box of the ::view-transition-group(clipper) got adjusted, the snapshots need to be repositioned using a negative object-position */
object-position: calc(var(--border-width) * -1) calc(var(--border-width) * -1);
object-fit: none;
}
::view-transition-group(.item) {
/* Because the content-box of the ::view-transition-group(clipper) got adjusted, the nested elements need to be repositioned using a negative margin */
margin: calc(var(--border-width) * -1) 0 0 calc(var(--border-width) * -1);
} This approach is implemented in the “Nested View Transitions ( ![]() All this “reposition all the things” does not need to happen with a new
With the first approach, the ::view-transition-nested-content(clipper) {
width: _(width of the content box the clipper)_;
height: _(height of the content box the clipper)_;
inset: _(insets of the content box against the border-box of the clipper)_;
}
::view-transition-nested-content(.album) {
top: _(calculated against the content box of the clipper)_;
left: _(calculated against the content box of the clipper)_;
} As a result, the author only needs to write these styles anymore: ::view-transition-group-content(clipper) {
border-radius: calc(var(--border-radius) - var(--border-width));
overflow: clip;
} The tricky thing for authors is that they need to compute the inner radius, which is equal to the outer-radius minus the border-size. You can see this in action in the “Nested View Transitions, alt (::v-t-group in ::v-t-content)” (the one with the yellow border, right after the hotpink one) section of the playground. ![]() With the first approach, the The UA generated styles would then look like this: ::view-transition-nested-content(clipper) {
box-sizing: border-box; /* Required to make this work! */
border: _border-size_ solid transparent;
}
::view-transition-nested-content(.album) {
top: _(calculated against the content box of the clipper)_;
left: _(calculated against the content box of the clipper)_;
} This gives authors the benefit that they don’t need to calculate the inner radius for the ::view-transition-group-content(albums-wrapper) {
border-radius: var(--border-radius);
overflow: clip;
} You can see this in action in the “Nested View Transitions, alt (::v-t-group in ::v-t-content) + fake transparent border” (the one with the yellow and green dotted border) section of the playground. For demonstration purposes the transparent border was given the color ![]() (The green lines on the screenshot align with the left and top edge of the This leads me to the following questions we should ask ourselves:
My personal opinion:
|
This demo seems like a really good use case for scoped view transitions rather than nested as the transition is constrained to one segment. I would like to see how necessary this is for cases where it's true nesting rather than scoping, as in the element and its children participate in a root transition. See also #11927 |
The CSS Working Group just discussed The full IRC log of that discussion<ydaniv> vmpstr: in VTs we have a pseudo struct with group, under image-pair, under old and new<ydaniv> ... in group we added capability to nest groups <ydaniv> ... we found that you'll be able to clip in VTs that are not all flat <ydaniv> ... we found it's hard to do if element has borders, you'd clip the group instead of area that you inteded <ydaniv> ... proposal is to add a VT content or other pseudo elemnt that acts as the parent of all nested groups <ydaniv> ... so you have an image-pair and another child, say content <ydaniv> ... would allow you to clip just the group and not the content <bramus> q+ <ydaniv> ... proposal is to size to padding box which clips the padding box of the originating element that created the group <astearns> ack bramus <ydaniv> bramus: wanted to say that I tried this in playground by faking it real elements, and having this extra element makes it much much easier for authors to achieve this clipping <ydaniv> ... you can see in the playground, if you compare the code of element with hotpink <missed> <emilio> q+ <ydaniv> ... much more convinient with this extra pseudo <astearns> ack emilio <ydaniv> emilio: is that just a border that doesn't clip correctly? <ydaniv> vmpstr: it would be captured in either old or new that nested under pair that's under group <ydaniv> astearns: proposed resolution is to add a new VT container? <ydaniv> vmpstr: happy to change the name, it's technically group container <ntim> q+ <ydaniv> bramus: with the * that it doesn't include <missed> <bramus> s/<missed>/the outer group <ydaniv> flackr: I think the name content is roughly consistent with the box model <emilio> q+ <astearns> ack ntim <ydaniv> ntim: one question, are there any alt. solutions to address this use-case? <ydaniv> ... content is a better name than container cuase here it's a child of the group <ydaniv> vmpstr: both a child of the group but contains other groups <ydaniv> ,,, as for q, bramus can answer better, not easily, can have <missed> <astearns> ack fantasai <ydaniv> fantasai: we got the VT group, and then for each group it contains a pair and old/new, and in each thing we animate will have its own nested <ydaniv> ... within an existing groups <ydaniv> ... now we're saying to separate the image-pair, and now have another grouping element around all nested transitions <bramus> q+ <ydaniv> vmpstr: yes .... <ydaniv> fantasai: in many cases a lot of the content will be in the image-pair <ydaniv> ... so container make a lot of sense <fantasai> s/a lot of/more/ <ydaniv> ... I think since a lot of content will be in the image-pair, calling it content will be confusing <ntim> -1 to the specific word combination of `-group-container` <ydaniv> astearns: wondering if there are no nested VTs, is this new thing going to be empty? <ydaniv> vmpstr: good question, was thinking of having one that would be empty <astearns> q? <astearns> ack emilio <vmpstr> s/having one that would/not having one that would/ <ydaniv> emilio: this would be the only pseudo that would have special size, or we don't need this special case? <bramus> scribe+ <ydaniv> vmpstr: I only know of the VT's sizing, and yes, that would be the only case, the others are on border-box <bramus> bramus: not having a pseudo if there are no nested groups is consistent with not having a new or old pseudo in some cases. <ydaniv> emilio: sounds unfortunate, maybe we should allow sizing the groups? <ydaniv> ... if you allow that then we could use them <astearns> group-wrapper, group-parent… <ydaniv> vmpstr: problem is if you size them then the border in the old, adding a clip will clip that <ydaniv> emilio: doesn't that mean that the snapshot will overide the border? <ydaniv> ... I guess the old and new are things that actually need the border <ydaniv> ... would be nice to have a simpler use-case for this <ydaniv> ... maybe fine..., to me seems to want to have a border and not clip <ydaniv> ... my q, is this case worth adding the extra complexity? <ntim> +1 to emilio, magic is annoying <ydaniv> ... when you capture the snapshot old/new elements, the size is not necessarily size of border box, but ink overflow, so you need a size for the pseudo element that represents this <ntim> `::view-transition-nested-*` is my probably my favorite pick for a name <ydaniv> ... I assume it's a reasonable default, it's doable but I think it's unfortunate for special case <ydaniv> vmpstr: it contains the nested elements in it, so what would size it to? <ydaniv> ... if to border-box then elements at the border ... <ydaniv> ... would you be ok with content box? <ydaniv> emilio: groups do that <ydaniv> ... the things you're snapshotting and the box model of the pseudo tree makes sense <ydaniv> ... we're not discussing that <ydaniv> vmpstr: current box model of the pseudo tree makes sense <ydaniv> ... make the case that shouldn't be sized to border box? <ydaniv> emilio: yea <ydaniv> vmpstr: poissble <ydaniv> ... overall, with nesting as it's specified right now you have the ability to clip your groups it's only possble if you clip yourself <ydaniv> ... if the objection is only to sizing, to border box <ydaniv> ... so probably to padding box, which is what I'd recommend, that is my sense <ydaniv> astearns: I wonder would it make sense to have the box KWargs as params <ydaniv> emilio: doesn't quite work, because the function selects different elements <ydaniv> ... I do think that something along those lines that you can somehow specify how things are sized <astearns> q? <ydaniv> ... like ink overflow, maybe <ydaniv> ... to me adding extra container is not big deal but feels like a smell, for a particular case <ydaniv> ... maybe I'm misunderstanding <ydaniv> ... feels to special for something that we should address differently <ydaniv> ... haven't thought about this deep enough <astearns> ack bramus <ydaniv> bramus: wanted to address some remarks <ydaniv> ... I will record what I'm showing in the playground and add comments <ydaniv> ... why this extra element would make things easier <ydaniv> ... it's a code smell but help a lot <ydaniv> emilio: maybe we should allow the capture to be sized to padding rather than border <ydaniv> ... feels like more general issue, maybe I'm wrong <ydaniv> vmpstr: the part I'm missing, suppose you take clipping as an example, how else would you clip the group without clipping the content? <ydaniv> emilio: if the container of the snapshot that sized to the border box, ... <ydaniv> vmpstr: there's only one clip that clips both, they should have different clippings <ydaniv> emilio: I guess I need to wrap my head around the tree <ydaniv> flackr: another harder case is having shadows and decorations that go outside of the border box <ydaniv> ... and shouldn't be clipped <ydaniv> vmpstr: have a few diagrams in the issue you can respond to <ydaniv> emilio: sounds good <ydaniv> astearns: we'll take this back to the issue <ydaniv> ... I suppose talk about the possiblity of changing clipping of everything else? <ydaniv> vmpstr: yes and no (: |
@emilio let me know if the diagrams in the OP are sufficient to explain the situation. We basically have a situation
(
The proposal is to add another container element E for this:
|
Yeah, so I think you're right that you need some grouping pseudo to get the clipping you want. I think what I'm concerned about is the "sizing to padding-box" special-ness. @vmpstr and I quickly chatted, and I think that the padding-box behavior: overflow: clip;
border-radius: <original-border-radius-minus-border-width>; Is not a huge improvement from using the border-box and doing: clip-path: inset(<border-width> round <original-border-radius-minus-border-width>); So I think I'd prefer to add the pseudo-element with the border-box sizing so it's more consistent with all the already-existing grouping and v-t pseudo-elements. |
Proposing to add a new pseudo called |
Would this If not, then I don’t think we’d ship a good default, as you’d get this behavior out of the box (recording of this demo in Chrome Canary with flags; pay close attention to the top and bottom borders of the wrapping element while the VT runs): Kapture.2025-05-06.at.11.53.28.mp4If it does get generated by the UA then I think I’m fine with it. |
If we're clipping by default, then sure, but I think I'd rather not? Don't you have the same issue with the other alternative, as you'd need to manually specify the "inner" border radius yourself (even if we |
By sizing the When doing so, you’d get the following behavior out-of-the-box (assuming that the Kapture.2025-05-06.at.14.26.24.mp4(Recording of this demo in Chrome Canary with flags, it includes a manual (and convoluted) workaround/fix for the border issue) The above seems like a better default behavior to ship. It’s only when there are other things present that alter the shape of what gets rendered – such as a For |
I'm still not sure this is special (also still not sure we should apply So even if it makes this particular authoring use-case slightly less convenient I still think we should size these consistently... |
I see what you mean, as
I believe that having a border on a wrapper element is common enough to treat it as a use-case that should work out of the box. |
I had a chat with @bramus about this this morning. Having the The idea @bramus attributed to @flackr seemed great to me, where the Unless I'm mistaken, having the things inside |
I believe you mean sized to the border box here ;) This is effectively the tree from example 4 from the playground but with the CSS fixups from example 3 applied. What I forget to mention in our chat is what @emilio suggested: a
For reference, this is example 5 from the playground. For demonstration purposes I am using a |
What we've learned from layered capture is that there is no clear boundary of what the "simple" case is. In this case, for example, mismatches around It might become frustrating in organizations where the general styling/theming of elements is developed separately from the animation/transitions. Sometimes changing some innocent looking CSS properties of an element would make the transition appear incorrect and the author changing those properties wouldn't even know. |
Doh, yes, sorry. Edited. |
That's true in all the proposed solutions, right? But if the |
Yes, but copying some properties and leaving out others might give authors a false sense of security and break in unexpected ways later on. In this case, capturing the "border size" is insufficient when there is also a border radius or corner-shape, and future enhancements like |
I believe that capturing only the border would be a nice tradeoff here. It would result in:
|
@noamr am I right that, in scoped view transitions, the |
Yes, but also I expect the border and shapes etc would apply directly on top |
Do you mean only the border width? Please be specific. |
Also note that the nested elements are abspos positioned relative to whatever we agree on here. So if we create a border box sized pseudo element, and then add a transparent border of the same shape then naively that would shift the nested groups. However, if we are also going to automatically match the positioning with the expectation that the border is added (or that we automatically add it), that adds complexity in that then the developer can't remove the border without these shifts. clip-path, fwiw, avoids these issues since it's a non-layout-inducing clip. I agree with @noamr that this is trying to walk the line that dips into layered captured complexity in that it's not obvious where to draw a line between simple copyable props and others that cause problems |
Capturing some discussion that we had internally: We'd like to consider a model proposed by @flackr:
This seems like a plausible middle ground: there's no sizing magic in that everything is still technically sized to the border box. Because of automatically copied Please share your thoughts! |
To those wondering what it would look like: this is effectively demo 5 from the playground. For demonstration purposes the transparent border was made
Easy as in: authors as can copy styles such as the
I cannot help but stress that this is the most author-friendly solution we came up with – see the remark about “easy” above. All other explored solutions put an extra burden on authors. One question one could ask here is whether we should animate the border on the new pseudo between the old and new state. I have not dug into this yet, but my gut reaction would be that we do need to animate it because otherwise you’d have nested pseudos jump at the end of the transition. |
One of the common use cases for
view-transition-group: contain
that we're considering is a scroller with items within in and a view transition that shuffles the items within the container.This is achieved with something like
In the current spec this builds the following pseudo tree:
Essentially,
::view-transition-group
s for the items are siblings of::view-transition-image-pair(clipper)
Now, to achieve the desired effect we need to put a clip on
::view-transition-group(clipper)
. This works well in a limited number of cases. The problem happens once you have a border (with maybe a rounded clip) on the original clipper element. Because the::view-transition-group(clipper)
element is sized to be the border box of the original element, and the fact that border itself is baked into::view-transition-{old/new}(clipper)
, we end up in a situation where it isn't trivial to properly clip the nested groups and also not clip the border. The solution is possible, but not ergonomic where one has to copy the border to the group pseudo, and do some sizing/offset magic to account for the fact that we're sized to the border box.@bramus put together a demo here:
https://codepen.io/bramus/full/EaxvMXG/11b9fcbe8ebf42034110d3b486db037f
One improvement we can make is to introduce a new pseudo element
::view-transition-group-container
(name tbd) that's sized to the padding box of the original element (I think that's the right clipping box or is it content box?) and hosts the nested groups:With that, adding a clip to
::view-transition-group-container(clipper)
does not clip the captured box decorations of the original element, and there's less math required.Cons:
/cc @noamr @nt1m @bramus @jakearchibald
The text was updated successfully, but these errors were encountered: