5.

HTMLのa要素とbutton要素の使い分け|アクセシブルな実装方法

編集
この記事の要点
  • ナビゲーション(別ページに移動)= <a href>アクション(送信・モーダル等)= <button>
  • <button>type 必須。フォーム内は既定 submit、それ以外は type="button"
  • <input type="submit"> もボタンだが、現代は <button type="submit"> が主流(中に HTML 入れられる)
  • <a> をボタン風に見せるのは OK(クラスで装飾)。ただし遷移しない処理には使わない(アクセシビリティ違反)
  • ARIA: role="button" + tabindex="0" + キーボード(Enter/Space)対応。可能なら素直に <button> を使う

リンクとボタンの正しい使い分け

HTML には別ページや別位置に「移動」させる <a> と、「何かを実行」させる <button> があります。この 2 つは見た目が似ていても、ユーザ・支援技術・SEO 視点では別物です。

判断使う要素
別 URL(または同一ページ内アンカー)に移動<a href>「商品詳細」「目次へ戻る」「外部サイト」
フォーム送信<button type="submit">「送信」「保存」
モーダル開閉・タブ切替・JS で何かを実行<button type="button">「開く」「コピー」「削除」
フォーム内容のリセット<button type="reset">「リセット」
新規タブで開きたい<a target="_blank" rel="noopener">外部リンク

基本構文

<!-- リンク:別ページへ移動 -->
<a href="/products/123">商品詳細を見る</a>

<!-- 外部リンク(noopener + 視覚ヒント) -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
  外部サイト
  <span aria-hidden="true">↗</span>
</a>

<!-- ボタン:フォーム送信 -->
<form action="/login" method="post">
  <input name="email" type="email" required>
  <button type="submit">ログイン</button>
</form>

<!-- ボタン:JS でアクション -->
<button type="button" onclick="openModal()">モーダルを開く</button>

<!-- ボタン内に画像・アイコン -->
<button type="button" aria-label="検索">
  <svg width="20" height="20"><!-- 虫眼鏡 --></svg>
</button>

<input type="submit"> との違い

<!-- 古典: input -->
<input type="submit" value="送信">

<!-- 現代: button -->
<button type="submit">送信</button>

<!-- button の利点: HTML をネスト可能 -->
<button type="submit">
  <svg class="icon-send"></svg>
  <span>送信</span>
</button>

<input> はテキストしか入れられませんが、<button> はアイコン・改行・複数要素を入れられるので、現代では <button type="submit"> が主流です。

「リンクをボタン風」「ボタンをリンク風」の罠

見た目はクラスで自由に切り替えられますが、意味(セマンティクス)は要素そのもので決まります。

<!-- ✅ OK: リンクをボタン風に装飾(遷移する処理) -->
<a href="/signup" class="btn btn-primary">無料登録する</a>

<!-- ❌ NG: 遷移しない処理をリンクで実装 -->
<a href="javascript:void(0)" onclick="deleteUser()">削除</a>
<a href="#" onclick="openModal()">開く</a>
<!-- → 右クリックで「新規タブで開く」できてしまう
     → スクリーンリーダーは「リンク」と読み上げる
     → 遷移しないので混乱 -->

<!-- ✅ 修正: button を使う(クラスで見た目はリンク風に) -->
<button type="button" class="link-style" onclick="deleteUser()">
  削除
</button>

アクセシビリティ(ARIA・キーボード操作)

最良はネイティブ要素を使うことです。<button> は最初から:

  • Tab フォーカス可能
  • Enter / Space で発火
  • スクリーンリーダーが「ボタン」と読み上げ
  • 無効化 (disabled) 対応

どうしても <div><span> をボタンにしたい場合(既存デザインシステム等):

<!-- 全部自前で実装する必要がある -->
<div
  role="button"
  tabindex="0"
  onclick="doIt()"
  onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); doIt(); }"
  aria-label="保存"
  aria-disabled="false"
>
  保存
</div>

これだけ書くより、素直に <button> をスタイルした方が圧倒的に楽です。

主要 CSS フレームワークでの実装

フレームワーク典型コード
Tailwind CSS<button class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Bootstrap 5<button class="btn btn-primary">
Material UI<Button variant="contained" color="primary">
shadcn/ui<Button variant="default">
Chakra UI<Button colorScheme="blue">

セキュリティ:target="_blank" の落とし穴

外部リンクを新規タブで開くとき、リンク先 JS から window.opener 経由で元ページを操作される(タブナビング攻撃)リスクがあります。必ず rel を付けます:

<!-- ❌ NG -->
<a href="https://external.com" target="_blank">外部</a>

<!-- ✅ OK -->
<a href="https://external.com" target="_blank" rel="noopener noreferrer">外部</a>

<!-- 注: モダンブラウザは target="_blank" で暗黙的に noopener を有効化
   しているが、互換性のため明示推奨 -->

無効化(disabled)の挙動の違い

要素無効化方法クリックフォーカス
<button disabled>属性無効飛ばす
<a>disabled 属性は無効常に発火飛ばさない
<a> 無効化したい場合href を外す or aria-disabled="true" + CSS pointer-events:none + tabindex="-1"無効飛ばす

UX のベストプラクティス

  • 主要 CTA(Call To Action)は1 ページに 1 つだけ強調色。すべてを目立たせると優先度が伝わらない
  • テキストは具体的に。「クリック」ではなく「資料をダウンロード」「無料体験を始める」
  • ボタンの最小タップサイズは 44×44 px(WCAG / iOS HIG / Material 共通)
  • ローディング中は disabled + 「送信中…」表示で二重送信防止
  • 破壊的操作(削除等)は確認ダイアログを挟むか、警告色 + アンドゥ可能に

FAQ

Q: type を書かない <button> の挙動は?
A: フォーム内では submit 扱いになります。これが原因でモーダル開くつもりがフォーム送信される事故が多発。必ず type="button" を明示してください。

Q: アイコンだけのボタンに aria-label は必要?
A: 必須です。スクリーンリーダー利用者にはアイコンが見えません。<button aria-label="検索"> で必ず代替テキストを。

Q: <a><button> でフォーカスリングが違う
A: ブラウザの既定スタイルが異なります。:focus-visible でデザインシステムに合わせ統一しましょう。

編集
Post Share
子ページ
  1. a要素
  2. link要素
  3. button要素
同階層のページ
  1. 構造
  2. セクション
  3. テキスト・フォント
  4. リスト
  5. リンク・ボタン
  6. ルビ
  7. 画像
  8. オブジェクト
  9. 動画・音声
  10. フォーム要素
  11. その他の要素

最近更新/作成されたページ