HTML — One Tag Stands In For a Feature
Seven Things a Button Made From a div Loses
In one line
If you use <div onclick> instead of <button>, you lose seven things the browser used to do for you. And usually five of them never get rebuilt.
Why it is a problem — the invisible user
If you build with a mouse and check with a mouse, this accident never comes to light. <div onclick> works perfectly with a mouse. QA checks with a mouse too, so it ships as is.
The ones who break are users who use only a keyboard. Because they hurt their wrist, because they use a screen reader, or simply because Tab is faster. For them, that button is a picture that is visible on screen but can't be pressed. If it is the payment button, that person can't pay.
The cost of fixing it also grows over time. It is no longer a matter of changing one tag, but of following through every CSS selector, event delegation, and test selector stacked on top of it. That is why this article deals not with a "list to take care of later" but with the judgment made at the moment you pick a tag.
What you lose
What <div onclick="save()">저장</div> (the button label is the Korean word for "save") loses.
- You can't reach it with Tab — it needs
tabindex="0" - It isn't pressed with Enter or Space — you have to attach key handlers yourself
- Screen readers don't read it as "button" — it is just text
- There is no disabling —
disabledhas no effect - Form submission doesn't work — there is no
type="submit" - There is no focus ring — keyboard users can't tell where they are
- There is no default browser behavior — context menu, recognition by automation tools
If you write the one word <button>, all of it is free. Accessibility is not a feature you add later; most of it is decided at the moment you pick a tag.
Landmarks — the map of the page
Screen reader users don't read a page from the top. They jump between landmarks.
<header> 사이트 머리 </header>
<nav> 탐색 </nav>
<main> 본문 — 페이지에 하나만 </main>
<aside> 곁다리 </aside>
<footer> 꼬리 </footer>
If there is a <main>, "skip to content" works. <div class="main"> means nothing — a class name tells a machine nothing.
Headings are a table of contents
h1 to h6 are not font sizes. They are the table of contents of the document. Screen readers skim only the headings to grasp the structure.
- There is one
h1. What the page is about - Don't skip levels — an
h4must not come after anh2 - If you want to change the size, change it with CSS. Don't change the tag
Using an h3 because it looks nice is the most common accessibility problem.
alt is not a "description"
alt is the text that fills the spot when the image is gone.
| Image | alt |
|---|---|
| Product photo | alt="빨간 등산 배낭 45L" (the Korean alt text means "red hiking backpack 45L") |
| Logo inside a link | alt="홈으로" (the Korean alt text means "to home") — not the image but the purpose of the link |
| Decorative divider | alt="" — leave it empty |
| A picture with the same description next to it | alt="" — being read twice is a hindrance |
Omitting the alt attribute itself is different from alt="". If you omit it, the screen reader reads the file name (IMG_20240103.jpg). If you leave it empty, it quietly skips it. For decorative images, always specify alt="" explicitly.
An input with no label has no name
<!-- ❌ placeholder 는 라벨이 아니다 -->
<input placeholder="이메일">
<!-- ✅ -->
<label for="email">이메일</label>
<input id="email" type="email" autocomplete="email">
A placeholder disappears when you type. There is then no way to confirm what the field was for. For low-vision users, the contrast is also insufficient.
If you connect a label, clicking the text also focuses the field. It makes a big difference especially on mobile.
And if you set type and autocomplete properly, the mobile keyboard changes and autofill works — type="email", type="tel", autocomplete="one-time-code".
Links and buttons are different
A link goes somewhere. A button does something.
- If the address changes →
<a href>. Opening in a new tab, bookmarks, and the back button work - If the state changes →
<button>
<a href="#" onclick="..."> is neither. And link text must state its purpose on its own. Screen reader users pull out just the links and view them as a list. A list with twenty "here" and "read more" entries is useless.
Tables need header cells
<table>
<caption>월별 매출</caption>
<thead><tr><th scope="col">월</th><th scope="col">매출</th></tr></thead>
<tbody><tr><th scope="row">1월</th><td>1,200</td></tr></tbody>
</table>
Only with scope does a screen reader read it like "January, sales, 1200." Without it, it reads only the numbers one after another.
Don't use tables for layout. That is a job for CSS Grid.
Don't forget lang
<html lang="ko">
Without it, a screen reader reads Korean with English pronunciation rules. Of all the things you get by fixing one character, it has the biggest effect.
ARIA is a last resort
Bad ARIA is worse than none.
Attaching role="button" doesn't create keyboard operation. Only the apparent name changes, and the actual behavior stays as it is. So the rule is one.
If there is a suitable HTML element, use it. Use ARIA when HTML has no matching element (tab panels, trees, live regions).
How to check in the field
Automated check tools catch only 30–40% of the problems. They catch only what a machine can see, such as insufficient contrast or a missing alt. The rest a person has to look at.
- Try to go all the way through with the keyboard alone. Tab, Shift+Tab, Enter, Space, Esc. Put the mouse away and within 5 minutes most things come to light
- Is it always visible where focus is right now
- Is it still usable when zoomed to 200%
These three catch more than automated tools.
In practice, it lasts longer to fix this as a single line in the PR checklist. One is enough: "Did you use the newly built interactive element with the keyboard alone?" A rule that you expect people to remember every time ends up not being followed.