Accessibility Wiki

Community Wiki & Forum

Category

Frontend Development

Last Updated

January 2024

Contributors

42 active contributors

Related Topics

HTMLJavaScriptWeb Design

Build inclusive web experiences for everyone

Semantic HTML

Use semantic HTML elements to provide meaning and structure to your content for screen readers and assistive technologies.

<!-- Bad: Non-semantic -->
<div class="header">
  <div class="nav">
    <div class="nav-item">Home</div>
  </div>
</div>

<!-- Good: Semantic -->
<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>

<main>
  <article>
    <h1>Article Title</h1>
    <p>Article content...</p>
  </article>
</main>

<footer>
  <p>&copy; 2025 Site Name</p>
</footer>

ARIA Attributes

Use ARIA attributes to enhance accessibility when semantic HTML isn't enough.

<!-- Button with ARIA label -->
<button aria-label="Close dialog" onClick={closeDialog}>
  <X className="icon" aria-hidden="true" />
</button>

<!-- Custom dropdown -->
<div role="listbox" aria-label="Select option">
  <div role="option" aria-selected="true">Option 1</div>
  <div role="option" aria-selected="false">Option 2</div>
</div>

<!-- Loading state -->
<div aria-live="polite" aria-busy="true">
  Loading content...
</div>

<!-- Form with error -->
<input
  type="email"
  aria-label="Email address"
  aria-invalid="true"
  aria-describedby="email-error"
/>
<span id="email-error" role="alert">
  Please enter a valid email
</span>

Keyboard Navigation

Ensure all interactive elements are keyboard accessible and have visible focus states.

/* Visible focus indicators */
button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* Skip to main content link */
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: var(--primary);
  color: white;
  padding: 8px;
  text-decoration: none;
}

.skip-link:focus {
  top: 0;
}

/* Keyboard trap for modals */
function Modal({ onClose, children }) {
  const modalRef = useRef(null);
  
  useEffect(() => {
    const focusableElements = modalRef.current.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );
    const firstElement = focusableElements[0];
    const lastElement = focusableElements[focusableElements.length - 1];
    
    firstElement?.focus();
    
    const trapFocus = (e) => {
      if (e.key === 'Tab') {
        if (e.shiftKey && document.activeElement === firstElement) {
          lastElement?.focus();
          e.preventDefault();
        } else if (!e.shiftKey && document.activeElement === lastElement) {
          firstElement?.focus();
          e.preventDefault();
        }
      }
    };
    
    document.addEventListener('keydown', trapFocus);
    return () => document.removeEventListener('keydown', trapFocus);
  }, []);
  
  return <div ref={modalRef} role="dialog">{children}</div>;
}

Discussion (0)

Your avatar

No comments yet. Be the first to join the discussion!