Claude Skills Guide

Claude Code for Accessible Modal Dialog Implementation

Modal dialogs are ubiquitous in modern web applications, but implementing them accessibly remains a challenge for many developers. An inaccessible modal can exclude users who rely on screen readers, keyboard navigation, or assistive technologies. This guide walks you through building accessible modals using Claude Code, covering the essential patterns and techniques that ensure your modals work for everyone.

Understanding the Accessibility Requirements

Before diving into code, let’s establish what makes a modal dialog accessible. The WAI-ARIA Authoring Practices specify several key requirements:

Claude Code can help you implement these requirements systematically, generating clean, standards-compliant code that follows best practices.

Basic Accessible Modal Structure

Start by creating a modal component with the proper HTML structure. Here’s a foundational example that establishes the accessibility baseline:

<div 
  id="modal-example" 
  role="dialog" 
  aria-modal="true" 
  aria-labelledby="modal-title" 
  aria-describedby="modal-description"
  hidden
>
  <div class="modal-content">
    <h2 id="modal-title">Confirm Action</h2>
    <p id="modal-description">Are you sure you want to proceed with this action?</p>
    
    <div class="modal-actions">
      <button type="button" id="confirm-btn">Confirm</button>
      <button type="button" id="cancel-btn">Cancel</button>
    </div>
  </div>
</div>
<div id="modal-overlay" hidden></div>

The key accessibility attributes here are role="dialog", aria-modal="true", and the label/description references. Claude Code can generate this structure automatically when you describe your modal requirements.

Implementing Focus Management

Focus management is arguably the most critical aspect of accessible modal implementation. When a modal opens, focus must move to the modal itself, and it must stay trapped within the modal until closed.

The Open Focus Trap

When the modal opens, move focus to the first focusable element inside the dialog:

function openModal(modal) {
  const modalOverlay = document.getElementById('modal-overlay');
  const trigger = document.activeElement;
  
  // Store the trigger element for returning focus later
  modal.dataset.triggerId = trigger.id;
  
  // Show modal and overlay
  modal.hidden = false;
  modalOverlay.hidden = false;
  
  // Move focus to the first focusable element
  const focusableElements = modal.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  
  if (focusableElements.length > 0) {
    focusableElements[0].focus();
  } else {
    modal.focus();
  }
  
  // Add event listeners for focus trap
  document.addEventListener('keydown', handleKeyDown);
}

The Close Focus Return

When closing the modal, return focus to the element that triggered it:

function closeModal(modal) {
  const modalOverlay = document.getElementById('modal-overlay');
  
  // Hide modal and overlay
  modal.hidden = true;
  modalOverlay.hidden = true;
  
  // Return focus to the trigger element
  const triggerId = modal.dataset.triggerId;
  if (triggerId) {
    const trigger = document.getElementById(triggerId);
    if (trigger) {
      trigger.focus();
    }
  }
  
  // Remove keyboard trap
  document.removeEventListener('keydown', handleKeyDown);
}

Keyboard Navigation Patterns

Users must be able to navigate your modal entirely via keyboard. This means handling the Escape key to close and ensuring Tab cycles within the modal only.

Escape Key Handler

function handleKeyDown(event) {
  if (event.key === 'Escape') {
    const modal = document.querySelector('[role="dialog"]:not([hidden])');
    if (modal) {
      closeModal(modal);
    }
  }
}

Tab Key Trap

Prevent Tab from moving focus outside the modal:

function trapFocus(event) {
  if (event.key !== 'Tab') return;
  
  const modal = document.querySelector('[role="dialog"]:not([hidden])');
  if (!modal) return;
  
  const focusableElements = Array.from(
    modal.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    )
  );
  
  const firstElement = focusableElements[0];
  const lastElement = focusableElements[focusableElements.length - 1];
  
  if (event.shiftKey && document.activeElement === firstElement) {
    event.preventDefault();
    lastElement.focus();
  } else if (!event.shiftKey && document.activeElement === lastElement) {
    event.preventDefault();
    firstElement.focus();
  }
}

Using Claude Code to Generate Modal Components

Claude Code excels at generating complete, production-ready modal components. Here’s how to prompt Claude effectively:

“Create an accessible modal dialog component with the following requirements:

Claude will generate a comprehensive solution that you can customize for your needs. The advantage of using Claude Code is that it produces code following current best practices, including proper ARIA attributes and keyboard handling patterns.

Handling Multiple Modals

Applications often need to handle multiple modals or nested modal scenarios. Here’s a pattern for managing modal stacking:

const modalStack = [];

function openModal(modal) {
  // Mark background as hidden from assistive tech
  document.body.setAttribute('aria-hidden', 'true');
  
  modalStack.push(modal);
  // ... rest of open logic
}

function closeModal(modal) {
  modalStack.pop();
  
  // Restore accessibility if no modals remain
  if (modalStack.length === 0) {
    document.body.removeAttribute('aria-hidden');
  }
  
  // ... rest of close logic
}

Testing Your Accessible Modals

Implementation is only half the battle—you must test your modals to ensure accessibility. Here are practical testing approaches:

Keyboard Testing

  1. Open the modal using keyboard (Enter/Space on the trigger)
  2. Press Tab repeatedly—focus should cycle within the modal
  3. Press Shift+Tab—focus should cycle backward within the modal
  4. Press Escape—the modal should close
  5. After closing, focus should return to the trigger

Screen Reader Testing

Automated Testing

Use tools like axe-core to automate accessibility testing:

const { AxePuppeteer } = require('@axe-core/puppeteer');

async function testModalAccessibility(page) {
  const results = await new AxePuppeteer(page).analyze();
  console.log(results);
}

Common Pitfalls to Avoid

Several mistakes frequently appear in modal implementations:

Conclusion

Accessible modal dialog implementation requires attention to detail, but the patterns are straightforward once understood. Focus management, proper ARIA attributes, and keyboard navigation form the foundation of an accessible modal. Claude Code can generate production-ready implementations that follow these patterns, saving development time while ensuring accessibility compliance.

Remember: an accessible modal isn’t just about meeting WCAG requirements—it’s about ensuring all users can successfully interact with your application. Invest the time to get it right, and your users will thank you.

Built by theluckystrike — More at zovo.one