ツールチップやドロップダウンをボタンの近くへ置くとき、position: absoluteと座標計算を組み合わせる実装がよく使われます。しかし、スクロール、画面端、親要素のoverflowまで考えると配置処理は複雑になります。
CSS Anchor Positioningを使うと、ある要素をアンカーとして名前付けし、別の要素をその上下左右へ配置できます。この記事ではPopover APIのメニューをボタンの下へ追従させます。
Anchor Positioningの基本用語
| 指定 | 役割 |
|---|---|
anchor-name | 基準にする要素へアンカー名を付ける |
position-anchor | 配置する要素から使用するアンカーを指定する |
position-area | アンカーの上・下・左右など配置領域を指定する |
position-try-fallbacks | 画面端に収まらないときの代替位置を指定する |
完成コード
HTML
<main class="demo-card">
<img class="nines-logo" src="https://kjnine.com/wp-content/themes/kjnines_theme/images/svg2/nines_logo.svg" alt="NINES">
<p class="eyebrow">ANCHOR POSITIONING</p>
<h1>ボタンに追従するPopover</h1>
<button class="profile-button" type="button" popovertarget="accountMenu">
KJのメニュー
</button>
<nav id="accountMenu" class="account-menu" popover>
<a href="#profile">プロフィール</a>
<a href="#posts">投稿一覧</a>
<a href="#logout">ログアウト</a>
</nav>
</main>CSS
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
padding: 24px;
background: #edf1f7;
color: #111;
font-family: system-ui, sans-serif;
}
.demo-card {
position: relative;
width: min(100%, 600px);
min-height: 300px;
padding: 40px;
border: 1px solid #d5dce8;
background: #fff;
}
.nines-logo {
position: absolute;
top: 18px;
right: 18px;
width: 58px;
opacity: 0.16;
}
.eyebrow {
color: #0057ff;
font-size: 0.78rem;
font-weight: 800;
letter-spacing: 0.14em;
}
.profile-button {
anchor-name: --profile-trigger;
min-height: 44px;
padding: 10px 16px;
border: 0;
background: #0057ff;
color: #fff;
cursor: pointer;
font: inherit;
font-weight: 800;
}
.account-menu {
position: fixed;
position-anchor: --profile-trigger;
position-area: block-end span-inline-end;
width: 220px;
margin: 8px 0 0;
padding: 8px;
border: 1px solid #cfd7e5;
background: #fff;
box-shadow: 0 18px 48px rgb(15 30 60 / 24%);
position-try-fallbacks: flip-block, flip-inline;
}
.account-menu a {
display: block;
padding: 12px;
color: #111;
text-decoration: none;
}
.account-menu a:hover,
.account-menu a:focus-visible {
background: #edf3ff;
outline: 2px solid #0057ff;
outline-offset: -2px;
}
@supports not (position-area: block-end) {
.account-menu {
inset: auto 24px 24px auto;
margin: 0;
}
}JavaScript
// 開閉はPopover APIへ任せるため、基本例ではJavaScript不要です。
// 対応確認が必要ならCSS.supports()を使えます。
const hasAnchorPositioning = CSS.supports('position-area: block-end');
console.log({ hasAnchorPositioning });アンカーへ名前を付ける
基準にするボタンへanchor-name: --profile-trigger;を指定します。カスタムプロパティに似ていますが、ここではアンカーを識別する名前です。ページ内で用途が分かる名前にします。
配置する要素から参照する
Popover側へposition-anchor: --profile-trigger;を指定し、position-area: block-end span-inline-end;で基準要素の下側へ配置します。横書きだけを前提にしたtop・rightではなく、block・inline方向を使うと書字方向へ対応しやすくなります。
画面端で位置を切り替える
メニューが画面下に収まらない場合、固定位置のままだと内容が見切れます。position-try-fallbacksへflip-blockやflip-inlineを指定すると、反対側へ切り替える候補を持たせられます。
フォールバックを必ず用意する
新しいCSS機能を使うときは、未対応環境でも操作できることを優先します。サンプルでは@supports not (position-area: block-end)を使い、対応していない場合は画面右下へ固定表示します。位置が理想通りでなくても、メニューを読んで操作できれば段階的な強化になります。
absolute配置との違い
| 方法 | 長所 | 注意点 |
|---|---|---|
position: absolute | 広く使える、単純な配置に強い | 基準にする包含ブロックの管理が必要 |
| JavaScript座標計算 | 細かな制御ができる | スクロール・リサイズ・画面端の処理が増える |
| Anchor Positioning | CSSで要素間の位置関係を表現できる | 対応状況とフォールバックを確認する |
実装時のチェック項目
- 画面の上・下・左・右の端で開く
- ページをスクロールした状態で開く
- メニュー項目を長い日本語へ変える
- 200%ズームでも操作できるか確認する
- 未対応環境で内容が消えないか確認する
CodePen埋め込み用エリア
HTMLとCSSを貼るだけで基本動作を確認できます。CodePenの表示幅を狭くし、ボタンを画面端へ寄せて、フォールバック位置がどう変わるか試してください。
あわせて読みたい
参考資料
まとめ
CSS Anchor Positioningは、ボタンとメニューのような要素間の位置関係をCSSで表現する仕組みです。anchor-nameで基準を作り、position-anchorとposition-areaで配置し、画面端と未対応環境のフォールバックまで用意しましょう。