Popover Menü
Tıklandığında (veya odaklanıldığında) açılan ve fareyle üzerine gelindiğinde yerine eylemlerin listesini görüntüleyen bir açılır menü.
Açılır menüler, fareyle üzerine gelindiğinde görünen araç ipuçlarının aksine, tıklatıldığında açılır ve kullanıcıya eylemlerin (düzenleme veya silme gibi) bir listesini sunmak için kullanılır. Odak yönetimi ve tıklatmadan kapatma işlemi gibi erişilebilirlik göz önünde bulundurularak tasarlanmıştır.
Önizleme
Kullanım Durumları ve Özellikler
-
Seçenekleri göster: Bir menü simgesini (…) veya düğmeyi tıklatarak çeşitli eylemleri (düzenleme, silme, paylaşma vb.) görüntüleyen kullanıcı arayüzleri için idealdir.
-
Erişilebilirlikle ilgili hususlar: Escape tuşuyla gizleme ve odak yönetimini desteklemek için “aria-expanded” ve “aria-haspopup” özelliklerini kullanır.
-
Pürüzsüz animasyon: Doğal olmayan görüntü geçişini önlemek için, Y ekseni yönünde kaymayı ve solmayı birleştiren bir geçiş uyguladık.
Tasarım ve UX’in önemli noktaları (Tasarım ve UX)
-
Net Hiyerarşi: Gölgeler (“kutu-gölge”), yüzen bir his yaratmak ve içeriği arka plan içeriğinden net bir şekilde ayırmak için kullanılır.
-
Güvenli işlem: Tıklama aralığının dışındaki bir alana basılarak (Dışarıya Tıklama) menüyü kapatmaya yönelik bir işlem içerir; bu, hem mobil hem de PC’de çalışmayı sezgisel hale getirir.
-
Yıkıcı eylemlerin vurgulanması: Kullanıcı hatasını azaltmak amacıyla silme gibi geri alınamayan eylemler için kırmızı metin (Tehlike) kullanırız.
Nasıl kişiselleştirilir (Özelleştirme Kılavuzu)
-
Menü genişliğini değiştirmek istiyorsanız lütfen “.popover-menu”nün “genişliğini” ayarlayın.
-
Açılma yönünün sağ alt veya sol alt olmasını istiyorsanız
.popover-menününtransformveleftözelliklerini değiştirerek bunu kolayca özelleştirebilirsiniz.
Uygulama kodu (Uygulama)
Aşağıdaki kodu kullanarak projenize açılır menü ekleyebilirsiniz.
<div class="popover-wrapper">
<button class="popover-trigger" aria-haspopup="true" aria-expanded="false" id="popover-btn">
<span>eylem</span>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="m6 9 6 6 6-6"/>
</svg>
</button>
<div class="popover-menu" role="menu" aria-labelledby="popover-btn">
<div class="popover-content">
<button class="popover-item" role="menuitem">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
Düzenle
</button>
<button class="popover-item" role="menuitem">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><path d="M21 9H3"/><path d="M9 21V9"/></svg>
kopya
</button>
<div class="popover-divider"></div>
<button class="popover-item text-danger" role="menuitem">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>
sil
</button>
</div>
</div>
</div>
<style>
/* CSS'yi buraya yerleştirin */
.popover-wrapper {
position: relative;
display: inline-block;
}
.popover-trigger {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
font-size: 14px;
font-weight: 500;
color: #334155;
background-color: #ffffff;
border: 1px solid #cbd5e1;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.popover-trigger:hover {
background-color: #f1f5f9;
border-color: #94a3b8;
}
.popover-trigger:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
.popover-trigger svg {
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.popover-trigger.is-open svg {
transform: rotate(180deg);
}
.popover-menu {
position: absolute;
top: calc(100% + 8px);
left: 50%;
transform: translateX(-50%) translateY(-10px);
width: 200px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
opacity: 0;
visibility: hidden;
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
z-index: 50;
pointer-events: none;
}
.popover-wrapper.is-active .popover-menu {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
pointer-events: auto;
}
.popover-content {
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
.popover-item {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 8px 12px;
font-size: 14px;
color: #475569;
background: transparent;
border: none;
border-radius: 6px;
cursor: pointer;
text-align: left;
transition: background-color 0.15s ease, color 0.15s ease;
}
.popover-item:hover,
.popover-item:focus-visible {
background-color: #f1f5f9;
color: #0f172a;
outline: none;
}
.popover-item.text-danger {
color: #ef4444;
}
.popover-item.text-danger:hover,
.popover-item.text-danger:focus-visible {
background-color: #fef2f2;
color: #dc2626;
}
.popover-divider {
height: 1px;
background-color: #e2e8f0;
margin: 4px 0;
}
</style>
<script>
// JavaScript'i buraya yerleştir
document.addEventListener('DOMContentLoaded', () => {
const wrappers = document.querySelectorAll('.popover-wrapper');
wrappers.forEach(wrapper => {
const trigger = wrapper.querySelector('.popover-trigger');
const menu = wrapper.querySelector('.popover-menu');
if (!trigger || !menu) return;
const togglePopover = () => {
const isActive = wrapper.classList.contains('is-active');
if (isActive) {
closePopover();
} else {
openPopover();
}
};
const openPopover = () => {
wrapper.classList.add('is-active');
trigger.classList.add('is-open');
trigger.setAttribute('aria-expanded', 'true');
};
const closePopover = () => {
wrapper.classList.remove('is-active');
trigger.classList.remove('is-open');
trigger.setAttribute('aria-expanded', 'false');
};
trigger.addEventListener('click', togglePopover);
//Tıklama alanının dışını kapat
document.addEventListener('click', (e) => {
if (!wrapper.contains(e.target)) {
closePopover();
}
});
//Escape tuşu ile kapatıyoruz
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && wrapper.classList.contains('is-active')) {
closePopover();
trigger.focus();
}
});
});
});
</script>