Yan gezinme bileşeni oluşturma

Duyarlı bir yan gezinme paneli oluşturmaya dair temel bir genel bakış

Bu gönderide, web için duyarlı, durum bilgisine sahip, klavye gezinmesini destekleyen, JavaScript ile ve JavaScript olmadan çalışan ve tarayıcılarda çalışan bir Sidenav bileşeninin prototipini nasıl oluşturduğumu paylaşmak istiyorum. Demoyu deneyin.

Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:

Genel Bakış

Duyarlı bir gezinme sistemi oluşturmak zordur. Bazı kullanıcılar klavye kullanırken bazıları güçlü masaüstü bilgisayarlara sahip olur, bazıları ise küçük bir mobil cihazdan ziyaret eder. Ziyaret eden herkes menüyü açıp kapatabilmelidir.

Masaüstünden mobil cihazlara duyarlı düzen demosu
iOS ve Android'de açık ve koyu tema kullanımı azaltıldı

Web Taktikleri

Bu bileşen keşfinde birkaç kritik web platformu özelliğini bir araya getirmekten keyif aldım:

  1. CSS :target
  2. CSS ızgarası
  3. CSS dönüşümleri
  4. Görüntü alanı ve kullanıcı tercihi için CSS medya sorguları
  5. focus kullanıcı deneyimi geliştirmeleri için JS

Çözümümde bir kenar çubuğu var ve yalnızca 540px veya daha küçük bir "mobil" görüntü alanındayken açılıp kapanıyor. 540px, mobil etkileşimli düzen ile statik masaüstü düzeni arasında geçiş için ara noktamız olacak.

CSS :target sözde sınıfı

Bir <a> bağlantısı, URL karmasını #sidenav-open olarak, diğeri ise boş ('') olarak ayarlar. Son olarak, bir öğede karmayı eşleştirecek id bulunur:

<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<aside id="sidenav-open">
  …
</aside>

Bu bağlantıların her birini tıkladığınızda sayfa URL'mizin karma durumu değişir. Ardından, bir sözde sınıfla yan menüyü gösterir ve gizlerim:

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
  }

  #sidenav-open:target {
    visibility: visible;
  }
}

CSS Grid

Geçmişte yalnızca mutlak veya sabit konumlu kenar çubuğu düzenleri ve bileşenleri kullanıyordum. Ancak grid-area söz dizimi sayesinde grid, aynı satıra veya sütuna birden fazla öğe atamamıza olanak tanır.

Gruplar

Birincil düzen öğesi #sidenav-container, 1 satır ve 2 sütun oluşturan bir ızgaradır. Bu ızgaradaki her sütunun adı stack olarak belirlenir. Alan kısıtlı olduğunda CSS, <main> öğesinin tüm alt öğelerini aynı ızgara adına atar, tüm öğeleri aynı alana yerleştirir ve bir yığın oluşturur.

#sidenav-container {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;
  min-height: 100vh;
}

@media (max-width: 540px) {
  #sidenav-container > * {
    grid-area: stack;
  }
}

<aside>, yan gezinme menüsünü içeren animasyonlu öğedir. 2 alt öğesi vardır: [nav] adlı gezinme kapsayıcısı <nav> ve menüyü kapatmak için kullanılan [escape] adlı bir arka plan <a>.

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;
}

Menü yer paylaşımı ve negatif alanı kapat düğmesi için istediğiniz oranı bulmak üzere 2fr ve 1fr ayarlarını yapın.

Oran değiştirdiğinizde ne olacağını gösteren bir demo.

CSS 3D dönüştürme işlemleri ve geçişler

Düzenimiz artık mobil görüntü alanı boyutunda yığılmış durumda. Yeni stiller ekleyene kadar varsayılan olarak makalemizin üzerine biniyor. Bu sonraki bölümde hedeflediğim kullanıcı deneyimi özelliklerini aşağıda bulabilirsiniz:

  • Açma ve kapama animasyonları
  • Yalnızca kullanıcının kabul etmesi durumunda hareketle animasyon oluşturma
  • Klavye odağının ekran dışı öğeye girmemesi için visibility öğesini hareketlendirin

Hareket animasyonları uygulamaya başladığımda erişilebilirliği göz önünde bulundurarak başlamak istiyorum.

Erişilebilir hareket

Herkes kaydırarak açma hareketi deneyimini istemez. Çözümümüzde bu tercih, bir medya sorgusundaki --duration CSS değişkeni ayarlanarak uygulanır. Bu medya sorgusu değeri, kullanıcının işletim sistemi tercihini (varsa) temsil eder.

#sidenav-open {
  --duration: .6s;
}

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
Süre uygulanmış ve uygulanmamış haliyle etkileşimin bir demosu.

Artık kullanıcı, yan gezinme menüsünün açılmasını ve kapanmasını tercih ettiğinde, hareketi azaltmayı seçerse öğeyi hareketsiz durumdayken anında görüntüye getiririm.

Geçiş, dönüştürme, çeviri

Yan gezinme menüsü kapalı (varsayılan)

Mobil cihazlarda kenar çubuğumuzun varsayılan durumunu ekran dışında olacak şekilde ayarlamak için öğeyi transform: translateX(-110vw) ile konumlandırıyorum.

Yan gezinme menüsünün box-shadow öğesinin gizliyken ana görüntü alanını gözetlemesini önlemek için -100vw öğesinin tipik ekran dışı koduna başka bir 10vw eklediğimi unutmayın.

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);
  }
}
Yan gezinme menüsü

#sidenav öğesi :target olarak eşleştiğinde translateX() konumunu 0 ana tabanı olarak ayarlayın ve URL karması değiştirildiğinde CSS'nin öğeyi -110vw konumundan var(--duration) üzerindeki 0 "yer" konumuna kaydırmasını izleyin.

@media (max-width: 540px) {
  #sidenav-open:target {
    visibility: visible;
    transform: translateX(0);
    transition:
      transform var(--duration) var(--easeOutExpo);
  }
}

Geçiş görünürlüğü

Buradaki amaç, menü kullanılmadığında menünün ekran okuyuculardan gizlenmesini sağlamaktır. Böylece sistemler odak dışındaki menüye odaklanmaz. Bunu, :target değiştiğinde görünürlük geçişi ayarlayarak yapıyorum.

  • İçeri girerken görünürlüğü geçişli olarak göstermeyin. Öğenin içeri kaydığını ve odağı kabul ettiğini görebilmem için hemen görünür olmalıdır.
  • Görünürlük geçişini yapın ancak geçişin sonunda hidden olarak görünmesi için geçişi geciktirin.

Erişilebilirlik kullanıcı deneyimi iyileştirmeleri

Bu çözüm, durumun yönetilebilmesi için URL'nin değiştirilmesini gerektirir. Burada doğal olarak <a> öğesi kullanılmalıdır. Bu öğe, ücretsiz olarak bazı güzel erişilebilirlik özelliklerine sahiptir. Etkileşimli öğelerimizi, amacı açıkça ifade eden etiketlerle süsleyelim.

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
  <svg>...</svg>
</a>
Seslendirme ve klavye etkileşimiyle ilgili kullanıcı deneyiminin demosu.

Artık birincil etkileşim düğmelerimiz hem fare hem de klavye için amaçlarını net bir şekilde belirtiyor.

:is(:hover, :focus)

Bu kullanışlı CSS işlevsel sözde seçicisi, vurgulu öğeleri paylaşarak bunların üzerine gelme stillerimize hızla dahil edebilmemizi sağlıyor.

.hamburger:is(:hover, :focus) svg > line {
  stroke: hsl(var(--brandHSL));
}

JavaScript ekleme

Kapatmak için escape tuşuna basın

Klavyenizdeki Escape tuşu menüyü kapatıyor, değil mi? Kabloları bağlayalım.

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', event => {
  if (event.code === 'Escape') document.location.hash = '';
});
Tarayıcı geçmişi

Açılış ve kapanış etkileşiminin tarayıcı geçmişine birden fazla giriş eklemesini önlemek için kapat düğmesine aşağıdaki JavaScript'i satır içi olarak ekleyin:

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>

Bu işlem, kapatıldığında URL geçmişi girişini kaldırır ve menünün hiç açılmamış gibi görünmesini sağlar.

Focus UX

Sonraki snippet, açılıp kapatıldıktan sonra açma ve kapatma düğmelerine odaklanmamıza yardımcı olur. Geçiş yapmayı kolaylaştırmak istiyorum.

sidenav.addEventListener('transitionend', e => {
  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
      ? document.querySelector('#sidenav-close').focus()
      : document.querySelector('#sidenav-button').focus();
})

Yan gezinme menüsü açıldığında kapat düğmesine odaklanın. Yan gezinme menüsü kapandığında açık düğmesine odaklanın. Bunu, JavaScript'te öğede focus()'yi çağırarak yapıyorum.

Sonuç

Bunu nasıl yaptığımı öğrendiğinize göre, siz ne yapardınız? Bu da eğlenceli bir bileşen mimarisi oluşturur. 1. sürümü yuvalarla kim yapacak? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de uygulama geliştirmenin tüm yollarını öğrenelim. Bir Glitch oluşturun ve benimle tweet'de paylaşın. Topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri