Five ways websites lock people out, each one live and broken on this page —
then fixed, in the same breath, with the markup that did it.
Press play
Read the transcript
Intro
[record scratch] Yeah… Check the DOM. Check the tree. You can't tab through that.
Chorus
You can't click this. You can't click this. You can't click this. Stop! Semantic time!
Verse
My, my, my, my markup hits so bad, Makes the screen reader user straight-up mad. Thank you for blessing me With a clickable div that I just can't see! No role, no focus state, Hit the Tab key once, now I gotta wait. Buttons look real, but they have no tags, Accessibility backlog drags.
Chorus
Can't tab this. Look, man, you can't click this. No aria-label, can't click this. Stop! Semantic time!
Outro
Just use a button. CantClickThis.dev. Stop! Semantic time!
Greg Miller, Shrinkray InteractiveTrack: “Can't Click This” by toledogeeks
01 of 05
The clickable div
It looks like a button. It is styled like a button. The mouse agrees. Nothing else does.
WCAG 4.1.2 Name, Role, Value
WCAG 2.1.1 Keyboard
example.com
Broken markup
Put focus in the demo and press Tab. In the broken state there is nothing to land on.
✕ What breaks it
(flagged, see note 3) <divclass="btn"onclick="addToCart()"> Add to cart</div>
No role, so a screen reader announces this as plain text — or skips it entirely.
Not focusable: a div has no tabindex, so Tab walks straight past it.
A click handler alone ignores Enter and Space, which is how buttons are operated without a mouse.
✓ What fixes it
(flagged, see note 2) <buttontype="button"class="btn"> Add to cart</button>
Role, focusability, Enter and Space handling, and the disabled state all arrive free with the element.
type="button" stops it submitting a surrounding form by accident.
If it looks like a button, it has to be a button. Screen readers and keyboards only know what the code says, never what it looks like.
Si parece un botón, tiene que ser un botón. El lector de pantalla y el teclado solo saben lo que dice el código, nunca lo que se ve.
Semantic HTML sticker pack — $4
Add to cart
Cart:
Semantic HTML sticker pack — $4
Cart:
02 of 05
Alt text that says nothing
Every image gets an alt attribute. What goes inside it depends on whether the image carries information or just decorates.
WCAG 1.1.1 Non-text Content
example.com
Broken markup
The filename version is not a missing alt — it is worse. It is noise a screen reader has to read out loud.
✕ What breaks it
(flagged, see note 1) <imgsrc="hero.png"alt="IMG_2847_final_v3.png"> (flagged, see note 2) <imgsrc="swirl.svg" alt="decorative swirl divider graphic image">
A filename describes your export settings, not the picture. It tells the listener nothing.
Decoration announced out loud is clutter. "Image" is also redundant — the screen reader already said "graphic".
✓ What fixes it
<imgsrc="hero.png" (flagged, see note 1) alt="Blue high-top sneaker with a white sole, side view"> (flagged, see note 2) <imgsrc="swirl.svg"alt="">
Describes what a sighted person gets from the image, in the length of a caption.
An empty alt is a decision, not an omission: it tells assistive tech to skip this one on purpose.
If you deleted the image, what sentence would you write in its place? That sentence is your alt text. If you would write nothing, use alt="".
Si borraras la imagen, ¿qué frase escribirías en su lugar? Esa frase es tu texto alternativo. Si no escribirías nada, usa alt="".
Court High 84 — $120Court High 84 — $120
03 of 05
The invisible cursor
Someone removed the focus outline because it looked ugly on one button. Now nobody navigating by keyboard can tell where they are.
WCAG 2.4.7 Focus Visible
WCAG 2.4.11 Focus Not Obscured
example.com
Broken markup
Tab through this row in both states. Same tab order, same elements — only one of them tells you anything.
✕ What breaks it
a,button { (flagged, see note 2) outline: none;}
Nothing replaces it, so keyboard focus becomes invisible across the entire page.
This is usually copied in to hide the ring on mouse click — :focus-visible already does that for you.
✓ What fixes it
(flagged, see note 1) a:focus-visible,button:focus-visible {outline: 3px solid #ffffff;outline-offset: 2px; (flagged, see note 2) box-shadow: 0 0 0 6px #0e1420;}
:focus-visible fires for keyboard focus and stays quiet for mouse clicks — the behaviour people actually wanted.
A second ring in the opposite tone keeps the indicator visible on light and dark backgrounds alike.
Never remove a focus outline without replacing it with something at least as visible. If you cannot see where you are, neither can the person using only a keyboard.
Nunca quites el indicador de foco sin poner algo igual de visible en su lugar. Si tú no ves dónde estás, quien navega solo con teclado tampoco.
04 of 05
Click here, click here, click here
Screen reader users pull up a list of every link on the page to navigate it. Out of context, most link text collapses into noise.
WCAG 2.4.4 Link Purpose
WCAG 2.4.9 Link Purpose, Link Only
example.com
Broken markup
Use the links list button below. That list is exactly what a screen reader user gets.
✕ What breaks it
<p>Registration closes Friday. (flagged, see note 1) <ahref="/register">Click here</a> to sign up.</p><p>The schedule is posted. (flagged, see note 2) <ahref="/schedule">Read more</a>.</p>
"Click here" describes the mouse, not the destination — and the destination is the only useful part.
Three links reading "read more" are indistinguishable in a links list, in search results, and on a phone.
✓ What fixes it
<p>Registration closes Friday. (flagged, see note 1) <ahref="/register">Register for DevFest</a>.</p> (flagged, see note 2) <p><ahref="/schedule">View the full schedule</a> for all three tracks.</p>
The link says where it goes, so it survives being read on its own.
Front-loading the verb also makes the sentence shorter. Accessible copy is usually just better copy.
Link text has to make sense with the sentence around it removed. Read the links on their own — if you cannot tell them apart, neither can anyone else.
El texto de un enlace tiene que entenderse sin la frase que lo rodea. Lee solo los enlaces: si no los distingues, nadie los distingue.
Registration for DevFest closes Friday. Click here to sign up.
Placeholder text looks like a label until you start typing and it vanishes — taking the only instruction with it.
WCAG 1.3.1 Info and Relationships
WCAG 3.3.2 Labels or Instructions
example.com
Broken markup
Tab into each field. The broken version announces "edit text" three times with no way to tell them apart.
✕ What breaks it
(flagged, see note 1) <divclass="label">Email</div> (flagged, see note 2) <inputtype="text"placeholder="you@example.com"> (flagged, see note 3) <pclass="hint">* Required</p>
A styled div is not a label. Nothing connects it to the input, so it is never announced with the field.
Placeholder-only fields announce as "edit text" and the hint disappears as soon as typing starts.
Required is signalled by a red asterisk alone — colour carrying meaning on its own fails 1.4.1.
✓ What fixes it
(flagged, see note 1) <labelfor="email">Email</label><inputtype="email"id="email"name="email" (flagged, see note 2) autocomplete="email" (flagged, see note 3) aria-describedby="email-hint" required><pid="email-hint">We send one confirmation and nothing else.</p>
for and id pair them up, so the label is announced with the field and clicking the label focuses it.
autocomplete lets the browser fill it in — a genuine accessibility win under 1.3.5, not just convenience.
required is announced as a state, and aria-describedby attaches the hint without crowding the label.
Every input needs a real label a screen reader can announce. Placeholder text is a hint, not a label, and it disappears the moment it is needed.
Cada campo necesita una etiqueta real que el lector de pantalla pueda anunciar. El placeholder es una pista, no una etiqueta, y desaparece justo cuando hace falta.
Screen reader buffer
Approximate announcement for the focused element. Teaching aid, not a screen reader.