w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 661 forks source link

[css-view-transitions-2] Behavior of mismatching types between old and new document #9526

Closed noamr closed 7 months ago

noamr commented 1 year ago

As per https://github.com/w3c/csswg-drafts/pull/9523, both documents can specify "types" for the cross document view-transition, which affects the :active-view-transition selector. Note that these types apply both at the old and new document, as they can affect (for example) which elements are captured.

When capturing the old document, only types specified on that document can apply. On the new document, we can do one of the following:

  1. apply only types specified in the new document (override)
  2. apply both the types in the old and new documents (union)
  3. apply only the types that are in both documents (intersection)
  4. fail the transition if the types don't match

The current PR specified (1), the advantage being that it's the most flexible and least opinionated - if developers want to have different types in both documents they can do it, and if they want them to be strictly the same they can restrict it themselves, there is no technical reason to have this restriction or to force a union.

There are also advantages to the other approaches, perhaps mandating the same types would lead to design that is less error-prone.

khushalsagar commented 12 months ago

Discussion summary for the different options:

  1. apply only types specified in the new document (override) This option is easiest to reason about, the type is a document-scoped property. So new Document gets the type in its declaration only. It would also work if the transition type depends on parameters the new Document is aware of: navigation type and from URL.

  2. apply both the types in the old and new documents (union) This option helps with use-cases where the from URL is not enough to define the transition type. For example, a different transition happens if the user scrolls to a particular element or interacts with the page. We could use some use-cases to understand how common this is. There are ways to polyfill this in script (examples below) so this option would be a more declarative way to do something feasible in script:

    • Send info about the old Document's state to the new Document via other APIs. For example, https://github.com/whatwg/html/issues/9760 will provide a reference to the last navigation entry on the new Document which has getState() for arbitrary information. Though that won't work for location.replace cases where the from navigation entry is not preserved. @jakearchibald did you say there's a workaround for this?

    • Can also be done with session storage.

  3. apply only the types that are in both documents (intersection) The downside of this option is that the old Document needs to add a type for all possible states the new Document could be served in. And this might be dependent on state on the server.

  4. fail the transition if the types don't match Same con as above. There is a legit use-case here to use types to detect a change in website version (if a deployment happened before the transition starts). But seems better to use an explicit "version" descriptor for it.

vmpstr commented 12 months ago

@ydaniv @jakearchibald @bramus for thoughts on what is preferable here from a developer perspective

bokand commented 12 months ago

I'll add my thoughts with the disclaimer that I'm just now trying to catch up on earlier discussion and might have missed some discussion or use cases.

My interpretation of type is that it tags a transition (e.g. type StreamToItem), rather than a page, so it doesn't really make sense to me that B would be setting a type since the transition is already created. i.e. the author already has all the context necessary on A (they know: A, A's state, and B) and A has been captured with the given type which B cannot change.

So there's an additional option to consider:

  1. Apply only the type specified in the old document: @view-transition { type: foo } on a page A applies the type foo both on the outgoing and incoming pages but only for transitions initiated from page A; It isn't applied to transitions where A is the incoming page.

This means authors don't have to do from-URL matching to deduce an incoming type and also corresponds to the SPA startViewTransition model where the type is specified only on initiation.

However, I've since seen this comment rebutting this. i.e. B can have transition-affecting state that A may not know about (e.g. cache hit vs. loading page).

If that is a legit use case then I feel like it's a point in favor of option 2 (union). In that case, you still get the benefit of the above model where A defines the transition type which is usable on B, but B can override it, assuming additional types add specificity:

/* On page B */
@view-transition {
  navigation: auto;
  type: LoadingPage
}

html:active-view-transition(StreamToItem) ::view-transition-group(item) { /* Animate In */ } 
html:active-view-transition(NotificationsToItem) ::view-transition-group(item) { /* Animate In Differently */ }

/* On loading page and don't have an item yet so do something else.
   Applies because additional parameters add specificity */
html:active-view-transition(StreamToItem,LoadingPage) ::view-transition-group(item),
html:active-view-transition(NotificationsToItem,LoadingPage) ::view-transition-group(item) { ... }
khushalsagar commented 11 months ago

Those are excellent points @bokand! Another use-case to allow the new Document to specify types is render-blocking. The old Document doesn't know how much of the new Document will be fetched before the browser stops render-blocking. So letting the new Document add/specify types allows it to dynamically pick a fallback transition.

My preference would be option 1 or 2. Not leaning strongly on either of them. If we go with option 1 (only types from the new Document), authors can easily still do option 2 (union) by using NavigationHistoryEntry's getState API.


Btw, we might need to solve the use-case you mentioned (if it turns out to be common) regardless of this issue:

However, I've since seen https://github.com/w3c/csswg-drafts/issues/8960#:~:text=Another%20option%20is%20to%20let%20the%20old%20Document%20be%20the%20decider%20but%20that%27s%20bad%20if%20the%20new%20Document%20chooses%20a%20different%20transition%20based%20on%20load%20state%20(transition%20to%20content%20if%20cache%20hit%20vs%20a%20loading%20screen). rebutting this. i.e. B can have transition-affecting state that A may not know about (e.g. cache hit vs. loading page).

If the UI on the new Document is going to be different based on whether there's a cache hit, that could influence which elements on the old Document have a view-transition-name. Navigation API could help here: we could expose a bit indicating the Document is cached or not for same-origin pages; or allow the Document to update state stored on its NavigationHistoryEntry when its being evicted.

vmpstr commented 11 months ago

[...] i.e. B can have transition-affecting state that A may not know about [...]

[...] The old Document doesn't know how much of the new Document will be fetched [...]

Both of these say that a new document can have some state that the old document isn't aware of and doesn't need to be aware of. My argument for option 1 vs option 2 is the same: the old document may also have some state or configuration that the new document doesn't need to be aware of, but making types necessarily exposed from old document to the new has a potential of making this awkward.

My interpretation of type is that it tags a transition (e.g. type StreamToItem)

I fully agree with this interpretation. However, I think each page should be naming its incoming and outgoing transitions. This is consistent with option 1, where the old page names its outgoing transition something like "foo". The new page matches the transition based on URL and navigation type, and names this (incoming) transition. It should absolutely feel free to name it "foo" as well, or "bar" or whatever else.

The fundamental difference here is whether we want types to be a source of information passing from the old page to the new. If we don't care about this channel of information, then option 1 isolates both pages better making type name selection namespaced to the page. If we do want the type to send information from the old page to the new, which would be the case if there are cases where the old page can navigate to a new page in "different" ways, meaning different types but all else is the same, then we should obviously go with option 2.

As an aside, there's another issue with option 2: it limits what names the new page can use for its types by some unknown set that the old page can specify. For example, if the new page has types "foo", "bar", and "baz" set up for its internal transitions, then it has to be careful that the set of these names doesn't appear in the set of names that the old page can specify during a transition, since that would inadvertently apply styles that were not meant for the incoming transition.

khushalsagar commented 11 months ago

My argument for option 1 vs option 2 is the same: the old document may also have some state or configuration that the new document doesn't need to be aware of

It feels like this doesn't align with David's point, "My interpretation of type is that it tags a transition (e.g. type StreamToItem), rather than a page" but does sound reasonable. Can you think of an example?

The fundamental difference here is whether we want types to be a source of information passing from the old page to the new.

I like this line of reasoning. I'm convinced that transitions need information passing between the 2 Documents, but "type" doesn't need to be how that happens. Other APIs (like session storage or navigation) already provide a generic way to do this. And we can add a "oldTypes" attribute to the reveal event (or ViewTransition object) if it turns out that type is indeed a preferred way of passing transition related info to the new Document. So at least the default would be option 1 but authors will be able to get option 2 with trivial script.

I missed responding to this, "also corresponds to the SPA startViewTransition model where the type is specified only on initiation". The difference between SPA/MPA is that with SPA authors can initiate the transition once all relevant info about the new state is known. But that's not always possible with MPA. So the model for setting types needs to diverge here.

vmpstr commented 11 months ago

Can you think of an example?

I'm not sure what example you mean. The old page can have any number of states, like say expanded details vs collapsed details, and depending on the state of collapse the VT hero image will come from either "details-collapsed-transition" type or "details-expanded-transition" type. That's not something the new page cares about, it just needs to know there's a hero image

khushalsagar commented 11 months ago

like say expanded details vs collapsed details, and depending on the state of collapse the VT hero image will come from either "details-collapsed-transition" type or "details-expanded-transition" type

Thanks, that's the kind of example I was looking for. Where 2 different transition types on the old Document map to the same transition type on the new Document. In this case the set of elements with a view-transition-name and the CSS for the animation on the new Document will be the same irrespective of which element provides the hero image.

bokand commented 11 months ago

The fundamental difference here is whether we want types to be a source of information passing from the old page to the new. If we don't care about this channel of information

+1. I was thinking that type would be how incoming and outgoing pages sync up on their states and coordinate on matching transition styles but as long as the alternatives are ergonomic then I now agree it doesn't make sense to have the types between documents interact.

authors can easily still do option 2 (union) by using NavigationHistoryEntry's getState API.

Hmm, since there's no Javascript API, are you thinking authors will use CSSOM to insert the appropriate @view-transition (or modify an existing) rule? I suspect this might be non-trivial in complex cases given they might have to pick out the correct one manually. Maybe we need a better way to do this? (or maybe I'm missing an existing option)

additional tangential replies > The difference between SPA/MPA is that with SPA authors can initiate the transition once all relevant info about the new state is known. But that's not always possible with MPA. So the model for setting types needs to diverge here. Right, forgot the fetch can (and _should_) happen before calling `startViewTransition`, rather than inside of it. > As an aside, there's another issue with option 2: it limits what names the new page can use for its types by some unknown set that the old page can specify. It seems like authors will already have to do a fair bit of coordination between pages since they'll have to make sure `view-transition-names` are coordinated between pages (and don't clash with internal state transitions), but point taken.
khushalsagar commented 11 months ago

Hmm, since there's no Javascript API, are you thinking authors will use CSSOM to insert the appropriate @view-transition (or modify an existing) rule? I suspect this might be non-trivial in complex cases given they might have to pick out the correct one manually.

Good point. That's what I was thinking but didn't realize the clunkiness of using CSSOM. One idea based on a couple of related issues: #9542 and #9595. Add a couple of attributes to the ViewTransition IDL and keep the timing for parsing the CSS opt-in here as-is.

interface ViewTransition {
  readonly attribute oldTypes;
  attribute types;
};
vmpstr commented 11 months ago

Keeping this in CSS, would that be equivalent to types overriding (option 1) with a media-like query that is able to target old types?

  • In the SPA case, oldTypes and types will start off with the same value but authors can similarly overwrite types whenever needed.

I don't know if I like that. For MPA, it feels like the reveal event changing types would be "configuring a transition". I would prefer that after transition starts (for some definition of start), we don't update types. This simplifies spec and the mental model that types name the transition, rather than be dynamic types that can change at any time

noamr commented 11 months ago

Keeping #9542 in mind, I think that (1) still makes the most sense to me, and also it's the simplest in the sense that types don't transfer across documents.

Yes, types apply to the transition, but who said that they need to stay the same throughout the whole transition? They apply to the transition at a given moment. This would also be a solution for #9424 - in MPA we'd have different types in the two documents, and ichanging viewTransition.typeList in JS would be the equivalent.

The problem that I have with anything that takes the old document into account in the new document (which is 2-5) in the new document is that it's unnecessarily opinionated about what types mean. Why not let the authors do that themselves? if they want to apply any kind of restrictions they can do it themselves based on their content rather than mandating this in the platform.

khushalsagar commented 11 months ago

Keeping this in CSS, would that be equivalent to types overriding (option 1) with a media-like query that is able to target old types?

Yea. I'm ok with leaving this one to script until we see evidence that its a common use-case. Channeling the feedback that there's too many pseudos to learn with VT already. :)

I would prefer that after transition starts (for some definition of start), we don't update types.

We could say that types is immutable after the reveal event in the MPA case and that it starts off as immutable in the SPA case. But that feels more complicated to wrap my head around. And I'm not seeing the harm in keeping it mutable. Open to other suggestions to achieve this: default is option (1) but other options can be configured easily by authors.

vmpstr commented 11 months ago

Is the use case for SPA only parity with MPA? I see MPA vt object in the reveal event as having more options for configuration, because this is the first time this page had a chance to set anything up for a transition initiated from the previous page. I don't think there is such a use-case for SPA, since the setup code and starting code is one and the same.

My suggestion is to say that ViewTransition object for SPA does not expose mutable types (it also doesn't need oldTypes).

In general, are we trying to keep the two objects the same, and I wonder if that will lead us to awkward design choices

noamr commented 11 months ago

Is the use case for SPA only parity with MPA? I see MPA vt object in the reveal event as having more options for configuration, because this is the first time this page had a chance to set anything up for a transition initiated from the previous page. I don't think there is such a use-case for SPA, since the setup code and starting code is one and the same.

The use case is what's in #9424 - having different types for "old" vs "new" capture. But mainly this is about not adding an unnecessary limitation. What's the use case for the limitation of mandating that the types are identical on both sides?

vmpstr commented 11 months ago

What's the use case for the limitation of mandating that the types are identical on both sides?

Mostly complexity of the API. There are some things we set that are immutable and need to be known at the start of the transition, like the update callback for example. I figured types would be the same here. We use types during tag discovery, for example, which runs (and correct me if I'm wrong) outside of the lifecycle.

Here are some complexities for the developer to think about:

let transition = document.startViewTransition({
  type: ["foo"],
  update: ...
});
transition.typeList.Add("bar");

Would discover tags with just "foo", but then all other stages happen with "foo" and "bar". This also prevents the implementation from doing this as a separate task, but that's a smaller concern.

transition.updateCallbackDone.then(transition.types.Add("bar"))
transition.ready.then(transition.types.Add("bar"))

also happen to hit on two opposing sides of animate tag discovery, so they have different effects.

All in all, I just want to point out that the ability to mutate these types isn't "free", there are considerations we need to make. I'm fine with it if that's the consensus, but I don't want to take it as lightly as "why not"

khushalsagar commented 11 months ago

Would discover tags with just "foo", but then all other stages happen with "foo" and "bar".

I don't think this would be the case. We look for the old tags at the very end of the next frame after startViewTransition, triggered here. So mutating types right after calling startViewTransition will ensure its applied to the style cascade when we look for names.

also happen to hit on two opposing sides of animate tag discovery, so they have different effects.

This example sounds like working as intended to me? If you updates types in updateCallbackDone promise then it would apply before we discover names in the new DOM. If you update it in the ready promise then it would apply after the pseudo-DOM has already been generated. Is there any behaviour here that sounds unexpected to you?

vmpstr commented 11 months ago

Is there any behaviour here that sounds unexpected to you?

No, but I have the benefit of knowing the implementation. My point is that it's non-trivial to know this difference.

David mentioned, and I think you agreed with the following:

My interpretation of type is that it tags a transition (e.g. type StreamToItem), rather than a page

So in that model, what role does mutable types play in SPA? We name the transition and it's the same transition on the same document, but then allow the developer to rename it? That doesn't seem to align with this model.

noamr commented 11 months ago

Is there any behaviour here that sounds unexpected to you?

No, but I have the benefit of knowing the implementation. My point is that it's non-trivial to know this difference.

David mentioned, and I think you agreed with the following:

My interpretation of type is that it tags a transition (e.g. type StreamToItem), rather than a page

So in that model, what role does mutable types play in SPA? We name the transition and it's the same transition on the same document, but then allow the developer to rename it? That doesn't seem to align with this model.

The same role classes play in regular DOM. They tag an element, and transition types tag transitions, and they're both mutable. The point of them tagging a transition is that they don't go beyond the transition's end, but it doesn't mean they can't be changed.

khushalsagar commented 11 months ago

My point is that it's non-trivial to know this difference.

I agree that it can be subtle to understand that updateCallbackDone's promise should be used instead of ready to impact names applied in the new DOM. I actually assumed authors would intuitively do it as a part of the update callback. Similar to how we expect them to do it in reveal which is equivalent to the end of the DOM update in the MPA world.

let transition;
transition = document.startViewTransition( async () => {
   updateDOM();
   transition.types.Add("bar");
}

@calinoracation for dev input on if this is an intuitive way to fix https://github.com/w3c/csswg-drafts/issues/9424.

So in that model, what role does mutable types play in SPA? We name the transition and it's the same transition on the same document, but then allow the developer to rename it? That doesn't seem to align with this model.

Yea, I forgot about https://github.com/w3c/csswg-drafts/issues/9424: "use types to select which styles apply to the old DOM and new DOM". If we agree that the use-case is worth solving then mutable types seems like an elegant way to do it and it also works out for the other use-cases we're talking about:

vmpstr commented 11 months ago

I don't think this would be the case. We look for the old tags at the very end of the next frame after startViewTransition, triggered here.

No, but I have the benefit of knowing the implementation.

Btw, the irony is not lost on me that I claim I know the implementation, but I would get at one case wrong. :) Also, if types change in rAF or in ResizeObserver or in IntersectionObserver, I don't really know if I'm expecting those tags to participate in the transition

noamr commented 11 months ago

About the options: note that (4) and (5) are the only ones that guarantee that the types are identical in both documents. I like (4) better than (5) because it's consistent with the rest of the opt-in (both sides have to match).

I don't think this would be the case. We look for the old tags at the very end of the next frame after startViewTransition, triggered here.

No, but I have the benefit of knowing the implementation.

Btw, the irony is not lost on me that I claim I know the implementation, but I would get at one case wrong. :) Also, if types change in rAF or in ResizeObserver or in IntersectionObserver, I don't really know if I'm expecting those tags to participate in the transition

It affects what's in the active-view-transition pseudo-class from that moment on. If you're already running a transition, and you have VT pseudo-elements that rely on active-view-transition, they would get affected.

This is not different from changing an attribute of the document element mid-transition and relying on that. Which is another use case for this - it lets the author orchestrate a transition by changing types in the middle of the transition using rAF (the same way you would do with changing regular DOM classes).

vmpstr commented 11 months ago

I'm just reading through #9424 and I'm not sure how the discussion here solves it. @noamr raised a point that "can't you do this in the update callback already", and it seems like there are some complexities in doing this in the callback? But here we're proposing doing exactly this. Or am I misunderstanding the complexities?

noamr commented 11 months ago

I'm just reading through #9424 and I'm not sure how the discussion here solves it. @noamr raised a point that "can't you do this in the update callback already", and it seems like there are some complexities in doing this in the callback? But here we're proposing doing exactly this. Or am I misunderstanding the complexities?

It solves it in the sense that you can change the transition behavior without modifying the DOM, by changing the transition types in JS. The solution I proposed in #9424 is to modify the DOM.

bokand commented 11 months ago

David mentioned, and I think you agreed with the following:

My interpretation of type is that it tags a transition (e.g. type StreamToItem), rather than a page So in that model, what role does mutable types play in SPA? We name the transition and it's the same transition on the same document, but then allow the developer to rename it? That doesn't seem to align with this model.

The direction this has gone is makes me think this isn't actually the model. The fact that the transition can set different types on old and new document, and now even in-flight within the same document, means the type doesn't describe the transition ("transition" in the sense of what the user sees navigating between view A and view B). It's clearer, at least to me, that the "type" describes something (styling classes) about the current document but happens to be derived from the active view transition. (indeed, didn't this whole proposal start from syntactic sugar to users manually setting a class on the documentElement?)

FWIW I'm more in the camp of (1) now that old and new documents should be entirely independent and any coordination would have to be done by the author through other means.

Btw, the irony is not lost on me that I claim I know the implementation, but I would get at one case wrong. :) Also, if types change in rAF or in ResizeObserver or in IntersectionObserver, I don't really know if I'm expecting those tags to participate in the transition

I share Vlad's concern that making the types mutable will significantly increase the conceptual burden that authors have to understand. I'm not sure I fully grok the timing of it and I'm fairly well versed in the implementation. I suspect the view transition state machine will be much more of a black box to authors.

If distinguishing new/old is the main use case, could we do that in a simpler way by having the UA automatically apply these types? e.g. outgoing view always adds -ua-old and incoming gets -ua-new

bokand commented 11 months ago

For MPA, it feels like the reveal event changing types would be "configuring a transition". I would prefer that after transition starts (for some definition of start), we don't update types. This simplifies spec and the mental model that types name the transition, rather than be dynamic types that can change at any time

If it's allowed only at pagereveal time, technically that's occurring just before the transition on the new page is started so I think spec and mental-model wise it wouldn't change much? (i.e. I don't see this as that different from "configuring" it via @view-transition rules)

noamr commented 11 months ago

I share Vlad's concern that making the types mutable will significantly increase the conceptual burden that authors have to understand. I'm not sure I fully grok the timing of it and I'm fairly well versed in the implementation. I suspect the view transition state machine will be much more of a black box to authors.

For SPA, sure. I don't think we should march forward with #9542 just yet. For MPA, this is only for developers who define different types in different documents. The simple case is the same in all the options: have the same types.

Regardless of SPA though, the way I see the other alternatives is: 2-3 (combine old and new): the combination only happens in the new document. The old document only uses own types. so the types in the old and new document would not be the same anyway. 4-5 (strictly the same): in both 4 and 5 we somehow override something the author intended to do, either by ignoring it or by skipping the whole transition. (1) doesn't have either of these issues.

vmpstr commented 11 months ago

If distinguishing new/old is the main use case, could we do that in a simpler way by having the UA automatically apply these types? e.g. outgoing view always adds -ua-old and incoming gets -ua-new

FWIW, I'm in favor of this. If UA controls when -ua-old and -ua-new are added, then the complexity of whether it's added at the right spot is an implementation detail.

I also fully realize that the complexities we're talking about here are not complexities of mutable types, but of the VT model and that's already exposed via regular DOM classes mutations: replace all mentions of "change the type" to "change the class" and you have exactly the same arguments. Obviously we allow class mutations though.

Lastly, I just want to say my push back here is only for SPA. For MPA, I support some notion of changing types at the pagereveal event. This adds the flexibility for the script to select an option in this issue: opt 1 by default, but feel free to combine it with oldTypes to get option 2, etc. Having types exposed on the VT object is one such approach. We can also expose types on the event itself, or have some other method. But again, this is "configure the vt" state in my mind.

Even for SPA though, I'd like to reiterate that I'm ok with the proposed solution (mutable types) in lieu of other solutions.

khushalsagar commented 11 months ago

FWIW, I'm in favor of this. If UA controls when -ua-old and -ua-new are added, then the complexity of whether it's added at the right spot is an implementation detail.

That's actually what I proposed initially and https://github.com/w3c/csswg-drafts/issues/9595 changed my mind. There are 3 use-cases we're trying to tackle:

  1. 9424

  2. 9595

  3. Letting authors configure types as described in options 2-5 on this issue. Assuming 1 is the default : "apply only types specified in the new document (override)".

Cases 2 and 3 above require making types mutable at least within the reveal event. We can solve case 1 with UA supplied types but conceptually it seems like case 1 is SPA equivalent of case 3 (?) : let authors configure types in the old vs new DOM different than the browser's default.

The choices I see so far are:

  1. Make types mutable within the reveal event + UA supplied types.
  2. Make types mutable within the reveal event and in the update callback? Since reveal is the MPA equivalent of the end of the update callback in SPA.
  3. Make types mutable all the time.

Between these choices I lean towards the last one. The only reason I see for 1 or 2 is that the timing is subtle so authors will get it wrong. Let's hide the timing complexity in the implementation and explicitly let them mutate the types only at the "right" spot (a.k.a reveal event).

vmpstr commented 11 months ago

Some examples for option 1:

old page:

@view-transition {
  navigation: auto;
  to: "foo.html";
  type: foo;
}
@view-transition {
  navigation: auto;
  to: "bar.html";
  type: bar;
}
html:active-view-transition(foo) #foo-target {
  view-transition-name: target
}
html:active-view-transition(bar) #bar-target {
  view-transition-name: target
}

new page:

@view-transition {
  navigation: auto;
  type: mpa
}
html:active-view-transition(mpa) #mpa-target {
  view-transition-name: target
}
html:not(:active-view-transition(mpa)) #spa-target {
  view-transition-name: target
}

Here's equivalent for if only the old document names the transition type (not one of the options in the OP): old page:

@view-transition {
  navigation: auto;
  to: "foo.html";
  type: index-to-foo;
}
@view-transition {
  navigation: auto;
  to: "bar.html";
  type: index-to-bar;
}
html:active-view-transition(index-to-foo) #foo-target {
  view-transition-name: target
}
html:active-view-transition(index-to-bar) #bar-target {
  view-transition-name: target
}

new page:

@view-transition {
  navigation: auto;
}
html:active-view-transition(index-to-foo, list-to-foo, bar-to-foo) #mpa-target {
  view-transition-name: target
}
html:not(:active-view-transition(index-to-foo, list-to-foo, bar-to-foo)) #spa-target {
  view-transition-name: target
}
calinoracation commented 11 months ago

Just a quick clarification on https://github.com/w3c/csswg-drafts/issues/9424 and knowing it all up front. I'm not necessarily sure that'll always be the case. The majority of our use cases would know the current type such as SearchResults and then we'd have a generic router implementation that at that stage would know the "to" page, ie: home-details | experience-details.

I'm very much in favor of it being a mutable option for type to be able to use the active-view-transition to differentiate ones where we are going from specific types of pages to the other.

Another use case would be someone clicks from SearchResults to Profile, we would likely use multiple "tags" on the type, both ['profile', 'account-page'] and try to do a Slide in from edge or similar type effect and want to use the :active-view-transition to do that. I think with that though my original question on https://github.com/w3c/csswg-drafts/issues/9424 would still be how do we distinguish knowing old vs new is still a question.

To re-iterate our current use-cases and strategy: We semantically map all of our elements that might participate in a View Transition. So for a <navigation-panel> we might set a view-transition-name: --navigation-panel and on a more specific surface we might have a --details-amenities-panel. At the time we initiate the call to startViewTransition we apply a class to the document that maps --navigation-panel to navigation-panel and --details-amenities-panel to details-panel. Sometimes we might have to map navigation-panel to a different component on old vs new, old might map to --navigation-panel and new might map navigation-panel to --amenities-navigation-panel.

We do at our current stage know the type of animation we want to do, but as we scale up I would assume we're going to want to use the more generic bits like I mention before: profile -> search and search -> details -> checkout -> confirmation. We'd want to define both a generic and specific set of configurations on those so we have a general navigation model, but also using the specificity mentioned be able to do something more specific like a Contextual Grow. The more basic navigation model would apply to all navigations for common types, and if a surface wants to "get fancy" they can be more specific and do the multiple tags for their specific flow, like :active-view-transition(search-to-pdp, contextual-grow). In that case we would no longer match the generic search-to-pdp, slide-in-from-edge and would instead do the more fancy version.

Here are some examples so ya'll have an idea of some of what we're trying to accomplish.

https://github.com/w3c/csswg-drafts/assets/23196205/f55ec350-dd3c-4614-a620-07de593e43e5

https://github.com/w3c/csswg-drafts/assets/23196205/8f037bc9-fdbb-43e5-bcfe-87849274fa02

https://github.com/w3c/csswg-drafts/assets/23196205/7bf1ac64-24f4-4f27-873c-599dd92342e8

noamr commented 11 months ago

Thanks @calinoracation! This is really useful feedback. My takeaway (and feel free to respond if I got the wrong idea) is that we should be flexible and un-opinionated about what types are for. They can be mixed and match for different purposes. e.g.

noamr commented 8 months ago

Proposed resolution from internal sync: by default types are encapsulated to the document that defined them. We can add semantics for using old-document types in the new one in the future. (In essence, leave the spec as is in this regard, and add an informative note to this effect).

css-meeting-bot commented 7 months ago

The CSS Working Group just discussed [css-view-transitions-2] Behavior of mismatching types between old and new document, and agreed to the following:

The full IRC log of that discussion <RRSAgent> I have made the request to generate https://www.w3.org/2024/03/13-css-minutes.html fantasai
<TabAtkins> noamr: proposed resolution is to close no change, but wanted to clarify what that means
<TabAtkins> noamr: for cross-doc VTs, right now you can specify a VT at-rule, and say which types will be activated for the VT
<TabAtkins> noamr: On the old and new doc, you're not guaranteed to have the same list of types
<TabAtkins> noamr: Right now, it's written and implemented that it doesn't matter; if the old doc has type A and new has type B, it's up to the author to deal with this.
<TabAtkins> noamr: CSS doesn't do anything to reconcile the lists
<TabAtkins> noamr: Given our previous resolution, if you change the types in the old doc before you leave, it'll affect which elements are captured, but then the type will be re-read from the new doc and override it.
<TabAtkins> noamr: Whatever happens with the types stays in the doc where it's defined.
<TabAtkins> q+
<TabAtkins> noamr: so we think this is the most flexible and extensible pattern right now
<TabAtkins> noamr: Just want to add an informative note to be careful about changing types
<bramus> scribe+
<fantasai> scribe+
<bramus> TabAtkins: sounded like if type smismatch you en dup running vt with types from new doc
<bramus> noamr: yes, but when you capture old state you use types from old doc
<bramus> TabAtkins: ok, makes sense
<TabAtkins> astearns: proposed resolution, add an informative note but otherwise close no change
<TabAtkins> RESOLVED: Close no change (but add a note with some guidance)