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

Gatsby a11y helpers #26376

Closed
wants to merge 6 commits into from
Closed

Gatsby a11y helpers #26376

wants to merge 6 commits into from

Conversation

madalynrose
Copy link
Contributor

Description

This PR creates a solution for #21059.

There is currently a working bare-bones prototype that gives developers control around client-side routing behavior that Gatsby can hook into.

As it stands we have two components. They are based heavily on @reach/skip-nav and their current implementation is fairly simplistic.

RouteAnnouncement

Gatsby looks for this element when it navigates to a new page in cache-dir/navigation.js and outputs its contents to Gatsby's Route Announcer.

This component can be used multiple times in a site (e.g. implemented to always use page title in a general Layout.js file and then overridden in specific pages or posts for custom announcements) and Gatsby will use the most specific instance. If the announcement should be different from the title of the page, Gatsby makes it easy to change without visually displaying that custom text with the visuallyHidden flag.

Source

/**
 * Announce
 *
 * Renders a wrapping div around content to announce on client side route change
 *
 */
export const RouteAnnouncement: React.FC<RouteAnnouncementProps> = ({
  children,
  visuallyHidden = false,
  useTitle = false,
  ...props
}) => {
  const hiddenStyle: React.CSSProperties = {
    position: `absolute`,
    top: `0`,
    width: `1`,
    height: `1`,
    padding: `0`,
    overflow: `hidden`,
    clip: `rect(0, 0, 0, 0)`,
    whiteSpace: `nowrap`,
    border: `0`,
  }

  const initialStyle: React.CSSProperties = {}

  return (
    <div
      {...props}
      data-gatsby-route-announcement={true}
      data-gatsby-route-announcement-use-title={useTitle}
      style={visuallyHidden ? hiddenStyle : initialStyle}
    >
      {children}
    </div>
  )
}

Examples

const layout = ({ children }) => {
  return (
    <div>
      <Header/>
      <RouteAnnouncement useTitle={true}/>
      <main>{children}</main>
      <Footer/>
    </div>
  )
}
const blogPost = ({ data }) => {
  { title, html } = data.post
  return (
    <Layout>
      <RouteAnnouncement><h1>{title}</h1></RouteAnnouncement>
      <PostContent content={html} />
    </Layout>
  )
}
//if we want to announce something just for screen reader users because the title or h1 on the page isn't very descriptive we can!
const specialPage = ({ data }) => {
  { title, html } = data.post
  return (
    <Layout>
      <RouteAnnouncement visuallyHidden={true}>welcome to the special page!</RouteAnnouncement>
      <h1>Hello!</h1>
      <GenericContent />
    </Layout>
  )
}

RouteFocus

Source

/**
 * RouteFocus
 *
 * Renders a div that should wrap a small, interactive element (ideally a skip link) 
 * placed at the top of the page that Gatsby will detect and send focus to on 
 * client side route changes
 */
export const RouteFocus: React.FC<RouteFocusProps> = ({
  children,
  ...props
}) => {
  return (
    <div
      {...props}
      data-gatsby-csr-focus={true}
    >
      {children}
    </div>
  );
}; 

Example

const Layout = ({children}) => (
  <>
    <RouteFocus><SkipNavLink /></RouteFocus>
    <Header /> //main navigation lives here
    <SkipNavContent/>
     <main>{children}</main>
    <Footer />
  </>
)

TODO

Validation!

Let's set developers up for accessibility success by highlighting common mistakes. We'll likely do this validation inside of a util plugin shipped with Gatsby rather than in the components themselves so we can leverage a11y testing libraries to make our assertions without bloating the component package.

  • input to <RouteAnnouncement> must meet guidelines for page Titles to emulate the behavior of static browser route changes (e.g. check length of text to ensure it doesn't overwhelm)
  • <RouteFocus> must wrap an interactive element, ideally a skip link, per @marcysutton's research

Robustness

  • use refs and hooks/Providers to surface the most specific instance of <RouteAnnouncement>

Related Issues

Fixes #21059 & #20801
Related to #19290

@madalynrose madalynrose requested review from a team as code owners August 11, 2020 19:30
@gatsbot gatsbot bot added the status: triage needed Issue or pull request that need to be triaged and assigned to a reviewer label Aug 11, 2020
@gatsby-cloud

This comment has been minimized.

@gatsby-cloud

This comment has been minimized.

@gatsby-cloud

This comment has been minimized.

@gatsby-cloud-staging

This comment has been minimized.

@madalynrose madalynrose added status: inkteam assigned and removed status: triage needed Issue or pull request that need to be triaged and assigned to a reviewer labels Aug 11, 2020
@gatsby-cloud

This comment has been minimized.

@gatsby-cloud-staging

This comment has been minimized.

@bitmaks bitmaks added the topic: a11y Related to accessibility label Aug 12, 2020
Copy link
Contributor

@laurieontech laurieontech left a comment

Choose a reason for hiding this comment

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

Left some comments on the README. Overall looks great!

@@ -0,0 +1,102 @@
# Skip Navigation

Many people navigate the web primarily using the keyboard, often with a screen reader. If main content is not the first thing on a page (e.g. if there is a top site banner or navigation menu before the main content starts), these users have to tab through every single link before they can get to the main content. As this is cumbersome and not an ideal user experience, it is recommended that a "skip link" be added at the very top of every page that links to the main content, allowing users to skip right to the most relevant information on the page. It is idiomatic to position this link off of the page so it can only be reached with a keyboard and doesn't interrupt visual flow of the page, and to display the link if it is focused.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Many people navigate the web primarily using the keyboard, often with a screen reader. If main content is not the first thing on a page (e.g. if there is a top site banner or navigation menu before the main content starts), these users have to tab through every single link before they can get to the main content. As this is cumbersome and not an ideal user experience, it is recommended that a "skip link" be added at the very top of every page that links to the main content, allowing users to skip right to the most relevant information on the page. It is idiomatic to position this link off of the page so it can only be reached with a keyboard and doesn't interrupt visual flow of the page, and to display the link if it is focused.
Many people navigate the web primarily using the keyboard, often with a screen reader. If the main content is not the first thing on a page (e.g. if there is a top site banner or navigation menu before the main content starts), these users have to tab through every single link before they can get to the main content. As this is cumbersome and not an ideal user experience, it is recommended that a "skip link" be added at the very top of every page that links to the main content, allowing users to skip right to the most relevant information on the page. It is idiomatic to position this link off of the page so it can only be reached with a keyboard and doesn't interrupt visual flow of the page, and to display the link if it is focused.


### Add `SkipNavLink` and `SkipNavContent` to your Layout

```javascript:title=src/components/layout.js
Copy link
Contributor

Choose a reason for hiding this comment

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

This won't work in a README, better to remove the title piece

```

1. **`/src`**: This directory will contain all of the code related to what you will see on the front-end of your site. It has pages and components to be used in those pages.
1. **`/components`**: This directory will contain all of the code related to what you will see on the front-end of your site. It has pages and components to be used in those pages.
Copy link
Contributor

Choose a reason for hiding this comment

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

components doesn't have pages? I think this was inadvertently copied from src

1. **`/src`**: This directory will contain all of the code related to what you will see on the front-end of your site. It has pages and components to be used in those pages.
1. **`/components`**: This directory will contain all of the code related to what you will see on the front-end of your site. It has pages and components to be used in those pages.

1. **`/header.css`**: This ensures that our navigation links have focus rings, which are important visual cues when navigating by keyboard.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
1. **`/header.css`**: This ensures that our navigation links have focus rings, which are important visual cues when navigating by keyboard.
1. **`/header.css`**: This ensures that your navigation links have focus rings, which are important visual cues when navigating by keyboard.

1. **`/seo.js`**: This information is necessary to give the site a 100% in [Lighthouse](https://developers.google.com/web/tools/lighthouse) for accessibility since screen readers need a title and language.

1. **`/pages`**: This directory contains pages that will be automatically built and served by Gatsby. This example includes three pages to demonstrate navigation between them and how your skip nav link behaves. All of these pages use the `Layout` component.
1. **`gatsby-browser.js`**: This file is where you tell the Gatsby to focus the skip navigation when users navigate to a new page.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
1. **`gatsby-browser.js`**: This file is where you tell the Gatsby to focus the skip navigation when users navigate to a new page.
1. **`gatsby-browser.js`**: This file is where you tell the browser to focus the skip navigation when users navigate to a new page.


1. **`/pages`**: This directory contains pages that will be automatically built and served by Gatsby. This example includes three pages to demonstrate navigation between them and how your skip nav link behaves. All of these pages use the `Layout` component.
1. **`gatsby-browser.js`**: This file is where you tell the Gatsby to focus the skip navigation when users navigate to a new page.
1. **`/cypress`**: This directory is where tests and [Cypress](https://www.cypress.io/) configuration live. You're going to focus on the test. If you want to learn more about using Cypress, check out the [example](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-cypress).
Copy link
Contributor

Choose a reason for hiding this comment

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

"You're going to focus on the test". This is a bit confusing. Do you mean that the test makes use of navigation focus?

1. **`/pages`**: This directory contains pages that will be automatically built and served by Gatsby. This example includes three pages to demonstrate navigation between them and how your skip nav link behaves. All of these pages use the `Layout` component.
1. **`gatsby-browser.js`**: This file is where you tell the Gatsby to focus the skip navigation when users navigate to a new page.
1. **`/cypress`**: This directory is where tests and [Cypress](https://www.cypress.io/) configuration live. You're going to focus on the test. If you want to learn more about using Cypress, check out the [example](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-cypress).
1. **`/integrations/skip-nav.test.js`**: runs two tests to ensure that you have a skip link and that the skip link is focused on page navigation.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
1. **`/integrations/skip-nav.test.js`**: runs two tests to ensure that you have a skip link and that the skip link is focused on page navigation.
1. **`/integrations/skip-nav.test.js`**: This rile runs two tests to ensure that you have a skip link and that the skip link is focused on page navigation.


### Running the example

1. Use the CLI to create run this example
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add install instructions?


### Problem

Further research was conducted athte Inclusive Design 24 virtual conference in October 2019 and, new [techniques were developed](https://www.youtube.com/watch?v=Tr21FqQQv-U) to support a wider variety of workflows and disabilities.

Choose a reason for hiding this comment

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

Suggested change
Further research was conducted athte Inclusive Design 24 virtual conference in October 2019 and, new [techniques were developed](https://www.youtube.com/watch?v=Tr21FqQQv-U) to support a wider variety of workflows and disabilities.
Further research was conducted at the Inclusive Design 24 virtual conference in October 2019 and, new [techniques were developed](https://www.youtube.com/watch?v=Tr21FqQQv-U) to support a wider variety of workflows and disabilities.

@kyleboss
Copy link

LOVE THIS!!! 🥳 I stumbled upon this PR when looking for localizing the "Navigated to" screen-reader announcements. So much good info here!!!

QQ: What would the default experience be if a dev does not implement <RouteAnnouncement />? Does it default to announcing the title of the new page?

jtoar pushed a commit to redwoodjs/redwood that referenced this pull request Mar 2, 2021
all work borrows heavily from madalyn's great work at gatbsy:
- gatsbyjs/gatsby#26376

the changes here include:
- prerender compatability
- manage announcements (RouteAnnouncement -> h1 -> document.title -> location.pathname)
- manage focus (WIP)
- adds four components: RouteAnnouncement, RouteFocus, and Reach UI's
SkipNavLink, SkipNavContent (also WIP as they need CSS)
jtoar pushed a commit to redwoodjs/redwood that referenced this pull request Mar 17, 2021
all work borrows heavily from madalyn's great work at gatbsy:
- gatsbyjs/gatsby#26376

the changes here include:
- prerender compatability
- manage announcements (RouteAnnouncement -> h1 -> document.title -> location.pathname)
- manage focus (WIP)
- adds four components: RouteAnnouncement, RouteFocus, and Reach UI's
SkipNavLink, SkipNavContent (also WIP as they need CSS)
@LekoArts LekoArts mentioned this pull request Mar 22, 2022
@LekoArts LekoArts closed this Jul 20, 2022
@LekoArts LekoArts deleted the gatsby-a11y-helpers branch July 20, 2022 05:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: a11y Related to accessibility
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add additional configuration options for client-side routing announcements
5 participants