Accessibility basics
The World Health Organization estimates that one person in six lives with a significant disability. Accessible HTML lets them use your page with a screen reader, with a keyboard alone, with zoomed text or with high contrast. Most of it comes free when you use the right elements.
Use real buttons and links
<style>
.fake { display: inline-block; padding: 6px 12px; background: #5a48f5; color: white;
border-radius: 6px; cursor: pointer; font-family: system-ui; }
</style>
<div class="fake" onclick="alert('Clicked the div')">Fake button</div>
<button onclick="alert('Clicked the button')">Real button</button>Click inside the preview and press Tab: focus skips the fake button entirely, and Enter can't press it. A <button> can be focused, works with Enter and Space, and is announced as a button, all with no extra code. Use <a href> to go somewhere and <button> to do something.
Label every field
<!-- poor: the placeholder vanishes as you type, and isn't a reliable label -->
<input placeholder="Email">
<!-- good -->
<label for="email">Email</label>
<input id="email" type="email" autocomplete="email">Give images and icons a name
alt text says what an image shows or does, and decorative images get alt="" so screen readers skip them. A button that shows only an icon needs a name too, from its text or from aria-label:
<button aria-label="Close">✕</button>
<button><img src="https://picsum.photos/seed/gear/16/16" alt=""> Settings</button>Keyboard and focus
Everything that works with a mouse must work with a keyboard, and people need to see where the focus is. Never remove the focus outline without replacing it:
<style>
button, a { font: 16px system-ui; margin: 4px; }
.bad:focus { outline: none; }
.good:focus-visible { outline: 3px solid #5a48f5; outline-offset: 2px; }
</style>
<button class="bad">No focus style</button>
<button class="good">Clear focus style</button>
<a class="good" href="#">A link</a>Press Tab to move through them. :focus-visible shows the ring to keyboard users without flashing it on every mouse click.
Contrast
Text needs enough contrast with its background. The WCAG guidelines ask for a ratio of at least 4.5:1 for normal text and 3:1 for large text:
<p style="color:#aaa; background:#fff; font: 16px system-ui">Light grey on white: hard to read (2.3:1)</p>
<p style="color:#595959; background:#fff; font: 16px system-ui">Dark grey on white: easy to read (7:1)</p>Don't rely on colour alone, either. A form field marked only with a red border tells a colour-blind visitor nothing; add a message in words.
Language and title
<html lang="en">
<title>Python loops · CodeMIRAI</title>lang tells screen readers which language to pronounce, and the <title> is the first thing announced when a page opens.
ARIA, the last resort
ARIA attributes (role, aria-label, aria-expanded, aria-live...) add accessibility information that plain HTML can't express. The first rule of ARIA is don't use ARIA when a native element does the job: a <button> beats <div role="button" tabindex="0"> plus all the key handling you'd have to write yourself.
One genuinely useful ARIA attribute is aria-live, which announces changes to part of the page:
<button id="add">Add to cart</button>
<p id="status" aria-live="polite"></p>
<script>
let count = 0;
document.querySelector("#add").addEventListener("click", () => {
count++;
document.querySelector("#status").textContent = `${count} item(s) in your cart`;
});
</script>Screen readers announce the new text without moving the person's focus away from the button.