



WithCodeMedia-1-pc
WithCodeMedia-2-pc
WithCodeMedia-3-pc
WithCodeMedia-4-pc




WithCodeMedia-1-sp
WithCodeMedia-2-sp
WithCodeMedia-3-sp
WithCodeMedia-4-sp









生徒用語の横に「?」を置いて、カーソルを乗せたら説明が出るやつを作りたいです。
ペン博士ツールチップだね。擬似要素とdata属性だけで作れるよ。位置調整と、スマホやキーボードへの配慮までまとめて渡すね。
専門用語の補足、アイコンの意味、入力欄のヒント。画面を圧迫せずに情報を足せるのがツールチップです。
実装はシンプルで、data-*属性に文言を入れ、::afterで表示するだけ。JavaScriptもライブラリも要りません。
この記事では基本形から、4方向の位置指定、三角形の付け方、はみ出し対策、タッチ端末とキーボードへの対応まで解説します。

文言はHTMLのdata-tip属性に持たせ、CSSのattr()で取り出して表示します。
<p>
この処理は
<span class="tip" data-tip="ブラウザが表示を組み立て直すこと。頻発すると重くなります。">
リフロー
</span>
を発生させます。
</p>.tip {
position: relative; /* 吹き出しの基準 */
border-bottom: 1px dotted #6b7280;
cursor: help;
}
.tip::after {
content: attr(data-tip); /* 属性の値をそのまま表示 */
position: absolute;
bottom: calc(100% + 10px); /* 要素の上に出す */
left: 50%;
transform: translateX(-50%);
width: max-content;
max-width: 260px;
padding: 10px 14px;
border-radius: 8px;
background-color: #1f2937;
color: #fff;
font-size: 0.85rem;
line-height: 1.6;
text-align: left;
opacity: 0;
visibility: hidden;
transition: opacity .2s, visibility .2s;
z-index: 50;
}
.tip:hover::after,
.tip:focus-visible::after {
opacity: 1;
visibility: visible;
}width: max-contentとmax-widthの組み合わせが要点です。短い文は1行、長い文は折り返すという自然な挙動になります。

配置場所に応じて出す方向を変えられるよう、クラスで切り替えます。
/* 上(既定) */
.tip.-top::after {
bottom: calc(100% + 10px);
left: 50%;
transform: translateX(-50%);
}
/* 下 */
.tip.-bottom::after {
top: calc(100% + 10px);
bottom: auto;
left: 50%;
transform: translateX(-50%);
}
/* 左 */
.tip.-left::after {
right: calc(100% + 10px);
left: auto;
bottom: auto;
top: 50%;
transform: translateY(-50%);
}
/* 右 */
.tip.-right::after {
left: calc(100% + 10px);
bottom: auto;
top: 50%;
transform: translateY(-50%);
}基本は上方向です。ページ最上部の要素や、ヘッダー内のアイコンでは下方向に切り替えます。表の右端では左方向、というように配置場所で選びます。
吹き出しらしくするには、::beforeで小さな三角を描いて本体につなげます。
.tip::before {
content: "";
position: absolute;
bottom: calc(100% + 4px);
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #1f2937; /* 本体と同じ色 */
opacity: 0;
visibility: hidden;
transition: opacity .2s, visibility .2s;
z-index: 51;
}
.tip:hover::before,
.tip:focus-visible::before {
opacity: 1;
visibility: visible;
}
/* 下方向のときは三角も反転 */
.tip.-bottom::before {
bottom: auto;
top: calc(100% + 4px);
border-top-color: transparent;
border-bottom-color: #1f2937;
}三角形は幅と高さ0の要素にborderを当てて作ります。4辺のうち1辺だけ色を付け、残りを透明にすると三角に見えます。
出現に動きを付けると、視線が自然に誘導されます。移動距離は小さく保ちます。
.tip::after {
transform: translateX(-50%) translateY(6px);
transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
.tip:hover::after,
.tip:focus-visible::after {
transform: translateX(-50%) translateY(0);
}
/* 表示までの待ち時間を入れると誤爆が減る */
.tip::after,
.tip::before {
transition-delay: .25s;
}
@media (prefers-reduced-motion: reduce) {
.tip::after,
.tip::before {
transition: none;
}
}transition-delayを0.2〜0.3秒入れると、カーソルが通過しただけで開く「誤爆」が減ります。文章中に複数のツールチップがある場合は特に効果的です。
説明が長い場合は幅の上限を決め、行間を広めに取ります。表示位置も本体の上ではなく、余白のある方向へ寄せます。
.tip.-wide::after {
max-width: 320px;
white-space: normal; /* 折り返しを許可 */
line-height: 1.7;
padding: 12px 16px;
}
/* 改行を入れたい場合はdata属性に改行文字を含める */
.tip.-pre::after {
white-space: pre-line;
}<span class="tip -pre" data-tip="1行目の説明です。
2行目はこう書きます。">用語</span>white-space: pre-lineを使うと、data属性内の改行がそのまま反映されます。箇条書き風の補足を出したいときに便利です。
画面の左右端にある要素では、中央揃えのままだと吹き出しが切れます。端の要素だけ基準をずらします。
/* 左端用:左を基準にする */
.tip.-align-start::after {
left: 0;
transform: none;
}
/* 右端用:右を基準にする */
.tip.-align-end::after {
left: auto;
right: 0;
transform: none;
}
/* 親のはみ出し切り取りを避ける */
.table-wrap {
overflow-x: auto; /* 表の横スクロールは必要 */
}
.table-wrap .tip::after {
position: fixed; /* 切り取られたくない場合の回避策 */
}表の中で使う場合は要注意です。overflow: autoの親を持つと吹き出しが切れます。切れて困る場面では表示方向を内側に向けるのが最も確実です。

:hoverだけに頼ると、スマホの利用者には情報が届きません。3つの手当てをします。
/* 1. キーボードでも開く */
.tip {
/* HTML側で tabindex="0" を付けておく */
}
.tip:focus-visible {
outline: 2px solid #0ea5e9;
outline-offset: 2px;
}
.tip:focus-visible::after,
.tip:focus-visible::before {
opacity: 1;
visibility: visible;
}
/* 2. タッチ端末ではタップで開く(:focusを利用) */
@media (hover: none) {
.tip:focus::after,
.tip:focus::before {
opacity: 1;
visibility: visible;
}
}<span class="tip" tabindex="0" role="note"
data-tip="ブラウザが表示を組み立て直すこと。">リフロー</span>tabindex="0"を付けてフォーカス可能にする@media (hover: none)でタッチ端末の挙動を分けるaria-describedbyで別要素を参照させる原則として、ツールチップは無くても意味が通る補足に限定します。価格や注意事項など読まれるべき情報を隠すと、届かない利用者が出ます。
HTMLのtitle属性でも似た表示は出せますが、制御できる範囲が違います。
| 項目 | title属性 | CSSツールチップ |
|---|---|---|
| 見た目 | 変更できない | 自由に設計できる |
| 表示速度 | 1秒ほど待つ | 即時〜任意の遅延 |
| スマホ | ほぼ表示されない | タップで対応可能 |
| 読み上げ | 環境により読まれる | 設計次第 |
| 実装量 | 属性1つ | CSSが必要 |
簡易な補足ならtitleで十分です。デザインを合わせたい、スマホでも見せたい場合はCSSで作ります。両者を併用すると二重に表示されるため、片方に統一してください。

動かない場合、原因はほぼ次の6つです。
| 症状 | 原因 | 対処 |
|---|---|---|
| 何も出ない | contentの指定漏れ | content: attr(data-tip)を確認 |
| 位置がページ左上に出る | 親にpositionがない | .tipにrelative |
| 途中で切れる | 親のoverflow | 方向を内側に変える |
| 他の要素の下に隠れる | 重なり順が低い | z-indexを上げる |
| 文字が1行で伸び続ける | 折り返し設定がない | max-widthを指定 |
| スマホで出ない | :hover依存 | tabindex+:focus |
2番目は擬似要素全般で頻発します。position: absoluteの基準は「最も近い位置指定済みの祖先」なので、親側の指定を忘れると想定外の場所へ飛びます。
方向切り替えとキーボード対応を含んだ一式です。
.tip {
position: relative;
border-bottom: 1px dotted #6b7280;
cursor: help;
}
.tip::after,
.tip::before {
opacity: 0;
visibility: hidden;
transition: opacity .18s ease, transform .18s ease, visibility .18s;
transition-delay: .25s;
pointer-events: none;
}
.tip::after {
content: attr(data-tip);
position: absolute;
bottom: calc(100% + 10px);
left: 50%;
transform: translateX(-50%) translateY(6px);
width: max-content;
max-width: 260px;
padding: 10px 14px;
border-radius: 8px;
background-color: #1f2937;
color: #fff;
font-size: 0.85rem;
line-height: 1.6;
text-align: left;
white-space: normal;
z-index: 50;
}
.tip::before {
content: "";
position: absolute;
bottom: calc(100% + 4px);
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #1f2937;
z-index: 51;
}
.tip:hover::after,
.tip:hover::before,
.tip:focus-visible::after,
.tip:focus-visible::before {
opacity: 1;
visibility: visible;
}
.tip:hover::after,
.tip:focus-visible::after {
transform: translateX(-50%) translateY(0);
}
.tip:focus-visible {
outline: 2px solid #0ea5e9;
outline-offset: 2px;
}
@media (hover: none) {
.tip:focus::after,
.tip:focus::before {
opacity: 1;
visibility: visible;
}
}
@media (prefers-reduced-motion: reduce) {
.tip::after,
.tip::before {
transition: none;
}
}配色や形を変えると、サイトのトーンに合わせられます。よく使う5種類を置いておきます。
.tip.-light::after {
background-color: #fff;
color: #1f2937;
border: 1px solid #e5e7eb;
box-shadow: 0 8px 24px rgba(15, 23, 42, .12);
}
.tip.-light::before {
border-top-color: #fff;
}明るい配色のサイトでは、黒い吹き出しが浮きすぎることがあります。白背景に影を添えると馴染みます。
.tip.-warn::after {
background-color: #b91c1c;
color: #fff;
font-weight: 700;
}
.tip.-warn::before {
border-top-color: #b91c1c;
}入力エラーの補足など、見落としてほしくない補足に使います。多用すると効果が薄れるため、1画面に1〜2箇所までに留めます。
.tip.-icon {
border-bottom: none;
}
.tip.-icon::first-line {
color: inherit;
}
.tip-mark {
display: inline-grid;
place-items: center;
width: 18px;
height: 18px;
margin-left: 4px;
border-radius: 50%;
background-color: #94a3b8;
color: #fff;
font-size: 12px;
font-weight: 700;
vertical-align: middle;
}<span class="tip -icon" tabindex="0" data-tip="税抜価格です。">
月額料金<span class="tip-mark">?</span>
</span>「?」マークを添えると、補足があること自体に気づいてもらえます。マークは装飾なので、読み上げに不要ならaria-hiddenを付けます。
.tip.-lg::after {
padding: 14px 18px;
border-radius: 14px;
font-size: 0.95rem;
max-width: 340px;
}説明が長い場合や、高齢の利用者が多いサイトでは文字を大きめにします。読みやすさは装飾より優先されます。
.tip.-instant::after,
.tip.-instant::before {
transition-delay: 0s;
}管理画面のアイコン群など、次々と確認したい場面では遅延を消します。逆に文章中では遅延ありのほうが快適です。
実装より大事なのが「そもそも隠していいのか」という判断です。3つの基準で確認してください。
| 情報の種類 | ツールチップ | 推奨の置き場所 |
|---|---|---|
| 専門用語の意味 | 向く | ツールチップで補足 |
| アイコンの意味 | 向く | ラベル併記が理想 |
| 価格の条件 | 向かない | 本文または注記 |
| エラーの理由 | 向かない | 入力欄の直下に常時表示 |
| 操作手順 | 向かない | 本文で段階的に説明 |
判断の軸は見なくても困らないかです。見ないと判断を誤る情報は、必ず本文に置きます。
最後に、ここまでのパターンをどの場面で使うか整理しておきます。実装前の判断材料にしてください。
| 場面 | 推奨パターン | 理由 |
|---|---|---|
| 記事本文の用語補足 | 上方向・遅延あり | 読書の流れを妨げない |
| フォームの入力ヒント | 右方向・即時 | 入力欄の隣で確認できる |
| 表の中の注記 | 内側方向 | overflowで切れるのを避ける |
| ヘッダーのアイコン | 下方向 | 上に空間がない |
| スマホ主体のサイト | タップ開閉 | hoverが存在しない |
迷ったら上方向・遅延0.25秒・最大幅260pxの基本形で問題ありません。困る場面が出てから方向だけ変える運用が最も手戻りが少なくなります。
/* 基本形を1クラスにまとめておき、必要な時だけ方向クラスを足す */
.tip { position: relative; cursor: help; }
.tip.-bottom::after { top: calc(100% + 10px); bottom: auto; }
.tip.-right::after { left: calc(100% + 10px); top: 50%; bottom: auto;
transform: translateY(-50%); }方向クラスは後付けで足せる設計にしておくと、ページごとの微調整がCSSの追記だけで済みます。
CSSツールチップはdata-tip属性とcontent: attr()の組み合わせで作れます。基準となる要素にposition: relative、吹き出しにabsoluteを指定し、width: max-contentとmax-widthで自然な折り返しにするのが要点です。三角形はborderで描き、表示はopacityとvisibilityで切り替えるとアニメーションが効きます。タッチ端末ではtabindex="0"と:focusで開けるようにし、そもそも重要な情報はツールチップに隠さないという設計判断が最も大切です。
content: attr(data-tip)の属性名と、HTML側の属性名が一致しているか確認してください。data-tipとdata-tooltipのような綴りの違いが原因のことが多いです。また擬似要素はimgやinputには作れないため、spanなどで包む必要があります。
親にoverflow: hiddenやoverflow: autoがあると擬似要素は切り取られます。表の中などで外せない場合は、吹き出しを内側の方向に出すか、位置をfixedにする回避策を使ってください。
tabindex="0"を付けてタップでフォーカスできるようにし、:focusでも表示されるようにします。@media (hover: none)でタッチ端末だけ挙動を分けると、PCの操作感を保ったまま対応できます。
見た目を揃えたい、スマホでも見せたい、表示までの待ち時間を短くしたい場合はCSSで作ります。単純な補足で装飾が不要ならtitle属性で十分です。両方指定すると二重に表示されるため、どちらかに統一してください。
避けてください。マウス操作でしか気づけない、印刷に出ない、読み上げ環境によっては伝わらない、という制約があります。価格や注意事項など必ず読まれるべき内容は本文に書き、ツールチップは補足に限定するのが原則です。
CSSの実装テクニックを「レイアウト・装飾・アニメーション・基礎・トラブル解決」の目的別に探せる総まとめページを用意しています。実装で迷ったときの索引としてどうぞ。

目的に合わせて選べる「AI副業」「AI転職」「AI活用」の3コースを用意。副収入・キャリアチェンジ・日常の生産性アップまで、あなたのゴールに合わせてAIを学べます。
会員登録はカンタン30秒で完了します。まずは公式LINEから、無料でAI学習をスタートしましょう!
WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。
公式サイト より
今すぐ
無料カウンセリング
を予約!