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:
Displays products with images, names, prices, and descriptions.
Includes accessible markup, such as semantic HTML and ARIA attributes.
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 thearia-labelledby
attribute connecting it to the H1 heading. This provides screen readers with context about the content.<article>
: Each product is inside anarticle
element, giving semantic meaning to each individual product item.aria-labelledby
: This attribute ties thearticle
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!