-
Notifications
You must be signed in to change notification settings - Fork 666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[css-view-transitions-2] Behavior of mismatching types between old and new document #9526
Comments
Discussion summary for the different options:
|
@ydaniv @jakearchibald @bramus for thoughts on what is preferable here from a developer perspective |
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 So there's an additional option to consider:
This means authors don't have to do from-URL matching to deduce an incoming type and also corresponds to the SPA 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) { ... } |
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 Btw, we might need to solve the use-case you mentioned (if it turns out to be common) regardless of this issue:
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 |
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.
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. |
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?
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 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. |
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 |
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 |
+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.
Hmm, since there's no Javascript API, are you thinking authors will use CSSOM to insert the appropriate additional tangential replies
Right, forgot the fetch can (and should) happen before calling
It seems like authors will already have to do a fair bit of coordination between pages since they'll have to make sure |
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.
|
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?
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 |
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 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. |
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. :)
We could say that |
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 |
The use case is what's in #9424 - having different types for "old" vs "new" capture. |
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:
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.
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" |
I don't think this would be the case. We look for the old tags at the very end of the next frame after
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? |
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:
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. |
I agree that it can be subtle to understand that let transition;
transition = document.startViewTransition( async () => {
updateDOM();
transition.types.Add("bar");
} @calinoracation for dev input on if this is an intuitive way to fix #9424.
Yea, I forgot about #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:
|
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 |
About the options: note that (4) and (5) are the only ones that guarantee that the types are identical in both documents.
It affects what's in the This is not different from changing an attribute of the document element mid-transition and relying on that. |
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. |
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.
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 |
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 |
For SPA, sure. I don't think we should march forward with #9542 just yet. Regardless of SPA though, the way I see the other alternatives is: |
FWIW, I'm in favor of this. If UA controls when 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 Even for SPA though, I'd like to reiterate that I'm ok with the proposed solution (mutable types) in lieu of other solutions. |
That's actually what I proposed initially and #9595 changed my mind. There are 3 use-cases we're trying to tackle:
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:
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). |
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): @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
} |
Just a quick clarification on #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 I'm very much in favor of it being a mutable option for Another use case would be someone clicks from To re-iterate our current use-cases and strategy: 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: Here are some examples so ya'll have an idea of some of what we're trying to accomplish. Gallery_Photo_View_Grow_03.mp4Panel.Navigation_01.mp4Photo_Tour_Desktop_01.mp4 |
Thanks @calinoracation! This is really useful feedback.
|
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). |
The CSS Working Group just discussed
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) |
- Make types mutable using ViewTransition.typeList w3c#9542 (comment) - Separate `:active-view-transition` and `:active-view-transition-type` See w3c#9972 (comment) - Add notes about types being bound to one document See w3c#9526 (comment) Closes w3c#9972 Closes w3c#9542 Closes w3c#9526 Closes w3c#9626
As per #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:
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.
The text was updated successfully, but these errors were encountered: