csswg-drafts icon indicating copy to clipboard operation
csswg-drafts copied to clipboard

[css-view-transitions-2] [scoped] Clarify the ::v-t box and stacking context position relative to the scope

Open skobes-chromium opened this issue 6 months ago • 35 comments

This issue tracks an open question about the behavior of Scoped View Transitions. Further context appears in the document Self-Participating Scopes.

Q: What is the layout of the ::v-t pseudo in relation to the scope?

Because the scope owns the ::view-transition pseudo-element, it may seem natural for the pseudo to be laid out as an absolute-positioned child of the scope:

/* user-agent style */
::view-transition {
  position: absolute;
  inset: 0;
}

However, this becomes problematic when the scope is self-participating (#12319), because

  • the pseudo is sized to the scope's padding box, which doesn't include borders; and
  • if the scope is a scroller, the pseudo is placed inside the scrolling contents.

You can see this problem in action here (use Chrome 139+ with experimental web platform features enabled). The scope is a scroller that is scrolled down. During the transition, the "outside" view of the scroller (i.e. its border box) is captured and render-redirected into the ::view-transition... which is at the top of the scrolling contents inside the scroll clip.

An alternative might be to layout the pseudo as a sibling of the scope that is effectively anchor-positioned to the scope:

::view-transition {
  position-anchor: --scope;
  left: anchor(left);
  top: anchor(top);
  width: anchor-size(width);
  height: anchor-size(height);
}

There is some further discussion on crbug.com/417988089.

cc @noamr @vmpstr @flackr @bramus @jakearchibald

skobes-chromium avatar Jun 11 '25 19:06 skobes-chromium

There have been other requests, unrelated to View Transitions, for the ability to position something against a specific container:

  • #7475 (specifically this comment) to add an extension to position: fixed to allow one to indicate what to posfix against.
  • #8762 to add container-fixed as a keyword for position
  • #9868 to add a position-container property

I believe either one of those would solve this.

Also, if I read https://github.com/w3c/csswg-drafts/issues/11769#issuecomment-2776545248 correctly, I don’t think the ::view-transition can anchor itself to the transition root. I tried it in Chrome Canary and it didn’t work for me.

#scope {
  anchor-name: --scope;
}
#scope::view-transition {
  position-anchor: --scope;
  …
}

bramus avatar Sep 09 '25 11:09 bramus

The CSS Working Group just discussed [css-view-transitions-2] [scoped] What is the layout of the ::v-t pseudo in relation to the scope?, and agreed to the following:

  • RESOLVED: make the VT have a stacking context that's a sibling of the scope stacking context, but painted at a point before transforms/filters. Still a child of the scope.
The full IRC log of that discussion <TabAtkins> vmpstr: in scoped VT, reminder it's about VT running on subtrees, not just the document
<TabAtkins> vmpstr: setup is roughly the same, you just start the transition on an element, discover names in the subtree, create VT pseudos representing the transitioning parts of the subtree
<TabAtkins> vmpstr: for document VT we hoist the VT pseudos to the top layer so it can capture the document itself, too, and represent that in its pseudos
<TabAtkins> vmpstr: so we need an equivalent thing for elements. pseudos should be able to capture the hosting element's decorations/background/etc. can't be a child without it being circular
<JakeA> q+
<TabAtkins> vmpstr: so our idea is to have ::v-t pseudo-element be a next-sibling of the VT scope element
<TabAtkins> vmpstr: and then use anchor positioning to put it over top of the scope
<TabAtkins> vmpstr: this works pretty well for a lot of cases
<TabAtkins> vmpstr: one case where it doesn't
<TabAtkins> vmpstr: if the scope element is rotated or not axis aligned
<ntim> q+
<TabAtkins> vmpstr: anchor spec says the anchor box is the axis-aligned bounding box of the anchor
<JakeA> q-
<TabAtkins> vmpstr: there's a workaround, selfishly hoping the anchor spec can eventually allow positioned elements to be in the same positioned space as the anchor
<emilio> q+
<TabAtkins> vmpstr: so we'd like to go ahead with the current design of it being the next sibling, and just positioning it over
<astearns> ack ntim
<TabAtkins> ntim: from an impl pov, a bit worried about making the pseudo a sibling
<TabAtkins> ntim: might have implications in the Dom tree, sounds tricks
<TabAtkins> noamr: it's just in the layout tree, the box tree
<JakeA> q+
<JakeA> q-
<JakeA> q++
<TabAtkins> noamr: reproducing the doc VT case where the VT is in the top layer, a "sibling" to the doc
<astearns> ack +
<TabAtkins> emilio: is the idea that we don't want this in the top layer? we want it clipped by ancestors?
<TabAtkins> TabAtkins: yes, absolutely
<TabAtkins> JakeA: yes, in Shopify days we couldn't use VT in some cases because they wanted a VT on just a small piece of the page.
<TabAtkins> JakeA: they used VT:none on root to prevent that from animating, but then if they had a tooltip on top it would drop below the top-layer VT stuff, so they made *it* VT as well, whack-a-mole
<JakeA> astearns: doh, hah!
<JakeA> q-
<TabAtkins> emilio: if you have something in-flow, and you put something out of flow on top of it, that doesn't quite match the in-flow painting order.
<TabAtkins> emilio: it's painted by the abspos's CB, might be higher up
<kush> q?
<ntim> q+
<fantasai> TabAtkins: We've had some cases where we want to make it easier to select other containing blocks
<noamr> TabAtkins: we want to make it easier to select other containing blocks, e.g. your parent
<fantasai> ... in particular, your parent
<noamr> emilio: let's say you have a thing with overflow: clip and animating something inside of it. now you call startViewTransition on that. you effectively created an abs-pos on top. it would escape the clip
<noamr> emilio: clips of any ancestor from the scoped element to the nearest abs-pos
<noamr> TabAtkins: we want to enable it in the future
<noamr> emilio: sounds like it's a bit in the air
<emilio> ack emilio
<astearns> ack fantasai
<Zakim> fantasai, you wanted to comment on containing blocks
<TabAtkins> fantasai: I don't think anchorpos is the right solution for this
<TabAtkins> fantasai: you really do want to change the CB to this element. not by pos:rel, that is disruptive
<flackr> q+
<TabAtkins> fantasai: but we should have some way to say that the scope is the CB for that
<fantasai> https://www.w3.org/TR/css-position-4/#scrollable-cb
<TabAtkins> fantasai: some points to talk about why not do that. scorlling is one. we recently talked about three CBs generated by a scroller, one is the fixed CB, that's what you want
<TabAtkins> fantasai: so there are plans to add switches for that, should rely on that
<TabAtkins> fantasai: for the transform case, when you're contained by the transformed element you're in it so you're transformed with ti
<TabAtkins> vmpstr: the problem here isn't so much positioning, as trying to capture the effects on the scope itself
<emilio> q+
<TabAtkins> vmpstr: like a filter on the scope, the pixels you capture from that filter have to go into the VT pseudo-element. if that pseudo is also a child of the filter, then you're double-filtered
<TabAtkins> vmpstr: or circular if you want to prevent that
<flackr> not being filtered by the element, not being clipped by the element, etc
<TabAtkins> vmpstr: so it's less positioning, more about not being in the same filter tree/effect tree as the scope
<astearns> ack ntim
<TabAtkins> ntim: back to DOM mutations
<TabAtkins> ntim: say you move the scope element elsewhere in the tree. do you track the pseudo and move it at the same time?
<TabAtkins> ntim: otherwise you get z-order issues
<TabAtkins> TabAtkins: moving is a remove/add, so you cancel everything
<TabAtkins> JakeA: no, new method just moves it directly
<astearns> ack flackr
<TabAtkins> noamr: just need to make sure that's an issue we track
<astearns> ack emilio
<TabAtkins> flackr: yeah, confirming that this can't be a box child so we don't clip/filter it
<noamr> q+
<TabAtkins> emilio: I wonder, if you make this API parallel to the doc version, the doc version isn't in the doc itself, but the contents of the document
<TabAtkins> [not sure what that means or if I minuted it right]
<TabAtkins> emilio: instead of capturing the whole scope, you capture just the contents of the element, then the issue goes away
<TabAtkins> JakeA: my first draft did that, people just end up creating a wrapper div
<TabAtkins> vmpstr: we did consider that, but a very common use-case people do is "run on a div, change its background". but that won't work, background is part of the element itself not its contents
<TabAtkins> vmpstr: so we're really trying to make it so the scope itself is represented
<kush> q+
<TabAtkins> emilio: I wonder if we could define, rather than a sibling, it's a regular child but define special painting/capturing rules, in a VT the stacking context your box creates is in one place...
<TabAtkins> emilio: so the pseudo isn't a sibling in the box tree, but in the stacking context sense. one order that's for capturing VT, another for actual rendering
<TabAtkins> emilio: that solves layout issues, unsure if it adds more complications
<TabAtkins> emilio: but this is similar to what you do for the doc transition to capture the canvas background/etc
<TabAtkins> vmpstr: i'm not opposed to that, it's just more magic
<TabAtkins> vmpstr: it's more *explainable* with anchorpos, while this is pure magic. but I'm fine with that
<TabAtkins> noamr: initially this was my mental model, an anonymous container that holds the element and the VT pseudos
<TabAtkins> emilio: yeah, same way you can define the order in which stacking contexts are applied, you can define filter/transform/opacity, etc in a particular order to be correct. can define VT at a particular spot so it's outside the filters but within the element still.
<TabAtkins> noamr: only thing is it's less explainable in CSS terms
<TabAtkins> emilio: but still the same as the doc VT, yeah? doc transitions capture the canvas background, that's outside the document element. so I think this matches doc VT more closely.
<TabAtkins> emilio: if it makes the whole setup simpler should just do it
<TabAtkins> vmpstr: difficult part would just be describing transforms/etc. doc is affected by less than elements are.
<TabAtkins> emilio: sure
<TabAtkins> vmpstr: I think it's doable, just a little awkward
<TabAtkins> emilio: do you know off top of you head if transform is before or after filters?
<TabAtkins> emilio: [goes into a little more detail]
<TabAtkins> emilio: I find that model a bit easier, or more parallel to doc VTs
<TabAtkins> fantasai: makes sense to me
<TabAtkins> emilio: like if you apply filter to root, it doesn't just apply to root it applies to whole doc. but doc VT escapes that.
<TabAtkins> vmpstr: I also need to think through how the VT pseudos can be styled
<TabAtkins> emilio: same as in doc VT, the pseudo isn't affected by the VT styles
<kush> q-
<TabAtkins> JakeA: btw, it's filter *then* transform
<noamr> q-
<TabAtkins> emilio: so yeah that works, transform is applied on top after the VT is already positioned, so it follows along
<flackr> +1 that sounds like it could work
<fantasai> +1
<TabAtkins> astearns: so sounds like a different route. Vlad, do you want to explore implication in the issue, or just take a resolution?
<TabAtkins> vmpstr: might as well resolve, if we have issues we'll bring it back
<TabAtkins> vmpstr: resolution is to make the VT have a stacking context that's a sibling of the scope stacking context, but painted at a point before transforms/filters. Still a child of the box tree.
<TabAtkins> emilio: should we enforce the scope to be a CB for abspos? if you start a VT on a staticpos, the insets wont' work
<kush> q+
<TabAtkins> noamr: it's layout-contained, that already makes it a CB
<TabAtkins> kush: I was trying to understand - if it's a sibling in the box tree... sorry, a child, but it's painting involves all the content besides itself
<TabAtkins> kush: does it get the local clips?
<TabAtkins> emilio: probably not. ancestor clips apply, but on the element itself won't
<flackr> it should only be affected by the transform of the element
<TabAtkins> emilio: it'll be a painting layer defined,
<TabAtkins> kush: q is just if we make it a child of the scope, how many things that are inherited from the scope will have to be handled special
<TabAtkins> emilio: fair, but all those special things are what you want
<TabAtkins> kush: right, but we got all those special things for free by making it a sibling, they're manual now that it's a child
<TabAtkins> emilio: but many other things are easier as a child
<TabAtkins> vmpstr: what I think we really want is the scope to be the child of the VT
<TabAtkins> vmpstr: but that's... complicated
<TabAtkins> astearns: we're at time. should we not resolve yet?
<TabAtkins> kush: taking resolution seems fine for now, curious about what we'll learn impl wise
<TabAtkins> kush: i'm on the same side as Emilio, argued for that in my head, but other than moving the pseudo alongside the element, what am I gaining by the child approach rather than sibling?
<TabAtkins> emilio: you also get abspos CB for free, rather than it escaping all the clips up to its actual abspos CB
<flackr> +1
<TabAtkins> astearns: this is details. back to the question. objections to the proposed resolution?
<TabAtkins> RESOLVED: make the VT have a stacking context that's a sibling of the scope stacking context, but painted at a point before transforms/filters. Still a child of the scope.

css-meeting-bot avatar Nov 14 '25 03:11 css-meeting-bot

Follow up question here is on the sizing of the root capture:

  1. Do we size ::vt to the padding box of the scope? If so, is the root capture also sized to the padding box with the border of the scope overflowing visually?
  2. Do we size ::vt to the border box of the scope (including the root capture)? This is more consistent with the rest of the capture elements but it's a bit awkward to size the vt to be larger than the padding box.

vmpstr avatar Nov 19 '25 16:11 vmpstr

I'm assuming we want inset: 0 box sizing for the ::v-t but also for the root capture (i.e. capture of a self-participating scope) to include the scope's borders and ink overflow (outline, box shadow, etc).

That entails the ::v-t itself may have some visual overflow as a result of such captures.

I'm not sure how we handle box sizing of the ::v-t-group currently but we should try to be consistent with whatever we already do for participants with box decorations...

skobes-chromium avatar Nov 19 '25 16:11 skobes-chromium

I have been trying to understand the resolution in https://github.com/w3c/csswg-drafts/issues/12324#issuecomment-3530620798 via discussion with @flackr and @vmpstr. Our current interpretation I believe is as follows:

  • The WG raised a concern that if the ::v-t is an abs-pos sibling of the scope then it can escape ancestor clips in ways the scope itself does not. This is a valid concern that I had not thought of before. I made some quick demos to prove this to myself: sibling, child.
  • Based on such concerns the new plan is to not use anchor positioning but go back to having the ::v-t pseudo be a layout-tree child of the scope, absolutely positioned with inset: 0.
  • Despite being laid out as a child the ::v-t pseudo will have some "magical" sibling-like behaviors that ordinary DOM children don't have:

(1) It is painted on top of everything else in the scope, including higher z-index children. (2) It is not affected by the scope's scroll offset, if the scope is a scroller. (3) It is not affected by clips or effects set on the scope itself(?)

I think the idea of an abs-pos child of a containing-block scroller where the child is not affected by the scroll offset may be a brand new concept (for Chromium requiring adjustments to hit testing / geometry mapping logic as well as scroll tree construction). Note that scopes are always containing blocks since we apply contain: layout during transition (crbug.com/434895697).

skobes-chromium avatar Nov 19 '25 18:11 skobes-chromium

@flackr confirms via chat that despite the apparent wording in the resolution, it was NOT actually the WG's intent for the VT pseudo to be "painted... before... filters" i.e. for the VT pseudo to be affected by filter effects declared on the scope.

So it acts like a sibling for clips, effects, and scrolling, but not transform.

It is not obvious to me that "layout tree child with magical sibling-like properties" was a cleaner solution than "anchor-positioned sibling with magical ancestor-clipping", but I guess it is good that one way or another we are going to have our cake and eat it too.

skobes-chromium avatar Nov 19 '25 18:11 skobes-chromium

Yes, this is a new feature / concept that cannot be replicated by standard CSS - however - it lets the view transition be naturally sized by the element, and visually matches its position and transform without the aforementioned issues inherent with trying to accomplish this using anchor positioning which is close but not quite meant for this case.

flackr avatar Nov 19 '25 18:11 flackr

One other consequence of the TPAC resolution is that since the ::vt pseudo escapes the clips, and contains all of the participating elements, it means that view transition participants in the subtree of the scope would not be clipped by the scope. We did introduce nested view transitions groups that can help with this, and I wonder if we should set up the basic scope->descendant nested relationship automatically.

vmpstr avatar Nov 21 '25 19:11 vmpstr

One other consequence of the TPAC resolution is that since the ::vt pseudo escapes the clips, and contains all of the participating elements, it means that view transition participants in the subtree of the scope would not be clipped by the scope.

This assumes self-participating scopes, which I think we are ready to propose by now as all signals so fare are in favor of doing that.

We did introduce nested view transitions groups that can help with this, and I wonder if we should set up the basic scope->descendant nested relationship automatically.

I’d go even further and then suggest to add overflow: clip to the ::view-transition-group-children(*) pseudos by default – it’s something I’ve always needed and that in this case right here would also give authors the wanted behavior out of the box.

bramus avatar Nov 24 '25 08:11 bramus

Come to think of it, though: how would this affect document-scoped transitions. Or is your (@vmpstr’s) proposal targeted only towards element-scoped transitions?

bramus avatar Nov 24 '25 08:11 bramus

I’d go even further and then suggest to add overflow: clip to the ::view-transition-group-children(*) pseudos by default

Even if the captured element didn't clip? Wouldn't that cause content to disappear surprisingly when you start a VT?

flackr avatar Nov 24 '25 15:11 flackr

I’d go even further and then suggest to add overflow: clip to the ::view-transition-group-children(*) pseudos by default

Even if the captured element didn't clip? Wouldn't that cause content to disappear surprisingly when you start a VT?

This came up for nested VTs in general as well. So far, in all nested demos I’ve built, I’ve applied a clip on the ::view-transition-group-children(*) pseudo. I believe that would give a better “out of the box” experience.

bramus avatar Nov 24 '25 15:11 bramus

I am still pretty confused about what clipping behavior we want for ::v-t-group(*) and how to achieve it.

I think that https://github.com/w3c/csswg-drafts/issues/12324#issuecomment-3564356623 is talking about ::v-t escaping clips that are set on the scope itself, not the problem of escaping ancestor clips that was discussed in the WG IRC log and in https://github.com/w3c/csswg-drafts/issues/12324#issuecomment-3554010585 (please correct me if I've misunderstood).

In the case of a self-participating scope, the ::v-t-group(root) is going to capture the borders and ink overflow (box shadows, outline, etc.) of the scope. So it makes sense that we don't want to clip that out.

In the case of a scroller scope with a participating child that is outside the scroller's visible area, it would be very weird if the ::v-t-group(child) were visible, since that would make the child appear to pop out of the scroller and render someplace that might be far away from the scope on top of unrelated content.

So it probably makes sense for the ::v-t-group(child), but not the ::v-t-group(root), to be clipped to the border box of the ::v-t.

Nested view transition groups would allow the ::v-t-group(child) to be inside (and clipped by) the ::v-t-group(root), and I think that https://github.com/w3c/csswg-drafts/issues/12324#issuecomment-3564356623 is proposing to auto-nest in this way, in the self-participating scope case (please correct me if I've misunderstood).

But when the scope is not self-participating, there is no ::v-t-group(root), so there cannot be any such nesting. We still don't want the child to appear to pop out of the scroller. In that case, I suppose we could set a clip on the ::v-t. But that means that whether the ::v-t has a clip depends on whether the scope is self-participating?

skobes-chromium avatar Nov 24 '25 17:11 skobes-chromium

If we make self participation mandatory, then I think that gives us a nice model to work with: we set up an automatic nesting, and have overflow: clip if the scope clips its content.

I agree that if we don't have self participation then it becomes somewhat awkward to clip the contents without excessive magic which complicates the mental model.

Because of this I propose that we say that self participation is mandatory. We can brainstorm later if and how we can loosen this restriction

vmpstr avatar Nov 25 '25 14:11 vmpstr

Hmm that's a big change that is potentially limiting for developers and inconsistent with document transitions. I think I prefer magic clipping to mandatory self-participation...

skobes-chromium avatar Nov 25 '25 17:11 skobes-chromium

We (@vmpstr and I) have a new proposal for how the scope’s clip should interact with view transition pseudos.

To recap: as explained in #issuecomment-3554010585, the ::view-transition pseudo is a position: absolute inset: 0 box-tree child of the scope that paints on top of everything else in the scope. This structure, in contrast to earlier designs based on anchor positioning, ensures that an ancestor of the scope will clip the scope and the ::v-t in the same way — even if that ancestor is not a containing block for abs-pos elements.

The remaining question is: should clips set on the scope itself — such as the scroll clip, if the scope is a scroller — affect transition participants? Our answer: only some participants:

  • If the scope is self-participating, the scope’s clip should not apply to the scope’s own snapshots — that is, its ::v-t-old and ::v-t-new pseudos — because those images need to capture and draw the scope’s borders and ink overflow (shadows, outlines, etc.).
  • However, the scope’s clip should apply to the snapshots of participants other than the scope (regardless of whether the scope is self-participating or not). This stops a participant from weirdly “popping out” of a scroller scope as described in #issuecomment-3571908163.

To achieve both of these goals, we propose the following:

  1. The ::v-t pseudo is magically unaffected by the scope’s clip despite it being a box-tree child of the scope. (This was already part of the #issuecomment-3530620798 resolution.)
  2. User-agent style sets view-transition-group: contain on the scope during its transition. See view-transition-group property and the resolution in #11926.
  3. Extend the semantics of view-transition-group so that contain generates a ::view-transition-group-children() pseudo even if the element has view-transition-name: none and is therefore not itself a participant in the transition.
  4. If the scope’s overflow style is not visible, then apply ::v-t-group-children(root) { overflow: clip }. This relies on the proposal in #12320 for root to be the scope’s default view-transition-name.)

This proposal ensures that all participants other than the scope will have their snapshots clipped by the ::v-t-group-children(root) pseudo, which will apply the same clip that the scope itself applies to its descendants.

However, a self-participating scope will have a snapshot that is not subject to that clip, because that snapshot is outside of the ::v-t-group-children(root) in the DOM.

The developer still has the option to create a scope that is not self-participating, by setting view-transition-name: none. Such a scope would generate a ::v-t-group-children but not a ::v-t-group or a ::v-t-old or a ::v-t-new.

The developer can also, if they want, set view-transition-group: none on the scope, to suppress the ::v-t-group-children and override the default nesting behavior in our proposal.

skobes-chromium avatar Dec 04 '25 16:12 skobes-chromium

  1. Extend the semantics of view-transition-group so that contain generates a ::view-transition-group-children() pseudo even if the element has view-transition-name: none and is therefore not itself a participant in the transition.

[…]

The developer still has the option to create a scope that is not self-participating, by setting view-transition-name: none. Such a scope would generate a ::v-t-group-children but not a ::v-t-group or a ::v-t-old or a ::v-t-new.

The developer can also, if they want, set view-transition-group: none on the scope, to suppress the ::v-t-group-children and override the default nesting behavior in our proposal.

We discussed this a bit more internally and I pointed out that this has some weird side-effects on the pseudo-tree that needs to be generated. Specifically, if the scope has no view-transition-name, then:

  • What would go inside of the parenthesis of ::view-transition-group-children()?
  • Where does this ::view-transition-group-children() pseudo get placed in the tree? Directly under the ::view-transition instead of under its ::view-transition-group()?

I mean, this seems weird:

#element
  └─ ::view-transition
      └─::view-transition-group-children(⁉️)
         ├─ ::view-transition-group(child1)
         │   └─ ::view-transition-image-pair(child1)
         │       ├─ ::view-transition-old(child1)
         │       └─ ::view-transition-new(child1)
         └─ ::view-transition-group(child2)
             └─ ::view-transition-image-pair(child2)
                 ├─ ::view-transition-old(child2)
                 └─ ::view-transition-new(child2)

A solution that doesn’t have this side-effect is to always capture the scope as part of a scoped view transition (thus: the scope is always self-participating), but give authors a way to indicate that they only want to capture its dimensions – so without its pixels/textures.

When only capturing the dimensions, this results in a more consistent pseudo-tree:

  • The ::view-transition-group(root) for that element is always generated
  • There is a ::view-transition-group-children(root) for its children that were captured (if any)
  • The ::view-transition-image-pair becomes conditional, without affecting the rest.

In practice, the pseudo-tree would look like this:

  • When capturing dimensions + texture (default behavior):

    #element
      └─ ::view-transition
          └─ ::view-transition-group(root)
              ├─ ::view-transition-image-pair(root)
              │  ├─ ::view-transition-old(root)
              │  └─ ::view-transition-new(root)
              └─::view-transition-group-children(root)
                 ├─ ::view-transition-group(child1)
                 │   └─ ::view-transition-image-pair(child1)
                 │       ├─ ::view-transition-old(child1)
                 │       └─ ::view-transition-new(child1)
                 └─ ::view-transition-group(child2)
                     └─ ::view-transition-image-pair(child2)
                         ├─ ::view-transition-old(child2)
                         └─ ::view-transition-new(child2)
    
  • When capturing only the dimensions:

    #element
      └─ ::view-transition
          └─ ::view-transition-group(root)
              └─::view-transition-group-children(root)
                 ├─ ::view-transition-group(child1)
                 │   └─ ::view-transition-image-pair(child1)
                 │       ├─ ::view-transition-old(child1)
                 │       └─ ::view-transition-new(child1)
                 └─ ::view-transition-group(child2)
                     └─ ::view-transition-image-pair(child2)
                         ├─ ::view-transition-old(child2)
                         └─ ::view-transition-new(child2)
    

Additionally, when only capturing the dimensions, the real (new) element still gets drawn underneath the scoped transition pseudo-tree.


There is still an open question on how authors can do this opt-in.

Some options we discussed:

  1. Give authors a CSS property to indicate this behavior.

    #element {
      view-transition-capture-mode: dimensions; /* initial value: normal */
    }
    

    I believe this view-transition-capture-mode: dimensions would also prove itself useful for other parts of View Transitions (not linked to scoped transitions) such as in-place element transitions, making https://www.bram.us/2025/05/15/view-transitions-border-radius-revisited/#a-simpler-approach more easier for authors to do.

    Note that for this to work, the UA would generate this stylesheet whenever you start a Scoped View Transition:

    vt-scope {
      view-transition-name: root !important;
      view-transition-group: contain;
    }
    
    /* Conditional. Only gets set if the scope’s overflow style is not visible */
    vt-scope::view-transition-group-children(root) {
      overflow: clip;
    }
    

    The !important might catch your eye there, but I think this is warranted here because you always want the scope to be captured.

    The effect on Steve’s proposal would be that bullet 3 in the list becomes this:

    1. User-agent style sets view-transition-group: root !important on the scope during its transition. Authors can opt-out from capturing the pixels of the scope by declaring view-transition-capture-mode: dimensions; on it.
  2. The same as 1, but do the opt-in from JS

    document.querySelector('#element').startViewTransition({
        callback: () => { … },
        rootCapture: "dimensions-only",
    );
    

    This feels pretty limited, as it’s a flag that only works for Scoped Transitions.

  3. Automagically do this when authors set view-transition-name: none on the scope root in their author stylesheet.

    Personally I find this a bit too magical, because authors are indicating that they don’t want to capture it, yet somehow it would still show up in the pseudo-tree.

  4. [insert-your-idea-here]

bramus avatar Dec 10 '25 08:12 bramus

For the record, I had imagined in my proposal that we would create ::view-transition-group-children(root) even if the developer sets view-transition-name: none. In other words by opting out of "self participation" they are effectively only opting out of pixel capture, which is what matters for allowing interactivity.

I would also be happy with ::view-transition-group-children(⁉️). :)

skobes-chromium avatar Dec 10 '25 13:12 skobes-chromium

A comment on whether group-children is a direct child of the ::v-t: I'm happy to say that we also create an empty ::view-transition-group(!?) to nest this, just not the image-pair and its children.

It sounds like the idea is roughly the same, modulo coming up with what goes in the pseudo argument. One idea is that since we can say that scope elements are special in that they are the only ones that can have a view-transition-group: contain without a name, and come up with new keyword to say that.. One option is root, but that's not a good one since it's already explicitly a non-reserved thing that we put on the documentElement by default. Maybe something like scope-root

Just to elaborate, I'm somewhat opposed to capture-mode: geometry style fixes, because this only makes sense for scope roots (since they host ::view-transition element and would move naturally with the element). If you try geometry capture on anything else, it's meaningless as an effect since the pseudo moves but neither the old nor the new pixels move

vmpstr avatar Dec 10 '25 15:12 vmpstr

we would create ::view-transition-group-children(root) even if the developer sets view-transition-name: none

This breaks the basic promise of “Elements with a view-transition-name (other than none) get captured”, which I think would be confusing.

bramus avatar Dec 10 '25 15:12 bramus

we would create ::view-transition-group-children(root) even if the developer sets view-transition-name: none

This breaks the basic promise of “Elements with a view-transition-name (other than none) get captured”, which I think would be confusing.

It depends on what we mean by "captured". In the pixel sense, it's still true. In the geometry sense, scopes would have to be special here in that they still create the necessary structure to retain the geometry information

vmpstr avatar Dec 10 '25 15:12 vmpstr

The CSS Working Group just discussed [css-view-transitions-2] [scoped] Clarify the ::v-t box and stacking context position relative to the scope.

The full IRC log of that discussion <bramus> astearns: we discussed this before
<bramus> vmpstr: yes
<bramus> … discussed at TPAC and had a resolution to say that the v-t pseudo stacking context ist he parent of the scope while the object remains the child
<bramus> … prototyped some of it to see if ti works, some details to work out
<bramus> … 1 thing to do with clipping; sepcifically if the SC of the ::VT is th parent ofthe scope then teh scope wouldn clip that pseudo
<bramus> … also shouldnt clip it because it self-contains the representation of the border box
<bramus> … consequence is that any descendeant is also not cliippe dby it, which is unexepected for devs
<bramus> … cos they would pop out
<JakeA> q+
<bramus> … so we do want them clipped
<bramus> … we resolved a while ago to have nested view transitions that allows you to construct a tree that allows clipping
<bramus> … used the ::view-transition-group-children pseudo
<bramus> … we like to apply this here, specifically for scoped view transitions
<bramus> … the scope el would by default have a `view-transition-group: contain`
<bramus> … there is a lot of discussion around the fact that this property only has an effect on els with a `view-transition-name`
<bramus> … should we therefor emandate if the scope element needs a name, and we think we do
<bramus> … but authors can remove that
<bramus> … some options here
<astearns> ack JakeA
<bramus> JakeA: I like the idea of using groups for this
<bramus> … the issue suggested that they would be clipped by the parent, not the group
<bramus> … what ahppens if an el is transitioning from 500x500 to 10x10 … when would the 10x10 clipping apply to the children?
<bramus> vmpstr: this is somewhat of a tangent, bc we have not discussed what happens when the scope resized … and do believe that …
<bramus> bramus: it animates, no?
<bramus> vmpstr: with a regular VT we skip the transition if you resize the document. Same could apply with scoped transitions
<bramus> … it would have to instantly move along with the scoped element
<bramus> … be instant, and move pseudos with it
<bramus> JakeA: Seems reasonable … it will catch people up … no argument from me on position – its less clear to width and height but can see why … makes sense
<bramus> vmpstr: can raise separate issue for this
<bramus> … so, back to this issue:
<bramus> … if we assume that we do but `v-t-g: contain` on this and want the scope participatiotn to be optional, what are our optinos?
<bramus> … 1. the group still applies if there is no name on the scope, and it just contstructs the `::view-transition-group-children` pseudo
<bramus> … as bramus pointed out, that is weird because it would have no name in between the brackets
<bramus> … my proposal there is to use a keyword
<bramus> … 2. alternative option from bramus/noam is to add a new property like `view-transition-capture-mode: geometry`
<bramus> … whcih measn we dont catpure old state, only show new state, and forward poitner events to it
<bramus> … would say that `view-transition-name: root !important` in the UA stylesheet
<astearns> instant allergic reaction to the word 'mode'
<bramus> … but author can opt out from old capture by specifying this new propert
<bramus> … can already polyfill that by styling their own elements, but makes it more convenient
<bramus> … minor question now is for `view-transition-name: containt` is that we create the structure for clipping but dont clip ourselves
<bramus> … can leave that open for now
<bramus> … curious to hear about the structure
<bramus> astearns: bewteen the two optoins, i have an allergic reactoin to adding a mode
<bramus> q+
<bramus> … my naive prefernece would be the other
<astearns> ack bramus
<ydaniv> scribe+
<JakeA> The demo bramus is referencing: https://simple-vt-demos.jakearchibald.com/video/
<ydaniv> bramus: 1 thing I like about the 2nd option is that it also helps with request from authors is that they want to morph elements in place
<vmpstr> s/view-transition-name: containt/view-transition-group: contain/
<ydaniv> ... and they now work around it
<ydaniv> ... and also showing the new element immediately in VT as it is ongoing
<ydaniv> ... also solves this issues authors had before
<ydaniv> ... I discussed it with noamr about pointer events, has separate issue, and this fits in bigger picture here
<ydaniv> vmpstr: you want to add a new property which allows you to act as if you didn't capture the old pixels
<ydaniv> bramus: yeah
<noamr> q+
<ydaniv> vmpstr: and also to confirm this is fully implementable today
<ydaniv> ... as a perf improvement
<ydaniv> noamr: the hit testing thing with pointer events is not polyfillable today
<ydaniv> ... what this means is that the element in the pseudo tree is not a pair of snapshots, just a proxy to the element
<JakeA> q+
<ydaniv> ... instead of the pair of images we have today
<bramus> vmpstr: would still be stylable by authors
<ydaniv> vmpstr: it would still be styled by have object-view-box can change, and hit testing would do the proper mappings
<bramus> … hit testing would do all of the approrpiate mappings
<ydaniv> noamr: right
<astearns> ack noamr
<astearns> ack JakeA
<bramus> JakeA: im trying to separate out the two options
<bramus> … am I right that option 1 is that it doesnt matter if the root has a `view-tranisiton-name` and that it always get cpatured, whereas option 2 enforces it at the UA level
<noamr> q+
<bramus> … the 2nd proposal doenst have that, can still achieve with CSS
<bramus> vmpstr: modulo the pointer events
<bramus> noamr: and extra capture
<noamr> q-
<bramus> vmpstr: the difference is that in the first proposal, whethe ror not the scope has a name it still means we construct the group and group-children pseudos. IN the second we force the scope to have a name (UA Style Sheet) so it naively would have all the sturcture, but with this extra property can exclude the ::old and forward hit-testing
<bramus> JakeA: is the hit testing a new feature? or exist elswhere?
<bramus> bramus: discussed it before in august, but no resolution
<bramus> vmpstr: desirable effec tot have hover effects and such. if it solves this problem as well,t hen maybe its the right approach
<bramus> bramus: in the issue on the pointer events, I recall tim saying that this looks implementable on their end
<bramus> JakeA: If there are … because this is a group and there ar ethings insid wehich might show their old contant … how does the pointer forwarding happen in that case?
<bramus> vmpstr: there is no old content in this case
<bramus> … would not capture it
<bramus> JakeA: but you would in sid ethe group/ its only not there fo rthis 1 element ; and those children might be showing their old content
<bramus> … what happens if I click on one of those old ones?
<flackr> q+
<emilio> q+
<bramus> vmpstr: hit testing initially happens on the psueod tree. old pseudos end the journey. only when hit testing on a new, it forwards to the real eelemnt with this
<bramus> JakeA: So, i’ve got root with this new thing one it and it has a hover effect to change color on hover. gut there is a child element with a v-t-name and tha tis fadineg from it old to new state without this property. if my mouse is in that inner elmeent, is the hover effect happening on the parent?
<bramus> vmpstr: currently no
<bramus> JakeA: I wonder if trying to bundle complicates this … seems like a hard problem to solve …
<bramus> vmpstr: fwiw: neither of the options solve this
<bramus> … whehter you hit test via or (missed)
<bramus> skobes: it seems like option 2 is more complicated than 1
<bramus> JakeA: yes, because we have to solve all that
<astearns> ack flackr
<noamr> q+
<bramus> flackr: I think its prblematic that we are trying to bring in all this hit-testing stuff here, which we want to solve separately
<bramus> … how we forward the pointer events. dont see why this has to be part of how we capture the scope root’s clip
<bramus> … can set pointer=events: none on the ::view-transition and everything is fine
<emilio> q-
<bramus> … my preference would be something option 1-like
<emilio> +1 to flackr
<bramus> … dont need to be incorporating hittesting here
<astearns> ack noamr
<bramus> noamr: I tend to be ?? by this … have some tweak to optoin that could make it work really well
<vmpstr> s/??/convinced/
<bramus> … to force th ename only when capturing the new state
<astearns> s/??/convinced/
<bramus> … + having no animation when new is the only child
<bramus> … if the author would remove the root name from the scope you}d end up with only a new pseudo
<bramus> … and a UA style that doesnt animate it, so that it doesnt fade in
<vmpstr> q?
<vmpstr> q+
<bramus> … in essence, would have same rsult … optiont 1, but authors removing it from the old does not affect the new … we are forcing the new
<bramus> … if author removed root, it will be a non-animated new state
<astearns> ack vmpstr
<bramus> vmpstr: dont think tha twould more like option 1 but more like option 2
<bramus> noamr: yes, but would still behave like … if we fix opointer events in the other issue, then it would at least not capture only have the new pseudo
<bramus> vmpstr: From what Im hearing we should avoid dealing with poitner events for this issue
<bramus> … I think there is some details about the pointer-events … there are more implications than just hit testing … should discuss in that other issue and come to some resolution here.
<bramus> astearns: timecheck … halfway trhough … maybe take back to issue to have us just deal with clipping excluding pointer events for now
<bramus> vmpstr: other issues are far less consequential than this one
<bramus> …if I can make a bold suggestion, can we say that we want to suppport ht scope being optional to support interactive elements, but the solution to that should not incldue and of th eold or new pseudos. does this sound reasonable?
<bramus> … we want to scope participation be optional, and when it is not we dont want to be creating the old or new pseudos
<bramus> JakeA: what is consequenc eon pointer stuff?
<flackr> q+
<bramus> vmpstr: we avoid dealing with it
<bramus> q+
<astearns> ack flackr
<bramus> flackr: i think that is a resonalbe idea but dont udnerstsand how this complicates pointer events going to the scope
<bramus> … if you hit the pseudo, the original element is the scope, so you’d still get hover
<bramus> … seems to me tha tall tha tis required is to not have any pseudo that has pointer-events auto above that content
<bramus> … so whtahever capture we do (so no old or new) … you”ll just hit the content behind
<vmpstr> q?
<bramus> vmpstr: right
<ydaniv> bramus: this would still be under author control
<noamr> q+
<JakeA> q+
<ydaniv> ... they still want to cross fade content, but we still need some property to indicate to capture the pixels and do the regular things
<astearns> ack bramus
<emilio> q+
<ydaniv> ... and the thing noamr proposed also works for me, capturing something dimensions or pixels seems wierd, if you have VT-name none on something and you wouldn't force still capture that would seem wierd
<astearns> ack noamr
<bramus> noamr: wanted to say that maybe given the other work on pointer events, maybe its ok to force self-participation.
<bramus> … maybe the thing that we can do is use `pointer-events: none` like rob said
<bramus> … can force self-participation and leave thd `none` to just document vts
<bramus> … maybe this is not the mechanism that we should to for going forward just forward witht this
<JakeA> q-
<bramus> … aything with hit testing and optimzing capture can be done separately
<bramus> JakeA: sounds good
<astearns> ack JakeA
<astearns> ack emilio
<bramus> emilio: agree that probably tha tworks, but main issue with poitner events and the vts: one of them is scoped dont use the top layer by default so thats no issue here but for regular ones that is
<bramus> … if that doesnt happen here its fine
<vmpstr> q+
<bramus> … other one is that the cpatured content is supposed to behave as visibility: hidden, which does need fixing in the spec.
<astearns> ack vmpstr
<JakeA> q+
<bramus> vmpstr: I’m happy to say that for now we force the `view-transition-name` on the scope via the UA style sheet. that would unblock the work here. are we then ok here as well to have `view-transition-name: contain` here
<bramus> … if we can improve the performance later on, then we can do that later
<bramus> bramus: and then they can hide the old and not-animate the new to get the effect they want
<vmpstr> s/view-transition-name: contain/view-transition-group: contain/
<bramus> JakeA: yes
<bramus> JakeA: Remind me again for groups: is the clipping done by default?
<bramus> vmpstr: not by default. can say here that we do want to do that … no strong opinion
<bramus> bramus: didnt skobes propose to do it conditionally?
<bramus> skobes: yes
<bramus> bramus: but can resolve on that later
<bramus> vmpstr: want to propose to add `view-transition-name: root !important` and `view-transition-group: contain` in the UA stylesheet for scoped VTS
<JakeA> q+
<bramus> astearns: let’s state both parts
<noamr> proposed resolution:
<astearns> ack JakeA
<bramus> … so in a v-t scope it will have both declarations applied int he UA stylesheet?
<bramus> vmpstr: yes
<bramus> JakeA: “when this stye is applied” needs to not clash wiht a document VT
<bramus> vmpstr: that’s one of the next issues
<bramus> JakeA: I support it
<bramus> skobes: we do understand this breaks interactvitiy for non-participating elements until this solves the pointer events issue
<bramus> JakeA: seems good to solve that hollisticly
<bramus> flackr: dont think thats true … you can stll use pointer-events to interact with the content
<bramus> skobes: but you wont see it
<JakeA> q+
<bramus> flackr: in the new state speudo if you hide the old
<bramus> … or is this becauseof the line in the spec?
<JakeA> q-
<bramus> skobes: for doc VTs you can set v-t: none on root and pointere-events: none to regain interactivity
<bramus> flackr: if you hide the old and new then?
<bramus> bramus: then you dont see anythin
<bramus> flackr: if you only hide the old, then don’t they go to the orig element?
<bramus> JakeA: this boils down to what emilio said before, it gets treated as hidden
<bramus> vmpstr: which is by design
<bramus> flackr: can solve this generally later on
<bramus> astearns: skobes, are you concerned that we are lockin gourselves out for pointer events?
<noamr> q+
<emilio> q+
<bramus> skobes: no. my concern is that we might ship something that could be worse than the model that document transitions have enabled and that we are blocking it on a more complicated proposal
<bramus> JakeA: tend to agree. that authors can override to `none` but we would still capture it
<flackr> +q to JakeA, i think we should allow setting name to none, and still make group
<astearns> ack noamr
<bramus> noamr: current doc VT solution is kind of a half solution because it only works on the root
<JakeA> apologies for jumping queue
<bramus> … very big thing with lot of subthings. Makes me feel this probably OK.
<bramus> … wether we solve it here, the other problem is going to be there anyway
<emilio> q- was about to make a similar argument as noam
<bramus> … can say the solution is a document VT
<emilio> q-
<astearns> ack emilio
<astearns> ack flackr
<Zakim> flackr, you wanted to JakeA, i think we should allow setting name to none, and still make group
<bramus> flackr: donts e eproblem with capturing things without a name
<bramus> q+
<bramus> … can give them a special name
<bramus> … dont feel so strongly that I want to oppose a resolution here
<bramus> … can have v-t-name: none and it would still create a group
<JakeA> +1
<astearns> ack bramus
<ydaniv> bramus: styling VT-name none and still have captured, and pseudo element created, is hard to explain to authors
<emilio> To be clear I don't feel too strongly about allowing none, but I do think the whole doc is a bit special
<vmpstr> +1 to emilio
<ydaniv> flackr: the pixels aren't captured, we just create something that captures the root's scope, not the image
<ydaniv> bramus: I think this is weird because everything has to be backed by somehting in the UA stylesheet
<JakeA> q+
<ydaniv> ... the thing being captured has a name, and that is what caused it
<ydaniv> vmpstr: would be backed by having group contain
<noamr> ::view-tranisition-group(-ua-scope-root)
<vmpstr> q+
<ydaniv> bramus: also if you do VT group on bunch of elements then they suddnley also get captured even when they have no name
<ydaniv> ... hard to explain to ppl
<ydaniv> ... the properties and values do something that's confusing
<astearns> ack JakeA
<bramus> JakeA: common complaint from devs is that htye have a button taht triggers the VT and the button doenst move and when the press it the hove r style disappaears
<bramus> … with scoped we can hope it is outside of the scope
<bramus> … as olution wher ethe hover style contianues to do the right thing, sounds like a pretty good deal
<flackr> The vt spec says something about a root element, couldn't this use the same unnamed root? https://drafts.csswg.org/css-view-transitions-2/#pseudo-element-root
<bramus> … so if the scope is not particiapting by default and the button has no, then it seems like the right thing is ??? of that?
<astearns> ack vmpstr
<bramus> vmpstr: I think you want the scope to parcipate by default
<bramus> … base use case is change its bg color, and if it doesnt participat eby default then thats weird
<JakeA> q+
<bramus> … re emilio, I think doc is special because ???. for scoped you control how much is particpating, so yes we lose interactivity of those elements but its up to the author to structure that.
<bramus> … author has fine-grained control
<bramus> … losing itneractivity … would like to see how actually important that is
<bramus> … do want to go back to say that the scope has to participate, and work on pointer events indendpenttly
<bramus> … dont want to block progress on this issue therefore
<bramus> JakeA: OK with that
<astearns> ack JakeA
<bramus> … if we decide dnow that UA stylesheet has `v-t-n: root !important` on it, what are the chances of us changing that in the fture?
<bramus> vmpstr: can measure that and the risk wont be too severe
<bramus> … can cautiously take it
<bramus> JakeA: ok with the resolution then
<bramus> astearns: how about skobes?
<bramus> skobes: what is the protocol here? need to be unanimous?
<bramus> astearns: if you think we are taking the wrong route you should say so
<bramus> skobes: tricky, had hoped for a resolution today but concerns about forcing self-participation
<bramus> astearns: need to take it back to the issue then … vmpstr, will you write up things and skobes reply then?
<bramus> … then hopefully a much shorter discussion in the future
<bramus> … and now on to the regular meeting
<noamr> present-

css-meeting-bot avatar Dec 10 '25 17:12 css-meeting-bot

I've summarized the couple of options discussed and concerns about those below:

  1. Make scope view-transition-name optional and have view-transition-group: contain by default. In a situation where view-transition-name is none, this construct ::view-transition-group(-ua-root) and ::view-transition-group-children(-ua-root) but none of the other vt pseudos.

The main objection is that this departs from what the spec defines as when to capture things, and also that this doesn't work for non-scope-roots (ie in any other situation view-transition-group: contain with view-transition-name: none would have no effect)

  1. Add view-transition-name: root !important and view-transition-group: contain in the UA stylesheet. This forces capture of the scope and thus view-transition-group applies naturally.

The main objection here is that this mandates self scope participation and makes the non-captured content in the scope not interactive (modulo general pointer event improvements in another issue)

Please let me know if I missed another leading option


My personal opinions on the objections are below:

  1. I think the scope is already a sufficiently special element -- you have to call startVT on it -- that we can afford some magic in that we have to capture geometry of the thing you call startVT on, so I think that's a good option

  2. Scoped VT allows the author to select how much or how little of the content they want transitioning, which is a vast improvement over the current state. The inability to fine tune the smaller capture to also allow interactivity within it seems inconsequential in practice. We also have some plans to improve pointer event forwarding which may further improve this. So I think this is also a good option

To clarify, I don't mind either one of these.

vmpstr avatar Dec 10 '25 17:12 vmpstr

For option 1, I’m assuming there would also be a view-transition-name: root on the scope by default? So that authors opt-out from getting its pixels captured?

bramus avatar Dec 10 '25 17:12 bramus

Yes, that's correct. Option 1 UA stylesheet would be view-transition-name: root; view-transition-group: contain with extra magic if name is none. Option 2 is view-transition-name: root !important; view-transition-group: contain, so no magic is required

vmpstr avatar Dec 10 '25 17:12 vmpstr

I prefer option 1. If we require the root to participate, there are two concerns:

  1. If we can't agree on a reasonable way to handle pointer-events for participating elements, you have to carefully ensure the scope is a descendant of interactive elements even if it has no animation.
  2. Even if we do have a way to allow pointer-events, it is potentially permanently requiring that we capture the root.

ie in any other situation view-transition-group: contain with view-transition-name: none would have no effect

I could see enabling this more generally being useful to keep your participating vts from escaping ancestor clips without having to have those ancestor clips explicitly capture their content. E.g. we would create the necessary structural pseudo-elements to apply the element's clipping only.

It seems like the main concerns being raised is both wanting to see selectable pseudo-elements in the tree, but not wanting to see them because the name was set to none. Do they need to be uniquely selectable by developers or could they have a generic unnamed pseudo-element tag or perhaps a name similar to match-element (i.e. not developer exposed).

flackr avatar Dec 10 '25 17:12 flackr

Do they need to be uniquely selectable by developers or could they have a generic unnamed pseudo-element tag or perhaps a name similar to match-element (i.e. not developer exposed).

Yeah, this is absolutely an option. You can already use view-transition-class if styling is required

vmpstr avatar Dec 10 '25 17:12 vmpstr

I also favor option 1. My primary concern is that it should be possible, inside the scope, to mix participating elements with interactive non-participating elements, an effect that developers currently achieve with document transitions by writing html { view-transition-name: none } ::view-transition { pointer-events: none }.

Option 2 makes this effect impossible. The developer then has to restructure their DOM so that the interactive elements are outside of the scope, which may be very constraining and is at odds with the principle that view transitions are independent of DOM structure. Option 2 pushes people away from using scoped view transitions, since the answer to "Q: How do I mix interactive and transitioning elements?" will be "A: Sorry this can't really be done with scoped, you have to keep using document.startViewTransition()..." So I don't think it is inconsequential.

I am happy with the -ua-root tag, or something not developer-exposed, for the non-participating scope's ::v-t-group.

skobes-chromium avatar Dec 11 '25 14:12 skobes-chromium

@bramus, are you happy with pursuing option 1 in light of -ua-root and other discussion here? Specifically, limiting the view-transition-name: none with view-transition-group: contain to only scopes on which startVT is called and giving the group/group-children the parameter of -ua-root

vmpstr avatar Dec 11 '25 14:12 vmpstr

Yeah, I think we can get that explained properly to authors: “To make sure view-transition-group: contain on the vt-root has an effect, we need to ensure a proper pseudo-tree, so we generate a ::v-t-g(-ua-root) and ::v-t-g-c(-ua-root) in that case.”

Two follow-up questions:

  • Is it ::v-t-g(-ua-root) animated based on the old and new dimensions? (I don’t think it should. The pseudo should immeidately take up the full size of the new element’s state, as it is that what you are looking at)
  • Should we do something similar for document.startViewTransition? As in: if there is a view-transition-group: contain declared on the root (be it document root, or scoped root), then we do this pseudo-tree fixup thing.

This can be food for two separate follow-up issues, separate to this issue.

bramus avatar Dec 11 '25 15:12 bramus