CSSで、ボタンの押せる範囲を見た目より広げる方法
ボタンの押せる範囲を見た目より広げるには、ボタンに position: relative を付け、透明な ::before を position: absolute; inset: -10px; でボタンの外にはみ出させます。はみ出した部分をクリックしても、click はボタンに届きます。見た目もレイアウトも変わりません。ただし、隣のボタンと並べたときは、広げる幅を隙間の半分までにしないと、隙間を押したときに隣のボタンのほうが反応します。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS だけのページを Playwright で開き、位置を決めてマウスでクリックして、click イベントが届いた要素と document.elementFromPoint() の結果を記録しました。タッチ操作は Playwright のタッチのエミュレーションで試しただけで、スマートフォンの実機では試していません。Firefox と Safari も試していません。

::before をボタンの外にはみ出させる
例として、直径 24px の丸い「閉じる」ボタンを、44px 四方まで押せるようにします。
<button class="icon-btn" type="button" aria-label="閉じる">×</button>
.icon-btn {
position: relative;
width: 24px;
height: 24px;
padding: 0;
border: none;
border-radius: 50%;
}
.icon-btn::before {
content: "";
position: absolute;
inset: -10px;
}
inset は top・right・bottom・left をまとめて書くプロパティです。-10px にすると、::before はボタンの四方に 10px ずつはみ出し、24 + 10 + 10 で 44px 四方になります。position: absolute の要素は、位置指定された一番近い祖先を基準に置かれるので、ボタンに position: relative を付けて、ボタンを基準にしています。
検証環境で、見た目の円の右端から右へ少しずつ離れた位置をクリックしました。
| 円の右端からの距離 | 何もしないボタン | ::before で広げたボタン |
|---|---|---|
| 2px | 届かない | 届く |
| 6px | 届かない | 届く |
| 9px | 届かない | 届く |
| 11px | 届かない | 届かない |
広げたボタンでは、10px の内側まで click がボタンに届き、10px より外では届きませんでした。click イベントの event.target はボタンそのもので、::before を気にせずにボタンのイベントとして受け取れます。
はみ出した部分にマウスを重ねると、ボタンは :hover に当てはまりました。cursor は子に引き継がれるプロパティなので、ボタンに cursor: pointer を書いていれば、はみ出した部分でも指の形のカーソルになります(検証では、::before の cursor は pointer でした)。
::before は透明で、ボタンの外にはみ出しているだけなので、ボタンの大きさや、周りの要素の位置は変わりません。検証でも、ボタンの右に置いた文字の位置は、何もしないボタンのときと同じでした。
padding と負の margin で広げる方法との違い
::before を使わずに、ボタン自体の padding を増やし、同じ分だけ負の margin で打ち消す方法もあります。背景を background-clip: content-box で中身の部分だけに塗れば、見た目の大きさは変わりません。
<button class="pad-btn" type="button" aria-label="閉じる"><span>×</span></button>
.pad-btn {
padding: 10px;
margin: -10px;
border: none;
border-radius: 50%;
box-sizing: content-box;
background: #067a98;
background-clip: content-box;
}
.pad-btn > span {
display: block;
width: 24px;
height: 24px;
}
ボタンの大きさは中の span で決めます。
この方法でも、ボタンの右の文字の位置は変わらず、円の右端から 9px 外のクリックはボタンに届きました。ボタンの箱(getBoundingClientRect())は 44×44px になります。
違いが出たのは、ボタンの箱の角の部分でした。elementFromPoint() で、ボタンに当たる位置を 1px ずつ調べて塗ると、次のようになりました。

padding で広げた方法では、ボタンの箱の左上の角から 2px 内側をクリックしても、ボタンには届きませんでした。CSS の仕様(CSS Backgrounds and Borders Module Level 3)では、border-radius を付けた要素は、ポインターのイベントを受け付ける範囲も、丸めた border の外側の線で切り取られます。padding で広げると、border-radius: 50% も広がった箱に効くので、押せる範囲は直径 44px の円になります。::before には border-radius を付けていないので、四角のままです。
角まで押せるようにしたいなら ::before の方法、丸いボタンで円のままでよいなら padding の方法、と使い分けられます。padding の方法は ::before を使いません。
隣のボタンとの隙間より広げると、隣のボタンが反応する
隙間 8px で並べた「前へ」「次へ」の2つのボタンを、どちらも inset: -10px で広げました。片方を 10px 広げただけで、8px の隙間を越えてしまいます。
この状態で、「前へ」の右端から 1px 右(隙間のいちばん左)をクリックすると、click は「次へ」に届きました。3px、5px、7px の位置も、すべて「次へ」でした。8px の隙間は、どこを押しても「次へ」が反応します。さらに、「前へ」の円の右端から 1px 内側をクリックしても、「次へ」に届きました(3px 内側は「前へ」)。「次へ」の ::before が、「前へ」の円の右端の 2px にまで入り込んでいるためです。

2つの ::before が重なった部分では、上に描かれているほうがクリックを受け取ります。document.elementsFromPoint() の仕様(CSSOM View Module)でも、その位置にある要素を、描かれる順の上から並べることになっています。どちらのボタンも position: relative なので、z-index のない重ね合わせでは HTML に出てくる順に下から描かれ、後ろにある「次へ」の ::before が上に来ます。
広げる幅を隙間の半分の 4px にすると、「前へ」の円の中と、右端から 1px・3px は「前へ」、4px から右(4px・5px・7px)は「次へ」に届き、隙間の真ん中で分かれました。
並んだボタンでは、広げる幅を隙間の半分までにします。隙間 8px で 10px ずつ広げると、隙間を押したときに、後ろにあるボタンが反応します。
overflow: hidden の親の外には広がらない
ボタンを overflow: hidden の要素の中に置き、ボタンの左上の角を、親の左上の角にそろえました。ボタンの右や下の 6px 外(親の中)をクリックするとボタンに届き、左や上の 6px 外(親の外)をクリックしても届きませんでした。
MDN の overflow のページにあるとおり、hidden では親の padding の枠からはみ出した部分が切り取られます。はみ出した ::before も同じで、切り取られた部分は押せません。カードの右上の「閉じる」ボタンのように、overflow: hidden の要素の端にボタンを置くときは、親の外に出る側は広がらないので、ボタンを内側に寄せて置きます。
どこまで広げるか
WCAG 2.2 の達成基準 2.5.8「ターゲットのサイズ(最低限)」(レベル AA)では、ポインターで操作するターゲットの大きさを、少なくとも 24×24 CSS ピクセルにすることを求めています。24px より小さいターゲットでも、それぞれの中心に直径 24px の円を描いて、円がほかのターゲットや円と重ならなければ、例外の1つとして認められます。レベル AAA の 2.5.5「ターゲットのサイズ(強化)」では、44×44 CSS ピクセルです。
WCAG の「ターゲット」の定義は、ポインターの操作を受け付ける画面の領域です。定義には、ターゲットどうしが重なっているときは、同じ操作をする場合や同じページを開く場合を除いて、重なった部分を大きさに含めないことも書かれています。隣のボタンとの節で見たように、広げた範囲どうしが重なっても、重なった部分は大きさに数えられません。
2.5.8 の解説(Understanding)には、見た目の大きさを変えずに、操作できる範囲を広げる選択肢を用意すると役に立つことがある、とも書かれています。
タッチのエミュレーションでは、CSS の範囲より外でも反応する
Playwright でタッチ操作のエミュレーション(hasTouch: true、isMobile: true)を有効にし、何もしないボタンの右端から 14px 外や 20px 外をタップしても、click はボタンに届きました。30px 外では届きませんでした。hasTouch: true のまま isMobile: false にすると、何もしないボタンは 11px 外まで届き、14px 外では届きませんでした。
Chromium のソースにある touch_adjustment.cc には、タップの位置を、近くにある押せる要素に寄せる処理(touch adjustment)が書かれています。タッチで試すと、CSS で決めた範囲よりも外で反応することがあるので、押せる範囲が CSS のとおりになっているかは、マウスでクリックするか、document.elementFromPoint(x, y) で、その位置にある要素を調べて確かめます。
// ボタンの右端から 9px 外の位置にある要素を調べる
const r = document.querySelector('.icon-btn').getBoundingClientRect();
document.elementFromPoint(r.right + 9, r.top + r.height / 2);
::before で広げた位置なら、ボタンの要素が返ります。この記事の画像の、押せる範囲を塗った部分も、この方法で 1px ずつ調べて塗りました。





