React ARIA Widgets

React ARIA Widgets is a collection of React primitives designed to help developers implement the patterns found in the ARIA Authoring Practices Guide (APG).

Please note that this library should NOT be used in a production environment! It's still in a pre-alpha stage and the API is subject to major breaking changes.

Features

  • Unstyled, accessible components that are easily composable and customizable
  • Modularized design that gives developers the freedom to choose which hooks, components, etc. they wish to import
  • Adheres to the APG complete with focus control and keyboard support.

Installation

With npm:

npm install react-aria-widgets

With Yarn:

yarn add react-aria-widgets

Please note that, at the moment, React ARIA Widgets only supports React v18.

Usage

React ARIA Widgets provides building blocks for developers to implement their own component libraries. Though it provides features such as accessibility and state management, that alone is insufficient for it to be used out of the box.

For example, certain patterns rely on styling to handle some of the key features that define that pattern. One instance is the accordion pattern - without styles, expanding/collapsing the constituent sections wouldn't behave properly. However, React ARIA Widgets provides no default styles.

Still, React ARIA Widgets provides state management and aims to maximize compatibility with the variety of CSS libraries, frameworks, etc., in the front-end ecosystem. Building fully working implementations of each pattern can be as simple as passing in some className props using your framework of choice.

Hello world!

Hello world!

Hello world!

import { Accordion, AccordionItem, AccordionHeader, AccordionPanel } from 'react-aria-widgets/accordion';

export default BasicAccordion() {
  return (
    <Accordion headerLevel={ 3 }>
      <AccordionItem id="item1">
        <AccordionHeader>
          Accordion Item 1
        </AccordionHeader>
        <AccordionPanel>
          Hello world!
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem id="item2">
        <AccordionHeader>
          Accordion Item 2
        </AccordionHeader>
        <AccordionPanel>
          Hello world!
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem id="item3">
        <AccordionHeader>
          Accordion Item 3
        </AccordionHeader>
        <AccordionPanel>
          Hello world!
        </AccordionPanel>
      </AccordionItem>
    </Accordion>
  );
}

If you open your browser's developer tools and you click on the buttons, you'll see that the accordion will set the HTML and ARIA attributes correctly. However, other than the default styles that come from your browser and this website's CSS framework, the accordion is completely unstyled, including any styles that should handle collapsing an accordion item when toggled.

React ARIA Widgets aims to be as flexible as possible, and several different styling options are displayed in the following example.

This accordion item is styled by CSS that targets the default classes provided by React ARIA Widgets. Since React ARIA Widgets also exposes the accordion's state via HTML data attributes, we can target selectors such as [data-expanded] or [data-disabled].

This accordion item is styled by passing in strings for className and CSS that targets the supplied classes and the state exposed by React ARIA Widgets.

This accordion item is styled by passing in objects for style.

import { Accordion, AccordionItem, AccordionHeader, AccordionPanel } from 'react-aria-widgets/accordion';

function StyledAccordion() {
  return (
    <Accordion headerLevel={ 3 }>
      <AccordionItem id="item1">
        <AccordionHeader>
          Accordion Item 1
        </AccordionHeader>
        <AccordionPanel>
          <p>
            This accordion item is styled by CSS that targets the default classes provided by React ARIA
            Widgets. Since React ARIA Widgets also exposes the accordion&apos;s state via HTML data attributes,
            we can target selectors such as <code>[data-expanded]</code> or <code>[data-disabled]</code>.
          </p>
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem id="item2">
        <AccordionHeader
          headerProps={{ className: 'custom-accordion-header' }}
          buttonProps={{ className: 'custom-accordion-button' }}
        >
          Accordion Item 2
        </AccordionHeader>
        <AccordionPanel className="custom-accordion-panel">
          <p> 
            This accordion item is styled by passing in strings for <code>className</code> and
            CSS that targets the supplied classes and the state exposed by React ARIA Widgets.
          </p>
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem id="item3">
        <AccordionHeader
          headerProps={{ style: { color: 'hsl(217, 71%, 45%)' } }}
          buttonProps={{ style: { color: 'inherit' } }}
        >
          Accordion Item 3
        </AccordionHeader>
        <AccordionPanel style={{ color: 'hsl(217, 71%, 45%)' }}>
          <p className="mb-4">
            This accordion item is styled by passing in objects for <code>style</code>.
          </p>
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem id="item4">
        <AccordionHeader
          headerProps={{ className: ({ isExpanded }) => `another-custom-header ${isExpanded ? 'expanded' : 'collapsed'}` }}
          buttonProps={{ className: ({ isExpanded }) => `another-custom-button ${isExpanded ? 'expanded' : 'collapsed'}` }}
        >
          Accordion Item 4
        </AccordionHeader>
        <AccordionPanel className={ ({ isExpanded }) => `another-custom-panel ${isExpanded ? 'expanded' : 'collapsed'}` }>
          <p>
            This accordion item is styled by passing in functions for <code>className</code>. These functions
            have access to the accordion&apos;s state, allowing you to dynamically apply classes.
          </p>
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem id="item5">
        <AccordionHeader
          headerProps={{ style: ({ isExpanded }) => isExpanded ? { color: 'hsl(0, 0%, 100%)' } : {} }}
          buttonProps={{ style: ({ isExpanded }) => isExpanded ? { color: 'inherit', backgroundColor: 'hsl(217, 71%, 53%' } : {} }}
        >
          Accordion Item 5
        </AccordionHeader>
        <AccordionPanel style={ ({ isExpanded }) => isExpanded ? {} : { display: 'none' } }>
          <p className="mb-4">
            This accordion item is styled by passing in functions for <code>style</code>. As before, these
            functions allow you to dynamically apply styles based on the accordion&apos;s state.
          </p>
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem id="item6">
        <AccordionHeader>
          { ({ id, getIsExpanded }) => (
            <span
              className={ getIsExpanded(id) ? 'expanded' : 'collapsed' }
              style={ getIsExpanded(id) ? { color: 'hsl(217, 71%, 45%)' } : {} }
            >
              Accordion Item 6
            </span>
          ) }
        </AccordionHeader>
        <AccordionPanel>
          { ({ id, getIsExpanded }) => (
            <p
              className={ getIsExpanded(id) ? 'expanded' : 'collapsed' }
              style={ getIsExpanded(id) ? { color: 'hsl(217, 71%, 45%)' } : {} }
            >
              The content for this accordion item is rendered with a render function. Since these render
              functions have access to the accordion&apos;s state, you can dynamically style your content.
            </p>
          ) }
        </AccordionPanel>
      </AccordionItem>
    </Accordion>
  );
}
.react-aria-widgets-accordion-panel[data-expanded=false] {
  display: none;
}

.custom-accordion-panel[data-expanded=false] {
  display: none;
}

.another-custom-panel.collapsed {
  display: none;
}

In addition to styling options, React ARIA Widgets also provides hooks and other primitives that can be used to build finer-tuned implementations. For more information, see each patterns' individual documentation page.