[CSS] ボタンのcursorはpointerにすべきか

概要

ボタンに cursor: pointer を当てるか迷うことがある。仕様、OSのガイドライン、CSSWGの最新の議論から、どちらを選ぶかの手順と実装例をまとめる。

私自身はこれまでBootstrapに倣ってボタンに pointer を当ててきたが、ライブラリごとに既定が異なり、利用者が手のカーソルから受け取る意味もそろっていないように感じる。

迷ったらpointerにそろえ、まず見た目でボタンと分かるようにする

結論は以下の通り。

  • ボタンはカーソルに頼らず、背景色や枠線などの見た目だけでボタンと分かるようにする
  • 新規のプロダクトや、pointer と default が混在しているプロダクトは、pointer にそろえる
  • ただし、OSのアプリに近い操作感を目指すUIは、OSとの一貫性を優先して default にそろえる
  • すでに default で統一しているプロダクトは、無理に変えない

理由は以下の通り。

  1. 仕様は開発者のカーソルの選び方を縛っていない。CSSの仕様を策定するCSSWG(CSS Working Group)も、2018年に、リンク以外に pointer を使ってよいと決議した。
  2. カーソルはマウスでhoverしたときにしか見えない、補助の手がかりである。しかも、手がかりとして働くのは、同じ形がいつも同じ意味を持つときだけである。だから見た目を先に整え、すでにどちらかで統一できているなら無理に変えない。
  3. CSSWG自身が2026年10月、フォームコントロールの新しい描画モードでは pointer を既定にすると決議した。議事録はその考え方を「pointer は『これをクリック』を示す」と要約している。仕様への反映はこれからだが、仕様を作る側の向きは pointer にある。

私の pointer も1つ目と3つ目の理由に合致する。ただし、Bootstrapの既定だという事実は根拠にならない。Bootstrap自身がこの指定を「Opinionated」(意見の入った設定)と書いているためである。

仕様はpointerをリンクの印と定義し、使い方は開発者に委ねている

CSSの仕様は pointer(指差しの手。以下、手のカーソル)を「リンクを示すポインタ」と定義している。現行の仕様はCSS Basic User Interface Module Level 4(UI 4)で、定義の文は1998年のCSS 2と同じである。HTML仕様もリンク(:link と :visited)の既定スタイルに cursor: pointer を含めている。

ブラウザが内蔵するCSSを、UAスタイルシートと呼ぶ。Chromium、WebKit、GeckoのUAスタイルシートは、button に default(矢印)を当てる。例外は <input type="image"> で、どのブラウザでも pointer になる。この点はissue #12910で指摘されている。

一方で、仕様は開発者のカーソルの選び方を縛っていない。UI 4の編集者Florian Rivoalも、リンク以外に pointer を使っても、仕様に反するわけではないと述べている。

2017年には、pointer の定義をクリックできる要素全般に広げる提案が、issue #1936で出た。定義は変わらなかったが、CSSWGは2018年9月12日、開発者向けに次の一文を加えると決議した。

Authors should use pointer on links and may use on other interactive elements

出典: CSSWGの議事録(2018-09-12)

訳すと「開発者は、リンクには pointer を使うべきであり、ほかの対話的な要素にも使ってよい」となる。ただし、2026年11月に確認したUI 4(10月15日付)の本文には、この一文が見当たらない。

両派の対立は「どの一貫性を守るか」の違いである

両派はどちらも一貫性を重んじるが、守ろうとする一貫性が違う。default 派はOSやブラウザのUIとの一貫性を、pointer 派はWeb上での一貫性を守る。

default 派の根拠になっているのが、手のカーソルをリンク専用とするOSのガイドラインである。

MicrosoftのWindows 7向けのガイドは、リンクに手のカーソルを使う理由を、アフォーダンスの弱さとする。アフォーダンスとは見た目から操作方法が分かる性質である。そのうえで、アフォーダンスが強いコマンドボタンには、手のカーソルを使わないよう求める。ただし、このガイドはその後更新されていない。

Appleの利用者向けヘルプPointers in macOSも、手のカーソルはWebページや書類などへのリンクの上に出ると説明している。

主な論点は次の通りである。

  • 手のカーソルの意味
    • default 派:手はリンクの印で、クリックできる印ではない(Adam Silver、Microsoft)
    • pointer 派:Webでは、手がクリックできる印として定着している。MDNも2017年からそう説明していると、#1936で指摘された
  • ネイティブUIとの一貫性
    • default 派:OSのボタンは手にならない。利用者はOSとページの境目を意識しない(#1936)
    • pointer 派:Webは操作できない文章が大半で、ネイティブアプリとは慣習が違う(Tailwind CSSのPR #8962へのコメント)
  • リンクとの区別
    • default 派:新しいウィンドウで開く、URLをコピーするといった操作は、リンクにしかない(Adam Silver)
    • pointer 派:「トップへ戻る」リンクのように、リンクとボタンの境界はあいまいである(Hidde de Vries)
  • アクセシビリティ
    • default 派:アクセシビリティのため、ボタンの pointer は外すべきである(Tailwind CSS #8961)
    • pointer 派:pointer を外すと、クリックできる要素を見つけにくくなる(PR #8962へのコメント)

両派ともアクセシビリティを根拠に挙げるが、カーソルが見えるのはマウスなどでhoverしたときだけで、キーボードやタッチの利用者には、カーソルの形は伝わらない。マウスの利用者にとっても、カーソルを合わせるきっかけは要素の見た目である。#1936でも、ボタンをボタンらしく見せることが最善だという意見が出ている。つまり、どちらのカーソルも見た目の手がかりを補うものにすぎない。

そして、カーソルが手がかりとして働くのは、同じ形が同じ意味を持つときだけである。Hidde de Vriesも2016年の記事で、2つの標準が並立していて、どちらを使ってもよいとした。そのうえで、少なくとも同じサイトの中では一貫させるのが最善だと結論づけている。

CSSWGはbase appearanceの既定をpointerにすると決議した

2026年、CSSWGはフォームコントロールの新しい描画モードについて、既定のカーソルを決めた。この描画モードをbase appearanceと呼び、appearance: base で有効になる。OSネイティブの外観を外し、CSSで装飾しやすい既定スタイルを与えるものである。これに対し、従来どおりOSの外観で描く描画モードは appearance: auto である。

議論の場はissue #14251で、決議は3回あった。

  • 9月2日:disabled属性で無効化したフォームコントロールに、not-allowed を当てる(base のみ)
  • 9月9日:button や select などに default を当てる(auto と base の両方)
  • 10月21日:button や select などを pointer にする規則を採用する
10月21日の規則の抜粋
/* 2026-10-21 の規則の抜粋 */
select,
option,
input[type='checkbox'],
input[type='radio'],
button,
input[type='button'],
input[type='submit'],
input[type='reset'],
input[type='color'],
input[type='image'],
::file-selector-button {
  cursor: pointer;
}

規則にはほかに、次の指定も含まれる。

  • 無効なコントロール:not-allowed
  • 範囲スライダー:grab(つかんでいる間は grabbing)
  • 読み取り専用のコントロール:default

議事録は規則の考え方を次のように要約している。

SUMMARY: Pointer = "click this thing", text cursor says "prepare to enter text", grabbing hand says "drag this".

出典: CSSWGの議事録(2026-10-21)

  • pointer:これをクリックする
  • テキストカーソル:文字を入力する準備をする
  • つかむ手:これをドラッグする

反対の意見も出ている。9月9日の会議では、pointer を既定にすると、リンク以外にも使う前例になるという懸念が示された。

デザインシステムの調査

議論にはデザインシステムの調査も出ている。Nicole Sullivanは9月9日のコメントで、cursor を指定していたデザインシステムを数え、33件のうち27件(82%)が pointer だったと報告している。10月21日の会議では、対象を広げて数え直すと、default と pointer はほぼ半々だったと述べている。pointer の多くはnpmで配布されるモジュールのものだった。BootstrapやTailwindのようなデザインシステムに限ると、60対40で default が多いとも述べている。

反映状況

これらの決議はまだ仕様に入っていない。11月3日時点のcss-forms-1のEditor's Draftは10月8日付の版で、cursor の規則がない。

ブラウザではWebKitの開発中のUAスタイルシートに、base appearanceの select を pointer、無効時を not-allowed にする指定がすでにある。Chromiumにも、base appearanceで not-allowed を当てる指定が、フラグ付きで入っている。

10月21日の規則が appearance: auto にも及ぶかは、議事録から確認できなかった。そのため、この決議は仕様を作る側の向きを示すものとして扱う。

フレームワークの既定も割れている

主要なフレームワークがボタンに当てる既定のカーソルは、次の通りである。

フレームワークボタンの既定補足
Bootstrap 5.3pointer[role="button"] と summary も pointer
Tailwind CSS v3pointer無効なボタンは default に戻す
Tailwind CSS v4defaultブラウザの既定に合わせたと説明
shadcn/ui(Tailwind v4版)defaultTailwind v4への対応時に変更
Bootstrapのpointerの経緯

私の前提だったBootstrapの pointer は、すべてのボタンのカーソルをそろえる意図で2011年の1.2.0に入った。同じ指定を持っていたNormalize.cssは、2016年の4.1.0でこれを「opinionated」として削除した。Bootstrap自身も2018年の4.0.0では、.btn の指定に「Opinionated」と明記した。現行の5.3でも指定は残っており、変数 $enable-button-pointers で無効にできる。

出典は次の通りである。

Tailwind CSSは2025年1月のv4.0でボタンを default に変えた。作者のAdam Wathanは2022年のPR #8962で、この変更の狙いを説明している。pointer がリンク用だと知る開発者が増えれば、default が広まるという見込みである。PR本文へのリアクションは、2026年11月時点で反対が35件、賛成が7件である。

迷いやすい要素の扱い

迷いやすい要素は次のように扱う。

  • ボタンの見た目にしたリンク(<a class="btn"> など):ブラウザの既定で pointer になる。リンクであるため、default にそろえる場合も pointer のままにする
  • summary:指定しなければ cursor が auto になる。どちらにそろえる場合も、明示的に指定する(実装例を参照)
  • label:ブラウザの既定の default のままにする。CSSWGも9月9日に default とし、10月21日の規則には含めていない
  • 無効な要素:disabled属性と aria-disabled の両方を拾う(実装例を参照)

実装例

以下のCSSはChrome 153の getComputedStyle() で計算値を確かめた。FirefoxとSafariでは確かめていない。Tailwind CSSで使う場合は、@layer base の中に書く。

pointerにそろえる場合

10月21日の規則にならい、フォームのボタン類や select も対象にする。加えて、summary と role="button" の要素も含める。

:is(
  button,
  [type='button'],
  [type='submit'],
  [type='reset'],
  [type='checkbox'],
  [type='radio'],
  select,
  summary,
  [role='button']
):not(:disabled, [aria-disabled='true']),
:enabled::file-selector-button {
  cursor: pointer;
}

:disabled はdisabled属性を持てるフォームコントロールにしか一致しない。そのため、aria-disabled="true" で無効を示した要素は、別に除外している。

defaultにそろえる場合

button や select はブラウザの既定のままで default になる。ただし、role="button" を付けた div や summary は、cursor が auto のままである。

UI 4の定義では、auto は選択できるテキストの上で text(Iビーム)として振る舞う。9月9日の会議でも、この点が指摘されていた。これを防ぐため、default を明示する。

:is([role='button'], summary) {
  cursor: default;
}

無効な要素

CSSWGの決議では、無効なコントロールへの not-allowed はbase appearanceに限られる。開発者のCSSは描画モードに関係なく適用されるので、これを当てるかは独立した設計判断になる。当てる場合は、次のように書く。default にしたいなら、値を default に替える。

:is(
  button,
  input,
  select,
  textarea,
  [role='button']
):is(
  :disabled,
  [aria-disabled='true']
),
:disabled::file-selector-button {
  cursor: not-allowed;
}

<fieldset disabled> の中にある role="button" の要素は、この規則に一致しない。そうした要素には aria-disabled="true" を個別に付ける。

まとめ

  • ボタンはカーソルに頼らず、見た目でボタンと分かるようにする
    • そのうえで迷ったら pointer にそろえ、すでにどちらかで統一できているなら無理に変えない
  • 無効な要素はdisabled属性と aria-disabled の両方を拾う。

参考