| この記事の要点 |
|
リンクとボタンの正しい使い分け
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 でデザインシステムに合わせ統一しましょう。
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Git pushした変更の取消し|revert・履歴変更と保護の注意点 2026-10-15 23:49:01
- Gitでローカルを戻す前に|reset・restoreの違いと退避範囲 2026-10-15 23:49:01
- Git rebaseの使い方|対象範囲・競合解決・pushの判断 2026-10-15 22:50:59
- Git pullの使い方|取込み先・ff-only・競合とstashの注意点 2026-10-15 22:50:59
- Git pushの使い方|送信先・タグ・force-with-leaseの条件 2026-10-15 22:12:47
- Git remoteの設定・確認・削除|SSH URLとfetch/pushの違い 2026-10-15 22:12:47
- Gitリモートリポジトリの作成|初回push・既存履歴・mirrorの注意 2026-10-15 15:48:16
- Gitのuser.name・user.email設定|local/global・確認と過去履歴の注意 2026-10-15 15:30:07
- LinuxにGitをインストール|配布元・初期設定・TLSと認証の確認 2026-10-15 15:30:06
- Git導入と初回push|Linuxサーバー・Windows/macOS・SSH設定 2026-10-15 14:51:54
- Db2のCREATE INDEX|索引定義・INCLUDEと実行計画の確認 2026-10-15 14:10:22
- Db2の自動採番|IDENTITY・ID取得とRESTARTの注意点 2026-10-15 14:10:22
- 【DB2】特定スキーマの全テーブルの全カラム情報を取得する方法 2026-10-15 13:53:15
- Db2のシーケンスとIDENTITYの違い|採番とカタログの確認SQL 2026-10-15 13:53:15
- Db2で複数カラムをUPDATEする方法|SET・WHEREと更新前の値 2026-10-15 13:53:15