Anchor Positioning

앵커 포지셔닝

CSS-only positioning that pins one element to another without JS measuring coordinates — replaces tooltip/popover positioning libraries.

Also known as: CSS Anchor APIanchor()
···
html
<div class="wrap">
  <div class="badge" id="badge"><svg viewBox="0 0 10 10"><path id="bpath" d="M1.5 5.2l2.6 2.6L8.5 2.4" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg><span id="btext">확인 중</span></div>
  <button class="anchor" id="btn">기준 버튼</button>
  <div class="tip" id="tip">anchor(bottom)로 붙었어요</div>
</div>
css
.wrap{position:relative;width:100%;height:100%;display:grid;place-items:center}
.anchor{anchor-name:--trg;padding:9px 18px;border-radius:9px;border:1px solid var(--line);background:var(--surface);color:var(--fg);font-size:13px;font-weight:600}
.tip{position:absolute;position-anchor:--trg;top:anchor(bottom);left:anchor(center);translate:-50% 10px;
  padding:7px 12px;border-radius:8px;background:var(--accent);color:#fff;font-size:11px;font-weight:600;white-space:nowrap;
  opacity:0;animation:show 2.4s ease infinite}
.tip.fallback{position:fixed;translate:-50% 0}
@keyframes show{0%,15%{opacity:0}25%,75%{opacity:1}90%,100%{opacity:0}}
.badge{position:absolute;top:10px;right:10px;display:flex;align-items:center;gap:5px;padding:4px 9px;border-radius:999px;font-size:10px;font-weight:700;background:var(--bg);border:1px solid var(--line);color:var(--accent-3);z-index:2}
.badge.no{color:var(--accent-2)}
.badge svg{width:9px;height:9px}
js
const ok = CSS.supports('anchor-name', '--x');
const b=document.getElementById('badge'),p=document.getElementById('bpath'),t=document.getElementById('btext');
b.classList.toggle('no', !ok);
p.setAttribute('d', ok ? 'M1.5 5.2l2.6 2.6L8.5 2.4' : 'M2 2l6 6M8 2l-6 6');
t.textContent = ok ? 'anchor() 지원됨' : '미지원 · JS 폴백';
// 폴백: getBoundingClientRect() 로 직접 좌표를 재서 붙인다.
if (!ok) {
  const tip = document.getElementById('tip'), btn = document.getElementById('btn');
  tip.classList.add('fallback');
  function place(){ const r = btn.getBoundingClientRect(); tip.style.left = (r.left + r.width/2) + 'px'; tip.style.top = (r.bottom + 10) + 'px'; }
  place();
  window.addEventListener('resize', place);
}

Pinning a tooltip or dropdown next to its trigger used to all but require a JS library (Popper, Floating UI) that measures getBoundingClientRect() and recalculates on every scroll and resize. CSS anchor positioning gives the trigger an `anchor-name` and lets the positioned element reference it with `position-anchor` and functions like `top: anchor(bottom)`, so the browser computes it during layout.

Paired with `position-try` / `position-try-fallbacks`, collision avoidance — flipping to the other side when it would run off-screen — also happens without JS. It pairs especially well with the popover API and the top layer, so anchoring a native popover to its trigger is an increasingly common combination.

Check caniuse/MDN baseline for support. Browsers without it still need JS-based getBoundingClientRect() positioning — the fallback below is the minimal version of that.

When to use

Building trigger-anchored UI — tooltips, popovers, dropdowns — without a JS positioning library.