by MintJams

JSを最小化せよ! Popover APIとAnchor Positioningで構築するモダンUI

複雑なJSライブラリに頼らず、HTMLとCSSだけでモダンなUIコンポーネントを構築しませんか?この記事では、ポップオーバーやツールチップをよりシンプルに、よりパフォーマンス高く実装する実践的なテクニックを紹介します。もうUI構築で悩む必要はありません。

ウェブ開発におけるUIコンポーネント、特にモーダルやツールチップ、コンテキストメニューの構築は、常に開発者の悩みの種でした。従来の解決策は、多くの場合、複雑なJavaScriptライブラリやフレームワークに依存しており、パフォーマンスのオーバーヘッドやメンテナンスの負担が増大するという課題がありました。しかし、近年、ウェブプラットフォーム自体の進化により、HTMLとCSSのみでこれらのUIをシンプルかつセマンティックに構築する道が開かれました。

この記事では、新しく登場したPopover APICSS Anchor Positioningという強力なブラウザネイティブ機能に焦点を当てます。これらを活用することで、最小限のJavaScriptで、アクセシビリティ(A11y)にも配慮したモダンなUIコンポーネントをいかに構築できるかを、具体的なコード例を交えて解説します。


この記事でわかること

  • Popover APIの基本的な使い方とpopover属性の役割
  • CSS Anchor Positioningによる要素の相対的な位置決め方法
  • JavaScriptを最小限に抑えながら、アクセシブルなUIを構築する実践的な方法
  • よくある落とし穴と、それらを回避するためのヒント
  • HTMLとCSSだけでモーダル、ツールチップ、メニューを実装するためのアプローチ
  • どこまでがノーJSで実現でき、どこからJSが必要になるかの線引き

Popover APIとAnchor Positioningの基本

まず、Popover APIとAnchor Positioningの概念を理解しましょう。これらは密接に関連しながらも、異なる役割を持っています。

Popover API

Popover APIは、非モーダルなUIを構築するための新しいHTML属性です。これはdialog要素が提供するモーダルな動作とは異なり、ページ内の他のコンテンツとのインタラクションを妨げません。popover属性を要素に追加するだけで、その要素がポップオーバーとして機能するようになります。

<button popovertarget="my-popover">ツールチップを表示</button>

<div id="my-popover" popover>
  これはツールチップです。
</div>

上記のコードでは、buttonpopovertarget属性に、表示したいポップオーバーのIDを指定しています。このボタンをクリックすると、div要素がポップオーバーとして自動的に表示・非表示されます。JavaScriptを一切書かずにポップオーバーの表示を制御する、これが基本的な方法です。

CSS Anchor Positioning

CSS Anchor Positioningは、特定の要素(ポップオーバーなど)を、別の要素(アンカー)に対して相対的に配置するための新しいCSS機能です。これにより、要素の絶対的な位置を計算するためにJavaScriptを使う必要がなくなります。

anchor-nameというCSSプロパティをアンカー側に付与し、位置決めされる側anchor()関数を使って位置を決定します。Popover APIと組み合わせる場合、popovertargetで紐づけられたボタンが暗黙のアンカーになるという特別な挙動があります。このため、anchor-nameを明示的に指定しなくても、ボタンを基準にポップオーバーを配置できます。

明示的なアンカー名を使う場合

位置決めされる側で明示的にposition-anchorプロパティを使って、どのアンカーを基準にするかを指定します。

<button class="my-anchor" popovertarget="my-popover">ツールチップを表示</button>
<div id="my-popover" popover class="popover">これはツールチップです。</div>
/* アンカー側の要素に anchor-name を付与 */
.my-anchor {
  anchor-name: --my-anchor;
}

/* 位置決めされる側で関連付けプロパティ position-anchor を追加 */
#my-popover {
  position: absolute;
  position-anchor: --my-anchor;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 0;
}

実践! シンプルなツールチップとメニューの構築

ここでは、Popover APIとAnchor Positioningを組み合わせて、より実践的なUIコンポーネントを構築する方法を見ていきましょう。

ツールチップの完全な例

以下は、HTMLとCSSだけで、はみ出しを考慮して自動で位置を調整するツールチップの完全な例です。なお、ポップオーバーはユーザーエージェントの既定スタイルで position: fixed となり、画面中央に配置されるため、これを打ち消す必要があります。この例では、popovertarget で紐づけられたボタンを暗黙のアンカーとして利用しています。

<!DOCTYPE html>
<html lang="ja">
<head>
  <style>
    body {
      padding: 50px;
    }
    
    /* Popover のデフォルトスタイルをリセット */
    .tooltip {
      margin: 0;
      inset: auto;
      position: absolute;
    }

    /* ポジションの候補を定義 */
    @position-try --tooltip-top {
      bottom: anchor(top);
      left: anchor(center);
      translate: -50% 0;
    }
    @position-try --tooltip-bottom {
      top: anchor(bottom);
      left: anchor(center);
      translate: -50% 0;
    }
    
    .tooltip {
      position-try: --tooltip-bottom, --tooltip-top;
    }
  </style>
</head>
<body>

<button popovertarget="my-tooltip">
  クリックしてね
</button>

<div id="my-tooltip" class="tooltip" popover="auto">
  <p>これがツールチップです。</p>
</div>

</body>
</html>

トラブル時のチェックポイント

  • ブラウザ対応状況の確認: Popover APIはすでに主要なブラウザで広く利用可能ですが、CSS Anchor Positioningはまだ限定的なサポートに留まっています。実装前にCan I use...で最新の対応状況を確認しましょう。
  • ポップオーバーが表示されない?: popover属性が正しく設定されているか、またpopovertarget属性のIDがpopoverを持つ要素のIDと一致しているか確認しましょう。
  • 意図しない位置に表示される?: Popoverはデフォルトで画面中央に配置されるため、margin: 0; inset: auto; position: absolute; でデフォルトスタイルをリセットし、anchor()関数で明示的な位置決めを行う必要があります。
  • よくあるミス: popover属性のつけ忘れ、anchor-nameのスペルミス、HTMLにおけるIDの重複など、基本的なミスが原因であることが多いです。

どこで使える?ユースケースのヒント

  • フォームのバリデーションエラー表示: ユーザーが入力ミスをした際に、関連する入力フィールドの近くにポップオーバーでエラーメッセージを表示します。
  • インライン編集のヒント: テキストの近くに、編集やフォーマットのオプションを含む小さなポップアップを表示します。
  • スマホUIでの挙動: 画面サイズが小さい場合でも、Anchor Positioningが自動的に最適な位置に調整してくれるため、メディアクエリを多用した位置調整が不要になります。
  • 開閉時のスタイル制御: 開いているポップオーバーにスタイルを適用したい場合は、:popover-open 疑似クラスが便利です。

アクセシビリティの深掘りと「あえてJSを書くなら」の境界線

Popover APIは、ポップオーバー要素自体に role を自動付与しません。ただし、popovertarget で結びついた呼び出し元(ボタン)には、aria-expandedaria-details といった状態・関係が対応ブラウザでは自動で付与され、キーボードフォーカス移動も適切に制御されます。したがって、必要に応じて role="tooltip"role="menu" などは手動で指定してください。

  • セマンティクスは手動で: 用途に応じて role="tooltip"role="menu" などを手動で付与する必要があります。
  • aria-describedby: ツールチップとそれをトリガーする要素を関連付けるために、この属性を手動で付加すると良いでしょう。
  • キーボードナビゲーション: メニュー内のアイテムを矢印キーで移動するような操作は、現時点ではJavaScriptでの実装が必須です。
  • 動的なコンテンツの生成: データベースから取得したデータに基づいて動的にポップオーバーの内容を生成したり、複数の要素を同時に操作したりする場合も、JavaScriptの出番です。

これらの線引きを理解することで、「どこまでをネイティブ機能に任せるか」という判断が明確になります。


まとめ

この記事では、ウェブ開発のUI構築における新しいアプローチとして、Popover APICSS Anchor Positioningを紹介しました。これらは、従来の複雑なJavaScriptに依存したソリューションから脱却し、よりシンプルでパフォーマンスの高い、そしてアクセシビリティに優れたUIコンポーネントを構築するための強力なネイティブ機能です。

これらの機能を活用することで、開発者はHTMLとCSSを「UIの振る舞いを定義する言語」として捉え直すことができます。これにより、JavaScriptの役割は、UIのロジックや動的なデータ操作といった、より複雑なタスクに集中させることが可能になります。

この記事が、あなたのウェブ開発におけるUI構築の理解を深める一助となれば幸いです。ご覧いただきありがとうございました。


参考資料