iTranslated by AI
[Basics] Let's Implement an Accessible Accordion in React Following the W3C Accessibility Guide!
Overview
In this article, we will explore the ARIA Authoring Practices Guide (hereinafter referred to as APG) provided by the W3C and implement an accessible accordion in React.
The goal of the Basic Part is to read the APG and understand the accessibility requirements for an accordion.
What you will gain from this article (Basic/Implementation Part)
✅ What the APG is
✅ The flow of understanding the APG
✅ Accessibility specifications for the Accordion
✅ How to implement a UI that ensures accessibility based on the APG by yourself
🔶 Basics of accessibility
🔶 About WAI-ARIA
❌ Code ready for production environments (APG explicitly states it is not intended for production environments[1])
Flow of this article (Basic/Implementation Part)
- What is the ARIA Authoring Practices Guide (APG)?
- Understanding the APG
- Deep dive into the Accordion Pattern/Example
--- Up to here in this part --- - React environment setup
- Implementing Accordion Group
- Implementing Accordion Header
- Implementing Accordion Panel
- Testing it out
1. What is the ARIA Authoring Practices Guide (APG)?
The ARIA Authoring Practices Guide (APG) is a site provided by the W3C where you can learn how to implement accessible UI.
In this article, we will read the guide for accordions, but guides for various UI patterns such as buttons, dialogs, and breadcrumbs are also provided.
According to the Introduction, the APG is described as follows:
What is the APG?
The APG creates guidance for developing web experiences for people who use assistive technologies and keyboard interfaces by integrating requirements from multiple accessibility and web technology specifications. The APG includes a library of accessible user interface patterns, implementation examples for the patterns, and guidance sections describing how to:
- Make common user interface patterns accessible by using the features of the Accessible Rich Internet Applications (ARIA) specification in combination with HTML, CSS, JavaScript, and other web technologies.
- Implement fundamental accessibility practices, such as providing accessible names, enabling keyboard interaction, communicating page structure to assistive technologies, and supporting high-contrast operating system settings using ARIA, HTML, and CSS.
Purpose of the APG
The primary purpose of the APG is to increase the delivery of robust and accessible experiences by building a broad understanding and fostering adoption of web accessibility practices that are:
- Consistent with the intent of ARIA, HTML, CSS, and related specifications.
- Support interoperability with assistive technologies as evaluated by the ARIA and Assistive Technologies project.
- Support usability for people with disabilities and promote practical techniques for developing modern GUIs that comply with WCAG requirements (Link to Japanese translation).
2. Understanding the APG
The APG defines all specifications for each pattern across two pages: the Pattern page and the Example page.
Each page consists of the following sections:
Pattern (Pattern page)
About This Pattern
Contains an overview of the pattern. This mainly includes what features it has and for what purpose it is used.
Example
Contains links to the Example page (Implementation example page).
Keyboard Interaction
Describes the keyboard operations supported by the component implemented with this pattern. It lists keys and their corresponding actions.
WAI-ARIA Roles, States, and Properties:
Describes the names of the WAI-ARIA roles, states, and properties used when implementing this pattern, along with their functions.
Example (Implementation example page)
About This Example
Describes the functional overview of the component implemented in the example.
Example
The execution result of the example code. You can actually interact with it.
Accessibility Features
Lists the accessibility-related features implemented in this example.
Keyboard Support
Lists keys and their corresponding actions. It is easier to understand when viewed alongside Pattern.Keyboard Interaction.
Role, Property, State, and Tabindex Attributes
Lists the WAI-ARIA names and roles used in this example in a table format. It is easier to understand when viewed alongside Pattern.WAI-ARIA Roles, States, and Properties:.
Assistive Technology Support
This is not included on every page, so it is omitted here.
Provides a practical summary of interoperability tests performed by the ARIA-AT project. Guidance on how to read this section is provided here:
JavaScript and CSS Source Code
Contains the JS and CSS source code for the example's execution result. You can run it on CodePen.
HTML Source Code
Contains the HTML source code for the example's execution result.
3. Analyzing the Accordion Pattern/Example
It's been a long read, but now we're getting to the main point.
We will analyze the APG Accordion Pattern/Example and translate it into a React implementation.
First, let's look through the Accordion Pattern/Example.
In the following flow, we will summarize the necessary DOM elements (WAI-ARIA for elements, etc.), keyboard operations, and accessibility operations in tables.
- Grasp the overview of necessary features
- Create a table of necessary DOM elements per component
- Summarize keyboard operations in a table
3.1. Grasping the Overview of Necessary Features
Reference Sections
- Pattern.About This Pattern
- Example.Example
Let's summarize Pattern.About This Pattern.
- Accordion: Used to reduce the need for scrolling when displaying multiple content sections on a single page. It displays a vertical series of headings and panels. When a user selects a heading, its panel is opened or closed.
- Accordion Header: A label or thumbnail representing a section of content. It also acts as a control for showing, and in some implementations, hiding the section of content. Additional elements like menu buttons may be added.
- Accordion Panel: Content associated with an accordion header. In some cases, a snippet of the content may remain visually persistent even when hidden.
It describes things that seem obvious.
Let's also look at the Example.
Everything written in "About This Pattern" is achieved.
3.2. Creating a Table of Necessary DOM Elements per Component
Reference Sections
- Pattern.WAI-ARIA Roles, States, and Properties:
- Example.Example
- Example.Role, Property, State, and Tabindex Attributes
- Example.HTML Source Code
3.2.1. Reading the General Structure from the Previous Section and Example
In section 3.1, we learned that two parts are necessary: the "Accordion Header" and "Accordion Panel".
Let's look for them in the Example.HTML Source Code.
Example.HTML Source Code Original
<div id="accordionGroup" class="accordion">
<h3>
<button type="button" aria-expanded="true" class="accordion-trigger" aria-controls="sect1" id="accordion1id">
<span class="accordion-title">
Personal Information
<span class="accordion-icon"></span>
</span>
</button>
</h3>
<div id="sect1" role="region" aria-labelledby="accordion1id" class="accordion-panel">
<div>
<fieldset>
<p>
<label for="cufc1">
Name
<span aria-hidden="true"> * </span>
:
</label>
<input type="text" value="" name="Name" id="cufc1" class="required" aria-required="true" />
</p>
<p>
<label for="cufc2">
Email
<span aria-hidden="true"> * </span>
:
</label>
<input type="text" value="" name="Email" id="cufc2" aria-required="true" />
</p>
<p>
<label for="cufc3"> Phone: </label>
<input type="text" value="" name="Phone" id="cufc3" />
</p>
<p>
<label for="cufc4"> Extension: </label>
<input type="text" value="" name="Ext" id="cufc4" />
</p>
<p>
<label for="cufc5"> Country: </label>
<input type="text" value="" name="Country" id="cufc5" />
</p>
<p>
<label for="cufc6"> City/Province: </label>
<input type="text" value="" name="City_Province" id="cufc6" />
</p>
</fieldset>
</div>
</div>
<h3>
<button type="button" aria-expanded="false" class="accordion-trigger" aria-controls="sect2" id="accordion2id">
<span class="accordion-title">
Billing Address
<span class="accordion-icon"></span>
</span>
</button>
</h3>
<div id="sect2" role="region" aria-labelledby="accordion2id" class="accordion-panel" hidden="">
<div>
<fieldset>
<p>
<label for="b-add1"> Address 1: </label>
<input type="text" name="b-add1" id="b-add1" />
</p>
<p>
<label for="b-add2"> Address 2: </label>
<input type="text" name="b-add2" id="b-add2" />
</p>
<p>
<label for="b-city"> City: </label>
<input type="text" name="b-city" id="b-city" />
</p>
<p>
<label for="b-state"> State: </label>
<input type="text" name="b-state" id="b-state" />
</p>
<p>
<label for="b-zip"> Zip Code: </label>
<input type="text" name="b-zip" id="b-zip" />
</p>
</fieldset>
</div>
</div>
<h3>
<button type="button" aria-expanded="false" class="accordion-trigger" aria-controls="sect3" id="accordion3id">
<span class="accordion-title">
Shipping Address
<span class="accordion-icon"></span>
</span>
</button>
</h3>
<div id="sect3" role="region" aria-labelledby="accordion3id" class="accordion-panel" hidden="">
<div>
<fieldset>
<p>
<label for="m-add1"> Address 1: </label>
<input type="text" name="m-add1" id="m-add1" />
</p>
<p>
<label for="m-add2"> Address 2: </label>
<input type="text" name="m-add2" id="m-add2" />
</p>
<p>
<label for="m-city"> City: </label>
<input type="text" name="m-city" id="m-city" />
</p>
<p>
<label for="m-state"> State: </label>
<input type="text" name="m-state" id="m-state" />
</p>
<p>
<label for="m-zip"> Zip Code: </label>
<input type="text" name="m-zip" id="m-zip" />
</p>
</fieldset>
</div>
</div>
</div>
Lines 2–13 appear to be the Accordion Header, and lines 14–86 appear to be the Accordion Panel.
<h3>
<button type="button" aria-expanded="true" class="accordion-trigger" aria-controls="sect1" id="accordion1id">
<span class="accordion-title">
Personal Information
<span class="accordion-icon"></span>
</span>
</button>
</h3>
<div id="sect1" role="region" aria-labelledby="accordion1id" class="accordion-panel">
<div>
<!-- //... -->
</div>
</div>
Also, the first line seems to be the parent element wrapping the Header and Panel.
<div class="accordion">
<!-- Headers and Panels follow -->
</div>
3.3.2. Creating a DOM Structure Table Based on the Information in 3.2.1
We will summarize them in the following format:
| No. | Component Name | Element (Parent) | attr | attr Type | Initial Value |
|---|---|---|---|---|---|
| 1 | Component Name | e.g., div or p (No. of parent if exists) | role | TypeScript type of the attribute | Initial Value |
From 3.2.1, we know it consists of three components:
- AccordionGroup
- AccordionHeader
- AccordionPanel
We will list the attributes for each component.
However, please note that Example.Example may not reflect everything described on the Pattern page (as it is just an example!).
Therefore, we should summarize the attributes while also referring to Pattern.WAI-ARIA Roles, States, and Properties: and Example.Role, Property, State, and Tabindex Attributes.
AccordionGroup
| No. | Component Name | Element (Parent) | attr | attr Type | Initial Value |
|---|---|---|---|---|---|
| 1 | AccordionGroup | div | class | string | - |
AccordionHeader
| No. | Component Name | Element (Parent) | attr | attr Type | Initial Value |
|---|---|---|---|---|---|
| 2 | AccordionHeader | h3 (1) | - | - | - |
| 3 | 〃 | button (2) | aria-expanded | boolean |
false |
| 4 | 〃 | 〃 | aria-disabled | boolean |
false |
| 5 | 〃 | 〃 | aria-controls | string |
ID of the corresponding AccordionPanel |
| 6 | 〃 | 〃 | id | string |
Unique ID |
| 7 | 〃 | 〃 | class | string |
- |
The reasons why WAI-ARIA is necessary are as follows:
- No.2
Pattern.WAI-ARIA Roles, States, and Properties: Point 2: Has arole headingwith anaria-levelvalue appropriate for the page's information architecture. - No.3
Pattern.WAI-ARIA Roles, States, and Properties: Point 1: The header title is contained within an element with a button role.
Pattern.WAI-ARIA Roles, States, and Properties: Point 3: Indicates whether the panel element associated with the header is expanded or collapsed. - No.4
Pattern.WAI-ARIA Roles, States, and Properties: Point 5: If the panel associated with the header is visible and the accordion does not permit collapsing of the panel, the button element has thearia-disabledattribute set. - No.5
Pattern.WAI-ARIA Roles, States, and Properties: Point 4:aria-controlsis set to the ID of the element containing the accordion panel content.
AccordionPanel
| No. | Component Name | Element (Parent) | attr | attr Type | Initial Value |
|---|---|---|---|---|---|
| 7 | AccordionPanel | div (1) | role | "region" |
- |
| 8 | 〃 | 〃 | aria-labelledby | string |
ID of the corresponding AccordionHeader |
| 9 | 〃 | 〃 | class | string |
- |
| 10 | 〃 | 〃 | hidden | boolean |
true |
The reasons why WAI-ARIA is necessary are as follows:
- No.7, 8
Pattern.WAI-ARIA Roles, States, and Properties: Point 6: Optional Each element that serves as a container for panel content hasrole regionandaria-labelledbywith a value that refers to the button that controls the panel's display.
Deliverables
Here is everything summarized together:
| No. | Component Name | Element (Parent) | attr | attr Type | Initial Value |
|---|---|---|---|---|---|
| 1 | AccordionGroup | div | class | string |
- |
| 2 | AccordionHeader | h3 (1) | - | - | - |
| 3 | 〃 | button (2) | aria-expanded | boolean |
false |
| 4 | 〃 | 〃 | aria-disabled | boolean |
false |
| 5 | 〃 | 〃 | aria-controls | string |
ID of the corresponding AccordionPanel |
| 6 | 〃 | 〃 | id | string |
Unique ID |
| 7 | 〃 | 〃 | class | string |
- |
| 8 | AccordionPanel | div (1) | role | "region" |
- |
| 9 | 〃 | 〃 | aria-labelledby | string |
ID of the corresponding AccordionHeader |
| 10 | 〃 | 〃 | class | string |
- |
| 11 | 〃 | 〃 | hidden | boolean |
true |
3.3. Summarizing Keyboard Operations in a Table
Reference Sections
- Pattern.Keyboard Interaction
- Example.Keyboard Support
- Example.JavaScript and CSS Source Code
- Example.HTML Source Code
We will summarize the keyboard operations in a table based on Pattern.Keyboard Interaction and Example.Keyboard Support.
Deliverables
| No. | Key | Action |
|---|---|---|
| 1 | Enter or Space | When focus is on the accordion header of a collapsed panel: Expands the associated panel. If the implementation allows only one panel to be expanded: Collapses any other expanded panel. When focus is on the accordion header of an expanded panel: Collapses the panel (if the implementation supports collapsing). |
| 2 | Tab | Moves focus to the next focusable element. All focusable elements in the accordion are included in the page's Tab sequence. |
| 3 | Shift + Tab | Moves focus to the previous focusable element. All focusable elements in the accordion are included in the page's Tab sequence. |
| 4 | Down Arrow | When focus is on an accordion header, Moves focus to the next accordion header. If focus is on the last accordion header, either does nothing or moves focus to the first accordion header. |
| 5 | Up Arrow | When focus is on an accordion header, Moves focus to the previous accordion header. If focus is on the first accordion header, either does nothing or moves focus to the last accordion header. |
| 6 | Home | When focus is on an accordion header, Moves focus to the first accordion header. |
| 7 | End | When focus is on an accordion header, Moves focus to the last accordion header. |
With this, I believe you now have a complete understanding of all the components, accessibility attributes, and keyboard operations!
5. Summary
Did you understand how to read the APG?
In this article, we analyzed the accordion pattern and understood its accessibility specifications.
If you understand the flow of understanding the APG, you should be able to read any guide!
In the next part, we will implement it in React!
See you soon!
Discussion