Amblem
Furkan Baytekin

Web Accessibility 101: Building Inclusive Sites That Shine

Build accessible web forms: a practical guide to inclusive web development

Web Accessibility 101: Building Inclusive Sites That Shine
121
5 minutes

Hey there, web dev crew! Let’s talk about something that’s not just a nice-to-have but a must-do in 2025: web accessibility, or “a11y” as the cool kids call it (that’s “accessibility” with 11 letters in between—cute, right?). It’s all about making sure your site works for everyone—folks using screen readers, keyboards, or just struggling with tiny buttons on a shaky bus ride. Today, we’ll break down the basics, why it matters, and walk through a real-world example to make your site more inclusive. No jargon overload—just practical tips and code.

What’s Web Accessibility All About?

Imagine you build an awesome app, but 15% of your users can’t navigate it because they’re visually impaired, motor-challenged, or just don’t process info the same way. That’s where accessibility comes in—it’s designing with empathy so everyone gets a seat at the table. Think screen readers announcing your nav links, keyboard controls for button clicks, or text that’s legible without squinting.

In 2025, it’s not optional. Beyond the warm fuzzies of inclusion, there’s SEO juice (Google loves a11y), legal pressure and plain old user retention. Plus, it’s not as hard as it sounds—small tweaks can go a long way. Let’s see it in action with a relatable use case.

The Use Case: Fixing a Form

Forms are everywhere—signups, logins, feedback—and they’re a11y minefields if you’re not careful. Say you’ve got a simple contact form: name, email, message, submit. Here’s the naive version:

html
<div> <input placeholder="Name"> <input placeholder="Email"> <textarea placeholder="Message"></textarea> <button>Send</button> </div>

Looks fine, right? But try it with a screen reader or keyboard. Chaos. No labels, no focus indicators, no feedback—users are lost. Let’s make it shine with some a11y magic.

Step 1: Semantic HTML and Labels

First, ditch the div soup and add proper labels tied to inputs. Screen readers need this to announce what’s what:

html
<form> <label for="name">Name</label> <input id="name" type="text" placeholder="Your name"> <label for="email">Email</label> <input id="email" type="email" placeholder="Your email"> <label for="message">Message</label> <textarea id="message" placeholder="Your message"></textarea> <button type="submit">Send</button> </form>

Already better—keyboard users can tab through, and screen readers make sense of it. But we’re not done.

Step 2: Keyboard Navigation and Focus

Out of the box, browsers give focus outlines, but what if your CSS nukes them with outline: none? Bad move. Let’s style focus states intentionally:

css
input, textarea, button { padding: 8px; margin: 4px 0; border: 1px solid #ccc; } input:focus, textarea:focus, button:focus { outline: 2px solid #007bff; /* Visible focus ring */ outline-offset: 2px; /* Space it out */ }

Now, tabbing through highlights each field clearly. Bonus: add :focus-visible if you want to show outlines only for keyboard users, not mouse clicks (modern browsers love it):

css
input:focus-visible, textarea:focus-visible, button:focus-visible { outline: 2px solid #007bff; }

Keyboard folks rejoice—no more guessing where they’re at.

Step 3: ARIA for Clarity

ARIA (Accessible Rich Internet Applications) sprinkles extra info for assistive tech. Our form’s simple, but let’s say the submit button triggers a loading state. Without feedback, screen readers miss it. Add some ARIA love:

html
<form id="contact-form"> <label for="name">Name</label> <input id="name" type="text" required> <label for="email">Email</label> <input id="email" type="email" required> <label for="message">Message</label> <textarea id="message" required></textarea> <button type="submit" aria-busy="false" aria-describedby="form-status">Send</button> <span id="form-status" aria-live="polite" hidden>Submitting...</span> </form>
javascript
document.getElementById('contact-form').addEventListener('submit', async (e) => { e.preventDefault(); const button = e.target.querySelector('button'); const status = e.target.querySelector('#form-status'); button.setAttribute('aria-busy', 'true'); status.removeAttribute('hidden'); // Fake API call await new Promise(resolve => setTimeout(resolve, 2000)); button.setAttribute('aria-busy', 'false'); status.textContent = 'Sent!'; });

Users hear “Submitting…” then “Sent!”—no confusion.

Step 4: Validation and Errors

Required fields need teeth. Add required and some error handling:

html
<form id="contact-form"> <div class="field"> <label for="name">Name</label> <input id="name" type="text" required aria-describedby="name-error"> <span id="name-error" class="error" hidden>Please enter your name</span> </div> <!-- Repeat for email, message --> <button type="submit">Send</button> </form>
css
.error { color: #d00; font-size: 0.9em; } .error:not([hidden]) { display: block; }
javascript
document.getElementById('contact-form').addEventListener('submit', (e) => { e.preventDefault(); const nameInput = e.target.querySelector('#name'); const nameError = e.target.querySelector('#name-error'); if (!nameInput.value.trim()) { nameError.removeAttribute('hidden'); nameInput.focus(); return; } // Proceed with submission });

Pros, Cons, and Edge Cases

Pros:

Cons:

Edge Cases:

Real-World Payoff

Test this form with VoiceOver (macOS) or NVDA (Windows). You’ll hear clear labels, status updates, and errors—all while tabbing works like a charm. Users with vision impairments or motor issues won’t curse your name, and your site’s rep gets a boost. Plus, tools like Lighthouse will give you a gold star for a11y.

Wrapping Up

Web accessibility isn’t rocket science. It’s a handful of smart tweaks that make a big difference. Start with semantic HTML, nail keyboard support, sprinkle ARIA where needed, and you’re most of the way there. Our form’s just the beginning—try auditing your nav or images next.


Album of the day:

Suggested Blog Posts