CSSアンカーポジショニングとPopover API:2026年ついにJSでポップアップ位置を計算する必要がなくなった
前端工程
CSS Anchor Positioning:フロントエンド開発の「解放宣言」
すべてのフロントエンドエンジニアがこの苦痛を経験している:JSでTooltipの位置を計算し、境界オーバーフローを処理し、スクロールとresizeイベントをリッスンする……今、CSSがネイティブでこれらすべてを処理できる。
歴史の振り返り:2016年にPopper.js、2020年にFloating UI、2026年にようやくCSSネイティブアンカーポジショニングが使える。10年経って、ポップアップの位置決めはついにJSの仕事ではなくなった。
JSポジショニングからCSSポジショニングへの進化
2016 jQuery offset() + 手動計算
スクロール/resizeのたびに再計算
2020 Floating UI / Popper.js
自動フリップ、オフセット、境界検出
しかしJSの初期化と破棄が必要
2024 CSS Anchor Positioning(Chrome 125+)
純CSS宣言型ポジショニング
ブラウザがネイティブで境界とスクロールを処理
2026 Anchor Positioning + Popover API
純CSSポップアップ:ゼロJSトグル、ゼロJSポジショニング
完全な宣言型ポップアップソリューション
anchor-nameとposition-anchor
基本的な使い方
<div class="card">
<button class="trigger">その他の操作</button>
<div class="dropdown">ドロップダウンコンテンツ</div>
</div>
.trigger {
anchor-name: --my-anchor;
}
.dropdown {
position: fixed;
position-anchor: --my-anchor;
top: anchor(bottom);
left: anchor(left);
}
anchor()関数リファレンス
.tooltip {
position: fixed;
position-anchor: --trigger-anchor;
/* アンカーの各エッジに対して相対的 */
top: anchor(bottom);
bottom: anchor(top);
left: anchor(left);
right: anchor(right);
/* アンカーの中心点 */
left: anchor(50%);
top: anchor(50%);
/* オフセット付き */
top: calc(anchor(bottom) + 8px);
left: calc(anchor(left) - 4px);
}
position-area九宮格ポジショニング
九宮格構文
┌─────────────┬─────────────┬─────────────┐
│ top left │ top center│ top right │
│ │ │ │
├─────────────┼─────────────┼─────────────┤
│ center left │ center │ center right│
│ │ │ │
├─────────────┼─────────────┼─────────────┤
│ bottom left │bottom center│ bottom right│
│ │ │ │
└─────────────┴─────────────┴─────────────┘
.tooltip-below {
position-area: bottom center;
}
.tooltip-right {
position-area: center right;
}
.dropdown-below-left {
position-area: bottom left;
}
position-fallbackとマルチレベルフォールバック戦略
境界オーバーフローの自動フリップ
.dropdown {
position: fixed;
position-anchor: --trigger;
/* 優先:アンカーの下 */
top: anchor(bottom);
left: anchor(left);
position-fallback: --dropdown-fallback;
}
@position-fallback --dropdown-fallback {
/* フォールバック1:アンカーの上 */
@try {
bottom: anchor(top);
left: anchor(left);
}
/* フォールバック2:アンカーの下、右寄せ */
@try {
top: anchor(bottom);
right: anchor(right);
}
/* フォールバック3:アンカーの上、右寄せ */
@try {
bottom: anchor(top);
right: anchor(right);
}
}
Popover API:ネイティブポップオーバーレイヤー
基本的な使い方
<button popovertarget="my-popover">ポップオーバーを開く</button>
<div id="my-popover" popover>
<p>これはネイティブPopoverコンテンツです</p>
<button popovertarget="my-popover" popovertargetaction="hide">閉じる</button>
</div>
Popover + Anchorの組み合わせ
<button class="menu-trigger" popovertarget="menu-popover">メニュー</button>
<div id="menu-popover" popover class="menu-dropdown">
<a href="/profile">プロフィール</a>
<a href="/settings">設定</a>
<a href="/logout">ログアウト</a>
</div>
.menu-trigger {
anchor-name: --menu-anchor;
}
.menu-dropdown {
position-anchor: --menu-anchor;
position-area: bottom left;
margin: 4px;
}
anchor-size():アンカー要素のサイズに基づく自動適応
.trigger {
anchor-name: --select-anchor;
}
.select-dropdown {
position: fixed;
position-anchor: --select-anchor;
top: anchor(bottom);
left: anchor(left);
/* ドロップダウン幅がトリガーボタンに追従 */
width: anchor-size(width);
/* または最小幅を設定 */
min-width: anchor-size(width);
}
実践:純CSSでよくあるUIコンポーネントを実装
Tooltip
<span class="tooltip-trigger" tabindex="0">
ヘルプ
<span class="tooltip-content" role="tooltip">これはヘルプツールチップです</span>
</span>
.tooltip-trigger {
anchor-name: --tt-anchor;
position: relative;
cursor: help;
text-decoration: underline dotted;
}
.tooltip-content {
position: fixed;
position-anchor: --tt-anchor;
position-area: top center;
margin: 8px;
padding: 6px 10px;
background: #1f2937;
color: white;
border-radius: 4px;
font-size: 13px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.15s;
position-fallback: --tt-fallback;
}
.tooltip-trigger:hover .tooltip-content,
.tooltip-trigger:focus .tooltip-content {
opacity: 1;
}
@position-fallback --tt-fallback {
@try { position-area: bottom center; }
@try { position-area: center right; }
@try { position-area: center left; }
}
ドロップダウンメニュー
<button class="dropdown-trigger" popovertarget="dd-menu">
オプション ▾
</button>
<div id="dd-menu" popover class="dropdown-menu">
<button class="dropdown-item">編集</button>
<button class="dropdown-item">コピー</button>
<button class="dropdown-item">削除</button>
</div>
.dropdown-trigger {
anchor-name: --dd-anchor;
}
.dropdown-menu {
position-anchor: --dd-anchor;
position-area: bottom left;
margin: 4px;
width: anchor-size(width);
min-width: 160px;
padding: 4px;
border-radius: 8px;
background: white;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
position-fallback: --dd-fallback;
}
.dropdown-item {
display: block;
width: 100%;
padding: 8px 12px;
border: none;
background: none;
text-align: left;
border-radius: 4px;
cursor: pointer;
}
.dropdown-item:hover {
background: #f3f4f6;
}
@position-fallback --dd-fallback {
@try { position-area: top left; }
}
Floating UI/Popper.jsとの比較
| 次元 | CSS Anchor + Popover | Floating UI |
|---|---|---|
| JS依存 | ゼロJS | 12KB gzip |
| パフォーマンス | ブラウザネイティブ | JS計算 |
| 宣言型 | CSS宣言型 | JS命令型 |
| 境界フリップ | position-fallback | flip() middleware |
| オフセット | margin/anchor() | offset() middleware |
| 矢印 | CSS疑似要素 | SVG/HTML要素 |
| 仮想要素 | 非対応 | 対応 |
| ブラウザ対応 | Chrome 125+ | 全ブラウザ |
| SSRフレンドリー | 完全フレンドリー | クライアントJSが必要 |
まだJSライブラリが必要な場合
Floating UIがまだ必要なシナリオ:
1. 古いブラウザのサポートが必要(Safari < 17.5, Firefox < 131)
2. 仮想要素のポジショニングが必要(カーソル位置の追従)
3. 複雑なミドルウェアチェーンが必要
CSS Anchorで十分なシナリオ:
1. Tooltip、Dropdown、Popoverなどの標準的なポップアップ
2. モダンブラウザのみサポートすればよい
3. ゼロJS依存を追求
4. パフォーマンス重視のシナリオ
ブラウザ互換性とPolyfill
サポート状況
| ブラウザ | Anchor Positioning | Popover API |
|---|---|---|
| Chrome 125+ | ✅ | ✅ |
| Edge 125+ | ✅ | ✅ |
| Safari 17.5+ | ✅ | ✅ |
| Firefox 131+ | ✅ | ✅ |
| iOS Safari 17.5+ | ✅ | ✅ |
Polyfillソリューション
<script src="https://cdn.jsdelivr.net/npm/@oddbird/css-anchor-positioning@0.3.0/dist/css-anchor-positioning.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oddbird/popover-polyfill@0.4.0/dist/popover.min.js"></script>
@starting-styleとの組み合わせでポップアップアニメーションを実現
[popover] {
opacity: 0;
transform: translateY(-8px) scale(0.95);
transition: opacity 0.2s, transform 0.2s, display 0.2s allow-discrete;
overlay: none;
}
[popover]:popover-open {
opacity: 1;
transform: translateY(0) scale(1);
overlay: auto;
}
@starting-style {
[popover]:popover-open {
opacity: 0;
transform: translateY(-8px) scale(0.95);
}
}
まとめ
- CSS Anchor Positioningはポップアップポジショニングの究極のソリューション — ゼロJS、宣言型、ブラウザネイティブ
- Popover APIでポップアップのトグルもゼロJSに — popovertarget属性1行で完了
- position-fallbackが境界オーバーフローを解決 — JSでフリップを計算する必要はもうない
- 2026年すべての主要ブラウザが対応 — 本番環境で安心して使用可能
10年経って、ポップアップのポジショニングはついにCSSがあるべき場所に戻った。これは漸進的な改善ではなく、パラダイムシフトだ——Flexboxがフロートレイアウトに取って代わったのと同じように。
ブラウザローカルツールを無料で試す →
#CSS锚点定位#Anchor Positioning#Popover API#弹出层#CSS