iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article

[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)

  1. What is the ARIA Authoring Practices Guide (APG)?
  2. Understanding the APG
  3. Deep dive into the Accordion Pattern/Example
    --- Up to here in this part ---
  4. React environment setup
  5. Implementing Accordion Group
  6. Implementing Accordion Header
  7. Implementing Accordion Panel
  8. 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:
https://www.w3.org/WAI/ARIA/apg/about/at-support-tables/

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.
https://www.w3.org/WAI/ARIA/apg/patterns/accordion/
https://www.w3.org/WAI/ARIA/apg/patterns/accordion/examples/accordion/

In the following flow, we will summarize the necessary DOM elements (WAI-ARIA for elements, etc.), keyboard operations, and accessibility operations in tables.

  1. Grasp the overview of necessary features
  2. Create a table of necessary DOM elements per component
  3. 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:

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 has role region and aria-labelledby with 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!

脚注
  1. Introduction - APG is Not a Normative Standard ↩︎

GitHubで編集を提案

Discussion