w3c / csswg-drafts

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

[css-view-transitions-2] Declarative opt-in for cross-document navigations #8048

Closed jakearchibald closed 9 months ago

jakearchibald commented 1 year ago

Cross-document transitions need some way for both the old & new page to opt-in to a transition.

The SPA API uses JS for this, but it would be nice if cross-document transitions didn't depend on JS.

It feels like this should be in CSS, so it can be influenced by media queries, particularly prefers-reduced-motion.

The tricky part here is it's a "global" setting, so it can't be a CSS property.

Proposal:

@cross-document-transition allow;

@media (prefers-reduced-motion) {
  @cross-document-transition deny;
}

However, I'm just making stuff up here. Is there any prior art here for setting 'global' things?

khushalsagar commented 1 year ago

The current proposed API is using a meta tag for this, similar to other Document global settings like viewport meta tag.

But doing this in CSS would be better, for the reasons Jake mentioned like conditionally doing this based on prefers-reduced-motion.

LeaVerou commented 1 year ago

Would it be useful for this to be more fine-grained? I.e. allow transitions from certain pages/domains but not others?

jakearchibald commented 1 year ago

Yes, that absolutely should be a goal. My preference would be to allow pages to declare themselves to be of a particular type, which is just an ident. Then you can express that you're happy to perform a transition from "article" to "profile" or whatever.

Although, if we go with URLs, https://wicg.github.io/urlpattern/ will be handy.

jakearchibald commented 1 year ago

We chatted with @tabatkins and he's happy with the general shape of the syntax here.

We would need to define what the CSSOM looks like.

It might be worth making @cross-document-transition allow; a shorthand for something like:

@cross-document-transition {
  state: allow;
}

…which would allow us to add other descriptors in future.

In the new page, the opt-in would need to be present before first render. This works out, since CSS in the head is render-blocking.

noamr commented 1 year ago

Perhaps it can be a pseudo-state of the ::view-transition rather than a new top-level rule? e.g. ::view-transition:cross-document, and then you can disable the transition in CSS yourself

::root:has(::view-transition:cross-document) {
  view-transition-name: "none" !important;
}
bramus commented 1 year ago

Not a fan of this :has() approach. Feels rather hacky.

Iterating on the at-rule – which makes more sense to me – here’s two variants on that:

  1. If more configuration things like this are planned over time (e.g. global custom properties), maybe instead of a new at-rule @cross-document-transition, it could be a descriptor (property?) in @document? I.e. @document { cross-document-transition: allow; }

  2. Maybe, instead of a new at-rule, it would be treated as a media condition? I.e. @media (prefers-reduced-motion and cross-document-transition: allow) { … }

bramus commented 1 year ago

Oh, wait … that second one doesn’t make sense on its own, as that one doesn’t set anything – it only detects stuff 🤦‍♂️

noamr commented 1 year ago

The :has thing is just part of an example, It can also be

@media (prefers-reduced-motion) {
  ::view-transition:cross-document {
    display: none;
  }
}

This allows more fine-grained control over how things behave if this is a cross-document transition rather than totally disabling the transition.

For totally disabling the transitions based on media queries, I'd suggest adding a media attribute to the meta tag, like we have for preloads, or as a parameter to the content attribute, e.g. <meta name="view-transition" content="same-origin;media=(prefers-reduce-motion:no-preference)"> or splitting the opt-in to several meta-tags:

<meta name="view-transition:referrer" content="same-origin">
<meta name="view-transition:media" content="(prefers-reduce-motion:no-preference)">

I'm offering these alternatives because I don't see how using CSS to declare something that's completely global plays to what CSS is good at, and it's also not very consistent with other things CSS does.

jakearchibald commented 1 year ago

@font-face and @property are also global declarations in CSS.

noamr commented 1 year ago

@font-face and @property are also global declarations in CSS.

They give meaning to all kind of names, but they don't affect the style directly.

noamr commented 1 year ago

Thinking about this again.

I see the problem the opt-in tries to solve as mismatch between same-origin transitions. It's not a security problem, but rather an issue of unintended visual effects by having e.g. an SPA transition in both documents lead to a spurious MPA transition between the two documents.

IMO any kind of boolean opt-in doesn't solve this, either in HTML or CSS. You could opt-in to some MPA transitions within your origin, and still reach a case where some pages are uncoordinated with you and cause an unintended MPA transition.

Instead, I propose something a bit more flexible and : the :root of both documents has to share some sort of name. It can be the view-transition-name (perhaps if it's not "root", but that might be a bit too implicit), or a new property like a page-transition-name. Only if the names match, the view transition would be enabled.

The purpose in doing something like that is to ensure a match in a flexible way, that's still ergonomic and consistent with how SPA navigations declare matching.

khushalsagar commented 1 year ago

Instead, I propose something a bit more flexible and : the :root of both documents has to share some sort of name. It can be the view-transition-name (perhaps if it's not "root", but that might be a bit too implicit), or a new property like a page-transition-name. Only if the names match, the view transition would be enabled.

The problem with this is that UA CSS implicitly adds a view-transition-name to the root element here. So we can't rely on authors setting it as an opt-in. Also, we can't ask for the root of both docs sharing the same name, some transitions require the root of one document to match with a DOM element on the second document, example.

If we're introducing something new (like page-transition-name), then we should avoid overloading it with the same purpose that view-transition-name is for. We also want the opt-in syntax to be extendible to declaratively opt-in to a subset of navigations (same-origin, same-site, URL regex, same-document) which doesn't fit well with reusing what view-transition-name is for.

noamr commented 1 year ago

Instead, I propose something a bit more flexible and : the :root of both documents has to share some sort of name.

It can be the view-transition-name (perhaps if it's not "root", but that might be a bit too implicit), or a new property like a page-transition-name. Only if the names match, the view transition would be enabled.

The problem with this is that UA CSS implicitly adds a view-transition-name to the root element here. So we can't rely on authors setting it as an opt-in. Also, we can't ask for the root of both docs sharing the same name, some transitions require the root of one document to match with a DOM element on the second document, example.

If we're introducing something new (like page-transition-name), then we should avoid overloading it with the same purpose that view-transition-name is for. We also want the opt-in syntax to be extendible to declaratively opt-in to a subset of navigations (same-origin, same-site, URL regex, same-document) which doesn't fit well with reusing what view-transition-name is for.

Agreed, but we can flesh this out. The main concept is that this opt in should be by name matching rather than Boolean.

Regarding cross-origin, I think it's a much bigger discussion. This might end up being something with security implications, in which case we would want to have a meta thing, or add a new css property. I don't think that should stop us from having a flexible opt-in for same-origin transitions.

khushalsagar commented 1 year ago

Regarding cross-origin, I think it's a much bigger discussion.

I don't think we'll be able to ship this for all cross-origin...ever. I specifically meant the same-site case, the security/privacy implications there are not as strict as different domains?

The main concept is that this opt in should be by name matching rather than Boolean.

The fundamental issue with either, a boolean or name matching, is that the syntax is not extendible to even same-origin cases like a subset of pages. For example:

/ * Opt-in for all same-origin navigations */
@view-transition same-origin;

/* Opt-in for same-origin navigations only to URLs which match this pattern */
@view-transition urlPattern(...);

I don't see how name matching or a boolean can be extended to cover this use-case. We've talked about an option like a media query which matches based on old/new URL that potentially could :

@media (old-url: urlPattern(...)) {
   :root { view-transition-name: foo }
}

But that's harder to do since now we have to define the exact time in the old/new Document's lifecycle when this media query activates. I think it's the same problem as defining events to be dispatched on the 2 Documents that you've thought out.

noamr commented 1 year ago

Regarding cross-origin, I think it's a much bigger discussion.

I don't think we'll be able to ship this for all cross-origin...ever.

I agree :)

I specifically meant the same-site case, the security/privacy implications there are not as strict as different domains?

Yup, but also in the same-site cross-origin case there are some security implications that we don't have at all in same-origin.

The main concept is that this opt in should be by name matching rather than Boolean.

The fundamental issue with either, a boolean or name matching, is that the syntax is not extendible to even same-origin cases like a subset of pages. For example:

/ * Opt-in for all same-origin navigations */
@view-transition same-origin;

/* Opt-in for same-origin navigations only to URLs which match this pattern */
@view-transition urlPattern(...);

The way I think about it is that there are two issues, and they are totally separate:

  1. making sure that cross-document view transitions are intentional and the internal transition names match what the author has meant
  2. accepting or rejecting a view transition from a same-site cross-origin referrer.

For (1), I think having something flexible like matching a root name/namespace attribute in CSS for the transition, without a meta tag or any boolean opt-in, would work really well and would be consistent with the spirit of how SPA transitions work.

For (2), to solve this in CSS you'd have to coerce CSS to do several things that it's not used to doing... for example, you might have @view-transition same-origin in your inline css, and then include some cross-origin stylesheet that would override it with @view-transition same-site or @view-transition urlpattern(...), which would require fiddling with cascading rules. I think we would have to solve (2) on the HTML level, or even CSP, and my main concern there would be that it would become a side-channel for cross-origin pages to pass privacy-related information to each other, which an opt-in wouldn't solve.

jakearchibald commented 1 year ago

@noamr

@font-face and @property are also global declarations in CSS.

They give meaning to all kind of names, but they don't affect the style directly.

The proposed opt-in doesn't affect style either. It opts into a view transition on cross-document navigation (similar to how calling startViewTransition is the opt-in for SPA).

an issue of unintended visual effects by having e.g. an SPA transition in both documents lead to a spurious MPA transition between the two documents.

Can you go into more detail about that? See https://github.com/w3c/csswg-drafts/issues/8677 - I don't think it'll be unusual for the same animation declarations to be used in both SPA and MPA transitions.

There's a doc internally that compares SPA transition features with MPA, where a lot of this is discussed (although it should be across various issues now), @khushalsagar can give you access.

IMO any kind of boolean opt-in doesn't solve this

This proposal allows for more than a simple boolean https://github.com/w3c/csswg-drafts/issues/8048#issuecomment-1495777539. Also, the conditionals can be complex thanks to media queries (see the other issues on url matching).

Instead, I propose something a bit more flexible and : the :root of both documents has to share some sort of name. It can be the view-transition-name (perhaps if it's not "root", but that might be a bit too implicit), or a new property like a page-transition-name. Only if the names match, the view transition would be enabled.

Two problems with this: You have to do special hard-to-reason-about things to use the same transitions for both SPA and MPA, and it creates a CSS property that only works on one element (or a property that works very differently on one particular element). That's the whole reason a global switch was proposed rather than something per-element.

  • making sure that cross-document view transitions are intentional and the internal transition names match what the author has meant

I think a key to this is ensuring that SPA and MPA transitions across the same two pages are as similar as possible. The difference being that one opts in with JS (by calling startViewTransition), and the other opts-in via some other simple means.

to solve this in CSS you'd have to coerce CSS to do several things that it's not used to doing... for example, you might have @view-transition same-origin in your inline css, and then include some cross-origin stylesheet that would override it with @view-transition same-site or @view-transition urlpattern(...), which would require fiddling with cascading rules.

This is already an issue with @keyframes, @font-face, @property etc etc, so we can follow the same rules.

I think we would have to solve (2) on the HTML level, or even CSP, and my main concern there would be that it would become a side-channel for cross-origin pages to pass privacy-related information to each other, which an opt-in wouldn't solve.

Can you provide details of the attack you're trying to prevent? Third party CSS isn't safe.

noamr commented 1 year ago

@noamr

@font-face and @property are also global declarations in CSS.

They give meaning to all kind of names, but they don't affect the style directly.

The proposed opt-in doesn't affect style either. It opts into a view transition on navigation (like calling startViewTransition).

an issue of unintended visual effects by having e.g. an SPA transition in both documents lead to a spurious MPA transition between the two documents.

Can you go into more detail about that? See #8677 - I don't think it'll be unusual for the same animation declarations to be used in both SPA and MPA transitions.

Let's say example.com/bank and example.com/insurance both work on their SPA transitions independently, and then find that going from one to the other created an unintended MPA transition with random weird associations between elements that happen to have the same transition names. Let's say we go with a "boolean with future extensions" option. That would solve the initial problem if they're both SPAs.

But what if they're MPAs? example.com/bank/transaction has a transition with example.com/bank/summary, and example.com/insurance/view has a transition with example.com/insurance/home. Suddenly the transitions from bank<->insurance would work or none would work. A boolean is an all-or-nothing for same-origin MPA transitions, which is sometimes what you want and sometimes it isn't.

We can solve this either by a URL pattern, or by a namespace, which is the same thing in a way, but there's something more cleaner with names that's consistent with the existing view-transition-name.

There's a doc internally that compares SPA transition features with MPA, where a lot of this is discussed (although it should be across various issues now), @khushalsagar can give you access.

IMO any kind of boolean opt-in doesn't solve this

This proposal allows for more than a simple boolean #8048 (comment). Also, the conditionals can be complex thanks to media queries (see the other issues on url matching).

It's a simple boolean with "we might add more things to it in the future". I'm saying, let's use name-matching now instead of "boolean plus future unknown things".

Instead, I propose something a bit more flexible and : the :root of both documents has to share some sort of name. It can be the view-transition-name (perhaps if it's not "root", but that might be a bit too implicit), or a new property like a page-transition-name. Only if the names match, the view transition would be enabled.

Two problems with this: You have to do special hard-to-reason-about things to use the same transitions for both SPA and MPA

I don't understand this problem. You have to give your whole transition a global name/namespace, and make sure both documents know about it. This is similar to giving elements that transition to each other matching names. Where is the "hard to reason" part?

it creates a CSS property that only works on one element (or a property that works very differently on one particular element). That's the whole reason a global switch was proposed rather than something per-element.

It creates one element in the same way ::view-transition is a pseudo-element of only one element. If in the future we enable several view transitions to work simultaneously, this can be more flexible.

  • making sure that cross-document view transitions are intentional and the internal transition names match what the author has meant

I think a key to this is ensuring that SPA and MPA transitions across the same two pages are as similar as possible. The difference being that one opts in with JS (by calling startViewTransition), and the other opts-in via some other simple means.

to solve this in CSS you'd have to coerce CSS to do several things that it's not used to doing... for example, you might have @view-transition same-origin in your inline css, and then include some cross-origin stylesheet that would override it with @view-transition same-site or @view-transition urlpattern(...), which would require fiddling with cascading rules.

This is already an issue with @Keyframes, @font-face, @Property etc etc, so we can follow the same rules.

All of these are kind of "definitions" - having a named thing (font, animation, property) and declaring what it does. For example, you can't put those inside a media-query.

I think we would have to solve (2) on the HTML level, or even CSP, and my main concern there would be that it would become a side-channel for cross-origin pages to pass privacy-related information to each other, which an opt-in wouldn't solve.

Can you provide details of the attack you're trying to prevent? Third party CSS isn't safe.

The whole cross-origin problem has not been discussed yet so this is more of an initial thought. The problem space is of passing user information in a side-channel, like a make-shift same-site cookie that's not guarded by the regular cookie protections. My main point is that cross-origin is a different problem space than maintaining author intention when switching document.

One thing I don't understand is - if this is not about maintaining author intention, why do we need an opt in for same-origin in the first place?

jakearchibald commented 1 year ago

It's a simple boolean with "we might add more things to it in the future". I'm saying, let's use name-matching now instead of "boolean plus future unknown things".

Media queries already exist, so they're not some magic future thing we don't know about. Also, I think a way to change transitions depending on the incoming and outgoing page should be part of the initial launch of the MPA feature.

With SPA, we ask developers to use class names on the root. The MPA solution should be similar to that, or the MPA solution should be available to SPA too. Note that these media queries don't just enable opt-in/out, they also allow for different transitions to be defined depending on the source and destination. The name-matching part doesn't allow for that, and I don't see what use it would be when there are more extensible solutions on the table.

Have you seen https://github.com/w3c/csswg-drafts/issues/8679? It seems similar to your name-matching proposal, but doesn't require a CSS property that only works on one element.

Two problems with this: You have to do special hard-to-reason-about things to use the same transitions for both SPA and MPA

I don't understand this problem. You have to give your whole transition a global name/namespace, and make sure both documents know about it.

It's global as much as the rest of CSS is - you can use media queries to say when particular rules apply.

This is similar to giving elements that transition to each other matching names. Where is the "hard to reason" part?

You're proposing a CSS property that will exist on every element, but only ever means something on one element. That seems confusing. Or, you're proposing that view-transition-name has an extra feature attached for the root element only, and for MPA transitions only, and means you need to do something special if you want to share transitions between your MPA and SPA (by forcing a name other than root for the root). This also seems confusing.

it creates a CSS property that only works on one element (or a property that works very differently on one particular element). That's the whole reason a global switch was proposed rather than something per-element.

It creates one element in the same way ::view-transition is a pseudo-element of only one element. If in the future we enable several view transitions to work simultaneously, this can be more flexible.

Maybe I'm misunderstanding what you're proposing, because that doesn't seem like a meaningful comparison.

I thought you were proposing a CSS property, page-transition-name, that would exist on every element (because that's how CSS properties work), but only ever apply to the root element.

::view-transition was designed from the start to be on other elements in future. It's one of the key reasons we went for pseudo-elements rather than shadow DOM. I don't think the simultaneous-transitions argument applies for page transitions, as there can only be one page transition at once due to the scope of navigations.

This is already an issue with @Keyframes, @font-face, @Property etc etc, so we can follow the same rules.

All of these are kind of "definitions" - having a named thing (font, animation, property) and declaring what it does. For example, you can't put those inside a media-query.

Sure, but I don't think that difference matters in this context. We still have an order to apply the rules from those other features.

One thing I don't understand is - if this is not about maintaining author intention, why do we need an opt in for same-origin in the first place?

What gave you the impression that author intention wasn't being maintained? That's the whole reason for the opt-in.

With SPA, the developer opts in via startViewTransition. They can make this opt-in conditionally with the knowledge of the before 'page' and after 'page', along with user preference. They use class names to customise the transition for those specific states.

With MPA, the developer opts in via the opt-in proposed in this issue. Via media queries they can make this opt-in conditionally. With https://github.com/w3c/csswg-drafts/issues/8683, they can make the before and after page part of this condition. The same feature allows them to customise the transition for specific states. Existing media queries let them react to user preference.

If https://github.com/w3c/csswg-drafts/issues/8683 is enabled for SPA too, developers will be able to use the same animation styles for both SPA and MPA, which is one of the key goals.

noamr commented 1 year ago

It's a simple boolean with "we might add more things to it in the future". I'm saying, let's use name-matching now instead of "boolean plus future unknown things".

Media queries already exist, so they're not some magic future thing we don't know about. Also, I think a way to change transitions depending on the incoming and outgoing page should be part of the initial launch of the MPA feature.

With SPA, we ask developers to use class names on the root. The MPA solution should be similar to that, or the MPA solution should be available to SPA too. Note that these media queries don't just enable opt-in/out, they also allow for different transitions to be defined depending on the source and destination. The name-matching part doesn't allow for that, and I don't see what use it would be when there are more extensible solutions on the table.

Have you seen https://github.com/w3c/csswg-drafts/issues/8679? It seems similar to your name-matching proposal, but doesn't require a CSS property that only works on one element.

Two problems with this: You have to do special hard-to-reason-about things to use the same transitions for both SPA and MPA

I don't understand this problem. You have to give your whole transition a global name/namespace, and make sure both documents know about it.

It's global as much as the rest of CSS is - you can use media queries to say when particular rules apply.

This is similar to giving elements that transition to each other matching names. Where is the "hard to reason" part?

You're proposing a CSS property that will exist on every element, but only ever means something on one element. That seems confusing. Or, you're proposing that view-transition-name has an extra feature attached for the root element only, and for MPA transitions only, and means you need to do something special if you want to share transitions between your MPA and SPA (by forcing a name other than root for the root). This also seems confusing.

it creates a CSS property that only works on one element (or a property that works very differently on one particular element). That's the whole reason a global switch was proposed rather than something per-element.

It creates one element in the same way ::view-transition is a pseudo-element of only one element. If in the future we enable several view transitions to work simultaneously, this can be more flexible.

Maybe I'm misunderstanding what you're proposing, because that doesn't seem like a meaningful comparison.

I thought you were proposing a CSS property, page-transition-name, that would exist on every element (because that's how CSS properties work), but only ever apply to the root element.

::view-transition was designed from the start to be on other elements in future. It's one of the key reasons we went for pseudo-elements rather than shadow DOM. I don't think the simultaneous-transitions argument applies for page transitions, as there can only be one page transition at once due to the scope of navigations.

This is already an issue with @Keyframes, @font-face, @Property etc etc, so we can follow the same rules.

All of these are kind of "definitions" - having a named thing (font, animation, property) and declaring what it does. For example, you can't put those inside a media-query.

Sure, but I don't think that difference matters in this context. We still have an order to apply the rules from those other features.

One thing I don't understand is - if this is not about maintaining author intention, why do we need an opt in for same-origin in the first place?

What gave you the impression that author intention wasn't being maintained? That's the whole reason for the opt-in.

With SPA, the developer opts in via startViewTransition. They can make this opt-in conditionally with the knowledge of the before 'page' and after 'page', along with user preference. They use class names to customise the transition for those specific states.

With MPA, the developer opts in via the opt-in proposed in this issue. Via media queries they can make this opt-in conditionally. With https://github.com/w3c/csswg-drafts/issues/8683, they can make the before and after page part of this condition. The same feature allows them to customise the transition for specific states. Existing media queries let them react to user preference.

If https://github.com/w3c/csswg-drafts/issues/8683 is enabled for SPA too, developers will be able to use the same animation styles for both SPA and MPA, which is one of the key goals.

Ok I think the main difference here is about whether it's a root property or a bespoke @ rule. I can see pros and cons to both but it's not my main issue to begin with...

I can see how this could be something like:

@allow-view-transitions: none | same-document | same-site | urlpattern(...) 

With same-document as default and perhaps extendable to names/versions/separate to/from in the future. This would also make it less of an MPA-specific feature.

jakearchibald commented 1 year ago

Hmm, would @allow-view-transitions: same-document trigger view transitions for same-document navigations? That's been discussed before, and although there are maybe edge cases where you might want that, generally you don't want that, because you can already add things like smooth scrolling.

noamr commented 1 year ago

Hmm, would @allow-view-transitions: same-document trigger view transitions for same-document navigations? That's been discussed before, and although there are maybe edge cases where you might want that, generally you don't want that, because you can already add things like smooth scrolling.

No, @allow-view-transitions: same-document would be the default, and @allow-view-transitions: none would be equivalent to ::view-transitions { display: none }.

Anyway, I'm working on some more comprehensive thing around the opt-in and added CSS (it's currently spread across 3 issues).

noamr commented 1 year ago

Regarding naming, I want to find something here that would be extendable to same-document declarative transitions (#8300) and to same-site cross-origin, if we ever want to do either.

Having an allow/deny thing would make that verbose, but perhaps OK?:

@cross-document-view-transtions: allow;
@cross-origin-view-transitions: allow;
@same-document-view-transtions: allow;

I think the essence of what we're "allowing" here is automatic view transitions, as in, transitions that occur without calling startViewTransition programatically.

So perhaps:

@auto-view-transitions {
  same-origin: allow;
  same-site: allow;
  same-document: allow;
}

or something like:

@auto-view-transitions: same-origin same-document same-site;
jakearchibald commented 1 year ago

@allow-view-transitions: same-document would be the default, and @allow-view-transitions: none would be equivalent to ::view-transitions { display: none }.

Bearing in mind that ::view-transitions { display: none } only hides transitions, it doesn't prevent them, do you really mean that?

@auto-view-transitions {
  same-origin: allow;
  same-site: allow;
  same-document: allow;
}

or something like:

@auto-view-transitions: same-origin same-document same-site;

This feels generally in the right direction. CSSWG folks who are across multiple specs will be able to help with the naming & syntax.

It might be worth thinking about what a 'manual' cross-document view transition would look like. As in, some kind of just-in-time opt-in. And, whether this syntax makes sense in that case, particularly in the new page that drives the animation.

noamr commented 1 year ago

@allow-view-transitions: same-document would be the default, and @allow-view-transitions: none would be equivalent to ::view-transitions { display: none }.

Bearing in mind that ::view-transitions { display: none } only hides transitions, it doesn't prevent them, do you really mean that?

It skips the animation, doesn't just hide it.


@auto-view-transitions {

  same-origin: allow;

  same-site: allow;

  same-document: allow;

}

or something like:


@auto-view-transitions: same-origin same-document same-site;

This feels generally in the right direction. CSSWG folks who are across multiple specs will be able to help with the naming & syntax.

It might be worth thinking about what a 'manual' cross-document view transition would look like. As in, some kind of just-in-time opt-in. And, whether this syntax makes sense in that case, particularly in the new page that drives the animation.

jakearchibald commented 1 year ago

It skips the animation, doesn't just hide it.

It doesn't. All the capturing still happens. transition.ready still fulfils. If we ever exposed a boolean to indicate wether the transition was skipped, or played to completion, it would say it played to completion, unless this was weirdly special-cased, which it currently isn't.

An opt-out would not perform a capture. transition.ready would reject, since the transition doesn't reach the 'ready' state etc etc.

noamr commented 1 year ago

It skips the animation, doesn't just hide it.

It doesn't. All the capturing still happens. transition.ready still fulfils. If we ever exposed a boolean to indicate wether the transition was skipped, or played to completion, it would say it played to completion, unless this was weirdly special-cased, which it currently isn't.

An opt-out would not perform a capture. transition.ready would reject, since the transition doesn't reach the 'ready' state etc etc.

I meant that the animation is skipped, not exactly "hidden". But anyway, I don't want to nitpick on this, not pursuing this way of opting out.

jakearchibald commented 1 year ago

Fwiw, I'm not nitpicking, since it has a large impact on the behaviour of the API you're designing.

noamr commented 1 year ago

Fwiw, I'm not nitpicking, since it has a large impact on the behaviour of the API you're designing.

I was referring to my own nitpicking :)

bramus commented 1 year ago

I like the suggestion of a few configuration switches through some at-rule.

Looking a bit broader than just View Transitions, I’m starting to wonder if we need a @config at-rule to – in the future – also include other configuration data such as preferred scrollbar types, viewport unit resize behaviors, etc. view-transitions could be a descriptor in this at-rule.

noamr commented 12 months ago

For the purpose of the F2F, summarizing current thinking:

To maintain consistency with other CSS features, the idea is to have a new rule, currently called @auto-view-transitions, which can contain declarations that indicate to the UA the conditions in which view-transitions are to be automatically/declaratively performed (as opposed to programmatically as in vt-1).

Initially, this rule would only have one possible declaration: same-origin: enabled | disabled (disabled by default). If enabled in both sides, a same-origin navigation would trigger an automatic view transition.

See current spec draft.

Note that this value is read at exactly one point in both documents:

In the future we would want this opt-in to respond to media queries, e.g. for prefers-reduced-motion.

Main purpose of the discussion is:

jakearchibald commented 12 months ago

Is the intent still to allow this rule to be nested within a media query? That seems important to avoid opting in when prefers-reduced-motion. There needs to be some rule to decide which value 'wins' if there are multiple definitions. Source order is one option, but it's worth thinking about how this interacts with @layer.

noamr commented 12 months ago

Is the intent still to allow this rule to be nested within a media query? That seems important to avoid opting in when prefers-reduced-motion. There needs to be some rule to decide which value 'wins' if there are multiple definitions. Source order is one option, but it's worth thinking about how this interacts with @layer.

Yes, the intent is to either nest it in media-queries or support media matching with a different syntax that supports some specificity as you said. But it's not there in the first draft, suggesting to add that incrementally.

khushalsagar commented 12 months ago

There needs to be some rule to decide which value 'wins' if there are multiple definitions. Source order is one option, but it's worth thinking about how this interacts with @layer.

+1 to something other than source order. Ideally I would've liked something analogous to specificity in CSS selectors but media queries don't work that way.

bramus commented 12 months ago

Source order is one option, but it's worth thinking about how this interacts with @layer.

My jetlag might be taking the upper hand here, but I don’t immediately see that one would want to use layers to tweak this behavior. Do you have a specific example in mind? If so, what would be your suggestion?

Typically at-rules that “define stuff” – e.g. @property – get hoisted up to the global level, with the last declaration winning.

jakearchibald commented 12 months ago

Right, but we want these to work in media queries, and media queries can exist in @layer (I think??)

css-meeting-bot commented 12 months ago

The CSS Working Group just discussed [css-view-transitions-2] Declarative opt-in for cross-document navigations, and agreed to the following:

The full IRC log of that discussion <TabAtkins> noamr: VT2 adds cross-document transitions
<TabAtkins> noamr: This involves two added capabilities to same-doc transitions
<TabAtkins> noamr: One is the declarative opt-in, which is this issue
<TabAtkins> noamr: So instead of starting the transitio using JS, both transitions need to have an opt-in in CSS
<emilio> q+
<TabAtkins> noamr: If both documents approve, the transition happens, as if we'd called a startViewTransition()
<noamr> https://drafts.csswg.org/css-view-transitions-2/
<khush> q+
<noamr> https://github.com/w3c/csswg-drafts/issues/8048#issuecomment-1637806513
<TabAtkins> noamr: here's the spec, and heres the issue comment
<TabAtkins> noamr: So the current idea is to have a new at-rule, currnetly called @auto-view-transition
<TabAtkins> noamr: At first we'd have only one property in it, which is 'same-origin: enable | disable'
<TabAtkins> noamr: while we have only one value to do right now, we expect to need to extend this in the future so we're going with a declaration design
<TabAtkins> noamr: Welcome bikeshedding
<TabAtkins> noamr: In the future we want to be able to nest this inside of MQ/conditionals
<TabAtkins> noamr: So you can, for example, guard it by (prefers-reduced-motion)
<TabAtkins> noamr: also might want an opt-in to disable same-document view transitions
<TabAtkins> noamr: At the moment I think we could probably only want to ahve this affect automatic VTs, let JS deal with the rest
<TabAtkins> emilio: This is the kind of thing we've generally fixed via meta tags, like color-scheme, viewport, etc
<TabAtkins> emilio: it seems liek the mai point of putting this in CSS is to use MQs, but <meta media> exists
<TabAtkins> emilio: Has that been considered and discarded? If so, why?
<TabAtkins> noamr: It has been.
<TabAtkins> noamr: the original proposal was meta tags
<TabAtkins> noamr: it's mainly around keeping the styling all in css
<TabAtkins> q+
<TabAtkins> q++
<TabAtkins> qq+
<TabAtkins> ack +
<fantasai> TabAtkins: Part of the reason why it's not quite as necessary in meta
<bramus> q+
<astearns> ack TabAtkins
<Zakim> TabAtkins, you wanted to react to fantasai
<fantasai> TabAtkins: we do that for other things so you have it as early in loading as possible
<fantasai> TabAtkins: given you need enough styling to render the page, delaying the permission for cross-document transition doesn't hurt you at all
<fantasai> TabAtkins: you don't need to be early
<fantasai> TabAtkins: you might have to hold onto the first page a little longer
<fantasai> TabAtkins: but waiting until all style comes in to turn on is fine, because you need all that style to render to create a VT anyway
<TabAtkins> SebastianZ: having such an at-rule would also mean having it high in the stylesheet
<emilio> q+
<fantasai> SebastianZ: it would need to be near the top near @import, right
<fantasai> TabAtkins: you have to read the whole stylesheet to render the VT anyway, so doesn't matter where you put it
<fantasai> astearns: [missed something]
<TabAtkins> khush: from impl perspective, right now we want to avoid having to parse stuff in the new page before capturing new
<TabAtkins> khush: right now, since both are fro mthe same site, if the start page opts in it's likely the end page will also opt in so we eagerly capture the start page's elements
<TabAtkins> khush: there's also some concept in HTML about render-blocking stylesheets, hopefully we can get authors to get the rule in those stylesheets
<astearns> s/[missed something]/if the first page opts in, then we are spending time reading CSS for the second page even if it is opt-out
<TabAtkins> khush: We might be holding onto the start page a little longer, but the chance of discarding is pretty low. We think it's worth going for what's most ergonomic for webdevs
<astearns> q?
<TabAtkins> emilio: Do we want to make this work eventually for cross-origin?
<TabAtkins> emilio: If both pages opt in, it seems like it could work... (barring security constraints)
<TabAtkins> ack TabAtkins
<TabAtkins> khush: We have some use-cases right now for same-site, but not strong ones for cross-site
<TabAtkins> khush: and similarly in same-site it's likely both will opt in
<TabAtkins> vmpstr: And still, doing a little bit of extra work that's potentially wasted, it doesn't really affect how much of the end page you have to parse before you know.
<TabAtkins> vmpstr: Even if it was in meta tags we'd still have to eagerly capture the old page
<TabAtkins> emilio: so you have to wait for things to load and you have to delay rendering the new page until things are loaded
<TabAtkins> emilio: So if you define it's a meta tag that's read, you can start rendering the new page earlier, as we do now
<vmpstr> q?
<TabAtkins> khush: We're not trying to delay rendering just because there's a transition. there's already render-blocking in html
<TabAtkins> khush: We have to fetch enough of the dom and stylesheets before first render anyway
<TabAtkins> khush: So there will be some amoutn of CSS we have to block first frame on anyway
<TabAtkins> khush: They can put these rules into the same stylesheets and not delay rendering further than what it needs to
<TabAtkins> emilio: If you only have one dev that knows how to do this, sure, but that's not true generally
<TabAtkins> khush: the author has to think about it anyway
<TabAtkins> khush: So much CSS has to be parsed before you do first render anyway to make the animation work
<TabAtkins> emilio: VT doesn't wait, then? It does first render asap and relies on render blocking?
<TabAtkins> emilio: My understanding was we have to wait for the page to be fully loaded
<TabAtkins> khush: Right. In same-document you get a callback so you can block until ready.
<TabAtkins> khush: In cross-document we're relying on render blocking for the same
<TabAtkins> emilio: So if the stylesheets in the head are loaded we can start rendering before content is loaded
<TabAtkins> khush: there's an html issue where render blocking today is just stylesheets, but we are proposing extending it to content too
<TabAtkins> khush: So you can detail which elements need to exist before render starts. and a timeout does some fallback
<TabAtkins> emilio: So an important part of why specifying it in css is fine or not.[ not sure what this sentence meant]
<TabAtkins> emilio: with meta the order is clear
<TabAtkins> emilio: with an at-rule, then we need to define how they cascade
<TabAtkins> khush: I think that problem needs to be solved anyway.
<TabAtkins> khush: We have a feature request to have it apply only for some navigations - between certain urls, only on reloads, etc
<TabAtkins> khush: So author will need settings for these kinds of things
<TabAtkins> emilio: so if you ahve that, how they interact needs to be defined
<TabAtkins> noamr: we'll be using the same ordering that other at-rules use, stylesheet order
<TabAtkins> emilio: layers too
<TabAtkins> noamr: right, including all of that
<TabAtkins> (they'll cascade atomically, like most at-rules)
<astearns> q?
<astearns> ack khush
<TabAtkins> khush: just to give a concrete example
<SebastianZ> q+
<TabAtkins> khush: for same-site, going from foo.bar.com to baz.bar.com, you can say "im okay with most transitions, but veto this one url"
<TabAtkins> khush: so you'll specify two of these rules, one generally and one specialized to that path
<astearns> ack bramus
<TabAtkins> khush: so we'll need to make sure authors can specify a default behavior but override in special cases
<TabAtkins> bramus: i'm very in favor of doing this in CSS
<TabAtkins> bramus: was wondering about the name - auto-view-transition
<TabAtkins> bramus: Could it be more generic? @config? to cater to more descriptors in the future beyond VT
<TabAtkins> bramus: Also curious how this interacts with the cascade
<hober> qq+
<TabAtkins> bramus: Think I saw Tab saying no
<TabAtkins> (correct)
<TabAtkins> hober: I think it would be a mistake to rename to generic.
<TabAtkins> hober: We don't have use-cases for non-VT cases right now. Having a generic name would be an attractive nuisance, I'd prefer it be purpose-built
<khush> +1 to keep it VT specific. We can make the pattern generic if needed.
<astearns> ack emilio
<astearns> ack hober
<Zakim> hober, you wanted to react to bramus
<TabAtkins> (agree strongly, plus it does have some VT-specific things it needs in filtering/etc)
<emilio> +1, at-rules like this are relatively inexpensive
<TabAtkins> fantasai: strongly agree to keep it in CSS, using meta is awful. styling should be in css if at all possible, external stylesheets are great
<emilio> s/+1/+1 to hober's comment/
<TabAtkins> fantasai: With multiple of these rules, you probably want them to cascade together? if the last rule says you want to disable cross-origin transitions, you don't want it to break the rest of the settings.
<TabAtkins> fantasai: so we might want it to cascade individual descriptors, which some at-rules do
<bramus> +1 to that
<TabAtkins> (currently, only the ones that apply things to element-like stuff, i think?
<TabAtkins> )
<TabAtkins> fantasai: can you say same-origin:disable;cross-origin:enable? that's weird. would be good to explore the full space of likely descriptors first to make sure they're coherent together
<TabAtkins> fantasai: think there was some discussion about different classes of transitions dpeneding on page you're going to/from
<TabAtkins> fantasai: Might not be the first thing we do, but should be explored to make sure the shape of the at-rule is good.
<TabAtkins> fantasai: So overall I agree with the direction of this, using an at-rule rather than meta unless ther'es a good mechanical reason.
<astearns> q+
<astearns> ack fantasai
<TabAtkins> fantasai: but i think before we can udnerstand if the syntax is good we need to explore the spaces we're expecting to extend into first
<noamr> q+
<TabAtkins> SebastianZ: an idea from Bramus was to put the detection into a media feature
<astearns> ack SebastianZ
<bramus> Link: https://github.com/w3c/csswg-drafts/issues/8048#issuecomment-1551535268
<bramus> (second bullet)
<TabAtkins> SebastianZ: with that we could put everything that needs to be transitioned into that rule
<SebastianZ> https://github.com/w3c/csswg-drafts/issues/8048#issuecomment-1551535268
<fantasai> astearns: He's proposing to put into the @media rule
<TabAtkins> bramus: I immediatley discarded tha tidea in the next comment, it doesn't allow setting the choice
<TabAtkins> bramus: An MQ could complement the setting
<fantasai> emilio: wouldn't that create cycles
<khush> q+
<federicobucchi> presence+
<fantasai> TabAtkins: make it possible to change styling when VT is enabled
<fantasai> s/enabled/happening/
<fantasai> TabAtkins: you might want different styles for the page in that case
<TabAtkins> astearns: I'm not convinced yet that we won't need the meta tag
<TabAtkins> astearns: But I think we could start with the at-rule and if we find there is a perf reason to ahve the meta as the opt-in, we can add later
<khush> https://github.com/w3c/csswg-drafts/issues/8925 is related to media queries for setting state based on where you're navigating to.
<TabAtkins> astearns: the at-rule would still be necessary for other settings besides the bare opt-in
<astearns> q?
<TabAtkins> emilio: As long as there's a concrete point in time where the at-rule needs to be seen
<TabAtkins> emilio: then i don't object to an at-rule
<astearns> ack astearns
<TabAtkins> emilio: I just don't want to introduce unnecessary style updates to look up this config
<astearns> ack noamr
<TabAtkins> noamr: in reverse order
<TabAtkins> noamr: We're not adding new style updates, just parsing the stylesheet to capture the rules. no need for a full cascade
<emilio> q+
<TabAtkins> noamr: For MQs, that's a spearate feature, possibly an enhancement for later
<TabAtkins> noamr: wrt fantasai wanting more examples for th efuture, i think that's a fair request
<TabAtkins> noamr: something thats already come up is the ability to use this opt-in to disable same-document VTs.
<TabAtkins> noamr: So you can disable all VTs under prefers-reduced-motion
<TabAtkins> noamr: so that's one use-case we've already seen
<TabAtkins> noamr: Another was same-site or first-party-set
<TabAtkins> noamr: Another was having declarative same-document transitions, like allowing Navigation API to trigger VTs.
<astearns> ack khush
<TabAtkins> khush: one thing emilio brought up was having a defined point where this is queried
<TabAtkins> khush: we have to have a defined point where browser captures everything anyway
<TabAtkins> khush: so our thinking was to use the same point as when you set up styles and states for the navigation, shoudl b ethe same point we set the opt-in
<TabAtkins> khush: I also pasted 8925 which is related to this
<TabAtkins> khush: there's an idea of adding MQs to allow setting state based on old and new url
<TabAtkins> khush: this opt-in might play with that
<TabAtkins> khush: so you can use that MQ to set this at-rule to disable certain paths
<astearns> ack emilio
<TabAtkins> khush: so we can come back with more information on this
<TabAtkins> emilio: re noam's comment, you don't need to update dom styles but you still need to go thru all the stylesheets and make sure it's sorted correctly
<TabAtkins> emilio: adding a fast path for this doesn't seem appealing to me
<TabAtkins> emilio: in blink/wk terminology, this needs to "update the active stylesheets" and that still shows up considerably during page load
<TabAtkins> emilio: and i don't want to do that until all stylesheets are loaded
<TabAtkins> emilio: i'd rather make sure that on the page you're navigating to you don't need to do unnecessary work to hook this up
<vmpstr> q+
<TabAtkins> emilio: so ideally the point where you determine the VT doesn't involve synchronously updating the active stylesheets
<astearns> ack vmpstr
<TabAtkins> vmpstr: I think the way were' structuring this is we're using othe rproperties, such as render blocking and proposed additions to that, to control when the document will render
<TabAtkins> vmpstr: and defining VT to say "at the point when the doc will render, you have know the opt-in/out'
<TabAtkins> vmpstr: So no extra work, but giving the dev a litlte more control over first render timing
<TabAtkins> vmpstr: And on the meta tag, we've discussed a double opt-in - a simple meta that can indicate whether the VT is even possible, and then use the at-rule for all the actual options
<TabAtkins> vmpstr: I'm not opposed to that, but think we still want the css at-rule for more detailed config
<TabAtkins> emilio: Using the initial frame for this makes sense and satisfies my constraint
<TabAtkins> khush: Okay then the spec currently says that's when we should be chekcing the opt-in
<TabAtkins> astearns: For this issue can we have a resolution to use the at-rule initially?
<TabAtkins> emilio: seems fine
<TabAtkins> noamr: also to start with an at-rule, but bring more examples of future usage to determine exact syntax
<TabAtkins> fantasai: yeah we can resolve *an* at-rule, not necessarily this exact shape
<TabAtkins> astearns: so proposed resolution is to use an at-rule for cross-document VT transitions
<TabAtkins> noamr: and gather examplkes before we decide on final syntax
<TabAtkins> RESOLVED: Use an at-rule (of some kind) to control cross-document VT transitions, syntax tbd
<TabAtkins> fantasai: Note it's not just examples of what's already there, it's looking at what should be possible, and defining syntax for it
noamr commented 12 months ago

Following up on the F2F, this is the current suggestion including "future" features, based on the feedback from the f2f:

@view-transitions {
  /* default: none. Same-document is a potential future feature. */
  navigation-trigger: cross-document | same-document | any | none;

  /* If present on both sides, has to match, and affects transition-names/media-query */
  /* pending decision on view-transition-groups etc. See #8960  */
  group-name: foobar;

  /* If present on either, has to match. TBD whether we actually need this. */
  version: 1234;
}

To match navigations, the rule would have extra pseudo-classes, equivalent to page rules, e.g.:

/* exact syntax to qualify navigations TBD */
@view-transitions :navigating(from --articles) {
  group-name: from-articles;
}

In addition, the rule can be nested inside any media-query:

@media (prefers-reduced-motion: no-preference) {
  @view-transitions {
    navigation-trigger: cross-document;
  }
}

To support same-site in the future, the idea is that we would want the developer to be explicit about which origins are allowed rather than a blanket same-site.

For the current set of features, this is how it would look like when you want to enable MPA transitions:

@view-transitions { 
  navigation-trigger: cross-document;
}
jakearchibald commented 12 months ago

I'm not sure what group-name is. Is there a description of view-transition-groups anywhere?

noamr commented 12 months ago

I'm not sure what group-name is. Is there a description of view-transition-groups anywhere?

It came up at the F2F as a potential solution when we were discussing #8960 and a lot of its details are TBD. See resolution. I didn't want to get deep into this here, and when we have a better understanding of how we tackle #8960 we should update this. I put it here because of @fantasai's request that we should imagine the future things the at-rule could do and see how they fit in when we implement the basic case.

The main concept is that it's a string you pass to startViewTransition or via the opt-in, and becomes a namespace of sorts to which transition names are enabled and how the pseudo-elements are styled, potentially using it in a media query.

In the context of the rule, since the rule replaces startViewTransition in the cross-document world, this descriptor (with whatever name it ends up with) would be what translates the navigation parameters (old URL, new URL, navigation type) to whatever groups together different elements to a transition.

Going forward I'd like to keep the discussion around that in #8960, and update here if it affects the declarative opt-in.

jakearchibald commented 12 months ago

I put it here because of @fantasai's request that we should imagine the future things the at-rule could do and see how they fit in when we implement the basic case.

I think that's a reasonable thing to do. Similar to how we sketched out nested & scoped transitions, to prevent us designing ourselves in a corner.

bokand commented 11 months ago

What should we do with the at-rule inside shadow trees? Presumably it shouldn't opt-in the document but it also doesn't seem to have any meaning inside a tree - should we restrict it to only the root document? Not sure if there's any precedent with other rules.

+@tabatkins who may have an opinion based on https://drafts.csswg.org/css-scoping/

khushalsagar commented 11 months ago

should we restrict it to only the root document?

I'm assuming "root document" doesn't imply main frame. We do need the opt-in for Documents in iframes. Other than that makes sense to ignore the rule in shadow trees.

vmpstr commented 11 months ago

This is a summary of the offline discussions for this issue and the current proposal.

Overview

The base idea is still to have a new at-rule called @view-transition which decides whether a navigation will execute a ViewTransition.

The simplest API would be a single property with a binary value:

@view-transition {
   transitions: allow | disallow;
}

But authors need to configure the opt-in, and the transition animations, based on the type of navigation. The following summarizes all such cases based on 2 parameters explained below.

Case Needs opt-in Needs customization
same doc navigation
cross doc, same origin navigation
same site navigation
script initiated transition
old/new url selection
history navigation: back
history navigation: forward
reloads
replace
push

Needs customization

Authors are likely to customize the transition animation (which elements have a view-transition-name, style rules which apply to VT pseudo-elements) based on the case. We need to allow authors to control any CSS for these cases, which implies that it can also configure the opt-in.

A media-query seems like the best fit. For example, the following CSS for back/forward history traversal:

/* Back navigation has the current page slide right to reveal the back entry. */
@media (navigation: back) {
  ::view-transition-old(root) {
    animation: slide-to-right;
    z-index: 1;
  }
}

/* Forward navigation has the forward entry sliding from right to cover the old page */
@media (navigation: forward) {
  ::view-transition-new(root) {
    animation: slide-from-right;
  }
}

However, using a media-query implies that authors need to order their CSS correctly. For example, the following won’t work.

@media (prefers-reduced-motion) {
  @view-transition {
    transition: disallow;
  }
}

@view-transition {
  transition: allow;
}

We considered solving it by adding specificity if the declaration is gated by a media query. But since this is how media queries work by design, and authors are used to the pattern, it doesn’t seem necessary.

Needs opt-in

Authors are likely to enable or disable transitions based on the case. If a case only needs to configure the opt-in then it's simpler to add it to the opt-in syntax itself.

Proposals

Option 1

@view-transition {
  transition-trigger: [ same-document-navigation || cross-document-same-origin-navigation || same-site-navigation || script] | none;
}

transition-trigger identifies the source of the DOM change. The following tokens are part of this proposal (bikeshedding welcome). The initial value for transition-trigger will be script.

The following tokens will be added in the future:

This option is the proposed resolution.

Option 2

Option 1 implies that transitions will be enabled for reloads by default. Authors will have to use the following CSS to disable them:

@media (navigation: reload) {
  @view-transitions {
    transition-trigger: none;
  }
}

This goes against the resolution at https://github.com/w3c/csswg-drafts/issues/8784#issuecomment-1640701514 that reloads should not trigger a transition by default. Having something off-by-default is not feasible if the case is a media-query, it has to be a part of the at-rule. So the solutions are:

Related Issues

The following are issues which intersect with the new syntax but details should be discussed on the linked issues.

  1. The following media-style queries (I'm not sure what the @-rule should be called, and whether it should be @media proper) will be added for the “needs customization” cases above:

    • back history traversal
    • forward history traversal
    • history jump -- maybe? if the history traversal is larger than 1 step, or just adding the step count as a parameter
    • reload navigation
    • push
    • replace
    • old/new url selection

    There is related discussion on https://github.com/w3c/csswg-drafts/issues/8925.

  2. Specify a transition-name for the use-cases described in https://github.com/w3c/csswg-drafts/issues/8960#issuecomment-1640742768. It's a way to specify a type of the transition and be able to set styles based on that type, in some specialized way.

    The transition-name can be a new parameter in the @view-transition rule. The browser takes the value from the at-rule when the transition starts (same time as when the opt-in is checked) and then uses it for the duration of that transition.

SebastianZ commented 11 months ago

Option 1 implies that transitions will be enabled for reloads by default.

You mean cross-document-same-origin-navigation implies that? What if that explicitly excluded reloads? Then we can add a reload keyword to transition-trigger. A separate reload descriptor doesn't seem useful as reloads are meant to be a type of transition trigger. And some combinations of transition-trigger and reload like transition-trigger: none; reload: allow; don't make much sense.

So the whole syntax would then look like this:

@view-transition {
  transition-trigger: [ same-document-navigation || cross-document-same-origin-navigation || same-site-navigation || script || reload ] | none;
}

Sebastian

khushalsagar commented 11 months ago

You mean cross-document-same-origin-navigation implies that? What if that explicitly excluded reloads? Then we can add a reload keyword to transition-trigger.

That's an interesting idea!

One aspect, which I now realize wouldn't work for option 2 either, is that we wanted to ensure that all cases in transition-trigger are mutually exclusive. Except short-hands, for example a keyword like same-origin which expands to same-document-navigation, cross-document-same-origin-navigation.

The above allows developers to enable/disable all possible combinations. All navigation cases (same-document-navigation, cross-document-same-origin-navigation, same-site-navigation) can technically be triggered by a reload. So with the current syntax authors can't enable transitions only for reloads which are cross-document navigations. But I can't think of a case where authors would want different behaviour for cross-document vs same-document reloads.

Another thing to consider is we later on add reload to a media-query (because authors want to customize the transition differently for it). It would be a bit awkward to have reload in both the opt-in syntax and media-query but doable.

noamr commented 11 months ago

Option 1 implies that transitions will be enabled for reloads by default.

You mean cross-document-same-origin-navigation implies that? What if that explicitly excluded reloads? Then we can add a reload keyword to transition-trigger. A separate reload descriptor doesn't seem useful as reloads are meant to be a type of transition trigger. And some combinations of transition-trigger and reload like transition-trigger: none; reload: allow; don't make much sense.

So the whole syntax would then look like this:


@view-transition {
  transition-trigger: [ same-document-navigation || cross-document-same-origin-navigation || same-site-navigation || script || reload ] | none;

reload is mutually exclusive from cross-document vs. same-document. E.g. you can have a same-document reload with the navigation API.

I see a navigation as divided into 3:

The way I see it, the first two might affect which transition is triggered and not just whether it is triggered, while the last one shouldn't. e.g. you probably don't want a different transition based on whether this was a same-document or a cross-document navigation: only based on the type and origin of the navigation.

So to enable reloads in the future I see things this way: we would have a pseudo-class to the rule which qualifies the navigation (type+origin), but does not qualify based on mechanism:

/* UA stylesheet */
@view-transitions :reload {
  navigation-trigger: none;
}

@view-transitions {
  navigation-trigger: none;
}

/* user stylesheet: this would enable cross-document VT but *not* reload, due to specificity  */
@view-transitions {
  navigation-trigger: cross-document;
}

/* user stylesheet: this would enable cross-document VT on reload  */
@view-transitions :reload {
  navigation-trigger: cross-document;
}

This of course needs more work but the general idea is that whatever qualifies the navigation and can affect which transition is triggered and not just whether it's triggered, goes outside the brackets and not into the navigation-trigger rule. Happy to hear alternatives.

khushalsagar commented 11 months ago

@noamr sounds like you're suggesting using pseudo-classes instead of media queries for the "Needs customization" cases (which includes reload). Since pseudo-classes come with specificity, as opposed to media-queries, UA stylesheet can have a rule to disable reloads which can be overridden by the author. Just to put it again:

UA CSS

@view-transitions:reload {
  navigation-trigger: none;
}

Author CSS

@view-transitions {
  navigation-trigger: cross-document-same-origin-navigation script;
}

The UA rule will apply when there is a reload because it has higher specificity. But the same with media-queries won't work:

UA CSS

@media (navigation: reload) {
    @view-transitions {
      navigation-trigger: none;
    }
}

Author CSS

@view-transitions {
  navigation-trigger: cross-document-same-origin-navigation script;
}

I think pseudo-classes can work but IIUC they are meant to be for state which is per-element or a subset of elements. I haven't seen an example where a pseudo-class would activate for the whole Document.

Our primary motivation for using pseudo-classes is also specificity. Let's see if the WG thinks that's the right reason to use pseudo-classes instead of media-queries.

noamr commented 11 months ago

@noamr sounds like you're suggesting using pseudo-classes instead of media queries for the "Needs customization" cases (which includes reload). Since pseudo-classes come with specificity, as opposed to media-queries, UA stylesheet can have a rule to disable reloads which can be overridden by the author. Just to put it again:

UA CSS

@view-transitions:reload {
  navigation-trigger: none;
}

Author CSS

@view-transitions {
  navigation-trigger: cross-document-same-origin-navigation script;
}

The UA rule will apply when there is a reload because it has higher specificity. But the same with media-queries won't work:

UA CSS

@media (navigation: reload) {
    @view-transitions {
      navigation-trigger: none;
    }
}

Author CSS


@view-transitions {
  navigation-trigger: cross-document-same-origin-navigation script;

}

I'd probably just say navigation-trigger: cross-document or trigger: cross-document-navigation, and leave the same-origin part to URL specialization, e.g.:

@view-transitions :cross-origin {
  navigation-trigger: cross-document;
}

I think pseudo-classes can work but IIUC they are meant to be for state which is per-element or a subset of elements. I haven't seen an example where a pseudo-class would activate for the whole Document.

The @page CSS rule uses pseudo-classes in this way. Was using that for inspiration.

Our primary motivation for using pseudo-classes is also specificity. Let's see if the WG thinks that's the right reason to use pseudo-classes instead of media-queries.

Great, let's see! Note that for the purpose of the opt-in it doesn't really matter if it's in a media-query or in the rule, as long as it's outside the braces :)

khushalsagar commented 11 months ago

I'd probably just say navigation-trigger: cross-document or trigger: cross-document-navigation, and leave the same-origin part to URL specialization

Hmmm, I have a preference for having different tokens for same-origin vs same-site in the opt-in. Because there is a security aspect to opt-ing in for same-site, it's better for that to be explicit. Rather than a cross-document token which implicitly opt-ins for same-site URLs as well. Also makes it easier to add same-site support later.

The @page CSS rule uses pseudo-classes in this way. Was using that for inspiration.

Great! Indeed, I see an example here. I actually like it better because of specificity. Let me summarize Option 3 then.

Option 3

This is assuming we use pseudo-classes instead of media queries for "Needs Customization" cases. These pseudo-classes can be used with any selector and the @view-transitions rule. The initial value for transition-trigger is still script except we have the following style rule in UA CSS.

@view-transitions :reload {
  transition-trigger: script;
}

Other navigation types will also be using similar pseudo-classes.

bramus commented 11 months ago
/* UA stylesheet */
@view-transitions :reload {
  navigation-trigger: none;
}

@view-transitions {
  navigation-trigger: none;
}

/* user stylesheet: this would enable cross-document VT but *not* reload, due to specificity  */
@view-transitions {
  navigation-trigger: cross-document;
}

/* user stylesheet: this would enable cross-document VT on reload  */
@view-transitions :reload {
  navigation-trigger: cross-document;
}

I find this syntax is very confusing. It looks like state is added, yet the descriptors inside it apply when the state is no longer active? Tacking those states onto the @view-transition rule seems like the wrong place to do so.

Take :reload for example:

The first rule in the block above for example can be interpreted in various ways:

  1. While the page is doing a view transition triggered by a reload, don’t allow any other triggers?
  2. If the page got reloaded, don’t allow any other triggers
  3. If the page got reloaded, don’t do a transition while it is reloading.

I assume the third is what is meant, but I find the proposed syntax not to reflect this.


Rewinding back to what was proposed and refined here: the idea of the @view-transitions rule is only to configure things, not to respond to things.

You can still respond to various ways of how the view transition was invoked, but that’s not the responsibility of the @view-transitions rule. To respond to the type of navigations, something separate could cover that, e.g. an @media rule:

/* 1. VT Configuration */
/* Allow these types of triggers */
@view-transitions {
  transition-trigger: cross-document-same-origin-navigation reload script;
}

/* Disable VTs alltogether when the user does not want it */
@media (prefers-reduced-motion: reduce) {
  @view-transitions {
    transition-trigger: none;
  }
}

/* 2. Tweak the VTs in response to the type of navigation */
/* User has reloaded the page */
@media(navigation-type: reload) {
  ::view-transition-old(root) {
    display: none;
  }
  ::view-transition-new(root) {
    animation-name: fade-in;
  }
}

/* Apply a certain type of animation when navigation from the article detail page to the articles overview page */
@media (navigation: from --article to --articles) {
  ::view-transition-old(content) {
    animation-name: slide-out-to-right;
  }
  ::view-transition-new(content) {
    animation-name: slide-in-from-left;
  }
}

/* Etc. */