ボタンや言葉にマウスを乗せると、小さな吹き出しで説明が出る。この部品を「ツールチップ」と呼びます。見た目はCSSだけで作れますが、キーボードで選んだときにも出すこと、ESCキーで消せることまで考えると、HTMLの書き方と短いJavaScriptが1つ要ります。
この記事の要点
- ツールチップ(マウスを乗せると出る小さな説明)は、CSSの :hover と :focus-within で作れる。ESCキーで閉じる部分だけ短いJSを使う
- 使いどころは「なくても使えるけれど、あると迷わない」ひとこと。押すのに必要な情報は、最初から画面に書く
- スマホにはマウスを乗せる操作がないので、ほぼ見られないと考える。説明の文は短く、ボタンとは aria-describedby でつなぐ
完成見本:ツールチップ4種類
下の4つは、ページを開いた時点から「出る → 消える」を自動でくり返しています。実際にマウスを乗せたり、Tabキーで選んだりして試せる見本は、後半の「応用」に置いています。
※ 見本は読み上げソフトでは読まれないようにし、Tabキーでも選ばれないようにしています。試せる見本は「応用」の節にあります。
コピペ用のコード
HTMLは説明を付けたい場所に、CSSはサイトのCSSファイルや、WordPressならテーマの「追加CSS」などに入れます。1のCSSの前半(.kd-tt と .kd-tt-tip)は、4種類に共通する部分です。同じページで何種類か使うときも、1回だけ入れれば足ります。クラス名の頭の kd-tt は、サイトのほかのデザインと名前がぶつからないようにするための目印です。
id と、ボタンの aria-describedby(または aria-labelledby)は同じ名前にします。同じページに2つ以上置くときは、kd-tt-basic2 のように、ツールチップごとに別の名前にしてください。
1. ボタンの上に説明を出す
いちばん基本の形です。ボタンと説明を .kd-tt の箱でまとめ、説明は position: absolute でボタンの上に置いています。説明の文は role="tooltip" を付けた span に書き、ボタンの aria-describedby でつないでいます。
<span class="kd-tt">
<button type="button" class="kd-tt-btn" aria-describedby="kd-tt-basic">保存する</button>
<span class="kd-tt-tip" role="tooltip" id="kd-tt-basic">下書きとして保存します。まだ公開はされません</span>
</span>/* 説明を出す場所の基準。中の説明は、この箱を基準に置く */
.kd-tt {
position: relative;
display: inline-block;
}
/* 説明の吹き出し。普段は見えない */
.kd-tt-tip {
position: absolute;
z-index: 10;
left: 50%;
bottom: calc(100% + 10px);
width: max-content;
max-width: 16em;
padding: 6px 10px;
border-radius: 6px;
background: #10283A;
color: #FFFFFF;
font-size: 13px;
font-weight: 400;
line-height: 1.5;
text-align: left;
opacity: 0;
visibility: hidden;
transform: translate(-50%, 4px);
transition: opacity .2s ease, transform .2s ease, visibility 0s linear .2s;
}
/* 下向きの三角(しっぽ) */
.kd-tt-tip::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -6px;
border: 6px solid transparent;
border-top-color: #10283A;
}
/* 吹き出しとボタンのすき間に、見えない橋をかける(マウスを吹き出しへ動かしても消えないように) */
.kd-tt-tip::before {
content: "";
position: absolute;
top: 100%;
left: 0;
right: 0;
height: 12px;
}
/* マウスを乗せたとき・キーボードで選んだときに出す。0.3秒待ってから出す */
.kd-tt:hover .kd-tt-tip,
.kd-tt:focus-within .kd-tt-tip {
opacity: 1;
visibility: visible;
transform: translate(-50%, 0);
transition-delay: .3s;
}
/* ESCキーで閉じたとき(JSで .is-closed を付ける) */
.kd-tt.is-closed .kd-tt-tip {
opacity: 0;
visibility: hidden;
}
/* 動きを減らす設定の人には、上下にすべる動きをなくし、ふわっと出るだけにする */
@media (prefers-reduced-motion: reduce) {
.kd-tt-tip {
transition: opacity .2s ease, visibility 0s linear .2s;
}
}
.kd-tt-btn {
padding: 10px 20px;
border: 0;
border-radius: 8px;
background: #0369A1;
color: #FFFFFF;
font-size: 15px;
font-weight: 700;
cursor: pointer;
}
/* キーボードで選んだときの枠。消さずに、はっきり見せる */
.kd-tt-btn:focus-visible {
outline: 3px solid #10283A;
outline-offset: 3px;
}2. 下に出す
ヘッダーのように画面の上の端にあるボタンは、上に出すと説明が画面の外に切れます。1のCSSに続けて下のCSSを入れ、外側の箱に .kd-tt-bottom を足すと、上下を入れかえて下に出します。三角(しっぽ)の向きも、色を付ける辺を border-top-color から border-bottom-color に変えて上向きにしています。
<span class="kd-tt kd-tt-bottom">
<button type="button" class="kd-tt-btn" aria-describedby="kd-tt-bottom">お知らせ</button>
<span class="kd-tt-tip" role="tooltip" id="kd-tt-bottom">新しいお知らせが3件あります</span>
</span>/* 下に出す:上下を入れかえる */
.kd-tt-bottom .kd-tt-tip {
top: calc(100% + 10px);
bottom: auto;
transform: translate(-50%, -4px);
}
.kd-tt-bottom .kd-tt-tip::after {
top: auto;
bottom: 100%;
border-top-color: transparent;
border-bottom-color: #10283A;
}
.kd-tt-bottom .kd-tt-tip::before {
top: auto;
bottom: 100%;
}
.kd-tt-bottom:hover .kd-tt-tip,
.kd-tt-bottom:focus-within .kd-tt-tip {
transform: translate(-50%, 0);
}3. 文の中の言葉に説明を付ける
文の中の言葉に、用語の説明を付ける形です。言葉は button にしてキーボードで選べるようにし、見た目は文字のままにしています。点線の下線(border-bottom: 2px dotted)と、マウスの形を「?」付きにする cursor: help で、説明があることを伝えます。
<p>料金には<span class="kd-tt"><button type="button" class="kd-tt-term" aria-describedby="kd-tt-term">初期費用</button><span class="kd-tt-tip" role="tooltip" id="kd-tt-term">契約のときに1回だけかかる費用です</span></span>がふくまれます。</p>/* 文の中の言葉。見た目は文字のまま、点線の下線で「説明がある」と伝える */
.kd-tt-term {
padding: 0;
border: 0;
border-bottom: 2px dotted #0284C7;
background: none;
color: inherit;
font: inherit;
cursor: help;
}
.kd-tt-term:focus-visible {
outline: 3px solid #10283A;
outline-offset: 2px;
}4. アイコンだけのボタンに名前を出す
ゴミ箱のマークのように、文字がないアイコンのボタンに「削除」と名前を出す形です。この場合、説明の文がそのままボタンの名前になるので、aria-describedby ではなく aria-labelledby(この要素を名前として使う、という指定)でつないでいます。アイコンの svg は飾りなので、aria-hidden="true" で読み上げから外しています。
<span class="kd-tt">
<button type="button" class="kd-tt-icon" aria-labelledby="kd-tt-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/></svg>
</button>
<span class="kd-tt-tip" role="tooltip" id="kd-tt-icon">削除</span>
</span>/* アイコンだけのボタン */
.kd-tt-icon {
display: grid;
place-items: center;
width: 44px;
height: 44px;
padding: 0;
border: 1px solid #7B8A97;
border-radius: 50%;
background: #FFFFFF;
color: #10283A;
cursor: pointer;
}
.kd-tt-icon svg {
width: 22px;
height: 22px;
}
.kd-tt-icon:focus-visible {
outline: 3px solid #10283A;
outline-offset: 3px;
}ESCキーで閉じるJavaScript
4種類に共通で、ページに1回だけ入れます。ESCキーを押すと、説明に .is-closed を付けて隠します。次にマウスを乗せたり、Tabキーで選んだりしたときは、.is-closed を外して、また出します。WordPressなら、テーマのフッターにJavaScriptを入れる場所や、「カスタムHTML」ブロックの <script> に入れます。
/* ESCキーで説明を閉じる。次にマウスを乗せる・選んだときは、また出す */
(function () {
function go() {
var tips = document.querySelectorAll('.kd-tt');
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
tips.forEach(function (t) { t.classList.add('is-closed'); });
}
});
tips.forEach(function (t) {
function open() { t.classList.remove('is-closed'); }
t.addEventListener('mouseenter', open);
t.addEventListener('focusin', open);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', go);
else go();
})();仕組み:普段は隠しておき、乗せたら見せる
説明は、ボタンと同じ箱の中に置く
HTMLでは、ボタンと説明を .kd-tt という1つの箱に入れています。箱を position: relative(中の部品を置く基準にする指定)にして、説明を position: absolute で箱の上に浮かせます。bottom: calc(100% + 10px) は「箱の高さぶん上、さらに10px上」という意味で、ボタンと吹き出しのあいだに10pxのすき間ができます。left: 50% と translate(-50%, …) の組み合わせで、ボタンの真ん中にそろえています。
:hover と :focus-within で、マウスとキーボードの両方に対応する
:hover はマウスを乗せているとき、:focus-within は箱の中のどれかが選ばれている(フォーカスがある)ときに当てはまります。ボタンそのものではなく、外側の箱 .kd-tt に付けているので、Tabキーでボタンを選んだときにも説明が出ます。
opacity と visibility を組み合わせて消す
普段は opacity: 0(透明)と visibility: hidden(見えない状態)を両方付けています。透明にするだけだと、見えない吹き出しがそこに残り、その場所にマウスが当たっただけで説明が出てしまいます。visibility: hidden を付けると、見えない吹き出しにはマウスが当たらなくなります。
transition では、visibility だけ「0秒の変化を0.2秒遅らせる」と書いています。こうすると、消えるときは透明になり終わってから見えない状態に切りかわり、ふわっと消える動きが最後まで見えます。
すき間に「見えない橋」をかける
マウスを吹き出しの上へ動かしても消えないようにしておきます(理由は後半の配慮の節で説明します)。ただ、ボタンと吹き出しのあいだの10pxのすき間をマウスが通ると、箱から外れて説明が消えてしまいます。
そこで、吹き出しの ::before(CSSだけで足す小さな部品)を、すき間をうめる透明な帯にしています。帯も吹き出しの一部なので、すき間を通るあいだも「箱の上にマウスがある」状態が続きます。三角(しっぽ)は ::after に、border の1辺だけ色を付けて描いています。
数値を変えるとどうなるか
出るまでの待ち時間:transition-delay
0秒(左)だと、ページの上でマウスを動かしただけで、通り道のボタンの説明が次々に出て、画面がちらつきます。1秒(右)だと、説明を読みたい人が待たされて、出る前にマウスを離してしまいます。W3Cの解説でも、ツールチップは少し遅れて出るのがふつうとされています。0.2〜0.5秒あたりが、ちらつかず待たされない範囲です。
吹き出しの幅:max-width
7em(上)では1行が短すぎて、細長い吹き出しになります。上限をなくす(下)と、説明が1行で横に伸びて、画面の端からはみ出します(この比較では枠で切っています)。width: max-content(中身に合わせた幅)と max-width(これより広げない幅)を組み合わせると、短い説明は1行で、長い説明は決めた幅で折り返します。日本語なら14〜18em(1行に14〜18文字)くらいが読みやすい幅です。
| 変えるところ | 指定 | 今回の値 | 目安 |
|---|---|---|---|
| 出るまでの待ち時間 | transition-delay | 0.3秒 | 0.2〜0.5秒 |
| 吹き出しの幅の上限 | max-width | 16em | 14〜18em |
| ボタンとのすき間 | bottom: calc(100% + ○px) | 10px | 6〜12px(橋の高さも合わせる) |
| 文字の大きさ | font-size | 13px | 本文より1〜2px小さく |
スマホと、見る人への配慮
スマホでは、ほとんど見られない
スマホは指で触るので、マウスを乗せている状態がありません。指で押すとボタンが選ばれて説明が出るブラウザもありますが、MDNによると、ボタンを押したときに選択(フォーカス)が移るかどうかはブラウザによって違い、Safariは移らない作りです。つまり、iPhoneでは説明がほぼ出ないと考えておきます。
だから、押すのに必要な情報や、まちがえると困る注意はツールチップに入れません。「削除すると元に戻せません」のような注意は、最初から画面に書きます。ツールチップは「なくても使えるけれど、あると迷わない」ひとことのための部品です。
ESCキーで消せる・上にマウスを動かせる・勝手に消えない
マウスを乗せたり選んだりして出る内容について、WCAG(ウェブのアクセシビリティの国際的な基準)2.2の達成基準1.4.13には、3つの決まりがあります。ESCキーなどで消せること、出た内容の上にマウスを動かしても消えないこと、マウスを離すか自分で消すまで出たままになること、です。ESCキーで閉じるJavaScriptと「見えない橋」は、このためのものです。
読み上げソフトには、aria-describedby で伝える
説明の span に role="tooltip" を付け、ボタンの aria-describedby でつなぐと、読み上げソフトはボタンの名前のあとに説明の文を読みます。W3Cのツールチップの作り方の手引き(APG)も、この形です。説明の中には、リンクやボタンのような押せるものを入れません。手引きでは、ツールチップには選択が移らず、ボタンに残ったままとされています。押せるものを入れたいときは、ツールチップではなく、押して開く小さな画面(ダイアログ)にします。
title 属性を付けても、ブラウザの標準の説明が出ます。ただ、待ち時間や見た目を決められません。MDNでも、title 属性は、キーボードで操作する人やタッチ操作だけの機器を使う人などにとって問題が多いとされているので、今回は使っていません。
「動きを減らす」設定の人には、すべる動きをなくす
画面の動きが苦手な人は、スマホやパソコンで「動きを減らす」設定をオンにしていることがあります。prefers-reduced-motion を使い、この設定の人には、上下にすべる動きをなくして、その場でふわっと出るだけにしています。
やりすぎ注意
ツールチップを何個も並べると、マウスを動かすたびに説明が出て、画面が落ち着きません。説明は、ボタンの文字だけでは分からないことがあるときにだけ付けます。

ツールチップは「ここに説明を書いたから大丈夫」と、作る側が安心しやすい部品です。
でもスマホではほとんど見られません。本当に伝えたいことほど、最初から画面に書いておきたいです。
応用:実際にマウスを乗せて試す
ここの見本は自動では動きません。パソコンならマウスを乗せて、キーボードならTabキーで1つずつ選んでみてください。説明が出ている状態でESCキーを押すと消えます。吹き出しの上へマウスを動かしても消えないことも確かめられます。
料金には契約のときに1回だけかかる費用ですがふくまれます。
削除※ 新しいCSSの「アンカー位置指定(anchor positioning)」を使うと、吹き出しが画面の端で切れるときに、自動で反対側へ出す、といった指定もできます。MDNでは2026年9月から主要なブラウザの最新版で使える「Baseline 2026」とされていますが、古い機器では使えないため、今回は使っていません。
AIに頼むなら
同じような部品は、Claudeなどの生成AIに頼んでもすぐ作れます。そのときは、見た目だけでなく、キーボード操作・読み上げ・閉じ方まで伝えると、直す回数が減ります。
アイコンだけの「削除」ボタンに、マウスを乗せるとボタンの上に「削除」と出るツールチップを付けたい。
HTMLとCSSで、0.3秒待ってから出す。Tabキーで選んだときにも出して、ESCキーで消せるように(JSは短く)。
吹き出しの上にマウスを動かしても消えないようにして、読み上げは aria-labelledby でつないで。AIが出したコードは、そのまま使う前に、次の3つを確かめます。
- Tabキーで選んだときにも出るか。
:hoverだけで書かれていたら、:focus-within(または:focus-visible)を足します - ボタンと吹き出しのすき間で消えないか。マウスを吹き出しの上へゆっくり動かして、途中で消えたら、すき間をうめる指定があるかを見ます
- スマホの幅で、画面の端からはみ出していないか。画面の右端や左端に近いボタンで、横にスクロールできるようになっていたら、吹き出しの幅か出す向きを変えます
KAKERUならこう使う/使わない
最後に、作り手としての判断です。
使う場面
- アイコンだけのボタンには「名前を出す」。印刷・共有・削除のように、マークだけでは迷うことがあるボタンに付けます。管理画面のように、パソコンで毎日使う画面ほど役に立ちます
- 料金表や申込みページの用語には「文の中の言葉」。「初期費用」「税込」のように、知っている人には不要で、知らない人だけが確かめたい言葉に向いています
- 画面の上の端のボタンには「下に出す」。ヘッダーのアイコンなど、上に出すと切れる場所に使います
使わない場面
- 押すのに必要な情報や、注意書き。スマホではほぼ見られないので、最初から画面に書きます
- 説明の中にリンクやボタンを入れたいとき。ツールチップの中のものは押せないので、押して開く小さな画面にします
- ボタンの文字をくり返すだけの説明。「保存する」に「保存します」と出しても、何も足されません

KAKERUなら、まず「この説明、最初から画面に書けないか」を考えます。
書くと画面がごちゃつく、でも知らない人には必要。そういうひとことだけを、ツールチップに入れます。
まとめ
- ツールチップは、:hover と :focus-within で出し、opacity と visibility で隠すとCSSで作れる
- ESCキーで閉じる部分だけ、短いJavaScriptを使う。吹き出しとのすき間は、透明な帯でうめる
- 待ち時間は0.2〜0.5秒、幅の上限は日本語で14〜18emが目安
- role=”tooltip” と aria-describedby でつなぐ。アイコンの名前として出すときは aria-labelledby
- スマホではほぼ見られないので、押すのに必要な情報は最初から画面に書く
まずは1の「ボタンの上に説明を出す」をコピーして、説明の文を変えてみてください。
参考にした資料
いずれも2026年10月30日に確認しました。ブラウザの対応状況は変わることがあります。最新の情報は各ページでご確認ください。
- W3C WAI-ARIA APG「Tooltip Pattern」(英語)
https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/
※role=”tooltip” と aria-describedby でつなぐこと、キーボードで選んだときとマウスを乗せたときに出し、ESCキーで閉じること、ツールチップ自体には選択(フォーカス)を移さないこと - W3C WAI「Understanding SC 1.4.13: Content on Hover or Focus」(英語)
https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus.html
※マウスを乗せたり選んだりして出る内容は、消せる(ESCなど)・その上にマウスを動かせる・勝手に消えない、の3つを満たすこと(レベルAA) - MDN「ARIA: tooltip role」(英語)
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tooltip_role
※tooltip の役割の意味と使い方 - MDN「aria-describedby」(英語)
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-describedby
※説明の文を、要素とつなぐ属性 - MDN「aria-labelledby」(英語)
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby
※別の要素の文を、その要素の名前として使う属性 - MDN「:focus-within」(英語)
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:focus-within
※中の要素が選ばれている(フォーカスがある)ときに当てはまること - MDN「transition-delay」(英語)
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/transition-delay
※変化を始めるまでの待ち時間の指定 - MDN「visibility」(英語)
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/visibility
※hidden の要素は、Tabキーで選ばれないこと - MDN「title」(英語)
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/title
※title 属性は、キーボードで操作する人やタッチ操作だけの機器を使う人などにとって問題が多いこと - MDN「
- MDN「position-anchor」(英語)
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/position-anchor
※アンカー位置指定(anchor positioning)。Baseline 2026(2026年9月から主要ブラウザの最新版で使える)
ふだんの制作の話や、作ったものの裏側は Instagram と X でも出しています。




























連載「WEBの技術」の32回目です。
今回はツールチップ。便利そうに見えて、置き場所をまちがえると「誰にも読まれない説明」になりやすい部品です。