Skip to content

[css-view-transitions-2] MPA: Reacting to the old/new page type #8683

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

Open
jakearchibald opened this issue Apr 4, 2023 · 13 comments
Open

[css-view-transitions-2] MPA: Reacting to the old/new page type #8683

jakearchibald opened this issue Apr 4, 2023 · 13 comments
Labels
css-view-transitions-2 View Transitions; New feature requests

Comments

@jakearchibald
Copy link
Contributor

This covers a few use-cases:

  • The 'outgoing' old page wants to assign view-transition-name depending on its own page type and/or the 'incoming' new page type.
  • The 'outgoing' old page wants to opt-in and declare a version depending on its own page type and/or the 'incoming' new page type.
  • The new page wants to assign view-transition-name depending on its own page type and/or the old page type.
  • The new page wants to opt-in and declare a version depending on its own page type and/or the old page type.
  • The new page wants to assign different animations to the pseudos depending on its own page type and/or the old page type.

I refer to "page type" here because if you create a custom transition I think it's more likely to be from "index" to "article", even though there may be many URLs that implement the "article" layout.

Currently, with SPA transitions, we recommend using class names on the root:

const data = await getNextPageData();
const transition = document.startViewTransition(callback);

if (currentPageType === 'index') {
  document.documentElement.classList.add('from-index-page');

  transition.finished.finally(() => {
    document.documentElement.classList.remove('from-index-page');
  });
}

if (data.incomingPageType === 'article') {
  document.documentElement.classList.add('to-article-page');

  transition.finished.finally(() => {
    document.documentElement.classList.remove('to-article-page');
  });
}

Now those class names can be used in selectors to meet all of the use-cases above.

But how should we do it with MPA?

Option 1: Stick with class names

We could add events to allow MPAs to set class names on the root element:

// On the outgoing page:
document.addEventListener('beforecrossdocvtcapture', (event) => {
  sessionStorage.oldPageClass = 'from-index-page';

  document.documentElement.classList.add('from-index-page');
    
  addEventListener('pagehide', () => {
    document.documentElement.classList.remove('from-index-page');
  }, { once: true });

  if (event.incommingURL === articleURL) {
    document.documentElement.classList.add('to-article-page');
    
    addEventListener('pagehide', () => {
      document.documentElement.classList.remove('to-article-page');
    }, { once: true });
  }
});
// On the new page:
document.addEventListener('crossdocviewtransition', (event) => {
  document.documentElement.classList.add(sessionStorage.oldPageClass);
  document.documentElement.classList.add('to-article-page');

  event.finished.then(() => {
    document.documentElement.classList.remove(sessionStorage.oldPageClass);
    document.documentElement.classList.remove('to-article-page');
  });
});

But… part of the fun of MPA transitions is you don't need JavaScript.

Option 2: Media queries based on URL

@media (vt-next-page: urlpattern('/articles/*')) {
  /* … */
}

@media (vt-old-page: urlpattern('/')) and (vt-page: urlpattern('/articles/*')) {
  /* … */
}

This allows the developers to set styles depending on the next page URL (after redirects), and on the new page they can style depending on the current page and previous page URL.

URLPatterns are already defined.

However, URLs can be a little fragile. Often, you don't have all articles under /articles/* etc etc.

Option 3: Media queries based on page type

@media (vt-next-page: article) {
  /* … */
}

@media (vt-old-page: index) and (vt-page: article) {
  /* … */
}

Where pages declare their type via a meta tag:

<meta name="view-transition-page-type" content="article">

This could be easily used by SPA transitions too.

However, is it too hard to get the incoming page type from the meta tag for use in the outgoing page?

@jakearchibald jakearchibald added the css-view-transitions-2 View Transitions; New feature requests label Apr 4, 2023
@Martin-Pitt
Copy link

I've worked on creating choreographic page transitions in SPAs for a long while now and it was nostalgic going over the options described here as being quite familiar to when I was agonising over the different approaches.

Eventually I've come to greatly appreciate the last option most in my web apps, with the named page types, as being most maintainable and logical.

Storing weird state or depending on URLs has always been painful and awkward depending on designs and nested/hierarchical info architecture that don't conveniently match up.

So the separation with it's own system via named page types helps with the motion design.

--
Rambling thoughts:
However there can still be some challenges with nested transitions, in my use case for example where some anchor link in content would change to the next page and show a certain tab or popup on that page. For example a healthcare professional would click on a medical article on a link to the results of a trial or a mode of action for a drug in a section on a different page. So having the page-page transition and then transition for the section/popup/tab/whathaveyou in staggered sequence.
I don't know if that's something VT would be capable of handling but hey food for thought after playing around with nested named cascade layers.

@noamr
Copy link
Collaborator

noamr commented May 24, 2023

I like the 3rd option the best.
However, I wanted to point out a couple of security-related implications of these options:

  • If we share any information about the new document to the old document, including the fact that there is a transition coming, we could create a constraint that would make it difficult for us to make this feature same-site rather than same-origin in the future. There is no current mechanism I'm aware of to opt in to "let a same-site cross-origin page that navigates know where the navigation got redirected to". It might be ok to cement the same-origin constraint but we should be deliberate about it.
  • With option (2) and (3), this makes information about the next same-origin navigation (e.g., whether it was redirected) something that is now exposed to CSS. It means that 3rd party CSS, which could have different CSP privileges than JS, can now read and exfiltrate this data.

Both of these issues might be ok but should be properly reviewed. The same principles apply to #8785 and #8790: if we expose the exact time of the transition, we expose something about a navigation that might be cross-origin. This is OK if we don't want to support same-site in the future.

@noamr
Copy link
Collaborator

noamr commented May 24, 2023

(To be clear: my view is that I would love to make these features happen on the expense of same-site cross-origin, but the latter could be an important use case for some folks)

@bramus
Copy link
Contributor

bramus commented May 25, 2023

Would be nice if all could be done using only CSS, so evidently I am in favor of any approach that would afford this.

Proposals 2 and 3 look nice, but there’s still something missing: when doing transitions between two pages of the same type, you might need some extra info to determine the “direction of animation”.

Say you’re looking at a list of 100 items, paginated per 10 items, and you are currently on page 5. When going to page 4 you’d want all items (old and new) to slide to the right, whereas when going to page 6 you’d want to slide them in the opposite direction. Having just the page-type or url pattern available doesn’t cut it, as you’re doing transitions between the same type of pages.

@jakearchibald
Copy link
Contributor Author

jakearchibald commented May 25, 2023

@bramus

Proposals 2 and 3 look nice, but there’s still something missing: when doing transitions between two pages of the same type, you might need some extra info to determine the “direction of animation”.

Ohh, that's a good use-case to capture.

@noamr

  • With option (2) and (3), this makes information about the next same-origin navigation (e.g., whether it was redirected) something that is now exposed to CSS. It means that 3rd party CSS

Yeah, that's a significant issue, and it wasn't spotted before. It seems like there are multiple cases where risks with 3rd party CSS hold useful features back (eg #5092). I wonder if there's a general way to solve the issue.

@jakearchibald
Copy link
Contributor Author

I've been prototyping a bit with this. Let's say, when going between "home" and "article", I want to give .banner-img on the "article" a particular view-transition-name.

So, I want a rule like .banner-img { view-transition-name: banner-img; } to apply:

  • On "article", when coming from "home"
  • On "article", when going to "home"

In one of these cases, "article" is the old page, in the other it's the new page.

There are probably cases where you need to know all three bits of information:

  • The current page type
  • The old page type
  • The new page type

But in this case, and seemingly a lot of similar cases, you only need to know:

  • The current page type
  • The 'other' page type

Being able to express that would reduce code. Eg:

:root:vt-current-page-type(article):vt-other-page-type(home) .banner-img {
  view-transition-name: banner-img;
}

Compared to:

:root:vt-current-page-type(article):vt-old-page-type(home),
:root:vt-current-page-type(article):vt-new-page-type(home) {
  & .banner-img {
    view-transition-name: banner-img;
  }
}

@noamr
Copy link
Collaborator

noamr commented Sep 2, 2023

I still think that for most transitions we saw in the wild so far, "page type" was less the mental model than "transition type". In most cases there were a small number of transition types, plus maybe the odd customization for page type.

The nice thing about transition type is that it's flexible and unopinionated - you can feed the page type/direction into it, which doesn't work in the other direction.

@jakearchibald
Copy link
Contributor Author

jakearchibald commented Sep 3, 2023

Ok. The use-case I brought up is very common though. It'd be good to see how its implementation would look with the proposed solution.

@jakearchibald
Copy link
Contributor Author

So, @flackr pointed out that "current page type" can be much better handled by a class name on <html> (or similarly widely scoped)

@noamr
Copy link
Collaborator

noamr commented Sep 13, 2023

@fantasai responding to your IRC comment,
there are a lot of discussions here about how to declare page-types without URL patterns. The problem with that is that when we leave a page, we don't know anything about the page we're going to apart from it's URL (and whether this is a back navigation and so forth).

That's why #8048 (comment) focuses on URL matching as the only way to extract information about a navigation. Happy to hear alternatives!

@romainmenke
Copy link
Member

romainmenke commented Sep 13, 2023

We implemented MPA transitions for a project some years ago and we used attributes on the clicked anchor element to trigger the right animation.

The way we implemented MPA transitions before this API isn't really relevant, but we face the same problem. You need to know what kind of animation to start ahead of time.

In our case we knew server-side for each url what type of page it would be.
The type of transition depended on the element that triggered the navigation and the type of page that was being navigated to.

<a
  class="..."
  href="..."
  data-transition-type="foo"
  data-transition-duration="1150"
  data-transition-background-color="#dedcd9"
  data-transition-color="#000000"
>

@noamr
Copy link
Collaborator

noamr commented Sep 14, 2023

We implemented MPA transitions for a project some years ago and we used attributes on the clicked anchor element to trigger the right animation.

The way we implemented MPA transitions before this API isn't really relevant, but we face the same problem. You need to know what kind of animation to start ahead of time.

In our case we knew server-side for each url what type of page it would be. The type of transition depended on the element the triggered the navigation and the type of page that was being navigated to.

<a
  class="..."
  href="..."
  data-transition-type="foo"
  data-transition-duration="1150"
  data-transition-background-color="#dedcd9"
  data-transition-color="#000000"
>

Thanks for sharing!
I'm reading from this that you didn't invoke transitions on back navigations?

@romainmenke
Copy link
Member

I'm reading from this that you didn't invoke transitions on back navigations?

Correct and I see where you are going with this :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-view-transitions-2 View Transitions; New feature requests
Projects
None yet
Development

No branches or pull requests

5 participants