Skip to content

Commit

Permalink
add router skip link interaction and test
Browse files Browse the repository at this point in the history
TODO: figure out how to live code this from scratch and keep in source control
  • Loading branch information
Marcy Sutton committed Jul 29, 2019
1 parent b5f2332 commit 4f4cd63
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 24 deletions.
14 changes: 12 additions & 2 deletions cypress/integration/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
/// <reference types="Cypress" />
describe("Accessibility checks", () => {
beforeEach(() => {
})
it("Has no detectable a11y violations on load", () => {
cy.visit("http://localhost:8000")
cy.injectAxe()
cy.wait(500)
})
it("Has no detectable a11y violations on load", () => {
cy.checkA11y()
})
it("Handles focus on route change via click", () => {
cy.visit("http://localhost:8000")
cy.focused()
.should("not.have.class", "routeSkipLink")

cy.get('#page-navigation').find('a').eq(0).click()

cy.focused()
.should("have.class", "routeSkipLink")
})
})
8 changes: 8 additions & 0 deletions gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
exports.onRouteUpdate = ({ location, prevLocation }) => {
if (prevLocation !== null) {
const skipLink = document.querySelector('.routeSkipLink')
if (skipLink) {
skipLink.focus()
}
}
}
38 changes: 38 additions & 0 deletions src/components/better/route-target-heading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react"
import { css } from "@emotion/core"

const styles = css`
.routeSkipHeading {
position: relative;
}
.routeSkipLink {
display: inline-block;
margin-left: -0.75em;
opacity: 0;
position: absolute;
text-decoration: none;
}
.routeSkipLink:before {
content: '⇽';
display: block;
}
.routeSkipLink:focus,
.routeSkipLink:hover {
opacity: 1;
}
`
const RouteHeading = ({level = 1, targetID, children}) => {
const Heading = `h${level}`;
return (
<Heading css={styles} className="routeSkipHeading">
<a href={`#${targetID}`}
id="skip-main"
className="routeSkipLink"
aria-label={`back to ${targetID}`}
title={`Skip to ${targetID}`}>
</a>
{children}
</Heading>
)
}
export default RouteHeading
2 changes: 1 addition & 1 deletion src/components/site-chrome/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react'
const Header = ({ siteTitle }) => (
<header className="globalHeader">
<ul className="skip-link">
<li><a href="#main">Skip to main</a></li>
<li><a href="#skip-main">Skip to main</a></li>
</ul>
<h1>
<Link
Expand Down
2 changes: 1 addition & 1 deletion src/components/site-chrome/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Layout = ({ children }) => {
<div className="js-workshop-app">
<Header siteTitle={data.site.siteMetadata.title} />
<Navigation />
<main className="app" id="main" tabIndex="-1">
<main className="app" id="main">
{children}
</main>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/site-chrome/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ const Navigation = () => (
<h2 className="visually-hidden">
Navigation
</h2>
<ul>
<ul id="navigation" tabIndex="-1">
<li><Link to="/">App Home</Link></li>
<li><Link to="/slides">Slide deck</Link></li>
<li className={`navItemGroup ${navStyles.navItemGroup} `}>
<h3 className={`navHeading ${navStyles.navHeading}`}>
Demos
</h3>
<ul>
<ul id="page-navigation">
<li><Link to="/dropdown">Dropdown</Link></li>
<li><Link to="/async-form">Async Form</Link></li>
<li><Link to="/layout">Layout</Link></li>
Expand Down
9 changes: 8 additions & 1 deletion src/pages/404.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ import React from 'react'
import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "../components/better/route-target-heading.js"

const NotFoundPage = () => (
<Layout>
<SEO title="404: Not found" />
<h1>NOT FOUND</h1>
<RouteTargetHeading
level={2}
targetID="navigation"
>
NOT FOUND
</RouteTargetHeading>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
)
Expand Down
31 changes: 19 additions & 12 deletions src/pages/animation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@ import React from 'react'
import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "../components/better/route-target-heading.js"

import InaccessibleAnimationDemo from "../components/bad/animation"
import AccessibleAnimationDemo from "../components/better/animation"

const ReducedMotionDemoPage = () => (
<Layout>
<SEO title="Reducing motion" keywords={['javascript', 'accessibility', 'react']} />
<div>
<h2>Reducing motion for accessibility</h2>
<p>Let‘s build safe interfaces that delight.</p>
<section className="two-col">
<div>
<h3>Inaccessible animation demo [<a href="https://github.com/marcysutton/js-a11y-workshop/blob/master/src/components/bad/animation.js" aria-label="inaccessible demo source">source</a>]</h3>
<InaccessibleAnimationDemo />
</div>
<div>
<h3>More accessible animation demo [<a href="https://github.com/marcysutton/js-a11y-workshop/blob/master/src/components/better/animation.js" aria-label="more accessible demo source">source</a>]</h3>
<AccessibleAnimationDemo />
</div>
</section>
<RouteTargetHeading
level={2}
targetID="navigation"
>
Reducing motion for accessibility
</RouteTargetHeading>
<p>Let‘s build safe interfaces that delight.</p>
<section className="two-col">
<div>
<h3>Inaccessible animation demo [<a href="https://github.com/marcysutton/js-a11y-workshop/blob/master/src/components/bad/animation.js" aria-label="inaccessible demo source">source</a>]</h3>
<InaccessibleAnimationDemo />
</div>
<div>
<h3>More accessible animation demo [<a href="https://github.com/marcysutton/js-a11y-workshop/blob/master/src/components/better/animation.js" aria-label="more accessible demo source">source</a>]</h3>
<AccessibleAnimationDemo />
</div>
</section>
</div>
</Layout>
)
Expand Down
9 changes: 8 additions & 1 deletion src/pages/async-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React from "react"
import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "../components/better/route-target-heading.js"

import InaccessibleAsyncFormDemo from "../components/bad/async-form"
import BetterAsyncFormDemo from "../components/better/async-form"

Expand All @@ -11,7 +13,12 @@ const LiveRegionDemoPage = () => {
<Layout>
<SEO title="Live Regions" keywords={['javascript', 'accessibility', 'react']} />
<div>
<h2>Live Region Demo</h2>
<RouteTargetHeading
level={2}
targetID="navigation"
>
Live Region Demo
</RouteTargetHeading>
<section className="two-col">
<div>
<h3>Inaccessible async form demo [<a href="https://github.com/marcysutton/js-a11y-workshop/tree/master/src/components/bad/async-form.js" aria-label="accessible demo source">source</a>]</h3>
Expand Down
9 changes: 8 additions & 1 deletion src/pages/dropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ import React from "react"
import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "../components/better/route-target-heading.js"

import BadDropdown from '../components/bad/dropdown'
import BetterDropdown from '../components/better/dropdown'

const DropdownPage = () => (
<Layout>
<SEO title="Dropdown" keywords={['javascript', 'accessibility', 'react']} />
<div>
<h2>Dropdowns</h2>
<RouteTargetHeading
level={2}
targetID="navigation"
>
Dropdowns
</RouteTargetHeading>
<p>Tuck controls into an expandible, collapsible, accessible menu. Consider, however, whether you really need a dropdown.</p>
<p>Keyboard interactions will depend on what's in the menu. List of links? The TAB key is fine.<br />UI actions with a button or radio buttons? Script the arrow keys.</p>

Expand Down
9 changes: 8 additions & 1 deletion src/pages/enhanced-tablist.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ import React from "react"
import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "../components/better/route-target-heading.js"

import EnhancingList from "../components/better/enhancing-list"

const ProgressiveEnhancementPage = () => {
return (
<Layout>
<SEO title="Progressive Enhancement demos" keywords={['javascript', 'accessibility', 'react']} />
<div>
<h2>Progressive Enhancement Demo</h2>
<RouteTargetHeading
level={2}
targetID="navigation"
>
Progressive Enhancement Demo
</RouteTargetHeading>
<p>This widget outputs an unordered list in HTML at build time, with ARIA role enhancements when JavaScript loads.</p>
<p>To test this demo: run <code>gatsby build && gatsby serve</code> and turn off JavaScript in your browser</p>
<p><strong>[<a href="https://github.com/marcysutton/js-a11y-workshop/blob/master/src/components/better/enhancing-list.js">component source</a>]</strong></p>
Expand Down
10 changes: 9 additions & 1 deletion src/pages/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ import React from "react"
import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "../components/better/route-target-heading"

const IndexPage = () => (
<Layout>
<SEO title="Home" keywords={['javascript', 'accessibility', 'react']} />
<div>
<h2>Let’s make the web more accessible!</h2>
<RouteTargetHeading
level={2}
className="route-heading home"
targetID="navigation"
>
Let’s make the web more accessible!
</RouteTargetHeading>
<p>Here are some web accessibility tools, resources, and books for web designers and developers.</p>

<h3 id="Testing-Tools">Testing Tools</h3>
Expand Down
9 changes: 8 additions & 1 deletion src/pages/layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ import React from "react"
import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "../components/better/route-target-heading.js"

const SemanticsPage = () => (
<Layout>
<SEO title="Semantics" keywords={['javascript', 'accessibility', 'react']} />
<div>
<h2>Semantics and CSS layout</h2>
<RouteTargetHeading
level={2}
targetID="navigation"
>
Semantics and CSS layout
</RouteTargetHeading>
<p>CSS layouts are modern now. You can use CSS Grid and HTML5! Inspect these semantic layouts in DevTools and screen readers. Play with the rotor/elements list, heading navigation, </p>
<p><strong>[<a href="https://github.com/marcysutton/js-a11y-workshop/blob/master/src/pages/layout.jsx">page source</a>]</strong></p>
<div className="semantic-layout">
Expand Down
2 changes: 2 additions & 0 deletions src/slides/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,8 @@ export default AccessibleList

Play with progressive enhancement

https://codepen.io/marcysutton/pen/WBvRxq

https://marcysutton.github.io/js-a11y-workshop/enhanced-tablist

</Slide>
Expand Down

0 comments on commit 4f4cd63

Please sign in to comment.