@sedatdemirAra

CSS anchor positioning ile tooltip ve popover

Bir tooltip'i tetikleyici butonun tam üstüne hizalamak için bugüne kadar hep JavaScript'e başvurduk. Floating UI, Popper.js ya da kendi yazdığımız getBoundingClientRect() hesapları; hepsi aynı problemi çözüyordu: "Bu kutuyu şu elemana göre konumla, ekrandan taşarsa ters tarafa çevir." CSS Anchor Positioning API bu işi tarayıcının layout motoruna devrediyor.

Temel kavramlar

API iki taraf üzerine kurulu: bir anchor (çapa) ve bu çapaya göre konumlanan bir hedef eleman. Çapa herhangi bir DOM elemanı olabilir. Hedef ise position: absolute veya position: fixed olan, çapaya referans veren eleman.

Üç temel CSS özelliği var:

  • anchor-name: Bir elemana çapa kimliği atar.
  • position-anchor: Hedef elemanın hangi çapaya bağlanacağını belirtir.
  • position-area: Hedef elemanın çapaya göre nereye yerleşeceğini tanımlar.

İlk tooltip örneği

JavaScript sıfır. Sadece HTML ve CSS:

<button class="trigger" popovertarget="tip">Ayarlar</button>
<div id="tip" class="tooltip" popover>Hesap ayarlarını aç</div>
.trigger {
  anchor-name: --btn-ayarlar;
}

.tooltip {
  position-anchor: --btn-ayarlar;
  position: fixed;
  position-area: top;
  margin-bottom: 8px;
  background: #1a1a1a;
  color: #fff;
  padding: 6px 12px;
  border-radius: 6px;
  border: none;
  width: max-content;
}

Burada position-area: top hedef elemanı çapanın üstüne yerleştiriyor. margin-bottom: 8px ile çapa ile tooltip arasında boşluk bırakıyoruz. Popover API'si ile birleştiğinde tıklamayla açılıp kapanan, JavaScript'siz bir tooltip elde ediyoruz.

position-area değerleri bir ızgara mantığıyla çalışıyor: top, bottom, left, right, center ve bunların kombinasyonları (top left, bottom right gibi) geçerli.

Taşma kontrolü: position-try-fallbacks

Tooltip'i çapanın üstüne koyduk ama çapa ekranın üst kenarına yakınsa tooltip kesilir. Floating UI'da flip middleware'i ne yapıyorsa, CSS'te position-try-fallbacks aynısını yapıyor:

.tooltip {
  position-anchor: --btn-ayarlar;
  position: fixed;
  position-area: top;
  position-try-fallbacks: --try-bottom;
  margin-bottom: 8px;
}

@position-try --try-bottom {
  position-area: bottom;
  margin-top: 8px;
  margin-bottom: 0;
}

Tarayıcı önce position-area: top ile yerleştirmeye çalışır. Eleman görünür alanın (containing block) dışına taşarsa --try-bottom kuralına geçer. Birden fazla fallback zincirlenebilir:

.tooltip {
  position-try-fallbacks: --try-bottom, --try-left, --try-right;
}

Tarayıcı bunları sırayla dener, ilk taşmayan konumu seçer.

Yerleşik flip kısayolları

Her yön için ayrı @position-try bloğu yazmak istemiyorsanız, CSS'in yerleşik flip anahtar kelimeleri var:

.tooltip {
  position-area: top;
  position-try-fallbacks: flip-block;
}

flip-block blok ekseninde (dikey) çevirir, yani top çalışmazsa bottom'ı dener. flip-inline satır ekseninde çevirir (left yerine right). flip-block flip-inline yazarsanız her iki ekseni birden dener.

Bu kısayollar çoğu tooltip senaryosu için yeterli. Özel @position-try blokları, margin ya da boyut değişikliği gereken durumlar içindir.

Popover ile birlikte kullanım

HTML Popover API (popover attribute) ve Anchor Positioning birbirini tamamlıyor. Popover zaten position: fixed ve top layer'da render ediliyor; bu da anchor positioning ile doğal bir uyum sağlıyor.

<nav>
  <button class="nav-item" popovertarget="menu-dosya">Dosya</button>
  <div id="menu-dosya" class="dropdown" popover>
    <a href="#">Yeni</a>
    <a href="#"></a>
    <a href="#">Kaydet</a>
  </div>
</nav>
.nav-item {
  anchor-name: --nav-dosya;
}

.dropdown {
  position-anchor: --nav-dosya;
  position: fixed;
  position-area: bottom span-right;
  margin-top: 4px;
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 8px 0;
  min-width: 160px;
}

.dropdown a {
  display: block;
  padding: 6px 16px;
  text-decoration: none;
  color: inherit;
}

position-area: bottom span-right dropdown'ı çapanın altına koyarken sağa doğru genişlemesine izin veriyor. span-right olmadan dropdown çapanın genişliğiyle sınırlı kalırdı.

anchor() fonksiyonu ile hassas konumlama

position-area çoğu durumu karşılıyor ama piksel düzeyinde kontrol gerektiğinde anchor() fonksiyonu devreye girer:

.tooltip-custom {
  position-anchor: --btn-ayarlar;
  position: fixed;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 0;
  margin-top: 8px;
}

anchor(bottom) çapanın alt kenarının y koordinatını döndürür. anchor(center) çapanın yatay merkezini verir. translate: -50% 0 ile tooltip kendi genişliğinin yarısı kadar sola kayarak çapanın ortasına hizalanır.

Bu fonksiyon calc() ile de çalışır:

.tooltip-offset {
  left: calc(anchor(right) + 12px);
  top: anchor(top);
}

Bu şekilde çapanın sağ kenarından 12 piksel ötede, üst kenarıyla aynı hizada bir tooltip elde ediyoruz.

Birden fazla çapa ile çalışma

Bir eleman tek bir position-anchor değerine bağlanır ama anchor() fonksiyonu içinde farklı çapalara referans verebilir:

.connector-line {
  position: fixed;
  top: anchor(--box-a bottom);
  left: anchor(--box-a center);
  width: 2px;
  height: calc(anchor(--box-b top) - anchor(--box-a bottom));
  background: #666;
}

Bu teknikle iki kutu arasına bağlantı çizgisi çekebilirsiniz. anchor() fonksiyonunun ilk parametresi çapa ismi, ikincisi kenar (edge) değeri.

Tarayıcı desteği

Chromium tarafında destek Chrome 125 (Mayıs 2024) ile geldi. Edge aynı sürümle birlikte destekliyor. Firefox 131 (Ekim 2024) itibarıyla anchor-name, position-anchor ve position-area özelliklerini desteklemeye başladı; ancak position-try-fallbacks desteği Firefox'ta hâlâ bayrak arkasında (Nightly'de test ediliyor, Haziran 2025 itibarıyla stabil sürüme henüz geçmedi).

Safari tarafındaysa durum daha sorunlu. Safari 18.x serisinde anchor positioning desteği yok. WebKit'in issue tracker'ında konu açık ama bir sürüm takvimi paylaşılmadı.

Yani bugün production'da kullanmak istiyorsanız Safari kullanıcılarını düşünmeniz gerekiyor.

Progressive enhancement stratejisi

@supports sorgusuyla fallback yazılabilir:

/* Fallback: basit göreli konumlama */
.tooltip {
  position: absolute;
  top: -100%;
  left: 50%;
  translate: -50% 0;
}

/* Anchor positioning destekleniyorsa */
@supports (anchor-name: --x) {
  .tooltip {
    position: fixed;
    position-anchor: --btn-ayarlar;
    position-area: top;
    top: auto;
    left: auto;
    translate: none;
  }
}

Bu yaklaşımda Safari kullanıcıları basit bir position: absolute tooltip görür; taşma kontrolü çalışmaz ama tooltip yine de görünür. Chrome ve Firefox kullanıcıları tam anchor positioning deneyimini yaşar.

Erişilebilirlik notu

Anchor positioning saf bir görsel API; DOM sırası ya da ARIA ilişkilerini değiştirmiyor. Popover API ile birlikte kullandığınızda popovertarget attribute'u zaten buton ile popover arasında örtük bir ilişki kuruyor. Ancak tooltip senaryolarında aria-describedby bağlantısını kendiniz kurmanız gerekiyor:

<button class="trigger" aria-describedby="tip" popovertarget="tip">
  Ayarlar
</button>
<div id="tip" class="tooltip" popover role="tooltip">
  Hesap ayarlarını aç
</div>

role="tooltip" ve aria-describedby eklemek ekran okuyucuların tooltip içeriğini butonla ilişkilendirmesini sağlıyor.

Ne zaman JavaScript kütüphanesi yerine anchor positioning?

Safari desteği olmadan bu API'yi tek çözüm olarak kullanmak şu an riskli. Ama şu senaryolarda denemeye değer:

Dahili araçlar veya yönetim panelleri gibi tarayıcı kısıtlaması kontrolünüzde olan projeler. Chromium tabanlı tarayıcıları hedefleyen Electron uygulamaları. Progressive enhancement ile fallback yazmayı kabul ettiğiniz projeler.

Floating UI gibi kütüphaneler hâlâ scroll container'lar içinde dinamik yeniden konumlandırma, sanal elemanlar (reference olarak bir koordinat noktası verme) ve karmaşık ok (arrow) hesaplamaları konusunda daha fazla kontrol sunuyor. Anchor positioning bunların bir kısmını karşılıyor ama tamamını değil.

Benim tercihim: Safari desteği stabil sürüme gelene kadar @supports ile progressive enhancement yazmak, ama yeni projelerde anchor positioning'i birincil yol olarak planlamak. Floating UI'ı bir geçiş çözümü olarak düşünmek. Tarayıcıların bu API'yi benimseme hızına bakılırsa, 2026 ortasında JavaScript tooltip kütüphaneleri büyük ölçüde gereksizleşecek.