Section 2 · Semantic HTML First
Buttons, Links and the Clickable Div
17 min3 min readCode playground
Lesson at a glance
Choose correctly between a link and a button, and replace clickable divs and href=“#” links with native elements that work for keyboard and screen reader users.
- 1Links go, buttons do
- 2What the clickable div is missing
- 3Buttons in forms, and disabled buttons
- 4Text that makes sense alone
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Ticketly’s “Add to basket” control looks like a button: rounded corners, a brand colour, a pointer cursor on hover. It is a div with an onclick. Click it with a mouse and it works perfectly, which is exactly why nobody on the original team noticed. Press Tab and focus jumps straight past it. Samir’s screen reader reads it as plain text. A switch user can’t reach it at all.
Links go, buttons do
The decision between the two native controls comes down to one question: does it take the user somewhere?
- A link (
<a href="…">) navigates: to another page, another part of this page (#lineup), a file, an email address. Screen readers announce it as “link”. Users expect to open it in a new tab, copy its address, see it in history. It activates with Enter. - A button (
<button>) performs an action on the current page: add to basket, open a dialog, submit a form, toggle a filter. Screen readers announce it as “button”. It activates with Enter and Space.
Styling is irrelevant to the choice. A link can look like a big call-to-action and a button can look like a text link; CSS handles appearance, the element handles behaviour.
What the clickable div is missing
To understand why the native element wins, count what a div lacks. Here is the minimum to rebuild a button by hand:
<div class="btn" role="button" tabindex="0">Add to basket</div>const fake = document.querySelector('.btn');
fake.addEventListener('click', addToBasket);
fake.addEventListener('keydown', (event) => {
if (event.key === 'Enter') addToBasket();
if (event.key === ' ') event.preventDefault(); // stop the page scrolling
});
fake.addEventListener('keyup', (event) => {
if (event.key === ' ') addToBasket(); // native buttons fire on Space release
});That is a role, focus, Enter, Space and scroll prevention, and it still lacks a disabled state, form submission, and the way Windows contrast themes (forced colors mode) style buttons. Compare:
<button type="button" class="btn" onclick="addToBasket()">Add to basket</button>One element, and every behaviour is correct in every browser and every screen reader. This is the pattern you will see throughout the course: native HTML first, because the browser has already done the hard work.
Buttons in forms, and disabled buttons
Inside a <form>, a <button> defaults to type="submit". Ticketly’s checkout has an “Apply promo code” button that kept submitting the whole form. The fix is one attribute:
<button type="button" onclick="applyPromo()">Apply promo code</button>The disabled attribute removes a button from the tab order, so keyboard users tabbing through the form skip straight past it, and nobody is told why it is unavailable. A screen reader in browse mode still finds it, announced as “unavailable”, but with no reason attached. For a “Pay now” button that is waiting on a valid card, consider leaving it enabled and explaining the problem when it is pressed, which you will build in Accessible Forms.
Text that makes sense alone
Screen reader users often pull up a list of all links or buttons on a page, and voice control users say the visible text to click it. “Read more”, “Click here” and “Details” are meaningless in that list. WCAG 2.4.4 asks that a link’s purpose be clear from its text, or its text plus its surrounding sentence or list item. Aim higher than the minimum: write “Friday line-up and set times”, not “Read more”. If the design insists on short visible text, you will learn in Accessible Names and Descriptions how to extend the name without breaking voice control.
One more link habit: if a link opens a new tab or downloads a file, say so in its text, for example “Venue guide (PDF, 2 MB)” or “Seating plan (opens in a new tab)”. An unexpected new tab breaks the Back button for screen reader and magnifier users, who often don’t notice that the window changed and wonder why they can’t return.
Real buttons and links fix the controls that act. Next come the controls that collect information, and the place where most inaccessible sites lose their customers: forms.
Key takeaways
- A link takes the user somewhere (a new URL or a fragment); a button does something on the current page.
- Native buttons are focusable, announced as buttons, and activate with both Enter and Space, all for free.
- A div with a click handler has no role, no focus and no keyboard support; rebuilding those by hand takes four separate fixes and is easy to get wrong.
- Inside a form, give non-submitting buttons type=“button”, and write link and button text that makes sense out of context.