Skip to main content
Accessibility icons showing various assistive technologies

Building Accessible Web Applications: A Guide to WCAG 2.1

Fluxline Resonance Group

Published: December 2, 2025

Learn essential principles and practical techniques for creating web applications that are accessible to all users, following WCAG 2.1 AA standards.

Building Accessible Web Applications: A Guide to WCAG 2.1

Web accessibility isn't just a legal requirement—it's a moral imperative and a smart business decision. According to the WHO, over 1 billion people live with some form of disability. By making your web applications accessible, you're opening your digital doors to everyone.

Understanding WCAG 2.1

The Web Content Accessibility Guidelines (WCAG) 2.1 are the international standard for web accessibility. They're organized around four core principles, known as POUR:

1. Perceivable

Information must be presentable to users in ways they can perceive.

Key Requirements:

  • Text alternatives for non-text content

  • Captions for audio and video

  • Content that can be presented in different ways

  • Sufficient color contrast (4.5:1 for normal text)

Example:

<!-- Good: Image with descriptive alt text -->
<img
  src="/chart.png"
  alt="Bar chart showing 40% increase in mobile users over 2024"
/>

<!-- Bad: Generic or missing alt text -->
<img src="/chart.png" alt="chart" />

2. Operable

User interface components must be operable by everyone.

Key Requirements:

  • Keyboard accessible

  • Enough time to read and use content

  • No content that causes seizures

  • Easy navigation and findability

Example:

// Good: Keyboard accessible dropdown
const Dropdown = () => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button
        onClick={() => setIsOpen(!isOpen)}
        onKeyDown={(e) => {
          if (e.key === 'Enter' || e.key === ' ') {
            setIsOpen(!isOpen);
          }
        }}
        aria-expanded={isOpen}
        aria-haspopup="true"
      >
        Menu
      </button>
      {isOpen && (
        <ul role="menu">
          <li role="menuitem" tabIndex={0}>Item 1</li>
          <li role="menuitem" tabIndex={0}>Item 2</li>
        </ul>
      )}
    </div>
  );
};

3. Understandable

Information and operation of the user interface must be understandable.

Key Requirements:

  • Readable text

  • Predictable behavior

  • Input assistance and error prevention

Example:

// Good: Clear error messages with guidance
const FormField = ({ value, error }) => (
  <div>
    <label htmlFor="email">
      Email Address
      <span aria-label="required">*</span>
    </label>
    <input
      id="email"
      type="email"
      value={value}
      aria-required="true"
      aria-invalid={!!error}
      aria-describedby={error ? "email-error" : undefined}
    />
    {error && (
      <div id="email-error" role="alert" className="error">
        {error}
      </div>
    )}
  </div>
);

4. Robust

Content must be robust enough to be interpreted by a wide variety of user agents, including assistive technologies.

Key Requirements:

  • Valid HTML

  • Name, role, and value available for UI components

  • Compatible with current and future assistive technologies

Practical Implementation Guide

Semantic HTML

Use the right HTML elements for the job:

<!-- Good: Semantic structure -->
<header>
  <nav aria-label="Main navigation">
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/about">About</a></li>
    </ul>
  </nav>
</header>

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

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

<!-- Bad: Div soup -->
<div class="header">
  <div class="nav">
    <div class="nav-item">Home</div>
    <div class="nav-item">About</div>
  </div>
</div>

Focus Management

Ensure keyboard users can see where they are:

/* Good: Visible focus indicators */
button:focus,
a:focus,
input:focus {
  outline: 3px solid #0066cc;
  outline-offset: 2px;
}

/* Never do this */
* {
  outline: none; /* ❌ */
}

Color Contrast

Test your color combinations:

/* Good: High contrast (7.3:1 ratio) */
.text {
  color: #1a1a1a;
  background-color: #ffffff;
}

/* Bad: Low contrast (2.1:1 ratio) */
.text-poor {
  color: #cccccc;
  background-color: #ffffff;
}

ARIA Labels

Use ARIA attributes when HTML isn't enough:

// Good: Clear button purpose
<button
  aria-label="Close dialog"
  onClick={handleClose}
>
  <svg aria-hidden="true">
    <use xlinkHref="#icon-close" />
  </svg>
</button>

// Good: Live region for dynamic content
<div
  role="status"
  aria-live="polite"
  aria-atomic="true"
>
  {statusMessage}
</div>

Testing Your Application

Automated Testing Tools

  1. axe DevTools: Browser extension for instant feedback

  2. Lighthouse: Built into Chrome DevTools

  3. pa11y: Command-line accessibility testing

  4. jest-axe: Unit test accessibility

// Example: Testing with jest-axe
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('Button has no accessibility violations', async () => {
  const { container } = render(<Button>Click me</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Manual Testing Checklist

  • Navigate using only keyboard (Tab, Enter, Esc)

  • Test with screen reader (NVDA, JAWS, VoiceOver)

  • Zoom to 200% and verify layout

  • Check color contrast ratios

  • Disable images and verify alt text

  • Test with browser extensions disabled

  • Verify form error messages are clear

Common Accessibility Pitfalls

1. Missing Alt Text

<!-- Wrong -->
<img src="logo.png" />

<!-- Right -->
<img src="logo.png" alt="Company Name logo" />

<!-- Decorative image -->
<img src="decorative.png" alt="" role="presentation" />

2. Poor Form Labels

<!-- Wrong -->
<input type="text" placeholder="Enter your name" />

<!-- Right -->
<label for="name">Name</label>
<input id="name" type="text" placeholder="e.g., John Smith" />

3. Inaccessible Modals

// Good: Accessible modal
const Modal = ({ isOpen, onClose, children }) => {
  const modalRef = useRef();

  useEffect(() => {
    if (isOpen) {
      // Trap focus in modal
      const firstFocusable = modalRef.current.querySelector(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      firstFocusable?.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      role='dialog'
      aria-modal='true'
      aria-labelledby='modal-title'
      ref={modalRef}
    >
      <h2 id='modal-title'>Modal Title</h2>
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
};

Accessibility in Next.js

Next.js provides built-in accessibility features:

// Automatic focus management with router
import { useRouter } from 'next/navigation';

const Navigation = () => {
  const router = useRouter();

  const handleNavigation = (href: string) => {
    router.push(href);
    // Next.js automatically manages focus
  };

  return <button onClick={() => handleNavigation('/about')}>About</button>;
};

// Image optimization with alt text
import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Team collaborating on accessible design"
  width={1200}
  height={630}
  priority
/>

Resources and Further Reading

Official Guidelines

Testing Tools

Learning Resources

Conclusion

Building accessible web applications is a journey, not a destination. Start with the basics—semantic HTML, keyboard navigation, and color contrast—and progressively enhance from there.

Remember: Accessibility benefits everyone. Clear navigation helps all users. Good contrast improves readability for everyone. Keyboard shortcuts make power users more efficient.

By following WCAG 2.1 guidelines and testing thoroughly, you'll create applications that are not only compliant but genuinely inclusive and user-friendly for everyone.


Key Takeaways:

  • Use semantic HTML as your foundation

  • Test with real assistive technologies

  • Automate what you can, but don't skip manual testing

  • Consider accessibility from day one, not as an afterthought

  • Remember: accessible design is good design

Let's Connect

Book a session with us
Explore more about the Fluxline philosophy

Related Resources

Stay Connected

Get insights on the latest trends, best practices, and industry news.

Footer