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

App banners: Don't play animation if reduced motion's enabled #67826

Merged
merged 3 commits into from
Sep 20, 2022

Conversation

SiobhyB
Copy link
Contributor

@SiobhyB SiobhyB commented Sep 14, 2022

Proposed Changes

Following the feedback in #67013 (comment), this PR takes a user's "reduced motion" preference into account before displaying an animation in the app banners. The animation will not play if the "reduced motion" is enabled.

Animated icon Reduced motion
ltr.mov
reduced-motion.mov

Testing Instructions

Prerequisites:

  • Enable reduced motion on your device. To do that on a Mac, head to System PreferencesAccessibilityDisplay and check the box for Reduce motion.
  • The app banner can be viewed via the mobile web (which can also be emulated using browser tools) with this branch checked out.

Testing steps:

  • Navigate to the Reader, Stats, Notifications, or the editor within WordPress.com's to view the app banners on the mobile web.
  • Verify that the banner's icon doesn't play.
  • Next, turn off the "reduced motion" setting on your device, refresh the page with the banner, and confirm the icon is now animated.

Pre-merge Checklist

@github-actions
Copy link

github-actions bot commented Sep 14, 2022

@SiobhyB SiobhyB added the Accessibility (a11y) label Sep 14, 2022
@SiobhyB SiobhyB self-assigned this Sep 14, 2022
@matticbot
Copy link
Contributor

matticbot commented Sep 14, 2022

Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

Async-loaded Components (~68 bytes added 📈 [gzipped])

name                                  parsed_size           gzip_size
async-load-calypso-blocks-app-banner       +138 B  (+0.1%)      +68 B  (+0.1%)

React components that are loaded lazily, when a certain part of UI is displayed for the first time.

Legend

What is parsed and gzip size?

Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory.
Gzip Size: Compressed size of the JS and CSS files. This much data needs to be downloaded over network.

Generated by performance advisor bot at iscalypsofastyet.com.

@SiobhyB SiobhyB marked this pull request as ready for review September 14, 2022 17:21
@SiobhyB SiobhyB requested a review from a team September 14, 2022 17:23
@matticbot matticbot added the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Sep 14, 2022
Copy link
Member

@noahtallen noahtallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than my comment, I don't have any concerns with this approach 👍

const reducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches;

if ( reducedMotion ) {
animation.goToAndStop( 145, true );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only concern is whether the animations would ever get longer than this 😁

Maybe there's a way to do it dynamically?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we may be able to reference animation.totalFrames to avoid the 145 reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks both, I did experiment with animation.totalFrames but it only ever returned a value of 0 in my testing. 🤔 I'll have a further dive into this as I'm sure there must be some approach for getting the last frame that I'm missing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. It looks like we may need to await configuration ready. Also, I wonder if it makes sense to avoid autoplaying when the user prefers reduced motion.

diff --git a/client/blocks/app-banner/index.jsx b/client/blocks/app-banner/index.jsx
index 52b4c522b4..3e165828da 100644
--- a/client/blocks/app-banner/index.jsx
+++ b/client/blocks/app-banner/index.jsx
@@ -255,18 +255,20 @@ export class AppBanner extends Component {
 
 function BannerIcon( { icon } ) {
 	useEffect( () => {
+		const reducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches;
+
 		const animation = lottie.loadAnimation( {
 			container: document.querySelector( '.app-banner__icon' ),
 			renderer: 'svg',
 			loop: false,
-			autoplay: true,
+			autoplay: ! reducedMotion,
 			path: icon,
 		} );
 
-		const reducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches;
-
 		if ( reducedMotion ) {
-			animation.goToAndStop( 145, true );
+			animation.addEventListener( 'config_ready', () => {
+				animation.goToAndPlay( animation.totalFrames, true );
+			} );
 		}
 
 		return () => animation.destroy();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks David! I've gone ahead to implement your suggestion in f61ab8a.

Also, I wonder if it makes sense to avoid autoplaying when the user prefers reduced motion.

I think so, it was necessary to have this enabled when using goToAndStop, but not necessary with goToAndPlay, so I've followed your suggestion. 🙇‍♀️

Copy link
Member

@jsnajdr jsnajdr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little comment about the BannerIcon component: instead of globally querying an element with document.querySelector( '.app-banner__icon' ), it should get the element locally, with a ref.

const iconEl = useRef();
useEffect( () => {
  loadAnimation( { container: iconEl.current } );
}, [] );
return <div ref={ iconEl } className="app-banner__icon" />;

@SiobhyB
Copy link
Contributor Author

SiobhyB commented Sep 19, 2022

@jsnajdr, thank you for the suggestion! I've gone ahead to implement it in d680f2e and this PR's ready for review again. 🙇‍♀️

Copy link
Member

@dcalhoun dcalhoun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 LGTM. Tested the latest revision in Chrome.

Copy link
Member

@noahtallen noahtallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@SiobhyB SiobhyB merged commit 2536d9c into trunk Sep 20, 2022
@SiobhyB SiobhyB deleted the update/reduced-motion-check-in-app-banners branch September 20, 2022 08:34
@github-actions github-actions bot removed the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Sep 20, 2022
villanovachile pushed a commit that referenced this pull request Sep 27, 2022
This change takes a user's "reduced motion" preference into account before displaying an animation in the app banners. The animation will not play if the "reduced motion" is enabled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants