TT Lab
はじめる
学ぶ 学習パス コース

HTML — タグ一つが機能を肩代わりする

divで作ったボタンが失う七つのもの

TT Labで続きを見る

一言でいうと

<button>の代わりに<div onclick>を使うと、ブラウザーが代わりにやってくれていた7つのことを失います。そして、そのうち5つは、たいてい作り直されません。

なぜ問題になるのか: 見えないユーザー

マウスで作ってマウスで確認していると、この事故は最後まで表面化しません。<div onclick>は、マウスでは完璧に動作するからです。QAもマウスで確認するため、そのままデプロイされます。

壊れるのは、キーボードだけを使うユーザーの側です。手首を痛めている人、スクリーンリーダーを使う人、あるいは単にTabキーのほうが速い人です。その人たちにとって、そのボタンは、画面に見えているのに押せない絵です。それが決済ボタンなら、その人は決済ができません。

直すコストも、時間がたつほど大きくなります。タグ1つを変えるだけでは済まず、その上に積み重なったCSSセレクター・イベント委譲・テストのセレクターをすべて追いかけなければならなくなるからです。そのため、この文章は「あとで確認するリスト」ではなく、タグを選ぶ瞬間の判断を扱います。

失うもの

<div onclick="save()">저장</div>(韓国語で「保存」を意味する語です)が失うものは、次のとおりです。

  1. Tabキーで移動できません: tabindex="0"が必要です
  2. EnterキーやSpaceキーで押せません: キーハンドラーを自分で付ける必要があります
  3. スクリーンリーダーが「ボタン」と読み上げません: ただのテキストです
  4. 無効化がありません: disabledが効きません
  5. フォームを送信できません: type="submit"がありません
  6. フォーカスリングがありません: キーボードユーザーが今どこにいるかわかりません
  7. ブラウザーの標準動作がありません: コンテキストメニュー、自動化ツールによる認識

<button>と書くだけで、すべてが無料でついてきます。アクセシビリティは、あとから載せる機能ではなく、タグを選ぶ瞬間にほとんど決まります。

ランドマーク: ページの地図

スクリーンリーダーのユーザーは、ページを上から読んでいくわけではありません。ランドマークの間を飛びます。

<header>  사이트 머리   </header>
<nav>     탐색          </nav>
<main>    본문 — 페이지에 하나만 </main>
<aside>   곁다리        </aside>
<footer>  꼬리          </footer>

<main>があれば、「本文へスキップ」が動作します。<div class="main">には何の意味もありません。クラス名は、機械に何も教えてくれないからです。

見出しは目次です

h1–h6は文字の大きさではありません。文書の目次です。スクリーンリーダーは、見出しだけを走査して構造を把握します。

見た目がよいからという理由でh3を使うのが、アクセシビリティの問題の中で最もよくあるものです。

altは「説明」ではありません

altは、その画像が表示されなくなったときに、その場所を埋めるテキストです。

画像 alt
製品写真 alt="빨간 등산 배낭 45L"(韓国語の文は「赤い登山用バックパック45L」という意味です)
リンク内のロゴ alt="홈으로"(韓国語で「ホームへ」を意味する語です)。画像ではなくリンクの目的を書きます
装飾用の区切り線 alt="": 空にしておきます
隣に同じ説明がある画像 alt="": 2回読み上げられると邪魔になります

alt属性そのものを省くこととalt=""は別です。省くと、スクリーンリーダーがファイル名を読み上げます(IMG_20240103.jpg)。空にしておくと、静かに読み飛ばされます。装飾画像には、必ずalt=""を明示します。

ラベルのない入力欄は名前を持たない

<!-- ❌ placeholder 는 라벨이 아니다 -->
<input placeholder="이메일">

<!-- ✅ -->
<label for="email">이메일</label>
<input id="email" type="email" autocomplete="email">

placeholderは、入力すると消えます。何を書く欄だったのかを確認する方法がなくなります。弱視のユーザーにとっては、コントラストも不足しています。

ラベルを関連付けると、文字をクリックしても入力欄にフォーカスが移ります。モバイルでは特に大きな違いです。

また、typeとautocompleteを正しく指定すると、モバイルのキーボードが切り替わり、オートコンプリートが動作します。たとえばtype="email"、type="tel"、autocomplete="one-time-code"です。

リンクとボタンは異なります

リンクは移動し、ボタンは何かをします。

<a href="#" onclick="...">は、どちらでもありません。そして、リンクのテキストは、それ自体で目的を伝える必要があります。スクリーンリーダーのユーザーは、リンクだけを抜き出して一覧で見ます。「ここ」や「もっと見る」ばかりが20個並んだ一覧は、役に立ちません。

表には見出しセルが必要です

<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>

scopeがあって初めて、スクリーンリーダーが「1月、売上、1200」のように読み上げます。ないと、数字だけがだらだらと読み上げられます。

レイアウトのために表を使いません。それはCSS Gridの仕事です。

langを書き忘れない

<html lang="ko">

ないと、スクリーンリーダーが韓国語を英語の発音規則で読み上げます。1文字を直すだけで得られるものの中で、最も効果が大きいものです。

ARIAは最後の手段です

悪いARIAは、ないほうがましです。

role="button"を付けても、キーボード操作は生まれません。見かけの名前だけが変わり、実際の動作はそのままです。そのため、ルールは1つです。

適切なHTML要素があれば、それを使います。ARIAは、HTMLに対応する要素がないとき(タブパネル、ツリー、ライブリージョン)に使います。

現場での確認方法

自動検査ツールは、問題の30–40%だけを検出します。コントラストが足りない、altがないといった、機械が見られるものだけを検出するからです。残りは、人が見る必要があります。

この3つは、自動ツールよりも多くを検出します。

実務では、これをPRチェックリストの1行として固めておくほうが長続きします。「新しく作ったインタラクティブ要素をキーボードだけで使ってみたか」の1つで十分です。人が毎回覚えていることを期待するルールは、結局守られません。