ナビゲーションメニューやリストのリンクが、文字の上でしか反応せず押しづらい、ということがあります。
結論から書くと、原因は<a>のdisplayが初期値のinlineのままで、リンクテキストの大きさぶんしか領域を持っていないことです。直す場所は<li>ではなく<a>で、display: blockとpaddingを指定します。
この記事では、クリック範囲が狭くなる原因、基本の直し方、li全体を押せるようにする方法、そして「どこまで広げれば十分か」の基準までをまとめました。
スポンサーリンク
クリック範囲が狭くなる原因は、aタグがinlineのままだから
結論:<a>のdisplayの初期値はinlineで、inline要素はリンクテキストの大きさぶんしか領域を持ちません。親のli要素がどれだけ大きくても、その余白部分はクリックに反応しません。
<ul class="nav">
<li><a href="/about/">このサイトについて</a></li>
<li><a href="/contact/">お問い合わせ</a></li>
</ul>
このHTMLでは「このサイトについて」という文字の上だけがリンクです。liの左右に余白があっても、そこを押してもページは遷移しません。
liにmarginやpaddingを足してもクリック範囲は広がらない
結論:marginとpaddingをli要素に付けても、クリックできる範囲は1ピクセルも広がりません。
marginは要素の外側の余白で、そもそもどの要素にも属さない空間です。paddingはli要素の内側の余白ですが、li要素はリンクではないので、そこを押しても何も起きません。
/* これはクリック範囲の対策にならない */
li {
margin: 10px;
padding: 10px;
}
広げる対象はli要素ではなく、リンクそのものである<a>です。ここを取り違えると、見た目の余白だけが増えて押しづらさは残ります。
基本の直し方は、aにdisplay:blockとpaddingを指定すること
結論:<a>をdisplay: blockにして、余白は<a>のpaddingとして持たせます。
li a {
display: block;
padding: 12px 16px;
}
display: blockにすると<a>が親のli要素の幅いっぱいに広がります。さらにpaddingのぶんだけ上下の高さも増え、この余白は<a>自身の領域なので、そのままクリックできる範囲になります。
displayの値はメニューの並べ方で選ぶ
結論:縦積みならblock、横並びで幅を文字量に合わせたいならinline-block、アイコンと文字を並べるならflexです。
| 指定 | リンクの幅 | 向いている場面 |
|---|---|---|
display: block |
親要素いっぱい | 縦積みのメニュー・サイドバー |
display: inline-block |
文字量に合わせる | 横並びで幅を揃えたくないとき |
display: flex |
親要素いっぱい | アイコンと文字を横に並べるとき |
いずれの値でもpaddingがクリック範囲になる点は同じです。inlineのときだけ上下のpaddingが高さに反映されないため、クリック範囲が広がりません。
スポンサーリンク
横並びのメニューは、ulをflexにしてaにpaddingを持たせる
結論:並べ方はul側のdisplay: flexで決め、クリック範囲は<a>側のpaddingで決めます。
.nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.nav a {
display: block;
padding: 12px 16px;
}
li要素をfloat: leftで横に並べる書き方も今でも動きますが、回り込みの解除が必要になるぶん手間が増えます。flexなら親に1行足すだけで並びます。
「並べ方はul、クリック範囲は<a>」と役割を分けておくと、メニューの並び方を変えたときにクリック範囲まで崩れることがなくなります。
li全体を押せるようにするにはストレッチドリンクを使う
結論:li要素にposition: relative、<a>の疑似要素にposition: absoluteとinset: 0を指定すると、li要素の全面がクリックできるようになります。
リンクテキストのほかにサムネイルや説明文が入っていて、カード全体を押せるようにしたい場合はこの方法を使います。
li {
position: relative;
}
li a::after {
content: "";
position: absolute;
inset: 0;
}
inset: 0はtop: 0; right: 0; bottom: 0; left: 0をまとめた書き方です。中身が空の疑似要素をli要素いっぱいに広げていて、その疑似要素は<a>の一部なので、覆われた範囲がそのままリンクになります。
この書き方はBootstrapに.stretched-linkというヘルパークラスとして用意されています。フレームワークを使っているなら、クラスを1つ足すだけで同じことができます。フレームワークの選び方はCSSでオススメのフレームワークと実装例【Bootstrap/Tailwind】にまとめています。
1つのli要素にリンクが2つあるときは使えない
結論:ストレッチドリンクが使えるのは、1つのli要素にリンクが1つだけのときです。
疑似要素がli要素の全面を覆うため、同じli要素の中にある別のリンクやボタンが押せなくなります。テキストを選択する操作も効かなくなります。
中にリンクが複数あるときは、<a>にdisplay: blockとpaddingを指定する基本の方法に戻してください。
スポンサーリンク
クリック範囲はどれくらい広げれば十分か
結論:WCAG 2.2の達成基準2.5.8「ターゲットのサイズ (最小)」が、レベルAAで24×24 CSSピクセル以上を求めています。タッチ操作を主に考えるなら、Appleのヒューマンインターフェースガイドラインが示す44×44ptが目安になります。
| 基準 | 最小サイズ | 位置づけ |
|---|---|---|
| WCAG 2.2 達成基準2.5.8 | 24×24 CSSピクセル | レベルAA |
| Apple ヒューマンインターフェースガイドライン | 44×44 pt | iOS向けの推奨値 |
WCAGの達成基準2.5.8には例外があり、隣り合うリンクとの間隔が十分に空いている場合や、文章の中に埋め込まれたリンクは対象外です。ただしナビゲーションのように同じ大きさのリンクが隣接して並ぶ場面は例外に当たらないので、24×24は満たしておくのが安全です。
文字の高さが16pxのリンクにpadding: 12px 16pxを指定すると、高さは16 + 12 + 12 = 40pxになります。この時点で24pxの基準は超えます。
ホバーだけでなくフォーカスの見た目も付ける
結論::hoverだけを指定すると、キーボードで操作している人にはどこを選んでいるか分かりません。:focus-visibleも併せて指定します。
li a {
display: block;
padding: 12px 16px;
}
li a:hover {
background-color: #f2f2f2;
}
li a:focus-visible {
outline: 2px solid #1a73e8;
outline-offset: -2px;
}
:focus-visibleは、キーボードで移動してきたときだけアウトラインを出す疑似クラスです。マウスでクリックしたときには出ないので、見た目を理由にoutline: noneで消してしまう必要がありません。
背景色を変える以外の見せ方をしたいときは、CSSで影をつける方法は?box-shadow・text-shadow・drop-shadowの使い分けも参考にしてください。