Skip to main content
Back to Blog
Web Development

WCAG 2.1 Compliance: Build Accessible Websites Easily

A practical guide to making your website WCAG 2.1 compliant. Learn accessibility best practices that benefit all users, not just compliance checkboxes.

High Mountain Studio
10 min read
Illustration showing diverse users accessing a website through different assistive technologies

Building Accessible Websites: WCAG 2.1 Compliance Without the Headaches

Here's a truth that took our industry too long to accept: accessibility isn't a feature. It's a fundamental aspect of quality web development. Yet many teams treat it as an afterthought, a compliance checkbox to tick before launch, or worse, a "nice to have" that never makes the roadmap.

The reality? Building accessible websites from the start is easier, cheaper, and more effective than retrofitting later. And the benefits extend far beyond users with disabilities. Accessible design improves the experience for everyone.

Let's cut through the jargon and show you how to build WCAG 2.1 compliant websites without the usual headaches.

What WCAG 2.1 Actually Requires

The Web Content Accessibility Guidelines (WCAG) 2.1 organizes requirements into four principles, often called POUR:

  1. Perceivable: Information must be presentable in ways users can perceive
  2. Operable: Interface components must be operable by all users
  3. Understandable: Information and UI operation must be understandable
  4. Robust: Content must be robust enough for diverse user agents

Within these principles, there are three conformance levels:

  • Level A: Minimum accessibility (basic requirements)
  • Level AA: Standard accessibility (most common legal requirement)
  • Level AAA: Enhanced accessibility (highest standard)

For most websites and applications, Level AA is the target. It's what ADA lawsuits reference, what government contracts require, and what provides meaningful access to users with disabilities.

The High-Impact Fundamentals

Rather than listing all 50+ WCAG criteria, let's focus on the changes that provide the biggest impact with the least effort.

Semantic HTML: Your First Line of Defense

Before reaching for ARIA attributes, master semantic HTML. Screen readers and assistive technologies understand native HTML elements. Use them correctly, and you're already halfway to compliance.

<!-- Bad: Divs for everything -->
<div class="header">
  <div class="nav">
    <div class="nav-item" onclick="navigate('/')">Home</div>
  </div>
</div>

<!-- Good: Semantic elements -->
<header>
  <nav aria-label="Main navigation">
    <a href="/">Home</a>
  </nav>
</header>

Key semantic elements to use:

| Element | Purpose | |---------|---------| | <header> | Introductory content, navigation | | <nav> | Navigation sections | | <main> | Primary content of the page | | <article> | Self-contained compositions | | <section> | Thematic groupings | | <aside> | Tangentially related content | | <footer> | Footer information | | <button> | Clickable actions | | <a> | Navigation links |

Heading Hierarchy: Structure Matters

Screen reader users often navigate by headings. A logical heading structure acts as a table of contents for your page.

<!-- Bad: Skipping levels, using headings for styling -->
<h1>Welcome</h1>
<h4>Our Services</h4>  <!-- Skipped h2, h3 -->
<h2>Contact Us</h2>    <!-- Inconsistent -->

<!-- Good: Logical hierarchy -->
<h1>Welcome to Our Platform</h1>
  <h2>Our Services</h2>
    <h3>Web Development</h3>
    <h3>Design Services</h3>
  <h2>Contact Us</h2>

Rules for headings:

  • One <h1> per page (your main topic)
  • Don't skip levels (h1 → h2 → h3, not h1 → h3)
  • Use headings for structure, not styling
  • Keep headings descriptive and concise

Color Contrast: The Numbers Game

WCAG 2.1 requires minimum contrast ratios between text and backgrounds:

  • Normal text: 4.5:1 ratio (Level AA)
  • Large text (18pt+ or 14pt+ bold): 3:1 ratio
  • UI components and graphics: 3:1 ratio

Testing tools make this easy:

/* Fails contrast (2.5:1) */
.low-contrast {
  color: #999999;
  background: #ffffff;
}

/* Passes contrast (7:1) */
.high-contrast {
  color: #595959;
  background: #ffffff;
}

Recommended tools:

Keyboard Navigation: No Mouse Required

Every interactive element must be accessible via keyboard. This is non-negotiable for users who can't use a mouse.

The basics:

  • Tab moves forward through focusable elements
  • Shift + Tab moves backward
  • Enter activates buttons and links
  • Space activates buttons and checkboxes
  • Arrow keys navigate within components (menus, tabs, etc.)
// Bad: Click-only interaction
<div className="card" onClick={handleSelect}>
  Select this option
</div>

// Good: Keyboard accessible
<button 
  className="card" 
  onClick={handleSelect}
  onKeyDown={(e) => e.key === 'Enter' && handleSelect()}
>
  Select this option
</button>

// Best: Native button handles keyboard automatically
<button className="card" onClick={handleSelect}>
  Select this option
</button>

Focus indicators are mandatory. Never do this:

/* Accessibility violation */
*:focus {
  outline: none;
}

Instead, create visible, attractive focus states:

/* Custom focus indicator */
button:focus-visible {
  outline: 2px solid var(--brand-cyan);
  outline-offset: 2px;
}

Images and Alt Text: Context is Everything

Every image needs alternative text, but what you write matters:

<!-- Bad: Missing or useless -->
<img src="chart.png">
<img src="chart.png" alt="chart">
<img src="chart.png" alt="image">

<!-- Good: Descriptive -->
<img src="chart.png" alt="Bar chart showing 40% increase in Q4 sales">

<!-- Decorative images: empty alt -->
<img src="decorative-swoosh.svg" alt="">

Guidelines for alt text:

  • Informative images: Describe the content and function
  • Functional images: Describe the action (e.g., "Search" for a search icon button)
  • Decorative images: Use empty alt (alt="")
  • Complex images: Provide detailed descriptions nearby or via aria-describedby

Forms: Labels, Errors, and Instructions

Forms are accessibility minefields. Here's how to navigate them:

// Bad: No label association
<input type="email" placeholder="Email">

// Good: Explicit label association
<label htmlFor="email">Email address</label>
<input 
  type="email" 
  id="email" 
  name="email"
  aria-describedby="email-hint"
  aria-invalid={hasError}
  aria-errormessage={hasError ? "email-error" : undefined}
/>
<span id="email-hint">We'll never share your email</span>
{hasError && (
  <span id="email-error" role="alert">
    Please enter a valid email address
  </span>
)}

Form accessibility checklist:

  • ✅ Every input has an associated label
  • ✅ Required fields are indicated (not just by color)
  • ✅ Error messages are announced to screen readers
  • ✅ Error messages explain how to fix the problem
  • ✅ Form can be completed with keyboard alone

ARIA: When HTML Isn't Enough

ARIA (Accessible Rich Internet Applications) fills gaps where native HTML falls short. But here's the golden rule: No ARIA is better than bad ARIA.

When to Use ARIA

Use ARIA when you're building custom components that don't exist in native HTML:

// Custom dropdown menu
<button 
  aria-haspopup="true" 
  aria-expanded={isOpen}
  aria-controls="dropdown-menu"
>
  Options
</button>
<ul 
  id="dropdown-menu" 
  role="menu" 
  aria-hidden={!isOpen}
>
  <li role="menuitem">Edit</li>
  <li role="menuitem">Delete</li>
</ul>

Common ARIA Patterns

Live regions for dynamic content:

// Announce status updates to screen readers
<div aria-live="polite" aria-atomic="true">
  {statusMessage}
</div>

Modal dialogs:

<div 
  role="dialog" 
  aria-modal="true" 
  aria-labelledby="dialog-title"
  aria-describedby="dialog-description"
>
  <h2 id="dialog-title">Confirm Deletion</h2>
  <p id="dialog-description">
    Are you sure you want to delete this item?
  </p>
</div>

Tab interfaces:

<div role="tablist" aria-label="Account settings">
  <button role="tab" aria-selected="true" aria-controls="panel-1">
    Profile
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel-2">
    Security
  </button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  Profile content...
</div>

Testing Your Accessibility

Automated testing catches roughly 30% of accessibility issues. The rest require manual testing.

Automated Tools

Run these on every build:

  • axe-core: Industry standard engine (integrates with Jest, Playwright)
  • Lighthouse: Built into Chrome DevTools
  • eslint-plugin-jsx-a11y: Catch issues in your React code
// Example: axe-core with Playwright
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('homepage should be accessible', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page }).analyze();
  expect(results.violations).toEqual([]);
});

Manual Testing

Keyboard testing (5 minutes):

  1. Unplug your mouse
  2. Tab through the entire page
  3. Can you reach everything? Can you see where you are?
  4. Can you operate all controls?
  5. Can you escape from modals and menus?

Screen reader testing (15 minutes):

  • macOS: VoiceOver (built-in, Cmd + F5)
  • Windows: NVDA (free) or JAWS
  • Browser: ChromeVox extension

Quick checks:

  • [ ] Zoom to 200%. Does the layout break?
  • [ ] Turn off CSS. Is the content still logical?
  • [ ] Check with grayscale filter. Is meaning conveyed beyond color?

The Business Case for Accessibility

Beyond legal compliance, accessibility makes business sense:

Expanded Market

  • 15% of the world's population has some form of disability
  • 100% of users benefit from accessible design (temporary injuries, situational limitations, aging)

Legal Protection

Accessibility lawsuits have increased 300% since 2018. Proactive compliance is far cheaper than reactive legal defense.

SEO Benefits

Accessible websites tend to have:

  • Better heading structure
  • More descriptive content
  • Proper image alt text
  • Cleaner HTML

All factors that search engines reward.

Better Code Quality

Accessibility requirements push you toward:

  • Semantic HTML (more maintainable)
  • Consistent UI patterns (better UX)
  • Proper state management (fewer bugs)

Quick Wins to Implement Today

  1. Run Lighthouse on your homepage and fix critical issues
  2. Add skip links for keyboard users to bypass navigation
  3. Check your color contrast with WebAIM's tool
  4. Test with keyboard only for 10 minutes
  5. Audit your forms for proper labels and error handling

Building Accessibility Into Your Process

At High Mountain Studio, accessibility isn't a phase. It's embedded in how we work. From design systems with built-in contrast ratios to component libraries with proper ARIA attributes, we build accessible foundations that make compliance natural, not painful.

If you're struggling with accessibility compliance or want to build your next project right from the start, let's talk. We'll help you create a website that works for everyone.

AccessibilityWCAG 2.1Web DevelopmentADA ComplianceScreen ReaderKeyboard NavigationA11yInclusive DesignSemantic HTMLARIA