Templating Accessible Components in React Part 1: Navigation
Accessibility: we all know what it is and why it is important. So why do we not build accessible web applications? Does it cost more? In some instances, creating an accessible design and implementing that design can take longer than omitting accessibility altogether. But, we cannot forget about all of our users with impairments, whether permanent, temporary, or situational; an accessible web provides equal access for people of all abilities. We can make building accessible apps more cost effective by templating out some of our most used components.
Here at SmartLogic, all of our web applications will make use of some navigation, form inputs, and buttons, so we can start there. You can do the same; list out the components that your company, team, or yourself use most often and create accessible templates to reuse.
You do not need another library to make your app accessible. Barebones HTML is accessible so certainly we can make our React components accessible without the use of external libraries.
Navigation
For instance, the navigation of a web app can easily be made accessible. Let’s start with this simple bootstrap navigation:
Simple Bootstrap Navigation
<div className='d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm'>
<h5 className='my-0 mr-md-auto font-weight-normal'>Company name</h5>
<nav className='my-2 my-md-0 mr-md-3'>
<a className='p-2 text-dark' href='#'>
Features
</a>
<a className='p-2 text-dark' href='#'>
Enterprise
</a>
<a className='p-2 text-dark' href='#'>
Support
</a>
<a className='p-2 text-dark' href='#'>
Pricing
</a>
</nav>
<a className='btn btn-outline-primary' href='#'>
Sign up
</a>
</div>
Now we can Reactify it by creating a Navigation
and NavItem
component.
Navigation Component
<div className='d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm'>
<h5 className='my-0 mr-md-auto font-weight-normal'>Company name</h5>
<nav
className='my-2 my-md-0 mr-md-3'
aria-label='Accessibility App Main Navigation'
role='navigation'
>
<ul
id='mainnavmenu'
role='menubar'
aria-label='Accessibility App Main Navigation'
className='list-unstyled d-flex flex-row mb-0'
>
<NavItem />
</ul>
</nav>
</div>
Notice the accessibility attributes we provided for the navigation. First, we used an actual nav
tag versus a div
or a span. To that nav
tag we added an aria-label
and a role
. The aria-label
will describe what the navigation is for so here we stated the name of the web app ‘Accessibility App’ followed by the type of navigation ‘Main’ and then Navigation. You can use more descriptive language as you see fit. The more descriptive the better.
NavItem Component
<li role='none'>
<a className='p-2 text-dark' href=’#link’ role='menuitem'>
Link
</a>
</li>
We can utilize the power of reuse here by listing out all of our nav items and iterating through them to display the nav item component. We can spruce up the NavItem Component to the following:
import React from 'react'
interface IProps {
linkTo: string
name: string
}
const NavItem: React.FC<IProps> = ({ linkTo, name }) => {
const renderNavItem = (): JSX.Element => (
<li role='none'>
<a className='p-2 text-dark' href={`#${linkTo}`} role='menuitem'>
{name}
</a>
</li>
)
const renderAccessibilityError = (): JSX.Element => {
const hasHREF: boolean | string = linkTo ?? false
const hasName: boolean | string = name ?? false
const errorMessage: string | null = !hasHREF
? 'an href'
: !hasName
? 'a link name'
: null
return <p className='text-danger'>Please provide {errorMessage}</p>
}
return <>{(linkTo && name) ? renderNavItem() : renderAccessibilityError()}</>
}
export default NavItem
Looks like a lot going on but it is quite simple. First, we take two props
, the href
or linkTo
and the name
of the link. Both of these props are required. In our return, if those props
aren’t provided we render an error instead of the item. This will ensure that our devs remember to have a name
and an href
(if we do not have an href
it should not be a link).
The renderNavItem
function returns that original nav item component. The renderAccessibilityError
function does a couple things: 1) checks if there is an href missing or a name missing to determine the contents of the errorMessage
; and, 2) returns the error message as a p
tag.
We would use that component in our Navigation Component in our ul
tag. Updating that component looks like this:
import React from 'react'
import NavItem from './NavItem'
const Navigation: React.FC = () => {
const NAV_ITEMS = ['Features', 'Enterprise', 'Support', 'Sign Up']
const renderNavItems = (): JSX.Element[] => {
return NAV_ITEMS.map((item: string) => (
<NavItem name={item} linkTo={`#${item.toLowerCase()}`} />
))
}
return (
<div className='d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm'>
<h5 className='my-0 mr-md-auto font-weight-normal'>Company name</h5>
<nav
className='my-2 my-md-0 mr-md-3'
aria-label='Accessibility App Main Navigation'
role='navigation'
>
<ul
id='mainnavmenu'
role='menubar'
aria-label='Accessibility App Main Navigation'
className='list-unstyled d-flex flex-row mb-0'
>
{renderNavItems()}
</ul>
</nav>
</div>
)
}
export default Navigation
The renderNavItems
maps through the array of NAV_ITEMS
and returns that NavItem
component. Now anytime you want to add a nav item simply added it to that array and done. Your navigation remains accessible and it did not take much to get there.
The great thing about most frontend libraries like Bootstrap or Foundation is that the navigations are accessible out of the box. Input fields on the other hand require a little more thought.
Read more about templating accessible components in Part 2 on Inputs and Part 3 on Buttons.
Header photo by Pavel Nekoranec on Unsplash