How to Implement Accessibility in Astryx Components: Patterns and Best Practices

Astryx components implement accessibility through semantic ARIA attributes, a dedicated VisuallyHidden primitive for screen-reader-only content, and comprehensive unit testing that validates ARIA roles and properties.

Implementing accessibility in Astryx components follows a systematic approach built into the facebook/astryx repository. The component library embeds inclusive design patterns directly into its core architecture, ensuring that interactive elements work seamlessly with assistive technologies without requiring additional configuration.

Core Accessibility Architecture

Semantic ARIA Roles and Attributes

Every interactive element in Astryx includes appropriate ARIA roles such as button, status, and dialog, along with attributes like aria-label, aria-labelledby, aria-describedby, aria-live, aria-expanded, and aria-current. These properties are defined in component JSX within files like packages/core/src/Button/Button.tsx and propagated directly to the DOM.

The VisuallyHidden Primitive

The VisuallyHidden component located in packages/core/src/VisuallyHidden/VisuallyHidden.tsx provides a styling-free container that renders text off-screen while remaining accessible to assistive technology. It supports polymorphic rendering via the as prop, allowing it to function as any HTML element. This primitive is essential for:

  • Descriptive labels on inputs and buttons
  • Live region announcements
  • Accessibility-only descriptions for icon-only controls

Implementing Accessible Patterns

Labeling Inputs with VisuallyHidden

When building forms, associate labels with inputs using unique IDs and aria-labelledby. The TextInput component in packages/core/src/TextInput/TextInput.tsx follows this pattern:

import {TextInput} from '@astryxdesign/core/TextInput';
import {VisuallyHidden} from '@astryxdesign/core/VisuallyHidden';

function EmailField() {
  const inputLabelID = 'email-label';
  return (
    <>
      <VisuallyHidden id={inputLabelID}>Email address</VisuallyHidden>
      <TextInput
        id="email"
        aria-labelledby={inputLabelID}
        placeholder="you@example.com"
      />
    </>
  );
}

Live Region Announcements

Use VisuallyHidden with aria-live to announce status changes without visual disruption:

import {VisuallyHidden} from '@astryxdesign/core/VisuallyHidden';

function LoadingSpinner() {
  return (
    <div role="status">
      <Spinner size="md" />
      <VisuallyHidden aria-live="polite">Loading data</VisuallyHidden>
    </div>
  );
}

Multi-Step Navigation with aria-current

The Stepper component in packages/lab/src/Stepper/Stepper.tsx demonstrates advanced accessibility patterns:

import {Stepper} from '@astryxdesign/lab/Stepper';

<Stepper
  steps={['Step 1', 'Step 2', 'Step 3']}
  currentStep={1}
  aria-label="Progress through wizard"
/>

Internally, each step renders aria-current="step" on the active item and uses VisuallyHidden for descriptive labels.

Keyboard Navigation and Focus Management

Components such as Button, Switch, Drawer, and Stepper ensure logical focus order by exposing native HTML elements whenever possible. In packages/core/src/Button/Button.tsx and packages/lab/src/Drawer/Drawer.tsx, refs are forwarded to maintain programmatic focus control. This allows developers to manage focus shifts during interactions like opening modals or navigating between steps.

Automated Accessibility Testing

Astryx enforces accessibility through unit tests that assert the presence and correctness of ARIA attributes. Tests validate patterns such as expect(screen.getByRole('dialog')).toHaveAttribute('aria-modal', 'true') in component test files like packages/core/src/Button/Button.test.tsx and packages/lab/src/Drawer/Drawer.test.tsx. This testing strategy prevents regressions in screen reader compatibility.

Summary

  • Astryx embeds accessibility through semantic ARIA roles and attributes defined in component JSX
  • The VisuallyHidden primitive in packages/core/src/VisuallyHidden/VisuallyHidden.tsx enables screen-reader-only content without visual impact
  • Keyboard navigation relies on native HTML elements and ref forwarding in components like Button and Drawer
  • Unique ID generation pairs labels with controls via aria-labelledby and aria-describedby
  • Unit tests in *.test.tsx files validate ARIA attributes to prevent accessibility regressions

Frequently Asked Questions

What ARIA attributes does Astryx support?

Astryx components support standard ARIA attributes including aria-label, aria-labelledby, aria-describedby, aria-live, aria-expanded, aria-current, and role definitions such as button, status, and dialog. These are implemented in the component source files and propagated directly to the DOM.

How does the VisuallyHidden component work?

The VisuallyHidden component renders content off-screen using CSS while keeping it in the accessibility tree. Located in packages/core/src/VisuallyHidden/VisuallyHidden.tsx, it accepts an as prop for polymorphic rendering and is used throughout the library for labels, live regions, and icon descriptions.

Does Astryx handle focus management automatically?

Yes, components like Button, Switch, Drawer, and Stepper manage focus through native HTML elements and ref forwarding. This pattern, visible in packages/core/src/Button/Button.tsx and packages/lab/src/Drawer/Drawer.tsx, ensures logical tab order and programmatic focus control.

How are accessibility features tested in Astryx?

Each component includes unit tests that verify ARIA attributes using assertions like toHaveAttribute. Test files such as packages/core/src/TextInput/TextInput.test.tsx and packages/lab/src/Stepper/Stepper.test.tsx ensure that accessibility properties remain intact across updates.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →