Pure CSSによるツールチップの作成
軽量なツールチップが必要で、JavaScriptライブラリを引き込みたくない場合、CSSだけでも驚くほど対応できます。依存関係もイベントリスナーも不要 — 適切に配置されたいくつかのCSSルールだけで実現できます。
本記事では、疑似要素、data属性、トランジションを用いた実用的でモダンな実装方法と、実際に押さえておくべきアクセシビリティの考慮事項について解説します。
主なポイント
- Pure CSSのツールチップは、
::after疑似要素、content: attr(data-tooltip)、そして:hover/:focus-visibleトリガーを用いて構築できます。 displayではなくopacityとvisibilityを使うことで、ツールチップをスムーズにフェードイン・フェードアウトさせられます。pointer-events: noneによってツールチップ自体がホバーイベントを受け取らないようにし、ちらつきを防止します。- CSSツールチップにはアクセシビリティ上の明確な制約があります — スクリーンリーダーに確実に届かず、タッチデバイスでのホバー挙動も一貫しません。
- 重要なUIヒントには、JavaScript、Popover API、または可視要素を指す適切な
aria-describedbyを利用すべきです。
Pure CSSツールチップの仕組み
基本的な考え方はシンプルです。トリガー要素にposition: relativeを適用し、::after疑似要素をツールチップの吹き出しとして親要素に対して絶対配置します。デフォルトでは非表示にしておき、:hoverと:focus-visible時に表示します。
疑似要素はDOMから直接読み取れないため、ツールチップのテキストはdata-tooltip属性経由で渡し、content: attr(data-tooltip)で取り込みます。これによりHTMLがすっきりし、隠した<span>の中にテキストを重複させる必要もなくなります。
HTML構造
<button
class="tooltip-trigger"
data-tooltip="Saves your current progress"
>
Save
</button>
トリガー要素として<button>またはアンカー(<a>)を使うことが重要です。これらの要素はネイティブにフォーカス可能であり、キーボードユーザーが追加の手間なくアクセスできます。
注:
aria-describedbyを利用したい場合は、DOM内に実在する可視要素のidを指す必要があります — 疑似要素を指すことはできません。本手法では::afterを使用しているため、ツールチップのテキストを実在する要素内にもレンダリングしない限り、aria-describedbyは省略してください。
CSS
.tooltip-trigger {
position: relative;
cursor: pointer;
}
/* Tooltip bubble */
.tooltip-trigger::after {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background-color: #1a1a1a;
color: #fff;
font-size: 0.8rem;
white-space: nowrap;
padding: 5px 10px;
border-radius: 4px;
/* Hidden by default */
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
z-index: 10;
pointer-events: none;
}
/* Show on hover and keyboard focus */
.tooltip-trigger:hover::after,
.tooltip-trigger:focus-visible::after {
opacity: 1;
visibility: visible;
}
なぜdisplayではなくopacity + visibilityなのか?
display: noneとdisplay: blockの切り替えはトランジションできません — ツールチップが瞬時にパッと現れたり消えたりするだけになってしまいます。opacityとvisibility: hiddenを組み合わせることで、ツールチップをスムーズにフェードさせつつ、非表示時にはポインター操作の対象から外せます。さらにvisibilityを併用することで、非表示状態のツールチップが視覚的にインタラクティブなまま残ることも防げます。
なぜpointer-events: noneなのか?
これがないと、ツールチップの吹き出し自体がホバー状態をトリガーしてしまい、カーソルが上を通過する際にツールチップがちらつく原因になります。::after要素にpointer-events: noneを設定することで、これを完全に防げます。
Discover how at OpenReplay.com.
なぜ中央寄せにtransform: translateX(-50%)を使うのか?
left: 50%はツールチップの左端をトリガー要素の中央に配置します。次にtranslateX(-50%)が自身の幅の半分だけ左へ戻すことで、ツールチップのテキスト幅にかかわらず中央揃えになります — ピクセル値をハードコードする計算は不要です。
知っておくべきアクセシビリティの制限
Pure CSSのホバーツールチップには、現実的な制約があります。
- スクリーンリーダーが読み上げない可能性があります。 疑似要素の
contentプロパティは、すべてのスクリーンリーダーで確実に読み上げられるわけではありません。重要な情報は可視UIに含めるか、実在する要素を指すaria-describedbyを使用してください。 - タッチデバイスでのホバー挙動が一貫しません。 CSSのみのツールチップを必須情報の伝達手段として頼るべきではありません。
- roleやライブリージョンがありません。 CSSだけでは、ツールチップのセマンティクスを支援技術に伝えられません。
インタラクティブ要素に対する単純な補助的ヒントであれば、CSSツールチップで十分です。タスク完了に不可欠な情報については、フォーカス管理、アナウンス、ARIAステートの制御のためにJavaScriptを使うべきです。
今後の展望: CSS Anchor PositioningとPopover API
CSS Anchor Positioningを使うと、ツールチップの配置がはるかに柔軟になります — 親要素のposition: relativeに依存することなく、任意の要素にツールチップを紐付けられます。Anchor Positioningのブラウザサポートは、Can I Useによれば、現在モダンブラウザで広く利用可能です。
よりリッチなインタラクティブ挙動を求める場合は、インタラクティブなフローティングUIに対するネイティブな代替としてPopover APIを検討する価値があります。
まとめ
::after、content: attr(data-tooltip)、visibility/opacityのトランジションで構築されたPure CSSツールチップは、シンプルかつ高速で、依存関係もありません。:focus-visibleと組み合わせてキーボード対応を確保すれば、堅実なベースラインが得られます。ただし、CSSツールチップの限界には正直であるべきです — 補助的ヒントを超える用途では、JavaScriptや、Popover APIのようなネイティブのプラットフォーム機能に頼るべきです。
FAQ
はい。位置は疑似要素のオフセットプロパティで制御します。上に配置するにはbottomにcalc(100% + 8px)を、下に配置するにはtopにcalc(100% + 8px)を、横方向に配置するにはrightやleftを使用します。tooltip-bottomやtooltip-rightといったバリアントクラスを作成して、これらのプロパティをオーバーライドすることもできます。
タッチデバイスではホバー挙動が一貫しないため、CSSのみのツールチップを必須情報の伝達手段として頼るべきではありません。モバイルでツールチップの内容が重要な場合は、JavaScriptで制御する実在する要素にレンダリングするか、入力タイプを問わずインタラクティブなフローティングUIにより堅牢な基盤を提供するPopover APIを使用してください。
white-space: nowrapルールはツールチップを1行に保ちますが、狭い画面ではあふれる可能性があります。これをmax-widthに置き換えて折り返しを許可してください。例: max-width: 240pxとwhite-space: normal。動的なエッジ検出には、JavaScriptや、モダンブラウザでより柔軟なネイティブ配置挙動をサポートするCSS Anchor Positioningが必要になります。
ボタンやリンクのようなフォーカス可能な要素に対する非必須かつ補助的なヒントであれば許容できます。タスクを完了するために必要な情報をツールチップが担う場合は不十分です。疑似要素のコンテンツはスクリーンリーダーに確実に読み上げられず、タッチデバイスでのホバー挙動も一貫しないためです。重要なコンテンツには、aria-describedbyとJavaScriptで管理された挙動を備えた実在のDOM要素を使用するか、Popover APIを検討してください。
Truly understand users experience
See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..