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

> Learn to implement accessibility in Astryx components using ARIA attributes, VisuallyHidden primitive, and unit testing. Enhance your frontend accessibility today.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: best-practices
- Published: 2026-07-15

---

**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`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/packages/core/src/TextInput/TextInput.tsx) follows this pattern:

```tsx
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:

```tsx
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`](https://github.com/facebook/astryx/blob/main/packages/lab/src/Stepper/Stepper.tsx) demonstrates advanced accessibility patterns:

```tsx
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`](https://github.com/facebook/astryx/blob/main/packages/core/src/Button/Button.tsx) and [`packages/lab/src/Drawer/Drawer.tsx`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/packages/core/src/Button/Button.test.tsx) and [`packages/lab/src/Drawer/Drawer.test.tsx`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/packages/core/src/Button/Button.tsx) and [`packages/lab/src/Drawer/Drawer.tsx`](https://github.com/facebook/astryx/blob/main/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`](https://github.com/facebook/astryx/blob/main/packages/core/src/TextInput/TextInput.test.tsx) and [`packages/lab/src/Stepper/Stepper.test.tsx`](https://github.com/facebook/astryx/blob/main/packages/lab/src/Stepper/Stepper.test.tsx) ensure that accessibility properties remain intact across updates.