Building an Accessible Product List Page with React

Accessibility is a critical part of web development. Creating a product list page that everyone, including people with disabilities, can easily use should be a top priority for developers. In this blog, I’ll guide you through building an accessible product list page using React and Tailwind CSS, with a focus on using screen reader-friendly techniques like Tailwind’s sr-only class.

Why Accessibility Matters

Accessibility is about making your web content available to everyone, regardless of their physical or cognitive abilities. By improving accessibility, you’re ensuring that your site is navigable by screen readers, keyboards, and other assistive technologies, opening your content to a broader audience.

Overview of What We'll Build:

We'll create a product list page that:

  1. Displays products with images, names, prices, and descriptions.

  2. Includes accessible markup, such as semantic HTML and ARIA attributes.

  3. Ensures keyboard and screen reader accessibility.

Accessibility Principles to Keep in Mind:

  • Semantic HTML: Use correct HTML elements to convey meaning (e.g., headings, lists, buttons).

  • Keyboard Navigation: Ensure the page is fully navigable using the keyboard.

  • ARIA Attributes: Provide additional context to screen readers where necessary.

  • Contrast and Focus States: Ensure text is readable and interactive elements are highlighted when focused.

Step 1: Setting up the Project

First, let’s set up our project. If you don't already have a React project, you can start one with Vite:

npm create vite@latest accessible-product-list --template react
cd accessible-product-list
npm install
npm install -D tailwindcss
npx tailwindcss init

Configure Tailwind by updating tailwind.config.js to point to your project files:

module.exports = {
    content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
};

Finally, add Tailwind’s directives to src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Step 2: Creating the Product List Component

Now, let's create a ProductList component that will display our products. For accessibility, we’ll use semantic HTML elements and ARIA (Accessible Rich Internet Applications) roles to ensure a better experience for screen reader users.

import React from 'react';

interface Product {
  id: number;
  name: string;
  price: string;
  imageUrl: string;
  description: string;
}

const products: Product[] = [
  {
    id: 1,
    name: 'Product 1',
    price: '$20.00',
    imageUrl: 'https://via.placeholder.com/150',
    description: 'This is the first product description.',
  },
  {
    id: 2,
    name: 'Product 2',
    price: '$30.00',
    imageUrl: 'https://via.placeholder.com/150',
    description: 'This is the second product description.',
  },
];

const ProductList = () => {
  return (
    <section aria-labelledby="products-heading" className="bg-gray-100 py-8 px-4">
      <h1 id="products-heading" className="text-3xl font-bold text-gray-900">
        Product List
      </h1>

      <div className="mt-6 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
        {products.map((product) => (
          <article
            key={product.id}
            aria-labelledby={`product-title-${product.id}`}
            className="bg-white shadow-md rounded-lg p-4"
          >
            <img
              src={product.imageUrl}
              alt={`${product.name} image`}
              className="w-full h-48 object-cover rounded-md"
            />
            <div className="mt-4">
              <h2 id={`product-title-${product.id}`} className="text-xl font-semibold">
                {product.name}
              </h2>
              <p className="text-gray-500">{product.description}</p>
              <p className="mt-2 text-lg font-bold text-gray-900">{product.price}</p>
            </div>
            <button
              className="mt-4 w-full bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
              aria-label={`Add ${product.name} to cart`}
            >
              <span className="sr-only">{`Add ${product.name} to cart for ${product.price}`}</span>
              Add to Cart
            </button>
          </article>
        ))}
      </div>
    </section>
  );
};

export default ProductList;

Step 3: Understanding the Accessibility Features

Here’s what makes this page accessible:

1. Use of Semantic HTML and ARIA
  • <section>: We wrapped the entire product list in a <section> element, with the aria-labelledby attribute connecting it to the H1 heading. This provides screen readers with context about the content.

  • <article>: Each product is inside an article element, giving semantic meaning to each individual product item.

  • aria-labelledby: This attribute ties the article element to its respective product title, ensuring screen readers understand the content structure.

2. Screen Reader-Only Text with sr-only
<span className="sr-only">{`Add ${product.name} to cart for ${product.price}`}</span>

We've utilized Tailwind CSS, which includes the sr-only class for accessibility. However, if it's not available in your setup, you can add the following custom style:

/* Below css will hide the element visually, but keeps it accessible to screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
  white-space: nowrap;
}
3. Keyboard Focus Styles

Interactive elements like buttons must have clear focus styles for users navigating with a keyboard. We’ve added focus utilities (focus:outline-none and focus:ring-2) from Tailwind CSS to make the focus state clear:

<button
  className="focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
  aria-label={`Add ${product.name} to cart`}
>
  <span className="sr-only">{`Add ${product.name} to cart for ${product.price}`}</span>
  Add to Cart
</button>

This ensures that users can easily see which element is focused as they tab through the page.

Conclusion

By following these steps, you can create a product list page that not only looks great but also ensures accessibility for users of all abilities. Using semantic HTML, ARIA attributes, and Tailwind’s sr-only class for screen reader text, you’ve built a more inclusive experience. Remember, accessibility is not a feature; it’s a foundation of good web design.

Do let me know your thoughts on this approach! I’d love to hear your feedback and any suggestions you may have for improving accessibility in web development.

Thanks for reading!