Skip to content

Component Library a11y — Accessible Interactive Components

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Component Library a11y. We cover key concepts, practical examples, and best practices to help you master this topic.

Accessible component library patterns use semantic HTML elements first, add ARIA only when necessary, ensure full keyboard operability, manage focus correctly, and provide visible states for hover focus and active.

What You'll Learn

You will learn how to build accessible components for a design system, how to choose between native HTML and ARIA, how to handle keyboard interactions, and how to manage focus in complex components.

Why It Matters

Components are the building blocks of every product built from the design system. If components are inaccessible, every product will be inaccessible. Accessible components ensure accessible products.

Real-World Use

DodaKit provides 25 accessible components. Each component uses native HTML elements where possible. ARIA is added only for custom interaction patterns like tabs and accordions. Every component includes keyboard support and focus management.

flowchart TD
  A[Component Patterns] --> B[Buttons]
  A --> C[Inputs]
  A --> D[Modals]
  A --> E[Accordions]
  A --> F[Tabs]
  A --> G[Menus]
  B --> B1[button element]
  C --> C1[label + input]
  D --> D1[Focus trap]
  E --> E1[aria-expanded]
  F --> F1[role tablist]
  G --> G1[keyboard navigation]

Native HTML First

Always start with native HTML elements. Native elements have built-in keyboard support, focus management, and screen reader announcements. Only use ARIA when the native element does not exist.

Keyboard Handlers

Every interactive component must be fully keyboard operable. Define keyboard interactions in the component documentation.

// Accessible accordion component pattern
class AccessibleAccordion {
  constructor(sections) {
    this.sections = sections.map((s, i) => ({
      id: s.id || `accordion-section-${i}`,
      title: s.title,
      content: s.content,
      expanded: s.expanded || false
    }));
  }

  toggle(index) {
    const section = this.sections[index];
    if (!section) return;

    section.expanded = !section.expanded;
    return {
      id: section.id,
      expanded: section.expanded,
      buttonId: `${section.id}-trigger`,
      panelId: `${section.id}-panel`,
      buttonLabel: `${section.title}, ${section.expanded ? 'expanded' : 'collapsed'}`
    };
  }

  renderSectionHTML(section, index) {
    return `<div class="ds-accordion__section">
      <h3 class="ds-accordion__heading">
        <button
          id="${section.id}-trigger"
          class="ds-accordion__trigger"
          aria-expanded="${section.expanded}"
          aria-controls="${section.id}-panel"
          type="button">
          ${section.title}
          <span class="ds-accordion__icon" aria-hidden="true">${section.expanded ? '-' : '+'}</span>
        </button>
      </h3>
      <div
        id="${section.id}-panel"
        class="ds-accordion__panel"
        role="region"
        aria-labelledby="${section.id}-trigger"
        ${section.expanded ? '' : 'hidden'}>
        <p>${section.content}</p>
      </div>
    </div>`;
  }

  getKeyboardInstructions() {
    return {
      'Tab': 'Move focus to next accordion trigger',
      'Shift+Tab': 'Move focus to previous accordion trigger',
      'Enter or Space': 'Toggle accordion section',
      'Home': 'Move focus to first accordion trigger',
      'End': 'Move focus to last accordion trigger'
    };
  }
}

const accordion = new AccessibleAccordion([
  { title: 'Scan settings', content: 'Configure your scan preferences including schedule and exclusions.' },
  { title: 'Notification preferences', content: 'Choose how you want to be notified about scan results.' }
]);

console.log(accordion.toggle(0));
console.log(accordion.renderSectionHTML(accordion.sections[0], 0));

Expected output:

{
  id: 'accordion-section-0',
  expanded: true,
  buttonId: 'accordion-section-0-trigger',
  panelId: 'accordion-section-0-panel',
  buttonLabel: 'Scan settings, expanded'
}
<div class="ds-accordion__section">
  <h3 class="ds-accordion__heading">
    <button id="accordion-section-0-trigger" class="ds-accordion__trigger" aria-expanded="true" aria-controls="accordion-section-0-panel" type="button">
      Scan settings
      <span class="ds-accordion__icon" aria-hidden="true">-</span>
    </button>
  </h3>
  <div id="accordion-section-0-panel" class="ds-accordion__panel" role="region" aria-labelledby="accordion-section-0-trigger">
    <p>Configure your scan preferences including schedule and exclusions.</p>
  </div>
</div>

Focus Management

For components that open additional UI (modals, dropdowns, menus), manage focus carefully. Trap focus inside modals. Return focus to the triggering element when the component closes.

<!-- Accessible modal component -->
<div class="ds-modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
  <div class="ds-modal__backdrop" onclick="closeModal()"></div>
  <div class="ds-modal__content" role="document">
    <h2 id="modal-title" class="ds-modal__title">Confirm deletion</h2>
    <p class="ds-modal__body">Are you sure you want to delete this scan result? This action cannot be undone.</p>
    <div class="ds-modal__actions">
      <button class="ds-button ds-button--secondary" type="button" onclick="closeModal()">Cancel</button>
      <button class="ds-button ds-button--danger" type="button" onclick="confirmDelete()">Delete</button>
    </div>
  </div>
</div>

Common Mistakes

1. Using ARIA When Native HTML Works

ARIA is not needed for native button, input, select, or a elements. Using ARIA unnecessarily increases complexity and risk.

2. No Keyboard Support for Custom Widgets

Custom widgets like tabs, accordions, and menus must support arrow key navigation and standard keyboard interactions.

3. No Focus Trap in Modals

Modals that do not trap focus allow users to tab behind the modal, causing confusion and Accessibility failures.

4. No Visible Focus Indicator

Components that remove the default focus outline without providing an alternative fail SC 2.4.7.

5. Missing ARIA States

Components that expand or collapse need aria-expanded. Selected items need aria-selected or aria-current.

6. No Role for Custom Components

Custom components built from divs and spans need ARIA roles to convey their purpose to screen readers.

7. Inconsistent Component Behavior

The same component type should behave identically everywhere. If one button closes on Escape, all buttons should.

Practice Questions

1. What is the first rule of accessible components?

Use native HTML elements first. ARIA should only be added when no native element exists for the desired behavior.

2. What focus management is required for modals?

Focus must be trapped inside the modal when open. When closed, focus must return to the element that opened it.

3. What ARIA attribute indicates an expandable section?

aria-expanded with values true or false on the triggering button.

4. Why should components not remove the default focus outline?

SC 2.4.7 Focus Visible requires a visible focus indicator. If the default outline is removed, a custom indicator must replace it.

5. Challenge: Create an accessible tabs component with proper roles, keyboard navigation, and focus management.

FAQ

Can I use div for a button?

No. Use the button element. It provides keyboard support, focus management, and screen reader announcements for free.

How do I make a custom dropdown accessible?

Use role=listbox on the container, role=option on items, aria-selected on the selected item, and support arrow key navigation.

What keyboard interactions do tabs need?

Arrow keys to switch between tabs, Home and End to jump to first and last tab. Tab moves focus into the tab panel.

How do I test component accessibility?

Unit test with axe-core, manual keyboard test all interactions, screen reader test with NVDA or VoiceOver.

Should I document keyboard interactions for each component?

Yes. Component documentation must include a keyboard interaction table. Developers and testers rely on this documentation.

Mini Project

Create an accessible accordion component for a design system. Include proper ARIA attributes, keyboard support, and focus management. Write component documentation with keyboard interactions.

What's Next

Learn about Focus Indicators and how to design visible focus states for all interactive components. Then explore Accessible Forms System.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro