位置認識ツールチップ

CSS アンカー ポジショニングを使用してツールチップやポップオーバーを構築する場合、ビューポートからオーバーフローする可能性がある場合は、ブラウザが要素をフォールバック位置に自動的に「反転」させることができます。このような場合は、配置されたコンテンツのスタイルを調整して、配置されたコンテンツからアンカーを指す矢印の位置を変更することをおすすめします。

アンカー コンテナクエリは、要素のアクティブな位置指定状態をクエリし、それに応じてスタイルを適用できるようにすることで、この問題を解決します。ビューポートの衝突反転(方向矢印のスタイル変更なし)を使用した一般的なアンカー付きドロップダウンとフライアウトの配置については、ビューポート対応ドロップダウンをご覧ください。

問題

デフォルトでアンカーの上に表示されるツールチップを考えてみましょう。下部に下向き矢印があります。ユーザーがスクロールして、ツールチップが反転してアンカーの下に表示されると、矢印は間違った方向を指し、ツールチップの間違った側に表示されます。

解決策: アンカー付きコンテナクエリ

配置された要素に container-type: anchored を設定すると、その要素は独自のアンカー配置状態を認識するクエリコンテナになります。その後、@container anchored() クエリを使用して、その子孫または疑似要素を更新できます。

1. ツールチップとトリガーを作成する

Popover API を使用してツールチップを作成します。popovertarget を介してポップオーバーを呼び出すと、トリガーボタンとの暗黙的なアンカー接続が自動的に確立されるため、明示的な anchor-name や position-anchor は必要ありません。

<button popovertarget="tooltip" id="anchor" aria-describedby="tooltip">anchor</button>
<div id="tooltip" popover role="tooltip"></div>

アンカーの配置で使用するポップオーバーのインセットとマージンのスタイルをリセットします(アンカーの配置がサポートされている場合のみ)。

@supports (anchor-name: --my-anchor) {
  [popover] {
    inset: auto;
    margin: unset;
  }
}

2. コンテナをセットアップする

配置する要素に container-type: anchored を適用します。この要素には、反転動作を有効にするために position-try-fallbacks も定義する必要があります。

#tooltip {
  position: fixed;
  position-area: block-start;
  position-try-fallbacks: flip-block;

  /* Enable anchored container queries */
  container-type: anchored;
}

3. フォールバックに基づくスタイル

特定のフォールバックが有効な場合にスタイルを適用するには、@container anchored(fallback: <value>) を使用します。

すべてのコンテナクエリと同様に、@container はコンテナの子孫にのみスタイルを設定できます。矢印を作成する一般的な方法は、::before 疑似要素と ::after 疑似要素を使用することです。これらは子孫として扱われ、直接スタイルを設定できます。ただし、ツールチップ自体をスタイル設定するには(ステップ 4 を参照)、ツールチップに子要素を追加し、その ::before 疑似要素に矢印を作成します。

<div id="tooltip" popover role="tooltip">
  <div class="tooltip-content">Tooltip</div>
</div>
.tooltip-content::before {
  /* Default "down" arrow for the 'top' position */
  content: "▼";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 1rem;
}

/* Update to an "up" arrow when the 'flip-block' fallback (bottom) is active */
@container anchored(fallback: flip-block) {
  .tooltip-content::before {
    content: "▲";
    inset-block-start: 0;
    inset-block-end: auto;
  }
}

4. コンテナ自体のスタイル設定

反転時にコンテナ自体のプロパティ(margin や background-color など)を変更する必要がある場合は、内部ラッパー要素を使用する必要があります。

  1. 外側に配置された要素に container-type: anchored を適用します。
  2. @container ブロック内の内部要素をターゲットにします。
@container anchored(fallback: flip-block) {
  .tooltip-content {
    border-radius: 0 0 .5rem .5rem;
    margin-block-start: 0.25rem;
  }
}

ベスト プラクティス

  • 論理的なフォールバックを優先する: position-try-fallbacks で flip-block や flip-inline などのキーワードを使用すると、RTL やさまざまな記述モードを自動的に処理するシンプルなクエリを作成できます。
  • 矢印に疑似要素を使用する: ツールチップの矢印は純粋に装飾的なものであり、::before または ::after の候補として最適です。これらは、追加の DOM なしでアンカー付きコンテナクエリを介してスタイル設定できます。

代替戦略

適用されたフォールバックに基づいて矢印を配置することはプログレッシブ エンハンスメントであり、フォールバック位置に対応する別の方法はありません。アンカー位置コンテナクエリをサポートしていないブラウザで矢印を非表示にするには、@supports (container-type: anchored) で CSS のサポートをテストします。

@supports (container-type: anchored) {
  .tooltip-content::before {
    content: "▼";
  }
}

Popover のフォールバックとブラウザ サポート

Popover API は主にプログレッシブ エンハンスメントですが、その定義動作である最上位レイヤのプロモーション、ライトディスミス、popovertarget 呼び出しには、CSS のみの同等の機能がありません。古いブラウザでは、ポリフィルが必要です。ポリフィルを配布したくない場合は、手動でフォールバックする必要があります。

ポリフィル: 古いブラウザで popover 属性をサポートするには、条件付きで @oddbird/popover-polyfill を読み込みます。必須: HTMLElement.prototype の popover プロパティをチェックして機能を検出し、ネイティブ サポートがない場合にのみポリフィルを読み込みます。無条件に読み込まないでください。

バンドラまたはインポート マップを使用する場合:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

バンドラを使用しない場合は、<script type="module"> 内の CDN からインポートします。

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

スタイリングに関する注意事項: ポリフィルは実際の :popover-open 疑似クラスを定義できないため、代わりに .\:popover-open クラスを適用します。必須: :is() または :where() を使用して 2 つを結合します。そうしないと、:popover-open がないブラウザではルール全体が破棄されます。

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

または、ポリフィルを使用しないレガシー フォールバックの場合は、position: fixed を使用して getBoundingClientRect() で座標を手動で計算するか、ユースケースで許容される場合は inset: auto でデフォルトの配置を使用します。

Popover API をサポートしていないブラウザは、アンカーの配置もサポートしていないため、ツールチップは画面の中央に表示されます。