- これまでのツールチップ実装で必須だった JavaScript のイベントリスナー、状態管理、ARIA 属性の手動同期を、Popover API がブラウザネイティブ機能として置き換える
popover 属性と popovertarget 属性だけで、開閉、Esc キー処理、キーボードナビゲーションが自動で動作する
- スクリーンリーダーの予測可能性が向上し、
aria-expanded の自動同期、フォーカス復元など、アクセシビリティ関連のバグ分類そのものが取り除かれる
- タイミング制御やホバー意図の判定など、一部の領域では依然として JavaScript が必要だが、コアのインタラクションモデルはブラウザが担う
- 大規模なデザインシステムや複雑なポジショニングが必要な場合はライブラリが依然有効だが、デフォルトはネイティブ API へ移行中
従来のツールチップの問題点
- Popover API 以前は、マウス、キーボード、支援技術全般で動作するネイティブなツールチップの概念がブラウザに存在しなかった
- トリガー要素、隠されたツールチップ要素、その両方を調停する JavaScript という同じパターンの繰り返し
- 表面的には単純でも、実際のユーザーに配布するとさまざまな問題が露呈する
- キーボードユーザーが
Tab でトリガーに入ってもツールチップが表示されない
- スクリーンリーダーが二重に読んだり、まったく読まなかったりする
- マウスを素早く動かすと ちらつき(flicker) が発生する
- 小さな画面でコンテンツが重なる
Esc キーで閉じられず、フォーカスも失われる
- 時間が経つほどイベントリスナーが蓄積し、hover/focus の個別処理、外側クリックの特殊ケース、ARIA 属性の手動同期などでコードが肥大化していく
ライブラリが使われてきた理由
- ライブラリは、ポジショニング、ビューポート境界でのフリップ、入力タイプごとのイベント調停、複雑なレイアウト内でのスクロール認識など、実務上の処理を担ってきた
- ポジショニングだけでも、スクロールコンテナ、transform、レスポンシブレイアウトの処理が複雑なため、依存関係を正当化できるほどだった
- 本当の問題はアクセシビリティ動作で起きていた
- ツールチップが遅れて表示される、またはまったく表示されない
- すばやい Tab 移動でツールチップが飛ばされる
Esc による解除が不安定
- マウスユーザーは即時性を、キーボードユーザーは予測可能性を期待するため、両方を支援すると遅延やエッジケースが発生する
- スクリーンリーダーでは、ツールチップが読まれたり、読まれなかったり、二重に読まれたりするなど、一貫しない動作が起きる
- ARIA 属性の手動更新で 1 つでも漏れると、アクセシビリティツリーで混乱や非可視状態が発生する
問題はコードそのものではなくプラットフォームの限界
- 実装はテストされ、ライブラリも堅牢だったが、核心の問題はWeb プラットフォームに適切なアフォーダンスが存在しなかったことにあった
- ブラウザにはそれがツールチップ要素だと認識する方法がなく、すべてが汎用要素、イベントリスナー、手動 ARIA、カスタム解除ロジックといった**慣習(convention)**ベースだった
- 時間がたつと小さな変更にもリスクが伴い、些細な修正がリグレッションを引き起こす脆い構造になる
- 新しいツールチップを追加するたびに、同じ複雑さがそのまま継承される
Popover API の最初の導入
- 新しい実験をしたかったのではなく、ブラウザが本来理解すべきツールチップ動作の保守に疲れたことが移行の動機だった
- 最小構成で試した:
<button popovertarget="tip-1"> + <div id="tip-1" popover="manual" role="tooltip">
- イベントリスナーなし、状態追跡なし、JavaScript からの ARIA 更新なし
- ボタンにフォーカスするとツールチップが表示され、
Esc を押すと消える
すぐに実感できた違い
- JavaScript なしで開閉: ブラウザが HTML だけで呼び出しを処理し、トリガーとツールチップの関係が明示的になる
Esc キーが自動で機能: キーリスナーを追加しなくても、ブラウザが popover を解除できる対象だと理解している
- ARIA 状態の自動同期:
aria-expanded 属性は popover の開閉に応じて自動更新され、手動管理が不要になり、古い状態が残るリスクもなくなる
- コード削減以上に重要なのは責任の移譲: 以前は JavaScript がツールチップを「存在させて」いたが、今ではブラウザがマークアップ上の役割を理解し、フォーカスモデル、アクセシビリティツリー、ネイティブな解除ルールに参加する
Invoker Commands を理解する
popovertarget="id" はボタンを popover 要素に接続する
popovertargetaction で動作を指定する
show: 開くのみ
hide: 閉じるのみ
toggle(デフォルト): 開いていれば閉じ、閉じていれば開く
- 同じツールチップに複数のトリガーを持たせることができ、基本的なインタラクションに JavaScript は不要
アクセシビリティ面での無償の利得
-
キーボードが「そのまま動く」
- 以前は、フォーカスで表示し、blur で隠し、
Esc を手動で配線し、タイミングまで合わせなければならない多層構造だった
popover 属性(auto または manual)を設定すると、ブラウザが基本処理を担う: Tab / Shift+Tab が正常に動作し、Esc で毎回確実に閉じられる
- コードベースから、グローバルな keydown ハンドラ、
Esc 専用の後始末ロジック、キーボードナビゲーション中の状態チェックなどが削除される
-
スクリーンリーダーの予測可能性
- ここは最も改善が大きかった領域で、以前は慎重に ARIA を扱っても挙動が変わりやすく、小さな変更でも危険だった
popover="manual" role="tooltip" を使うと、はるかに安定して予測可能な動作になる
- 移行後は Lighthouse が誤った ARIA 状態の警告を表示しなくなった — そもそも間違えうるカスタム ARIA 状態自体がなくなったためだ
-
フォーカス管理
- 以前は、フォーカストリガーで表示し、ツールチップ内へフォーカスが移動したら閉じないようにし、blur を処理し、フォーカスを手動で復元するなど、複雑なルールが必要だった
- Popover API ではフォーカスが自然に popover へ移動し、閉じるとトリガーへ自動でフォーカスが復元される
- フォーカス復元コードを追加したのではなく、削除した
Popover API がまだ足りない領域
-
ツールチップのタイミング
- ネイティブ popover は即座に開閉するため、マウスを少し速く動かしたり、トリガーにかすっただけでもツールチップがちらつき、不安定に感じられることがある
- hover/focus と開く動作の間に、依然として遅延制御が必要
- 基本の開閉はブラウザと HTML invoker commands が担い、JavaScript はポインタがツールチップへ移動したら非表示を取り消すなど、意図に沿った動作改善にだけ使う
- CSS 側でもこの領域の検討が進んでおり、interest/invoker 関連の作業によって、入場/退出ディレイを CSS で直接表現できる方向へ進んでいる
-
ホバー意図と Invoker Commands
- ブラウザには、要素上にホバーした理由が分からない — 意図的なのか、ポインタが通過中なのかを判断できない
- Invoker commands がコアの開閉を処理するため、JavaScript はもはやインタラクションモデル全体を所有せず、その上に意図判定だけを追加する
- 非表示前の短い遅延や、ポインタがツールチップへ移動したときの解除キャンセルなど、ブラウザが推論できない動作にだけ JavaScript を使う
-
Manual Popover とフォーカス
popover="manual" では、auto popover と違ってブラウザがフォーカスを自動復元しない
- ツールチップがフォーカスで開き、blur で閉じる場合は、トリガーへ明示的にフォーカスを返す必要がある
- これはプラットフォームの動作とユーザー意図のあいだにある明確な境界を示している
-
率直な評価
- Popover API がツールチップを魔法のように解決するわけではないが、脆弱なインフラを再構築し続ける作業を止められる
- 依然として JavaScript は使い、エッジケースも考慮する必要はあるが、UI プリミティブの再生産ではなく、製品の問題解決に集中できる
それでもツールチップライブラリが必要な場合
-
大規模または成熟したデザインシステム
- 複数チームが使う大規模なデザインシステムでは、集中管理された動作、文書化されたパターン、一貫したデフォルトのためにライブラリが合理的だ
- 基盤となるインタラクションモデルの変更は、技術的判断であるだけでなく、組織的な判断でもある
- アクセシビリティのニュアンスに不慣れなチームメンバーにガードレールを提供できる
-
複雑なポジショニング要件
- 入れ子になったスクロールコンテナ間の衝突検知、カスタムフリップロジック、オフセット/境界の細かな制御が必要なら、Floating UI のようなライブラリが依然有利
- CSS anchor positioning はこの問題のかなりの部分をカバーし始めている — 純粋な CSS で、トリガー基準の相対配置、ビューポートを意識した配置、エッジでのフリップが可能になる
- まだ新しい機能であり、既知の問題もあるが、Interop に含まれており、完全で一貫したブラウザ対応が期待されている
- 現時点で一貫したクロスブラウザ動作が必要なら、ライブラリが実用的な選択肢になる
-
アクセシビリティ経験が不足しているチーム
- アクセシビリティ知識が不足しているチームでは、良いライブラリがセーフティネットとして機能する — 完璧なアクセシビリティを保証するわけではないが、よくあるミスを防げる
- Popover API はより良いデフォルトを提供するが、role、ラベル、フォーカス管理、テストをいつ追加すべきかを理解することは依然として必要
- 理解がなければ、ネイティブツールも誤用されうる
結論
- Popover API によって、ツールチップはもはやシミュレーションするものではなく、ブラウザが理解する要素になった
- 開く、閉じる、キーボード操作、
Esc 処理、アクセシビリティの大部分がプラットフォーム自体から提供される
- 複雑なデザインシステム、高度なカスタマイズ、レガシーな制約ではライブラリが依然有効だが、デフォルトは移行しつつある
- 最もシンプルなツールチップが、同時に最も正確なツールチップになりうる最初の時点
- 製品内のツールチップを 1 つだけでも Popover API に置き換えてみれば、コードから何が消えるか確認できる
まだコメントはありません。