Micah
Micah

Reputation: 691

Accessibility for Screen Readers on Stepper in Modal

Focus management in Alpine.js modal doesn't properly redirect screen readers between steps

The Problem

In my TALL (+Fillament) stack app, I have a multi-step modal dialog using Alpine.js and Filament UI components. While the visual focus correctly moves between steps, screen readers aren't properly announcing the content of the new step.

Step one of the modal

When a user clicks "Next" to advance to a new step, the browser focus (orange border) correctly moves to the heading of the next step, but the screen reader announces "Chrome unresponsive" before getting cut off and saying "group". The screen reader focus starts on the Back button instead of the heading content.

step 2 of the modal

Code

Here's the relevant part of my implementation:

<div x-data="{ page: 1 }">
    <x-filament::modal
        role="dialog"
        aria-labelledby="modal-title"
        aria-describedby="modal-content"
        id="protect-the-trust"
        width="lg"
        footerActionsAlignment="right"
        aria-label="Request Policy Agreement">
        
        <!-- Modal heading -->
        <x-slot name="heading" aria-hidden="true">
            <h2 id="modal-title" class="text-lg font-bold" tabindex="-1">Protect the Trust</h2>
        </x-slot>

        <div id="modal-content" class="flex flex-col">
            <!-- One of the steps (simplified for clarity) -->
            <div x-show="page == 3" role="region" aria-labelledby="step3-heading" id="step3-content" x-cloak>
                <h3 id="step3-heading" class="font-medium" tabindex="-1">CarePortal Appropriate (Step 3/3)</h3>
                <!-- Step content here -->
            </div>
        </div>

        <!-- Navigation buttons -->
        <x-slot name="footerActions">
            <div x-show="page == 2" x-cloak>
                <x-filament::button color="gray" x-on:click="page = 1; $nextTick(() => document.getElementById('step1-heading').focus())">Back</x-filament::button>
                <x-filament::button x-on:click="page = 3; $nextTick(() => document.getElementById('step3-heading').focus())" dusk="protectTheTrustIAgreePage2">Next</x-filament::button>
            </div>
        </x-slot>
    </x-filament::modal>
</div>

What I've tried

I'm using $nextTick() to focus the heading of the next step after changing pages:

x-on:click="page = 3; $nextTick(() => document.getElementById('step3-heading').focus())"

I've added tabindex="-1" to the headings to make them focusable, and set up proper ARIA attributes:

The visual focus works correctly (I can see the orange focus outline on the heading), but screen readers aren't announcing the heading content and are focusing on the Back button instead.

Environment

Question

How can I ensure that when navigating between steps in an Alpine.js modal, screen readers correctly focus on and announce the heading of each new step? Is there something I'm missing in my ARIA implementation or focus management approach?

Upvotes: 0

Views: 29

Answers (0)

Related Questions