ツールチップやドロップダウンメニューの位置がスクロールのたびにずれてしまう、画面端でポップオーバーが切れてしまう——そんな悩みを抱えて、JavaScriptでgetBoundingClientRect()を使った座標計算を書き続けてきた方は少なくないはずです。ライブラリを導入すれば楽にはなるものの、依存が増えバンドルサイズも膨らみます。
実はこの悩み、CSSだけで解決できる時代に入っています。CSS Anchor Positioningを使えば、要素同士の位置関係を宣言的なCSSで結び付けられ、画面端でのはみ出し対応までブラウザ側に任せられるようになりました。本記事では、この新しいレイアウト手法の基本概念から実務で使えるコンポーネント実装、そしてつまずきやすいポイントのデバッグ方法まで、実際に手を動かせるコードとともに解説していきます。
国内シェアNo.1のエックスサーバーが提供するVPSサーバー『XServer VPS』CSS Anchor Positioningとは?できることと採用メリット
CSS Anchor Positioningとは何か?従来のJS位置計算との違い
CSS Anchor Positioningは、ある要素(アンカー要素)を基準にして、別の要素(ターゲット要素)のサイズや位置をCSSだけで決定できる仕様です。CSSWG(CSS Working Group)が策定を進めている「CSS Anchor Positioning Module」で定義されており、position: absoluteやfixedで配置される要素を、DOM上の親子関係に縛られることなく任意の要素へ紐づけられる点が最大の特徴です。
これまでツールチップやポップオーバー、ドロップダウンメニューをボタンの近くに正確に表示させるには、JavaScriptでの座標計算が実質的に必須でした。典型的な処理は次のようなものです。
// 従来のJavaScriptによる位置計算の例
const button = document.querySelector('.trigger');
const tooltip = document.querySelector('.tooltip');
function updateTooltipPosition() {
const rect = button.getBoundingClientRect();
const tooltipRect = tooltip.getBoundingClientRect();
// ビューポート下端にはみ出す場合は上に表示を切り替える
const spaceBelow = window.innerHeight - rect.bottom;
const showAbove = spaceBelow < tooltipRect.height + 8;
tooltip.style.left = `${rect.left + window.scrollX}px`;
tooltip.style.top = showAbove
? `${rect.top + window.scrollY - tooltipRect.height - 8}px`
: `${rect.bottom + window.scrollY + 8}px`;
}
window.addEventListener('scroll', updateTooltipPosition);
window.addEventListener('resize', updateTooltipPosition);
button.addEventListener('mouseenter', updateTooltipPosition);
getBoundingClientRect()で座標を取得し、スクロールやリサイズのたびに再計算し、画面端でのはみ出しを検知して表示位置を切り替える——この一連の処理を自前で実装していたエンジニアは少なくないはずです。ライブラリを使えば楽にはなりますが、依存が増えるうえバンドルサイズも膨らみます。
CSS Anchor Positioningを使うと、同じ挙動が次のように宣言的なCSSへ置き換わります。
.trigger {
anchor-name: --my-button;
}
.tooltip {
position: absolute;
position-anchor: --my-button;
position-area: bottom;
margin-top: 8px;
}
JavaScriptによるイベント監視や座標計算は一切登場しません。スクロールやリサイズへの追従、画面端でのはみ出し回避まで、ブラウザのレンダリングエンジンが面倒を見てくれます。DOM構造上どこに配置されていても、CSSの識別子だけでアンカーとターゲットを結び付けられる設計になっているため、Reactやモーダル用のポータルのようにターゲット要素をbody直下に描画するケースとも相性が良好です。
CSS Anchor Positioningを採用するメリット
実務目線で見たときのメリットは、単に「JSが要らなくなる」という以上のものがあります。
パフォーマンス面の優位性
位置計算がブラウザのレイアウトエンジン内で完結するため、scrollやresizeイベントに紐づくJavaScriptの再計算処理が不要になります。メインスレッドの負荷が下がり、特にモバイル端末での描画コストを抑えやすくなります。
レイアウトの破綻に強い
position-areaやposition-try(フォールバック機能)を組み合わせることで、画面端での自動反転や位置調整をCSSの記述だけで実現できます。JavaScript側で「はみ出し判定」のロジックを書き直す必要がありません。
DOM構造から解放される配置指定
ターゲット要素は必ずしもアンカー要素の子孫や兄弟である必要がありません。ポータルレンダリングされたモーダルやポップオーバーでも、anchor-nameとposition-anchorのペアさえ一致していれば正しく紐づきます。
保守性の向上
位置計算のロジックがCSSファイル内に集約されるため、デザイン調整の際にJavaScriptを触る必要がなくなります。デザイナーとエンジニアの分業もしやすくなります。
ネイティブのPopover APIとの親和性
popover属性やTop Layerと組み合わせることで、z-indexの管理に頭を悩ませることなく確実に最前面へ表示できます。
一方でブラウザサポートは急速に普及が進んでいます。基本機能は2026年1月時点でBaseline「Newly available」(主要ブラウザの最新版で利用可能)となり、Chrome 125以降、Edge 125以降、Firefox、Safariなどの主要ブラウザで順次対応が完了しました。ただし、Anchor Positioning API Level 2にあたる高度な機能(@containerとの連携によるフォールバック値の検出など)は、執筆時点ではChromium系ブラウザでの先行実装に限定されています。そのため、本番環境で使う際は@supports (anchor-name: --x)によるフィーチャーディテクションと、非対応ブラウザ向けのフォールバックを併用する設計が現実的です。
position: absolute・fixed・transformとの違い
CSS Anchor Positioningは新しいpositionの値ではなく、既存のposition: absoluteまたはposition: fixedを拡張する仕組みとして設計されています。混同されがちな各手法との違いを整理すると次のようになります。
| 手法 | 配置の基準 | DOM構造の制約 | スクロール追従 | はみ出し対応 |
|---|---|---|---|---|
position: absolute(従来) | 直近の位置指定祖先要素 | 祖先・子孫関係が前提 | 基準要素とセットで動く | 手動でJS実装が必要 |
position: fixed | ビューポート | なし | 追従しない(固定) | 手動でJS実装が必要 |
CSSのtransformによるズレ調整 | 自身の初期位置 | 制約なし | 追従しない | 数値をハードコーディングしがち |
| CSS Anchor Positioning | 任意のアンカー要素 | 祖先・子孫関係が不要 | ブラウザが自動追従 | position-tryで自動フォールバック |
従来のposition: absoluteは「最も近い位置指定された祖先要素」を基準にするため、モーダルやツールチップをbody直下にポータル配置すると基準がずれてしまい、結局JavaScriptで座標を計算し直す羽目になっていました。position: fixedはビューポート基準で扱いやすい反面、アンカー要素がスクロールしても追従してくれません。transform: translate()でオフセットを当てる手法も、要素サイズが可変になった瞬間にズレが生じやすく、レスポンシブ対応との相性が良いとは言えませんでした。
CSS Anchor Positioningでは、anchor-nameで名前を付けたアンカー要素に対して、ターゲット要素側がposition-anchorで参照するだけで、DOM上の位置関係を問わず配置の基準を確立できます。基準となる座標系はブラウザが自動的に追従・再計算するため、開発者が手を動かすのは初期のCSS宣言だけで済みます。次章では、このanchor-nameとposition-anchorの具体的な指定方法から、実際に位置を決めるanchor()関数やposition-areaの使い方まで踏み込んでいきます。
CSS Anchor Positioningの基本的な使い方

anchor-name の役割と正しい指定方法
anchor-nameは、要素を「アンカー」として登録するためのプロパティです。値には--から始まるカスタム識別子(<dashed-ident>)を指定します。CSS変数と同じ構文ルールですが、あくまでアンカー専用の名前空間として扱われる点に注意してください。
.trigger-button {
anchor-name: --my-button;
}
1つの要素に複数の名前を与えることも可能です。カンマ区切りで並べれば、後述するposition-anchorやanchor()から異なる用途ごとに参照名を使い分けられます。
.card {
anchor-name: --card-anchor, --highlight-target;
}
指定にあたって押さえておきたい制約がいくつかあります。
anchor-nameが効くのは「位置指定された要素」だけではありません。
通常のstatic配置の要素にも付与できますが、実際にアンカーとして機能するのは、その要素がレンダリングツリー上でボックスを生成している場合に限られます。display: noneの要素はアンカーになれません。
同じ要素に同じ名前を重複させても意味がありません。
カンマで並べる場合は異なる名前を割り当てるのが基本です。
疑似要素にもアンカー名を付けられます。
::beforeや::afterをアンカーにしたいケースでは、対象の疑似要素セレクタに対してanchor-nameを宣言します。
.item::after {
anchor-name: --item-badge;
content: "";
}
命名規則としては、コンポーネント名や役割が分かるプレフィックスを付けておくと、大規模なプロジェクトでも名前の衝突を避けやすくなります。--tooltip-trigger-01のような機械的な命名より、--user-menu-triggerのように意味を持たせておくほうが、後からコードを読む際に迷いません。
position-anchor の役割と anchor-name との違い
anchor-nameが「基準となる側」を宣言するプロパティであるのに対し、position-anchorは「基準を参照する側(ターゲット要素)」に指定するプロパティです。両者は表裏一体の関係にあり、片方だけでは機能しません。
.tooltip {
position: absolute;
position-anchor: --my-button;
}
position-anchorで指定した識別子は、そのターゲット要素におけるデフォルトのアンカーとして扱われます。この「デフォルト」という位置づけが重要で、position-areaやposition-try、引数なしのanchor()・anchor-size()は、明示的な参照先が書かれていない限り自動的にこのデフォルトアンカーを基準に計算されます。
両者の役割を整理すると次のようになります。
| プロパティ | 付与する要素 | 役割 | 値の型 |
|---|---|---|---|
anchor-name | アンカー要素(基準側) | 自身に名前を付け、参照可能にする | <dashed-ident># |
position-anchor | ターゲット要素(配置される側) | 参照するアンカーを指定し、デフォルトに設定する | <dashed-ident> |
なお、position-anchorを指定しなくても、そのターゲット要素の暗黙のアンカーが存在するケースがあります。代表的なのがpopover属性とinvoketarget(旧popovertarget系の連携)で紐づけられたポップオーバー要素で、これらはHTML側の関連付けだけでアンカー関係が自動的に成立する仕様になっています。ただし挙動を明示的に制御したい場合は、position-anchorで名前を書いておくほうが意図が伝わりやすく、後から読むメンバーにも親切です。
position-anchorが効くのは、ターゲット要素がposition: absoluteまたはposition: fixedのときだけです。staticやrelativeのままでは無視されるため、配置方法の指定漏れには気を付けてください。
anchor()関数の基本:top・bottomなど各方向への配置指定
anchor()関数は、アンカー要素の特定の辺や位置を数値として取り出し、top・right・bottom・leftなどの配置プロパティへ渡すための関数です。従来JavaScriptでgetBoundingClientRect()を呼んで手計算していた座標を、CSSの関数一発で参照できるようになります。
基本構文は次の通りです。
anchor( [ <anchor-name> || <anchor-side> ]? , <length-percentage>? )第一引数にアンカー名を書けば、position-anchorで設定したデフォルトのアンカーとは別のアンカーを個別に参照できます。省略した場合はデフォルトアンカーが使われます。第二引数はフォールバック値で、対象のアンカー関係が無効になった際に使われる保険の数値です。
.trigger {
anchor-name: --my-button;
}
.tooltip {
position: absolute;
position-anchor: --my-button;
top: anchor(bottom); /* アンカーの下端に合わせる */
left: anchor(left); /* アンカーの左端に合わせる */
margin-top: 8px;
}
<anchor-side>に指定できる主な値は以下の通りです。
| キーワード | 意味 |
|---|---|
top / bottom / left / right | アンカーの各辺 |
start / end | 書字方向を考慮した論理的な開始・終了辺 |
center | 該当軸の中央 |
<percentage> | アンカーのボックスを0%〜100%で内分した位置 |
anchor()はinset系プロパティ(top、right、bottom、left、および対応する論理プロパティ)にしか使えません。widthやpaddingのようなサイズ系プロパティに渡すとその宣言は無効になるため、サイズを揃えたい場合は次項のanchor-size()を使う必要があります。
異なるアンカーを複数参照するケースでは、名前を明示的に渡すことで柔軟な組み合わせが可能になります。
.range-highlight {
position: absolute;
top: anchor(--start-marker top);
bottom: anchor(--end-marker bottom);
}
position-areaの役割と直感的なエリア指定によるレイアウト
anchor()は辺ごとの数値計算に向いていますが、「アンカーの右上に出したい」「下側中央に出したい」といった大まかな配置には、position-areaのほうが圧倒的に書きやすくなっています。
position-areaは、アンカー要素を中心とした3×3のグリッドを暗黙的に想定し、ターゲット要素をそのどのマスに置くかをキーワードで指定する仕組みです。中央のマスがアンカー自身の領域にあたります。
.tooltip {
position: absolute;
position-anchor: --my-button;
position-area: bottom; /* アンカーの真下 */
}
.tooltip--top-right {
position-area: top right; /* アンカーの右上 */
}
.tooltip--start {
position-area: block-end span-inline-start; /* 論理方向での指定 */
}
主なキーワードの組み合わせを一覧にすると理解が早くなります。
| position-area の値 | 配置イメージ |
|---|---|
top | アンカーの真上、水平方向は中央揃え |
bottom | アンカーの真下、水平方向は中央揃え |
left | アンカーの左側、垂直方向は中央揃え |
right | アンカーの右側、垂直方向は中央揃え |
top left | 左上に配置 |
bottom right | 右下に配置 |
center | アンカーと重なる中央配置 |
span-all | 該当軸のマス全体にまたがる配置 |
inset系プロパティを1つずつ書き並べる必要がなく、意図が一目で伝わるのがposition-areaの強みです。またキーワードによる指定であるため、writing-modeが縦書きに変わった場合でも論理方向のキーワード(block-startやinline-endなど)を使っておけば、レイアウトの反転に自動で追従してくれます。
ターゲット要素の自己整列(align-self・justify-self)はposition-areaの値に応じて既定値が変化する仕様になっているため、微調整したい場合はこれらのプロパティを明示的に上書きするとよいでしょう。
anchor-size()を利用したアンカー要素に対するサイズ連動と制御
anchor-size()関数は、anchor()と似た構文を持ちながら、参照する対象がアンカーの辺の位置ではなくサイズである点が異なります。ドロップダウンの幅をトリガーボタンの幅に揃えたい、といったケースで使います。
anchor-size( [ <anchor-name> || <anchor-size> ]? , <length-percentage>? )<anchor-size>に指定できるキーワードはwidth・height・block・inlineです。
.select-trigger {
anchor-name: --select-trigger;
}
.select-dropdown {
position: absolute;
position-anchor: --select-trigger;
position-area: bottom;
width: anchor-size(width); /* トリガーと同じ幅に揃える */
min-width: anchor-size(width, 200px); /* フォールバック付き */
}
anchor-size()はwidth・height・min-width・max-width・min-height・max-heightなど、サイズに関わるプロパティで使用できます。calc()と組み合わせれば、アンカーサイズを基準にした余白の調整も自然に書けます。
.popover {
width: calc(anchor-size(width) + 16px);
}
サイズ連動と位置連動を組み合わせることで、トリガー要素の見た目が変わってもターゲット要素が追従するUIを、JavaScriptによるリサイズ監視なしに実現できます。次章では、ここまで紹介したanchor-name・position-anchor・anchor()・position-area・anchor-size()を組み合わせて、実務で頻出するUIコンポーネントを具体的に組み立てていきます。
CSS Anchor Positioningを利用したUIコンポーネント実装集
吹き出し・ホバーカードの実装(position-try活用)
ツールチップや吹き出しでよくある悩みが、画面端でアンカーの下に表示しきれず要素が切れてしまう問題です。これを解決するのがposition-try-fallbacksプロパティ(およびposition-try-orderとのショートハンドであるposition-try)です。第一候補の配置がビューポートからはみ出す場合、あらかじめ用意した候補の中から収まるものへ自動的に切り替わります。
<button class="trigger" popovertarget="tooltip-01">詳細を見る</button>
<div class="tooltip" id="tooltip-01" popover>
ここに補足説明のテキストが入ります。
</div>.trigger {
anchor-name: --info-trigger;
}
.tooltip {
position: fixed;
position-anchor: --info-trigger;
position-area: bottom;
margin-top: 8px;
/* 下に入りきらない場合は上→左→右の順で候補を試す */
position-try-fallbacks: flip-block, flip-inline, flip-start;
padding: 8px 12px;
border-radius: 8px;
background: #1f2937;
color: #fff;
font-size: 14px;
max-width: 240px;
}
実際の表示
See the Pen ancho-positioning-01 by watashi-xyz (@watashi-xyz) on CodePen.
flip-blockやflip-inlineは仕様側で用意された組み込みキーワードで、それぞれブロック方向・インライン方向を反転させた候補を自動生成してくれます。独自の配置パターンを候補に加えたい場合は、@position-try規則でカスタムの候補を定義できます。
@position-try --custom-left {
position-area: left;
margin-right: 8px;
margin-top: 0;
}
.tooltip {
position-try-fallbacks: --custom-left, flip-block;
}
ホバーカードのように表示に若干の猶予を持たせたいUIでは、popover属性のbeforetoggleイベントと組み合わせてフェードインの制御を加えるのが実務では定番です。表示・非表示の遷移自体はCSSのtransition-behavior: allow-discreteと@starting-styleで完結させれば、こちらもJavaScriptを増やさずに済みます。
.tooltip {
opacity: 0;
transition: opacity 0.15s ease, display 0.15s allow-discrete;
}
.tooltip:popover-open {
opacity: 1;
}
@starting-style {
.tooltip:popover-open {
opacity: 0;
}
}
階層メニュー・メガメニューの実装
ナビゲーションの各項目に対してサブメニューをアンカー配置すれば、メガメニューの実装からJavaScriptの座標計算をほぼ排除できます。ポイントは、親メニュー項目ごとに個別のanchor-nameを割り当てることです。
<nav class="global-nav">
<ul>
<li>
<button class="nav-item" popovertarget="mega-products" style="anchor-name: --nav-products">
製品
</button>
<div class="mega-menu" id="mega-products" popover>
<div class="mega-menu__column">
<h3>クラウド</h3>
<a href="#">プラン一覧</a>
<a href="#">料金</a>
</div>
<div class="mega-menu__column">
<h3>サポート</h3>
<a href="#">ドキュメント</a>
<a href="#">お問い合わせ</a>
</div>
</div>
</li>
</ul>
</nav>
.mega-menu {
position: fixed;
position-anchor: --nav-products;
position-area: bottom span-inline-end;
position-try-fallbacks: flip-inline;
gap: 32px;
padding: 24px;
min-width: anchor-size(width, 480px);
border-radius: 12px;
box-shadow: 0 12px 32px rgb(0 0 0 / 0.15);
}
.mega-menu:popover-open {
display: flex;
}position-area: bottom span-inline-endのように「アンカーの下」かつ「インライン終端方向にまたがる」指定を組み合わせると、メニュー項目が画面の左寄りにあってもメガメニューの本体が右側へ広がって画面外にはみ出しにくくなります。ナビゲーション項目が横並びで複数存在する場合、各項目に一意なanchor-nameをインラインスタイルまたはCSSクラスで割り当てる運用にしておくと、Reactなどコンポーネント側でのID管理とも噛み合わせやすくなります。
実際の表示
See the Pen anchor-positioning-mega-menu by watashi-xyz (@watashi-xyz) on CodePen.
日付ピッカー・カラーピッカーへの応用
日付ピッカーやカラーピッカーは、入力欄の直下にパネルを開きつつ、パネル自体の幅を入力欄に揃えたいという要件が重なりやすいUIです。anchor-size()とposition-areaを併用すると、この2つの要件を同時に満たせます。
<div class="date-picker">
<label for="date-input">日付を選択</label>
<!-- アンカーとなる Input 要素 -->
<input
type="text"
id="date-input"
class="date-input"
value="2026-08-11"
readonly
aria-haspopup="dialog"
placeholder="YYYY-MM-DD"
/>
<!-- Popover パネル -->
<div class="date-panel" id="date-panel" popover>
<div class="calendar-header">
<span>2026年 8月</span>
</div>
<div class="calendar-grid">
<!-- 8月1日(土)からのダミーカレンダー -->
</div>
</div>
</div>body {
display: grid;
place-items: center;
height: 100svh;
}
/* フィールド全体のコンテナ */
.date-picker {
display: inline-flex;
flex-direction: column;
gap: 6px;
}
/* アンカー要素(Input) */
.date-input {
anchor-name: --date-input-anchor; /* アンカー名の定義 */
width: 200px;
padding: 8px 12px;
font-size: 14px;
border: 1px solid #d1d5db;
border-radius: 6px;
background-color: #fff;
cursor: pointer;
outline: none;
&:focus {
border-color: #2563eb;
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
}
}
/* Popover パネル(アンカー配置の対象) */
.date-panel {
/* --- Anchor Positioning の指定 --- */
position: fixed;
position-anchor: --date-input-anchor;
/* input の真下に配置、左端を揃える */
position-area: bottom center;
/* 画面下部に収まらない場合は自動的に上に反転 */
position-try-fallbacks: flip-block;
/* --- サイズ指定 --- */
width: 200px;
/* --- Popover のデフォルトスタイルの打ち消し --- */
margin: 0;
margin-top: 6px; /* input との隙間 */
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 16px;
background: #ffffff;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
/* パネル内のレイアウト設定 */
color: #1f2937;
}
/* カレンダーUIのスタイリング */
.calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-weight: 600;
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 4px;
text-align: center;
font-size: 12px;
}
.day-name {
color: #6b7280;
font-weight: 600;
padding-bottom: 4px;
}
.day-btn {
aspect-ratio: 1;
border: none;
background: none;
border-radius: 50%;
cursor: pointer;
font-size: 13px;
color: #374151;
&:hover {
background-color: #f3f4f6;
}
&.selected {
background-color: #2563eb;
color: #fff;
font-weight: 600;
}
}const input = document.getElementById('date-input');
const panel = document.getElementById('date-panel');
// 1. Input のクリック/フォーカス時に Popover を安全に表示
const showPicker = () => {
if (!panel.matches(':popover-open')) {
panel.showPopover();
}
};
input.addEventListener('click', showPicker);
input.addEventListener('focus', showPicker);
// 2. 日付ボタン選択時の挙動(値を更新して自動で閉じる)
panel.addEventListener('click', (e) => {
const btn = e.target.closest('.day-btn');
if (!btn) return;
// 選択クラスの切替
panel.querySelectorAll('.day-btn').forEach(b => b.classList.remove('selected'));
btn.classList.add('selected');
// 値のセットと閉じる処理
const day = btn.textContent.padStart(2, '0');
input.value = `2026-08-${day}`;
panel.hidePopover();
});実際の表示
See the Pen anchor-positioning-picker by watashi-xyz (@watashi-xyz) on CodePen.
カラーピッカーのようにパネルが正方形に近い固定サイズを取りたい場合は、widthではなくmin-width: anchor-size(width)にとどめておくほうが、極端に幅の狭い入力欄でもパネル内のUIが窮屈にならずに済みます。
.color-panel {
position: fixed;
position-anchor: --color-input;
position-area: bottom;
min-width: anchor-size(width, 240px);
}トースト・通知・バッジを特定要素へ追従表示する方法
通知バッジのように、特定のアイコンやアバターの右上に小さな要素を常時追従させたいケースにもAnchor Positioningは向いています。position-areaのマス目指定とinsetの微調整を組み合わせることで、要素の一部だけをアンカーの外側にはみ出させる、いわゆる「バッジ配置」が素直に書けます。
保存ボタンに追従するトースト
<button id="save" class="save-button">
保存
</button>
<div id="toast" class="toast">
保存しました
</div> body {
display: grid;
place-items: center;
height: 100svh;
}
.save-button {
anchor-name: --save-button;
}
.toast {
position: fixed;
position-anchor: --save-button;
position-area: bottom center;
min-width: 8em;
text-align: center;
margin-top: 12px;
padding: 12px 18px;
color: white;
background: #222;
border-radius: 8px;
box-shadow: 0 8px 30px rgba(0,0,0,.2);
opacity: 0;
visibility: hidden;
transform: translateY(-8px);
transition:
opacity .25s,
transform .25s,
visibility .25s;
}
.toast.show {
opacity: 1;
visibility: visible;
transform: translateY(0);
}const button = document.querySelector("#save");
const toast = document.querySelector("#toast");
button.addEventListener("click", () => {
toast.classList.add("show");
clearTimeout(toast.timer);
toast.timer = setTimeout(() => {
toast.classList.remove("show");
}, 2500);
});実際の表示
See the Pen anchor-positioning-toast by watashi-xyz (@watashi-xyz) on CodePen.
通知アイコンに追従する通知パネル
<button class="notification-button">
🔔
</button>
<div class="notification-panel">
<ul>
<li>コメントが追加されました</li>
<li>更新があります</li>
<li>新しいメッセージがあります</li>
</ul>
</div>
body {
display: grid;
place-items: center;
}
.notification-button{
anchor-name: --notification;
}
.notification-panel{
position: fixed;
position-anchor: --notification;
position-area: bottom center;
background: white;
border-radius: 12px;
border: 1px solid #ddd;
min-width: 220px;
padding: 10px 15px;
box-shadow: 0 12px 40px rgba(0,0,0,.18);
margin: 0;
margin-top: 8px;
font-size: 14px;
& > ul {
padding: 0;
margin: 0;
list-style-position: inside;
}
}実際の表示
See the Pen anchor-positioning-notice by watashi-xyz (@watashi-xyz) on CodePen.
プロフィール画像に追従するオンラインバッジ
<div class="avatar">
<img src="https://placehold.net/avatar-5.png" alt="">
</div>
<div class="online-badge"></div>
body {
display: grid;
place-items: center;
min-height: 100svh;
}
.avatar{
anchor-name: --avatar;
width: 120px;
}
.avatar img{
width:100%;
border-radius:50%;
}
.online-badge{
position:fixed;
position-anchor: --avatar;
position-area: top right;
translate: 30% 30%;
width:18px;
height:18px;
background:#00c853;
border-radius:50%;
border:3px solid white;
}実際の表示
See the Pen anchor-positioning-badge by watashi-xyz (@watashi-xyz) on CodePen.
トースト通知の場合は、アンカーとなる要素をページ内の固定領域(通知アイコンなど)に置き、position-try-fallbacksで画面端での反転候補を用意しておくと、通知が積み重なるレイアウトでも位置崩れを起こしにくくなります。複数のトーストを同時に表示する場合は、CSSのAnchor Positioningだけでスタック管理までは行えないため、表示順序やスタッキングのロジックは引き続きJavaScript側で持たせる設計が現実的です。位置決めの計算だけをCSSに委譲する、という役割分担で捉えておくとよいでしょう。
コンテキストメニューをAnchor Positioningで再現する
右クリックで表示するコンテキストメニューは、クリックされた座標を基準に表示位置を決める必要があるため、従来はcontextmenuイベントのclientX・clientYを使ってJavaScript側で位置を組み立てるのが一般的でした。Anchor Positioningでは、クリック位置に応じて動的に生成する仮想アンカーと組み合わせることで、この処理をシンプルにできます。
<!-- 仮想アンカー -->
<div
class="virtual-anchor"
aria-hidden="true">
</div>
<!-- コンテキストメニュー -->
<div
class="context-menu"
popover="auto">
<button type="button">📄 コピー</button>
<button type="button">📋 貼り付け</button>
<button type="button">✂️ 切り取り</button>
<hr>
<button type="button">🔗 リンクを開く</button>
<button type="button">🗑️ 削除</button>
</div>
const menu = document.querySelector('.context-menu');
const virtualAnchor = document.querySelector('.virtual-anchor');
document.addEventListener('contextmenu', (e) => {
e.preventDefault();
// クリック位置に1px四方の仮想アンカーを移動させる
virtualAnchor.style.left = `${e.clientX}px`;
virtualAnchor.style.top = `${e.clientY}px`;
menu.showPopover();
});
.virtual-anchor {
position: fixed;
anchor-name: --context-point;
width: 1px;
height: 1px;
pointer-events: none;
}
.context-menu {
position: fixed;
position-anchor: --context-point;
position-area: bottom span-inline-end;
position-try-fallbacks: flip-block, flip-inline;
margin: 4px 0 0 4px;
min-width: 180px;
padding: 4px;
border-radius: 8px;
background: #fff;
box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
}
実際の表示
See the Pen anchor-positioning-contextmenu by watashi-xyz (@watashi-xyz) on CodePen.
座標の追跡自体はイベントで受け取る必要がありますが、メニュー本体の「画面端でどう反転させるか」というロジックはすべてposition-try-fallbacksに任せられます。仮想アンカーを1px四方の透明な要素として用意しておくやり方は、Anchor Positioningの仕様上想定されている使い方のひとつで、座標ベースのUIとアンカーベースのUIを橋渡しする実践的なテクニックです。
CSS Anchor Positioningで起こりやすい問題とデバッグ方法
transform・contain・overflowが配置へ与える影響
Anchor Positioningを組み込んだ直後によく遭遇するのが、「CSSの記述は合っているはずなのに位置がずれる」「表示されるべき要素が消える」という現象です。原因の多くは、アンカー要素またはターゲット要素の祖先に存在するプロパティにあります。
transformが祖先要素に指定されているケース
transform(translateやscaleなど、none以外の値)を持つ要素は、CSSの仕様上、子孫のposition: fixed要素に対する新たな包含ブロックになります。これは今に始まった話ではなく従来から存在する仕様ですが、Anchor Positioningと組み合わせたときに影響が顕在化しやすくなっています。ターゲット要素をposition: fixedで配置しているのに、祖先のどこかにtransformが挟まっていると、ビューポート基準ではなくその祖先基準の座標系に切り替わってしまい、想定した位置からずれます。
/* この transform が原因で fixed の基準がビューポートでなくなる */
.card-wrapper {
transform: translateZ(0); /* GPUレイヤー化のために付けがちな一行 */
}
対処法としては、position: fixedをやめてposition: absoluteに切り替えるか、transformを持つ祖先の外側にターゲット要素をポータルレンダリングする設計に変更するかのいずれかになります。アニメーション用にwill-change: transformだけを付けている場合も同様の挙動を引き起こすため注意してください。
containプロパティによる包含ブロックの変化
contain: layout・contain: paint・contain: strict・contain: contentのいずれかが指定された要素も、position: fixedの子孫にとって新たな包含ブロックになります。パフォーマンス最適化の目的でcontainを広く適用しているコンポーネント設計だと、意図せずAnchor Positioningの基準がずれる原因になりがちです。
overflowによるクリッピング
アンカー要素がoverflow: hiddenやoverflow: autoを持つコンテナの内側にある場合、ターゲット要素自体はそのコンテナの外に飛び出して表示されますが、スクロール位置によってはアンカーがコンテナの外に隠れた瞬間、ターゲット要素も追従して非表示になる挙動が仕様で定義されています。これは「スクロールで見えなくなったアンカーに紐づく要素は一緒に隠す」という設計思想によるもので、バグではなく仕様通りの挙動です。意図的にこの挙動を止めたい場合は、position-visibility: alwaysを明示的に指定します。
.tooltip {
position: fixed;
position-anchor: --my-button;
position-visibility: always; /* アンカーが見切れても表示を維持 */
}
逆に、アンカーが完全にビューポート外へ出た場合まで表示を維持したくないケースが大半のはずなので、既定値であるanchor-center寄りの挙動(アンカーが見えている間だけ表示)を崩さない設計のほうが無難です。
z-index・Stacking Context・Top Layerとの関係
ツールチップやメガメニューが他の要素の下に隠れてしまうトラブルも定番です。原因の切り分けは、そのターゲット要素がTop Layerに属しているかどうかで大きく変わります。
popover属性を持つ要素、あるいは<dialog>要素は、開いている間ブラウザの管理するTop Layerへ昇格します。Top Layerは通常のスタッキングコンテキストの外側に存在するレイヤーで、z-indexの数値に関係なく常に最前面に描画されます。したがって、popoverと組み合わせてAnchor Positioningを使っている場合、z-indexの調整はほぼ不要です。
<div class="tooltip" popover>...</div>一方で、popover属性を使わず通常のposition: absolute要素としてツールチップを実装している場合は、従来どおりスタッキングコンテキストのルールがそのまま適用されます。祖先要素にz-indexを持つ位置指定要素や、opacityが1未満の要素、filterが指定された要素などがあると、その時点で新しいスタッキングコンテキストが生まれ、子孫のz-indexがそのコンテキスト内に閉じ込められてしまいます。
対応の優先順位としては次のように考えると整理しやすくなります。
- 最優先:可能な限り
popover属性(またはネイティブダイアログ)を使い、Top Layerに任せてz-index問題そのものを回避する - 次善策:
popoverが使えない設計上の制約がある場合は、ターゲット要素をなるべくスタッキングコンテキストの浅い位置(bodyの直下など)に配置し直す - 最終手段:
z-indexの数値を場当たり的に大きくする対応は、別のコンポーネントとの競合を招きやすいため避ける
Anchor Positioning自体はスタッキングの仕組みに直接手を加えるものではないため、位置がずれる問題と重なりの問題は切り分けて調査するとデバッグが早く終わります。
DevToolsでアンカーの関連付けと配置状態を確認する方法
Chrome DevToolsには、Anchor Positioningのデバッグに特化した機能が用意されています。手順は次の通りです。
- Elementsパネルでターゲット要素を選択する
position-anchorが指定された要素をDOMツリーで選択すると、Stylesペインの該当プロパティの隣に小さなアイコンが表示されます。 - アンカーバッジをクリックして紐づいた要素へジャンプする
Stylesペインに表示されるanchor-nameやposition-anchorの値の横にあるバッジをクリックすると、対応するアンカー要素またはターゲット要素へDOMツリー上でジャンプできます。どの要素とどの要素が紐づいているかを目視でたどる手間が省けます。 - キャンバス上でアンカーのオーバーレイを確認する
対象要素を選択した状態でElementsパネル右側の「Layout」タブを開くと、Grid・Flexboxのオーバーレイと同様に、Anchorのオーバーレイ表示を切り替えられます。有効にすると、アンカー要素の位置を示す線と、ターゲット要素がどのマス(position-areaのグリッド)に配置されているかがキャンバス上に可視化されます。 position-try-fallbacksの適用状況を確認する
複数の候補を指定している場合、Stylesペインには実際にどの候補が採用された結果として現在のレイアウトになっているかが表示されます。ビューポートのサイズをDevTools上で変えながら、どの時点でフォールバックが切り替わるかを確認すると、画面端での挙動を実装段階で潰し込めます。- Computed タブで最終的な
insetの実値を確認するanchor()やposition-areaはあくまで計算前の宣言であり、最終的にブラウザがどのピクセル値へ解決したかはComputedタブで確認するのが確実です。想定と違う値が入っている場合、参照しているアンカー名の指定ミスや、前述のtransform・containによる包含ブロックのずれを疑う手がかりになります。
Firefoxの開発者ツールでも同様にレイアウトタブからアンカー関連のオーバーレイを確認できますが、機能の充実度としては現時点でChrome DevTools側が先行しています。複数ブラウザでの検証時は、まずChrome DevToolsで意図した挙動を固めてから、他ブラウザでの見た目の差分を確認する進め方が効率的です。
格安ドメイン取得サービス─ムームードメイン─よくある質問 (FAQ)
-
CSS Anchor Positioningはどのブラウザで使えますか?
-
2026年1月時点でBaseline「Newly available」に到達しており、Chrome 125以降、Edge 125以降、Firefox、Safariをはじめとする主要ブラウザの最新版で基本機能が利用可能です。ただし、
@position-try規則の一部や高度なフォールバック挙動など、Anchor Positioning Level 2に含まれる機能はChromium系ブラウザが先行しており、FirefoxやSafariでの対応状況は機能のバージョンによって差があります。本番投入前にCan I Useや各ブラウザのリリースノートで最新の対応状況を確認しておくと安心です。
-
非対応ブラウザではどうなりますか?フォールバックは必要ですか?
-
anchor-nameやposition-anchorは非対応ブラウザでは単純に無視されるプロパティです。致命的なエラーにはなりませんが、position: absoluteやfixedはそのまま適用されるため、アンカー基準の位置指定なしで意図しない場所に要素が表示される可能性があります。@supports (anchor-name: --x)でフィーチャーディテクションを行い、非対応ブラウザ向けには従来のJavaScriptによる位置計算やCSSでの固定配置を用意しておく設計が現実的です。@supports not (anchor-name: --x) { .tooltip { /* 非対応ブラウザ向けのフォールバック配置 */ position: absolute; top: 100%; left: 0; } }
-
Floating UIなどの既存ライブラリは不要になりますか?
-
対応ブラウザのみをターゲットにできるプロジェクトであれば、シンプルなツールチップやドロップダウンの位置計算はCSSだけで完結し、Floating UIのようなライブラリへの依存を減らせます。ただし、非対応ブラウザへの配慮が必要な場合や、仮想要素を動的に生成する複雑な座標計算、衝突検出のきめ細かい制御が求められる場合は、引き続きライブラリの併用が現実的な選択肢になります。ライブラリ側もAnchor Positioningを内部で活用する形へ移行が進んでおり、今後は「対応ブラウザではネイティブ機能、非対応ブラウザではJS実装」という出し分けを自動化する流れが主流になっていくと見られます。
-
Popover APIとAnchor Positioningの関係を教えてください
-
両者は別々の仕様ですが、組み合わせて使うことを前提に設計されています。
popover属性はTop Layerへの昇格や表示・非表示のトグル制御を担い、Anchor Positioningはその要素をどこに配置するかを担います。役割が分かれているため、Anchor Positioningだけを単独で使い、表示制御は独自のクラス切り替えで行うという構成も問題なく成立します。
-
position-tryとposition-try-fallbacksは同じものですか?
-
仕様の策定過程で名称が整理され、フォールバックの候補リストを指定するプロパティは
position-try-fallbacksに統一されました。position-tryはposition-try-orderとposition-try-fallbacksをまとめて指定できるショートハンドとして位置づけられています。過去の記事や情報源によっては旧名称のposition-tryが単独プロパティとして紹介されている場合があるため、実装時は最新のMDNやCSSWG Draftの表記に合わせて記述してください。
-
anchor-name は1つの要素に何個まで指定できますか?
-
仕様上の上限はなく、カンマ区切りで複数の識別子を並べられます。ただし同じ要素に大量の名前を割り当てると、どの名前がどの用途で参照されているか追いにくくなるため、実務では役割ごとに1〜2個程度に留めておくと保守しやすくなります。
-
アンカー要素とターゲット要素は同じ祖先を共有している必要がありますか?
-
必要ありません。Anchor Positioningの大きな利点のひとつが、DOM構造上の親子関係や兄弟関係に縛られない点にあります。ターゲット要素がbody直下にポータルレンダリングされていても、
anchor-nameとposition-anchorの識別子さえ一致していれば正しく紐づきます。ただし、ターゲット要素の包含ブロックとして機能する祖先要素にtransformやcontainが指定されていると挙動が変わるため、その点は別途注意が必要です。
-
SEOやアクセシビリティへの影響はありますか?
-
Anchor Positioningはあくまで視覚的な配置を決めるCSSの仕組みであり、DOM構造やHTMLのセマンティクスには影響しません。検索エンジンのクローリングやスクリーンリーダーの読み上げ順序は、これまで通りDOM上の順序に従います。むしろJavaScriptでの動的な要素生成や位置計算を減らせる分、実装のシンプルさによって不具合の起きにくいマークアップを保ちやすくなる側面があります。
まとめ
CSS Anchor Positioningは、ツールチップやドロップダウン、メガメニューといったUIの位置計算を、JavaScriptの座標処理からCSSの宣言的な記述へと置き換える仕様です。anchor-nameでアンカー側に名前を付け、position-anchorでターゲット側から参照し、anchor()やposition-areaで具体的な配置を決める——この基本的な流れさえ押さえれば、getBoundingClientRect()によるイベント監視やスクロール追従のための再計算処理は不要になります。
position-try-fallbacksを組み合わせれば、画面端でのはみ出しを検知して配置を自動的に反転させる処理までブラウザ側に任せられます。従来はライブラリの導入が半ば前提だった衝突検出付きのポップオーバー実装が、素のCSSだけで完結する場面は今後さらに増えていくはずです。
一方で、ブラウザサポートは2026年1月時点でBaseline「Newly available」に達したばかりの段階にあり、Level 2にあたる高度な機能はブラウザ間で対応差が残っています。祖先要素のtransformやcontainが包含ブロックに与える影響、overflowによるアンカーのクリッピング挙動など、押さえておくべき仕様上の癖もいくつか存在します。本番投入にあたっては@supportsによるフィーチャーディテクションとフォールバック設計を欠かさず、Chrome DevToolsのアンカー可視化機能を使いながら挙動を一つずつ確認していく進め方が着実です。
実装対象のプロジェクトがモダンブラウザに限定できるものであれば、まずは小規模なツールチップやバッジ表示から試験導入し、感触を確かめてみる価値のある技術に仕上がっています。


