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

Scroll to top by default in full-page client-side navigation experiment #62342

Open
wants to merge 4 commits into
base: trunk
Choose a base branch
from

Conversation

SantosGuillamot
Copy link
Contributor

@SantosGuillamot SantosGuillamot commented Jun 5, 2024

What?

Automatically scroll to the top of the page when in full-page client-side navigation experiment.

Apart from that, it adds a check to ensure it doesn't overlap with other data-wp-on--click directives. This way, they can decide their own behavior as the query loop is doing, for example.

Why?

I believe it is the expected behavior.

How?

  • Using window.scrollTo( 0, 0 ) after calling actions.navigate.
  • Check that target doesn't have the data-wp-on--click directive.

Testing Instructions

  1. Go to Gutenberg->Experiments and enable the full-page client-side aviation experiment.
  2. Go to the frontend and scroll.
  3. Click on any link and check it scroll to the top of the page.
  4. Go to a query loop, click on "Next page", and check it doesn't scroll to the top of the page.
Copy link

github-actions bot commented Jun 5, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: SantosGuillamot <santosguillamot@git.wordpress.org>
Co-authored-by: luisherranz <luisherranz@git.wordpress.org>
Co-authored-by: michalczaplinski <czapla@git.wordpress.org>
Co-authored-by: alexstine <alexstine@git.wordpress.org>
Co-authored-by: joedolson <joedolson@git.wordpress.org>
Co-authored-by: afercia <afercia@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@SantosGuillamot SantosGuillamot added [Type] Enhancement A suggestion for improvement. [Feature] Interactivity API API to add frontend interactivity to blocks. labels Jun 5, 2024
Copy link
Member

@luisherranz luisherranz left a comment

Choose a reason for hiding this comment

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

Nice!! 😄👏👏

I haven't tested it yet, but one thing:

packages/interactivity-router/src/index.ts Outdated Show resolved Hide resolved
@alexstine
Copy link
Contributor

Is there a need to manage focus here if we're going to scroll the page?

@luisherranz
Copy link
Member

Is there a need to manage focus here if we're going to scroll the page?

Like to move it to the first focusable element on the page?

@SantosGuillamot
Copy link
Contributor Author

SantosGuillamot commented Jun 6, 2024

Like to move it to the first focusable element on the page?

If I am not mistaken, it is already doing that automatically.

EDIT: No, it doesn't 😄 I can try to make it work if that's the desired behavior.

@luisherranz
Copy link
Member

luisherranz commented Jun 6, 2024

It automatically announces the navigation on the screen reader and shows the animation with the top loading bar, right? But it doesn't change the focus.

@michalczaplinski michalczaplinski self-requested a review June 10, 2024 20:50
Copy link
Contributor

@michalczaplinski michalczaplinski left a comment

Choose a reason for hiding this comment

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

Looks good, Mario! 👍 Just one comment:

If the link includes an anchor, we should scroll to the anchored element. So, e.g. http://my-site.local/hello-world#first-heading should scroll to the element with the first-heading id.

@SantosGuillamot
Copy link
Contributor Author

You're right we are overriding the scroll to anchor element with this new scrollTo. I assume we want to move this logic outside of the navigate function as well.

@SantosGuillamot
Copy link
Contributor Author

I've just pushed a change, moving the scroll to anchor logic outside of navigate. That should solve the issue mentioned.

Apart from that, I am not sure how the screen reader and focus should work. There are a couple of things:

  • Right now, if I activate voiceOver, the screen reader doesn't seem to announce the navigation. Is that expected? I am not familiar with that part of the code.
  • In order to place focus on the first element, I tested something like the following, but I am not sure if there is a simpler approach:
// Set focus at the top of the page.

// Using selectors from https://github.com/KittyGiraudel/focusable-selectors/blob/main/index.js
const not = {
  inert: ":not([inert]):not([inert] *)",
  negTabIndex: ':not([tabindex^="-"])',
  disabled: ":not(:disabled)",
};

const focusableSelectors = [
  `a[href]${not.inert}${not.negTabIndex}`,
  `area[href]${not.inert}${not.negTabIndex}`,
  `input:not([type="hidden"]):not([type="radio"])${not.inert}${not.negTabIndex}${not.disabled}`,
  `input[type="radio"]${not.inert}${not.negTabIndex}${not.disabled}`,
  `select${not.inert}${not.negTabIndex}${not.disabled}`,
  `textarea${not.inert}${not.negTabIndex}${not.disabled}`,
  `button${not.inert}${not.negTabIndex}${not.disabled}`,
  `details${not.inert} > summary:first-of-type${not.negTabIndex}`,
  `iframe${not.inert}${not.negTabIndex}`,
  `audio[controls]${not.inert}${not.negTabIndex}`,
  `video[controls]${not.inert}${not.negTabIndex}`,
  `[contenteditable]${not.inert}${not.negTabIndex}`,
  `[tabindex]${not.inert}${not.negTabIndex}`,
];
const firstFocusableElement = document.querySelector(
  focusableSelectors.join(", ")
);
firstFocusableElement.focus();
@alexstine
Copy link
Contributor

@SantosGuillamot Focus does not need to change if the page change is announced.

@alexstine
Copy link
Contributor

@SantosGuillamot Okay, just gave this a test. There is no announcement on page transition so we've got two options.

  1. Try to guess where to drop focus.
  2. Use WordPress A11Y speak() to announce the new page. Something like this.
import { speak } from '@wordpress/a11y';
import { sprintf, __ } from '@wordpress/i18n'; 
// Page switch happens
const announceTitle = sprintf( __( 'Now displaying: %s', updatedTitle ) );
speak( announceTitle );

This way, the user is aware of the page change and can navigate from there. It is hard to say which would be better considering in standard page reloads, users always end up at the start of the site content, generally the skip to main content link.

This is a great example of where user testing would be super valuable, study group.

CC: @joedolson @afercia

Thanks.

@joedolson
Copy link
Contributor

If it's going to visually scroll to a new location, it definitely needs to also change the focus position. The preferred location would be a skip to content link at the top of the page; but if we need to account for the scenario where there is no skip to content link, then it may need to have a fallback, such as setting the focus position at the start of the body element.

But any visual change of position must include a change of focus.

Setting up a demo environment to conduct some user studies would be very valuable, so we could have people test it in common test scenarios using a variety of different cases: e.g., with skip link and without.

@afercia
Copy link
Contributor

afercia commented Jun 20, 2024

Single-page app navigation like this 'full-page client-side navigation' must replicate all the native feature of 'classic' navigation with page reload to be fully usable and accessible. Most of this feature are native browser features when a new resource (typically a web page) is loaded in the browser.

  • A new page loads: besides the visual clearly visible feedback that a new page loaded, screen readers do announce a new page is loading and / or when it has completely loaded. In a single-pave navigation model, this needs to be implemented with an audible message.
  • The document title updates with the one fo the new page. As fare as I see this is already implemented.
  • Screen readers do announce the document title as first thing when a new page loads. This needs to be implemented in a single-page app model.
  • Typically, screen readers start reading all the page from the beginning, unless users disable this feature. Not sure this can be implemented via scripting but at the very least focus should be moved at the top of the page.
  • In a native navigation model, the tab sequence stars from the document root when a new page loads. One more reason to move focus at the top.
  • A single-page app navigation model needs to be integrated with the Browser history API with a routing mechanism. As far as I can tell this is implemented already.
  • Any other point I may be missing? @joedolson @alexstine
@luisherranz
Copy link
Member

That makes sense, thank you.

For the screen reader announcement, we should reuse the same code that is being used in the region-based client-side navigation (I thought that was already included, but it might not be).

If it's also necessary to read the new page title, we can include it in the same announcement and also do the same for the region-based client-side navigation.

For the focus, we can try moving it to the SkipToContent link and if that's not available, fall back to the beginning of the body element.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Interactivity API API to add frontend interactivity to blocks. [Type] Enhancement A suggestion for improvement.
6 participants