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

Define behaviour for transitions if the user selects prefers-reduced-motion mode #19

Closed
vmpstr opened this issue Mar 12, 2021 · 18 comments
Labels
open-spec-issue Unaddressed issue in the spec

Comments

@vmpstr
Copy link
Collaborator

vmpstr commented Mar 12, 2021

If the user has prefers-reduced-motion enabled, we should disable the animation and just do a direct transition

@argyleink
Copy link

I recommend defaulting to a cross-fade if the user prefers reduced motion, as it's not motion but is still visual feedback of a transition. MacOS does this with lots of their motion effects.

@khushalsagar khushalsagar changed the title Automatically disabling animations in prefers-reduced-motion mode Define behaviour for transitions if the user selects prefers-reduced-motion mode Sep 8, 2021
@khushalsagar khushalsagar added the undefined-behaviour Functionality with undefined behaviour that needs clarification. label Sep 8, 2021
@khushalsagar
Copy link
Collaborator

Does that mean we ignore all per element transitions and do only a cross-fade on the root element? Is there any precedent for other web animation surfaces which silently modify the specified animation based on this preference?

The second question specifically came to my mind after reading this at #45 : "If issue #19 is not implemented, then it would be best if the Read Me file for shared element transitions includes instructions and sample code for respecting prefers-reduced-motion with respect to shared element transitions." If a page is already designing their UX with this preference in mind, then does it make sense for the browser to ignore the specified transition? Seems like at least there should be an option to opt-out of a default behaviour where the browser automatically modifies the transition based on this preference.

@khushalsagar khushalsagar added open-spec-issue Unaddressed issue in the spec and removed undefined-behaviour Functionality with undefined behaviour that needs clarification. labels Aug 16, 2022
@khushalsagar
Copy link
Collaborator

Let's add this to the spec and change the implementation. It would be trivial to remove the size/position animation when this mode is enabled.

@jakearchibald
Copy link
Collaborator

Remember that developers are building on top of the default animations. I think it creates a footgun if we create two different kinds of default animation.

We could consider disabling transitions if the user prefers-reduced-motion, but I worry that developers would go back to large libraries to create transitions if we sometimes try to prevent them.

We could make disable transitions if the user prefers-reduced-motion, but provide an opt-in to override that 🤷, but note that we don't force anything with animations/transitions when it comes to prefers-reduced-motion.

@khushalsagar
Copy link
Collaborator

Remember that developers are building on top of the default animations. I think it creates a footgun if we create two different kinds of default animation.

Ok. Thinking about this more, things can get difficult if the developer is not aware of the different transition and their CSS continues to override the default animation. Here is some thinking on what the behaviour could be and how we'd implement the default:

  1. No animation and no render-blocking. When document.createTransition() is called we immediately queue a task to run the callback. Developer can override and get the transition as usual.
  2. No animation but render-blocking. When document.createTransition() is called we immediately queue a task to run the callback but still suppress rendering until the callback is done. Developer can override and get the transition as usual.
  3. Only with opacity animations. I'm hard pressed to think about how we'd set this up without significantly altering the pseudo DOM structure or its CSS. Right now we have a single page-transition-container for the position/size of the outgoing/incoming states. But now we want the incoming one to fade-in at its final position and size. So maybe we want separate ::page-transition-container elements to position/size the images (as if its separate incoming and outgoing transitions). Magically make 2 tags from the same one? This will also break the case where incoming and outgoing have the same position, size and pixels. Like the static header case.
    One option is to have this imply a cross-fade between root elements. So we ignore developer tags and CSS for these pseudo-elements entirely and just set up a pseudo-DOM as if the outgoing and incoming roots were tagged. This gets gnarly when I think of JS trying to animate the pseudos.
  4. No special behaviour with this setting and have a recommendation for developers to feature detect this mode and use cross-fades with less or no shared elements.

I'm inclined to do 2. This forces the developer to think through the experience for users opting into this mode. And the cross-fade between roots is the UA default anyway so all they need to do is set up a transition with no elements tagged or any custom CSS.

@jakearchibald
Copy link
Collaborator

Yeah, 1 or 2 sound right to me. The benefit of 2 is, if the render blocking is being used to mask CLS (like the font loading case), that still works.

document.createTransition({
  reducedMotionHandling: 'manual', // 'auto' default
  updateDOM() {
    // …
  },
});

If 'auto', and the user prefers reduced motion, it should behave like skipTransition() is called. As in, the ready and finished promises will reject with AbortError.

Anecdotally, I've had folks tell me my demos aren't working, and it turned out they had 'reduced motion' set. Maybe we should make that clear in the AbortError message.

@khushalsagar
Copy link
Collaborator

Cool. Let's go with 2 then. I was initially unsure about the default browser behaviour throwing an AbortError but its really the naming that's bad here, it's not an error. :| We narrowed down on the fact that AbortError is being used when promises are rejected because a transition was intentionally aborted so devs can use that to filter real errors.

@khushalsagar
Copy link
Collaborator

Keeping this open for now to remember to do the spec change.

@jakearchibald
Copy link
Collaborator

For MPA, I guess the incoming page would do the opt-in, and just hope that the previous page provided the right elements for that case (which is reasonable since it's same-origin).

@ydaniv
Copy link

ydaniv commented Aug 26, 2022

When I introduced Promises into velocity.js's implementation, I recall @domenic did the review for the PR and specifically noted it's incorrect to reject an API call based on flow that's not erroneous.
Instead, you could provide an argument in the fulfilled callback that tells whether the transition is running or skipped.

@jakearchibald
Copy link
Collaborator

jakearchibald commented Aug 26, 2022

Hmm, it might be worth thinking more about this, but my current thinking is that this is erroneous, particularly in the 'abort' sense. The transition fails to reach a 'ready' state due to user preferences, and never finishes because it never starts.

'Abort' does sit uncomfortably between 'expected' and 'erroneous', which is why we should have had a different thing in JavaScript to represent this, but that thing doesn't exist.

Fwiw, in the Web Animation API, animation.finished will reject with an AbortError if the animation is cancelled.

@jakearchibald
Copy link
Collaborator

If the user changes their preference to request reduced motion, should we skip an in-progress transition that has "auto" reducedMotionHandling? My feeling is that transitions are short enough that it isn't necessary to respond immediately to this change.

@jakearchibald
Copy link
Collaborator

jakearchibald commented Aug 26, 2022

PR up tabatkins/specs#98

@ydaniv
Copy link

ydaniv commented Aug 26, 2022

IMHO, abandon and skipTransition are not quite like Abort. The former two are more like "soft abort" that are expected by the user, unlike the latter which should catch actual exceptions.
I'd also expect as an author for the. ready and finished promises to be fulfilled and not reject.
I guess aborting a transition is not really on same terms as aborting a fetch() or an actual navigation.

@jakearchibald
Copy link
Collaborator

It seems wrong to fulfill ready when the transition has failed to become ready.

@ydaniv
Copy link

ydaniv commented Aug 27, 2022

Hmm, good point, I guess it looks correct, but still, for "reduced motion" I'dd still expect the transition and related logic to continue as expected, with only the effect's motion to be suppressed by the UA, but not skipped or aborted.

@jakearchibald
Copy link
Collaborator

I think that could lead to a very buggy experience when developers try to drive animations with JS, and 'randomly' don't get an expected result.

@khushalsagar
Copy link
Collaborator

This issue came up during an in-person meeting today. @vmpstr and @flackr had concerns with automatically suppressing animations expressed with this feature. The concern for how to enable the user agent to intervene for users opting to this mode has come up in other animation features on the web. Rob pointed to this example: w3c/csswg-drafts#5321 (comment).

This is being further discussed in 2 related proposals:

  • CSSWG issue to add a new option which allows the user agent to potentially break content for enforcing this preference.
  • A meta tag to allow developers to indicate whether their site is designed to handle this setting and opt-out of user agent interventions.

Both proposals are similar in spirit to the conclusion in this issue but are geared towards settings which apply to all animation APIs as opposed to something specific for this feature. That does seem better and would mean we don't add any special behaviour to SET for this mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
open-spec-issue Unaddressed issue in the spec
Projects
None yet
Development

No branches or pull requests

5 participants