Skip to content
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

Disabling UA transitions for same-document navigations #8747

Open
khushalsagar opened this issue Apr 21, 2023 · 4 comments
Open

Disabling UA transitions for same-document navigations #8747

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

Comments

@khushalsagar
Copy link
Member

khushalsagar commented Apr 21, 2023

Smooth visual transitions as users navigate on the web can lower cognitive load by helping users stay in context. However, the user experience is bad if both the site author and the UA add these transitions: the transitions may conflict and cause confusion for the user.

The goal of this API is to enable authors to disable default UA transitions for a navigation in favour of custom transitions on their site. This explainer goes through the detailed thinking behind it, here is the TLDR:

  • We want to introduce an API which indicates whether same-document navigations from the current Document are allowed to execute a visual transition defined by the UA.
  • Navigations can be of 2 types: atomic (clicking the back button) or predictive/swipe (swipe from the edge of a mobile screen). The key difference is that with atomic navigations the visual transition starts when the navigation is initiated. With predictive/swipe, the visual transition starts with the gesture and may not necessarily result in a navigation.
  • The current proposal doesn't allow the value to be configured based on the destination URL for simplicity. But we'd like the API to be extensible to support this use-case.

The proposal is a new at-rule named same-document-ua-transition which can have the following values:

  • enable : Enables UA transitions for all navigations.
  • disable-swipe : Disables UA transitions for atomic navigations.
  • disable-atomic: Disables UA transitions for swipe navigations.
  • disable-swipe disable-atomic : Disables UA transitions for atomic and swipe navigations.

The value can be specified as follows:

@same-document-ua-transition: enable;

@media (min-width: 360px) {
  @same-document-ua-transition: disable-atomic;
}

Other options for this API are outlined here.

Other considerations when evaluating this proposal:

  • Default Value: It's unclear whether UA transitions should be allowed by default or not. Allowing them by default has a compat risk of breaking sites which ship with custom transitions already.
  • Integration with Navigation API: This API works in tandem with the proposal here. If the author disables UA transitions for atomic navigations but not swipe, they will be able to detect which one happened when the navigation events are fired.
  • Should this go in the View Transitions spec? The proposal is unrelated but VT comes closest to the concept of visual transitions on navigations in CSS.
@nickcoury
Copy link

This is extremely useful, and hope it is able to land in some form! Some feedback based on the explainer:

Swipe Types

Not all swipe navigations are edge swipes. Specifically, UC Browser uses whole-page swipes for navigation as long as the element being touched doesn't have room to scroll, and doesn't prevent default.

Opt-in vs out

Opt-out seems like the preferred behavior. Most users already expect UA transitions to be available, and disabling them by default will unnecessarily remove a useful gesture from much of the web.

CSS vs JavaScript API

I am curious if a JavaScript API has been considered yet. It might be a more natural fit for the swipe case, with simpler semantics. For example:

  • For atomic navigations, the disable flag could be provided as part of the Navigation API, particularly in intercept() where similar transition handling customizations occur.
  • Since swipe navigations require touch events, it might be as simple as providing a reliable way to preventDefault on touch events. An advantage of this is it can still (mostly) work with older browsers with the same code, even if more unreliable. It also would eliminate needing a specific way to disable swipe transitions - they would be avoided via preventDefault which is already a common paradigm for disabling UA handling of events.
  • An API could provide extra guidance on the touch regions or a pseudo-element that can be used to trap touch events. Separate areas/elements could be provided for back/forward element swipes making it especially easy to handle those cases. This provides more flexibility around how to disable UA animations, even when their implementations vary wildly.
  • The other organizational advantage of this over CSS is that it groups the logic deciding to disable UA transitions with the code providing the user benefit (transition animations + gesture logic).

A few other thoughts:

  • Is there a greater story where the CSS API is superior to JS, e.g. as part of interop with View Transitions? I believe it would still require JavaScript, so it could still be more natural to determine UA transition cancelling in the same place as startViewTransition(). Showing how it fits with VT API could be useful, as I imagine there's thoughts behind it complementing cross-document navigations where JavaScript doesn't apply as easily.
  • I'm not aware of any atomic default browser transitions, though it's easy enough to imagine them. Addressing that separately future proofs this, but if there are examples available I would be curious.

hasUAVisualTransition

On the JavaScript proposal for detecting hasUAVisualTransition, the mechanic seems in line with what I would expect. One possible enhancement is some sort of paint holding. In the ideal case this wouldn't be necessary and developers would immediately update to the end state of the transition without a user-visible flash. But depending on the website architecture, it's still possible for the predictive snapshot behind the swipe to show, then the live state of the site to show, then the desired end state to render (possibly progressively over multiple frames). There might be a reasonable mechanic to expose in the View Transition API, but it would be nice to have a similar mechanism even if developers do not use it. There still would likely be a hard timeout after which painting resumes regardless, but providing a short time window for a site to update rendering will reduce disorienting flashes in line with the overall goal of this proposal. E.g.

navigation.addEventListener("navigate", (event) => {
  if (event.hasUAVisualTransition)
    event.holdPaintUntil(new Promise(resolve => {
      updateDOM(event).then(resolve);
    });
  else
    updateDOMWithVisualTransition(event);
});

@khushalsagar
Copy link
Member Author

Thanks for the feedback Nick, those are very good insights. Responses below.

CSS vs JavaScript API

The benefit of a CSS API is reducing latency of the gesture since its declarative. We can start the visual animation as soon as the event is received from the OS. A script based API will require us to dispatch an event (cross-process) before the visual animation can start.

The handling of this gesture is also different from how touch events usually work. The common pattern would be to provide the touch event to script, and if it preventDefaults then the UA stops handling the gesture. In this case we don't want authors to be able to override what a swipe from the edge does:

  • It has to trigger a navigation (as opposed to animating some content on the page).
  • The target entry of the gesture is decided by the UA.
  • Whether the gesture results in a navigation depends on how far the user swipes. That decision will be up to the UA.

The control authors have should be limited to whether there is a visual animation involving the web content as the user swipes. The reason for this is that the swipe gesture could be the only way for a user to go back on a browser. If the page is allowed to override it, they can effectively prevent the user from leaving the page.

This is why I don't think dispatching touch events to script for this gesture is a good idea. Authors might assume they can take over the gesture using preventDefault and start animating content on the page. We'll eventually need an API to dispatch the event stream for authors that want to customize the visual transition, but it seems better to have an explicit API indicating that the gesture will trigger a navigation (and the target entry).

That said, I can see a JS API which complements the CSS API for this. Do you have any use-cases in mind which won't work with just the CSS version?

Is there a greater story where the CSS API is superior to JS, e.g. as part of interop with View Transitions?

For same-document navigations (which this proposal is targeting), it's agnostic to VT. Authors can use any other framework to do transitions during navigations.

For cross-document navigations, an API like this shouldn't be necessary. Since what this API conveys is explicit in that case. The only way to do a visual transition is using a primitive like View Transitions. VT would have a declarative opt-in, so the UA already knows that the site has a custom transition for atomic navigations.

The UA can also suppress the site's custom transition (in favour of a UA transition) by not executing the ViewTransition for swipe navigations (until there is a way for authors to customize it).

This effectively means the UA can do the right thing without the need for any new APIs. There is an open question here, if there are cases where a ViewTransition when the navigation commits is strictly better than the UA default. Did I miss any case?

I believe it would still require JavaScript, so it could still be more natural to determine UA transition cancelling in the same place as startViewTransition().

That would actually be too late, authors would call startViewTransition() in the navigate event. This is dispatched when the user gesture has ended in a state that the navigation should be triggered. There won't be a navigate event if the user swipes back. The decision for cancelling the UA transition is needed when the user starts swiping.

I'm not aware of any atomic default browser transitions

Me neither. :) We (Chrome) have discussed this and made sense to make the API future proof for that case.

Not all swipe navigations are edge swipes. Specifically, UC Browser uses whole-page swipes for navigation as long as the element being touched doesn't have room to scroll, and doesn't prevent default.

That's interesting. In this particular case the browser is choosing between using the gesture for page content vs navigation. Even if the author doesn't preventDefault, I'd expect the same-document-ua-transition setting to suppress the visual animation same as an edge swipe would. Wdyt?

One possible enhancement is some sort of paint holding.

That's an interesting idea. FWIW, Chrome will keep showing the preview until DOM changes after the navigate event are painted. I expect every UA implementation will need to do that to avoid a flash of pre-navigation content if the preview is dismissed sooner.

The API you mentioned is useful if there are cases where the frame after the navigate event needs to block on an async event. That sounds like generalizing the rendering suppression concept in VT. If there are compelling use-cases, we can add an API like you proposed to allow authors to suppress rendering outside of VT.

@nickcoury
Copy link

All that makes sense. A few followups:

We'll eventually need an API to dispatch the event stream for authors that want to customize the visual transition, but it seems better to have an explicit API indicating that the gesture will trigger a navigation (and the target entry).

I think this answers my questions below as this describes my use case, but adding the questions for the sake of discussion.

In this case we don't want authors to be able to override what a swipe from the edge does:
...

I'm wondering if this is a hard requirement. What about using an edge swipe to dismiss a full-width overlay/modal element that may or may not be a browser navigation? For better or worse it's already possible to prevent navigation this way, though unreliable as has been pointed out. In many browser implementations, users are also unable to swipe when carousels are full width, though I suppose that's different since they generally aren't full-height and it only applies when they aren't scrolled to the end.

Do you know cases where edge swipes are the only way to navigate backward? To my knowledge, browsers always provide a back button affordance even if swipe is more common.

Do you have any use-cases in mind which won't work with just the CSS version?

Most use cases would be possible with the CSS version, though some cases get complicated. Web carousels can do this natively e.g. Amazon.com on an iPhone. Instagram on iOS has something similar, though thier edge swipe does override the behavior and it only works for middle of page swipes. So the dismissal is dynamic based on which part of the screen is swiped and which element is below the swipe, which would be difficult to do with just the CSS API before receiving touch data.

I'd expect the same-document-ua-transition setting to suppress the visual animation same as an edge swipe would. Wdyt?

I agree.

@giveap
Copy link

giveap commented Aug 14, 2024

As someone who has developed web apps with same-page navigation, the ability to control the browser's swipe navigation animation in favor of our own is very welcome indeed. The problem is the swipe reveals a previously cached (static) page and getting the current page to match without a flash is impossible with dynamic apps. For instance, why can't an author request that a swipe back perform the same as the back button? The painting of a static page is at odds with same page navigation.

Similarly, an author should be able to disable default swipe navigation in instances (for example on carousels) where user touch can accidentally cause a navigation instead of horizontal scrolling or whatever the intended action might be.

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

4 participants