CSS Anchor Positioning入門:Popoverをボタンのそばに表示する
ボタンのすぐ下にメニューを表示したいとき、これまではJavaScriptでボタンの座標やサイズを調べ、メニューの位置を計算することがありました。CSS Anchor Positioningを使うと、基準となる要素(アンカー)と表示する要素をCSSで関連付けて配置できます。
この記事では、HTMLだけで開閉できるPopover APIと組み合わせ、ボタンのそばにメニューを表示する基本を解説します。
CSS Anchor Positioningとは
CSS Anchor Positioningは、ある要素の位置を基準にして、別の要素を配置するためのCSS機能です。
例えば、次のようなUIに利用できます。
- ボタンの下に表示する操作メニュー
- アイコンのそばに表示する説明
- 入力欄に関連付ける候補リスト
- 要素を指し示す吹き出し
基準にする要素にはanchor-nameで名前を付け、配置する要素にはposition-anchorでその名前を指定します。これにより、「どの要素を基準にするか」がCSSから分かるようになります。
Popover APIと組み合わせるメリット
Popover APIは、メニューや補足情報などをほかの内容より手前に表示するためのHTML機能です。popover属性を付けた要素は最初は非表示になり、popovertargetを持つボタンから開閉できます。
<button type="button" popovertarget="account-menu">
アカウント
</button>
<div id="account-menu" popover>
メニューの内容
</div>
この時点で開閉処理は動作するため、基本的なトグル処理をJavaScriptで書く必要はありません。また、自動で開く通常のPopoverは、外側をクリックする操作やEscキーでも閉じられます。
ただし、Popover APIだけではボタンのすぐ下に配置されるとは限りません。そこで、表示位置をCSS Anchor Positioningで指定します。
ボタンの下にメニューを配置する
まずは全体のHTMLです。ボタンとPopoverをaccount-menuというIDで関連付けています。
<button
class="account-button"
type="button"
popovertarget="account-menu"
>
アカウント
</button>
<nav id="account-menu" class="account-menu" popover aria-label="アカウントメニュー">
<a href="/profile/">プロフィール</a>
<a href="/settings/">設定</a>
<button type="button">ログアウト</button>
</nav>
続いて、ボタンをアンカーとして登録します。--account-buttonはCSS内で使うカスタム識別子で、名前の先頭には--が必要です。
.account-button {
anchor-name: --account-button;
}
Popover側では、position-anchorに同じ名前を指定します。さらにposition-area: bottomを指定すると、アンカーの下側を配置領域として利用できます。
.account-menu {
position: fixed;
position-anchor: --account-button;
position-area: bottom;
margin: 8px 0 0;
padding: 8px;
border: 1px solid #d0d7de;
border-radius: 8px;
background: #fff;
box-shadow: 0 8px 24px rgb(0 0 0 / 16%);
}
ここでのポイントは、次の3つです。
1. anchor-nameで基準となるボタンに名前を付ける
2. position-anchorでPopoverからアンカーを参照する
3. position-areaでアンカーに対する配置場所を決める
popover要素にはブラウザ標準の余白などがあるため、作りたいデザインに合わせてmarginやpaddingを明示すると調整しやすくなります。
画面端では表示位置を切り替える
ボタンが画面の下端にあると、下向きに開いたメニューが画面外にはみ出す可能性があります。position-try-fallbacksを使うと、最初の位置に十分な空間がない場合の候補を指定できます。
.account-menu {
position: fixed;
position-anchor: --account-button;
position-area: bottom;
position-try-fallbacks: flip-block, flip-inline;
margin: 8px;
}
flip-blockは上下方向、flip-inlineは左右方向で位置を反転する候補です。例えばボタンの下に空間が足りなければ、ブラウザは上側への配置を試せます。
フォールバックは「常に反転する」という指定ではありません。最初に指定した配置が収まらないときに、ブラウザが次の候補を試すための指定です。
anchor()で位置を細かく指定する
position-areaは簡潔ですが、アンカーの端を使って位置を細かく決めたい場合はanchor()関数も利用できます。
.account-menu {
position: fixed;
position-anchor: --account-button;
top: anchor(bottom);
right: anchor(right);
margin-top: 8px;
}
この例では、Popoverの上端をボタンの下端に、Popoverの右端をボタンの右端に合わせています。position-anchorで基準を指定しているため、anchor(bottom)やanchor(right)にはアンカー名を繰り返し書かずに済みます。
最初は読みやすいposition-areaから試し、位置を細かく調整する必要が出たらanchor()を検討するとよいでしょう。
未対応環境も考慮する
CSS Anchor Positioningの対応状況はブラウザやバージョンによって異なります。実際に利用するときは対象ブラウザを確認し、非対応でも操作できる作りにします。
今回の例では、配置用CSSが解釈されなくてもPopover自体の開閉機能は独立しています。まず通常の位置でも内容を利用できる状態を用意し、対応ブラウザだけ配置を拡張する方法があります。
.account-menu {
/* Anchor Positioningに依存しない見た目 */
padding: 8px;
border: 1px solid #d0d7de;
border-radius: 8px;
background: #fff;
}
@supports (anchor-name: --example) {
.account-button {
anchor-name: --account-button;
}
.account-menu {
position: fixed;
position-anchor: --account-button;
position-area: bottom;
position-try-fallbacks: flip-block, flip-inline;
margin: 8px;
}
}
Popover APIにも対応していない環境を対象に含める場合は、JavaScriptによる開閉処理や別ページへのリンクなど、要件に合った代替手段も検討してください。
実装時に確認したいこと
最後に、PopoverとAnchor Positioningを組み合わせるときの確認事項をまとめます。
- ボタンは
button要素にし、キーボードでも操作できるようにする popovertargetとPopoverのidを一致させるanchor-nameとposition-anchorの名前を一致させる- 画面の上下左右の端でも内容が見切れないか確認する
- ズーム時や狭い画面でも操作できるか確認する
- 対象ブラウザでPopover APIとCSS Anchor Positioningの対応状況を確認する
Popover APIが表示状態を管理し、CSS Anchor Positioningが表示場所を担当すると、HTML・CSSそれぞれの役割が分かりやすくなります。まずは小さな操作メニューから試し、JavaScriptによる座標計算を減らせるか検討してみてください。
今回は以上です。