accessibility, React, JavaScript

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.


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.

<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.

<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.

Stay tuned for the rest of the series, where we'll cover inputs, buttons, and checking accessibility.

Header photo by Pavel Nekoranec on Unsplash