Introduction
In this article, I'll explore one possible way to build a simple form in React, using Radix Primitives, with a special focus on accessibility. However, it's important to keep a perspective on the importance of this topic.
As Frontend/UX/Product engineers, we spend most of our days planning and developing products for people. Hence, I've always been interested in discovering how my work and ideas can make the world a better place for those who use our products, given the scope of my daily job.
Empathy should be a core skill and mindset for everyone designing and developing products for people. This core skill allows us to create products for people different from us and create products that fit their specific needs.
Designing and improving the usability of your products benefits everyone, not only those affected by a permanent or temporary disability.
If you're thinking about this last statement, take the Curb-Cut Effect as an example: a curb-cut is a small ramp placed on a curb. The idea behind this innovation dates back to the 1970s, and it was initially developed to help wheelchair users cross the street more safely.
However, this simple innovation led to the development of curb cuts as a standard feature in urban design, which not only helped the daily lives of wheelchair users but also improved the mobility of parents with strollers, travellers with rolling luggage and so on. You're probably now wondering how urban design specialists didn't even consider it a standard feature in the first place!
That's exactly what inclusive design tries to tackle: when we acknowledge our abilities as a baseline for the products we develop, we make this product easy to use for some people but exclude it from everyone else.
From my own experience, the moment you start to become aware of how the world still excludes people based on their disabilities, you will never see the world in the same way you did before and you'll suddenly become more aware of your surroundings.
Section I: Key Points on Web Accessibility
We talked about inclusive design and how it relates to accessibility. But what is accessibility, exactly? We can think of accessibility as a form of inclusion.
Accessibility is the extent to which a product can be used by people with disabilities. It aims to remove barriers and create solutions to include people with disabilities in accessing and using our products. It's also important to mention that the term disability can have different spectrums: it can be permanent (e.g., a blind person), temporary (e.g., a person that has an arm injury) or situational (e.g., a parent holding a child with one arm).
Even though this is not an extensive list, web accessibility encompasses all disabilities that affect how users access the web: auditory, visual, cognitive, neurological, physical, speech and visual impairments. However, as mentioned before, enforcing standards in your digital products also helps people without disabilities, for example, older people with changing abilities due to ageing, people who live in countries with a slow internet connection or expensive bandwidth, and so on. To gain some perspective on this subject, I invite you to take a look at the video "Web Accessibility Perspectives".
In this article, we'll focus more on the subset of web accessibility, which relates to the degree to which a digital product is usable by as many people as possible.
Beyond the spectrum of developing accessible products for everyone to enjoy, it's common that accessibility is not a priority on teams.
However, shifting your perspective regarding this subject will not only help your end users but also your business:
- Accessibility can have a major impact on the application's SEO (Search Engine Optimization). A website that is more usable for a wider range of users will naturally cause a higher engagement with the content, lowering bounce rates and causing a higher conversation rate, contributing to better SEO performance. An accessible website with a well-structured semantic HTML code can also be easier discovered by search engine crawlers, which can improve the relevance of your website's SEO;
- Improved customer satisfaction and brand reputation: when a website is more accessible than other competitors, the market share and reach of a business will increase, leading also increased revenue. Ultimately, not only will your business have a competitive edge, but you also have the opportunity to make a difference for people with disabilities, not only thinking of the impact you're creating in their lives but also demonstrating your commitment to inclusivity, diversity and social responsibility;
- Legal compliance: having an accessible digital product is already a legal requirement in certain countries and sectors. By being compliant with these laws, you can avoid potential legal penalties and avoid being sued. However, keep in mind that making your product available to a wider range of people due to legal reasons shouldn't be your only or main focus - our goal should always be to think about people first.
Regarding this last point, each country has different compliance standards and regulations. If you're curious about some data on lawsuits in the web accessibility field, given by the Bureau of Internet Accessibility:
- 2,352 web accessibility lawsuits were filed against U.S. businesses in 2021 (which represented a ~14% increase from 2020);
- The most-targeted industries were Consumer Goods, Services, & Retail, with 955 digital accessibility lawsuits;
- 97.4% of the top one million webpages on the internet have detectable WCAG 2.0 failures, an analysis from the non-profit WebAIM.
In Europe, the EU Web Accessibility Directive was adopted in 2016, providing a directive to oblige public websites and applications to be more accessible. This directive came into full effect in June 2021. However, the private sector will have three years, until June 2025, to oblige to this standard.
These international standards are established by the adoption of the Web Content Accessibility Guidelines (WCAG), which is a framework of principles, guidelines and checkpoints published by the World Wide Web Consortium (W3C). By conforming to the latest version of WCAG, we can ensure our applications are compliant with these standards. The WCAG is currently in version 2.1 and comes in 3 levels: A, AA, and AAA.
WCAG is based on four principles, which lay the foundation for anyone to access and use Web content: Perceivable, Operable, Understandable and Robust. WCAG defends that, if any of these principles are not true, then users with disabilities will be able to use the Web. I invite you to take a further reading about WCAG to gain a full picture of the work being done in this field.
To be completely up-to-date on the legislation for accessibility in a certain country or region, the W3C has a page that lists governmental policies by region.
Section II: Building an accessible e-commerce form
Introduction to Radix Primitives and Accessibility
As someone who creates digital technology, re-shifting your mindset into rethinking how products are built and developed is more challenging than learning a new tool or framework. However, nowadays, developers have a range of tools that allows them to make sure that accessibility is thought out from the very beginning of the development stage.
For this article, we're going to specifically focus on building an accessible React form, using Radix Primitives. This tutorial also assumes you are comfortable with React, Tailwind and Typescript.
However, keep in mind that this is only a possible tool to allow you to build more inclusive and accessible forms. Most of the UI component libraries have accessibility on top of their minds when being released. However, ensuring web accessibility standards is a process and not something you achieve only using these tools; these are all part of this process.
If you want to take a look at other component libraries, I invite you to read a little more about Chakra UI, which is a modular and accessible library which I personally really enjoyed working with. I also had the chance to read about React Aria, which is an accessible-focused package for React, developed by Adobe; however, this library has a great difference: instead of focusing on components, it uses a hooks-based API.
Regarding Radix Primitives, this is an open-source component library, focusing on accessible web applications, and providing an accessible set of low-level components. This library's components adhere to the WAI-ARIA framework, handling many of the implementation details related to accessibility. You can also take a look at the Accessibility documentation overview of the library.
Differently from other libraries, Radix Primitives has a particularity: it follows an incremental adoption. This means that each primitive can be installed individually instead of all library components. The primitives are also versioned independently, to facilitate incremental adoption.
Testing for accessibility
If you're curious about how other e-commerce websites tackle accessibility, one of the exercises I invite you to do is to turn on your screen reader (e.g., VoicerOver on Mac) and navigate into your favourite e-commerce website and try to buy a product and checkout.
Remember that you should simulate your environment as close as possible to a particular impairment, like blind or visually impaired users. This means that you will navigate on this website through a variety of keyboard commands to move the cursor, such as arrow keys, Tab, Shift+Tab, and other commands, while the screen reader tool announces its position. If you successfully made it to the last step, congratulations! Your favourite e-commerce does have accessibility as a priority, which is always something we should celebrate and encourage.
One of the first steps I would advise you is to precisely test out your application using a screen reader, to gain some perspective on how visually impaired users for example can navigate your menus. However, keep in mind that you're using your website as the one who built it, so make sure to defy your own biases against the products you are building.
In terms of automated tools, I'm going to mention a couple in this article, namely Axe, WAVE and Lighthouse. These tools are freely available and they can be an important starting point to start testing your application for accessibility issues:
- Axe is a set of tools created by Deque, to provide a solution for a testing library. To use Axe, one of the possible ways is to install Axe DevTools, which is a browser extension that provides you with a way to test your applications for accessibility issues. To start testing for accessibility using Axe DevTools, you just open the browser's Developer Tools, navigate to the Axe DevTools tab, and run an analysis on your desired webpage;
- WAVE is a suite of evaluation tools to allow developers to make their websites more accessible. There are two possible ways to run the tool: using the form, you can just enter a web page address and submit the form; or install a browser extension. Both give you a summary of the most important findings, like contrast errors, alerts, features, ARIA elements, etc.
- Lighthouse analyses web apps and pages, collecting modern performance metrics and insights on developer best practices. It's an open-source automated tool that you can run using Lighthouse in Chrome DevTools, allowing you to audits like Performance, Best Practices, SEO, PWA and Accessibility Audits. The tool uses WCAG guidelines to perform the audits for accessibility.
However, It's important to consider that while these tools can be very useful in detecting accessibility issues, they aren't completely foolproof, meaning that may not be able to identify every possible accessibility issue. The best way to ensure a throughout accessibility testing is to also make sure individuals with disabilities can be included in the testing process.
Building an accessible form with Radix Primitives & Tailwind
Whenever I'm working on creating a user interface without the help of a product manager or a designer, visualising the specific use case my users are facing helps me a lot in conceptualising the result.
For that, I try to create a basic wireframe to help me make sense of what I'm trying to achieve, as you can see below. For this tutorial, let's imagine that we have an e-commerce website that sells plants, and our focus is to build a checkout form:
Why are we focusing on the checkout process? This particular moment in the buying process has some potential consequences for the company, if users face any kind of difficulty, especially if the technology doesn't allow the user to complete this step:
- Users can abandon the checkout process. Imagine for example a use case where a visually impaired user can't navigate your website using a screen reader - causing frustration and potentially abandoning the purchase;
- Lost sales. Accessibility barriers in the checkout process may potentially lead to revenue loss for the company, excluding your product from several people;
- Legal consequences, depending on where your business is located;
- Damage to the company's reputation.
To ensure that the checkout process is as accessible as it can be, there are some key goals we need to ensure, functionally:
- The checkout process should be completed with a keyboard alone;
- The form should have correct and accurate descriptions and labels;
- Images and colours shouldn't be the only way to convey information (e.g. if the user inputs a wrong character and the form becomes red, without any other information, users with colour blindness, like Deuteranopia, may have trouble understanding what it's happening, because they don't see red the same way non-colour-blind users do);
- Mobile responsiveness should be guaranteed.
At this point, we have everything ready to start thinking about building our checkout form.
To start working on the form we'll be building, we want to start a new Vite project with React & Typescript, running:
npm create vite@latest reacting-to-a11y-article -- --template react-ts
cd reacting-to-a11y-article
npm install
npm run dev
To give some layout to the page, I'll be using Tailwind, but feel free to style your web app however you see fit.
If you don't want to go through the hassle of styling your app and want to jump straightaway to the form part, feel free to clone or fork the GitHub repo that contains the tutorial until this part and all the styles are configured already. This will allow you to start your web app with some styling already applied and ready to start tackling the challenge of building an accessible checkout form.
Since styling is not the main focus of this article, I'll only focus on the form part, so feel free to take a look at how the web app was styled up until this point.
The main branch contains all the necessary code for you to start working on the project, with the main layout and components already created.
However, if you want to jump until the final project with everything already created, navigate to the final branch.
If you decided to clone or fork the repo and you're on the main branch, you're expected to see this screen:
This means that the work we should do from now onwards will be related to adding the Radix Primitives components and making sure our form is as accessible as possible.
As you can see in the wireframe, we're going to ask the user for some data, to be able to finish the checkout process.
To start working on the form, start by installing the form component from your command line:
npm install @radix-ui/react-form
From now onwards, we're now able to start using the Form components from Radix Primitives.
For that, create a new .tsx
file in the components/checkout/formfolder
and import the newly installed component:
import * as Form from '@radix-ui/react-form';
Radix already provides developers with all the necessary parts we need to assemble an accessible form - our work should be to think about how we'll approach this.
First of all, we can start with the simplest part: asking for First and Last names. These inputs are not necessarily complex, but they should obey some rules (e.g., number of characters, special characters validation, etc.).
So we start by adding the necessary fields to our functional component, along with some styling:
import * as Form from "@radix-ui/react-form";
type UserInfoProps = {
type: "first" | "last";
};
function UserInfo({ type }: UserInfoProps) {
const errorMessageStyle = "text-[10px] text-red-700 font-bold opacity-80";
const formLabel = `${type.charAt(0).toUpperCase()}${type.slice(1)} Name`;
return (
<Form.Field name={`${type}name`}>
<Form.Label className="text-sm font-bold">{formLabel}</Form.Label>
<Form.Control asChild>
<input
className="mt-1 block w-full px-3 py-2 border-2 rounded-md text-sm shadow-md border-fernGreen"
type="text"
placeholder={`Your ${type} name`}
pattern="^[a-zA-Z0-9 ]*$"
required
/>
</Form.Control>
<Form.Message className={errorMessageStyle} match="valueMissing">
{`Please enter your ${type} name.`}
</Form.Message>
<Form.Message className={errorMessageStyle} match="patternMismatch">
Please enter only alphanumeric characters and spaces.
</Form.Message>
</Form.Field>
);
}
export default UserInfo;
Taking a further look into this code, we're using Field
as a wrapper for all the elements of our form: label
is responsible for labelling our form, while the control
is used to render the input field, which is an HTML input
element with common attributes like placeholder, type, etc. The message
components are used to display error messages when some of the conditions are present (one, for when the input field is empty and another one for when the input field contains characters that are not alphanumeric or spaces).
Forms are indeed a tricky subject because there's a lot for developers to consider. Forms have very complex validation requirements, they need to work consistently between browsers, and the user experience should be seamless, along with the accessibility requirements.
Until this point, we have only two fields:
How can we ensure these are accessible? One possible way is to turn on VoiceOver and look at how your interface is interpreted: Can you use the tab key to move between elements? Is your pointer device required? Is your form giving the right hints to allow the user to finish filling out all the necessary information? Another tool you could use is WAVE, and make sure there are no major accessibility issues detected; beyond WAVE, you can also try out Axe Tools and make sure no issues are detected.
Moving along to other fields, we should also request the user address and other fields, like Postal Code and Town/City:
import * as Form from "@radix-ui/react-form";
function UserAddress() {
const errorMessageStyle = "text-[10px] text-red-700 font-bold opacity-80";
return (
<Form.Field name="address1">
<Form.Label className="text-sm font-bold">Shipping Address</Form.Label>
<Form.Control asChild>
<input
className="mt-1 block w-full px-3 py-2 border-2 rounded-md text-sm shadow-md border-fernGreen"
type="text"
placeholder="Your street address"
required
/>
</Form.Control>
<Form.Message className={errorMessageStyle} match="valueMissing">
Please enter your street address.
</Form.Message>
</Form.Field>
);
}
export default UserAddress;
import * as Form from "@radix-ui/react-form";
function UserPostalCode() {
const errorMessageStyle = "text-[10px] text-red-700 font-bold opacity-80";
return (
<Form.Field name="zip">
<Form.Label className="text-sm font-bold">Postal Code</Form.Label>
<Form.Control asChild>
<input
className="mt-1 block w-full px-3 py-2 border-2 rounded-md text-sm shadow-md border-fernGreen"
type="text"
placeholder="e.g. 10001"
required
pattern="^\d{5}$"
/>
</Form.Control>
<Form.Message className={errorMessageStyle} match="valueMissing">
Please enter your postal code.
</Form.Message>
<Form.Message className={errorMessageStyle} match="patternMismatch">
Please enter a US postal code in the format of five digits.
</Form.Message>
</Form.Field>
);
}
export default UserPostalCode;
import * as Form from "@radix-ui/react-form";
function UserCity() {
return (
<Form.Field name="city">
<Form.Label className="text-sm font-bold">Town/City</Form.Label>
<Form.Control asChild>
<input
className="mt-1 block w-full px-3 py-2 border-2 rounded-md text-sm shadow-md border-fernGreen bg-gray-200"
type="text"
value="New York"
placeholder="e.g. New York"
disabled
/>
</Form.Control>
</Form.Field>
);
}
export default UserCity;
import * as Form from "@radix-ui/react-form";
function UserCountry() {
return (
<Form.Field name="country">
<Form.Label className="text-sm font-bold">Country</Form.Label>
<Form.Control asChild>
<input
className="mt-1 block w-full px-3 py-2 border-2 rounded-md text-sm shadow-md border-fernGreen bg-gray-200"
type="text"
value="United States"
placeholder="e.g. United States"
disabled
/>
</Form.Control>
</Form.Field>
);
}
export default UserCountry;
At the end of working on these fields, you should end up with a solution closer to this one:
Now that we finished building most of the fields we needed for this tutorial, how can we be sure that this interface is overall accessible?
One possible way is to turn on VoiceOver and have a look at how your interface is interpreted: Can you use the tab key to move between elements? Is your pointer device required? Is your form giving the right hints to allow the user to finish filling out all the necessary information? Using VoiceOver or another screen reader is a great way to perceive how your interface sounds to visually impaired users.
Another tool you could use is WAVE, and make sure there are no major accessibility issues detected; beyond WAVE, you can also try out Axe Tools and make sure no issues are detected.
Running these tools, it seems that this interface, overall, follows the best practices in terms of accessibility:
However, I would like to recall that accessibility guidelines help guarantee standards across the web, but your interfaces should be tested by users with disabilities; remember that since we are building our products, we have a bias towards thinking that if these tools don't point out clear problems, everything should be fine. Inclusion means being able to think beyond our perception and bias to consider that other people might not perceive our product in the same way as us.
Closing remarks
Accessibility should not be a one-person job. Building inclusive and accessible digital experiences is a team job: developers, designers, product managers, stakeholders, and so on.
Even though developers already have available a set of tools that allows them to test thoroughly their applications in terms of accessibility guidelines and best practices, ultimately, the best way to ensure our product is inclusive is to allow users with disabilities to try it out and see what kind of needs aren't we considering in developing the product and how it can be improved.
Every detail we take into consideration when building products that aim to fix accessibility issues is a step ahead for our users and it's something we should aim for when working on digital products.
This article only covers a very small spectrum of how e-commerce platforms can improve their product, be inclusive and also consider users with disabilities. As we discussed earlier in the introduction part, having an inclusion principle as a standard allows everyone to benefit from this - not only this will cover your business in terms of lawsuits, but more people will enjoy your product and that should be the main focus of guaranteeing accessibility: creating products for everyone to enjoy and contribute to.
Accessibility and inclusive design work together to ensure our products are compliant and usable for all. About 16% of the world's population lives with a form of disability. So, keep in mind: Not all disabilities are visible. We need to make sure we can reflect on our own biases to create an inclusive digital world.
If you're particularly interested in this subject and want to learn more about this sentence and how it's being applied, I invite you to see how London is trying to tackle the inclusion issue and promote travelling more kindly in the London transport system:
Plant photos used in "Greenery Co." by feey on Unsplash
Cover photo by Paul Green on Unsplash
Closing remarks photo by Ben Kolde on Unsplash
Top comments (0)