Yan gezinme bileşeni oluşturma

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

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

Video kullanmayı tercih ederseniz bu gönderinin YouTube versiyonunu kullanabilirsiniz:

Genel bakış

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

Masaüstünden mobil cihaza 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 transforms
  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 geçiş işlemi yalnızca "mobil" görüntü alanında 540px veya daha az olduğunda geçiş yapıyor. 540px, mobil etkileşimli düzen ile statik masaüstü düzeni arasında geçiş yapmak için kullandığımız ayrılma noktamız olacak.

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

<a> bağlantısı, URL karmasını #sidenav-open, diğeri de boş ('') olarak ayarlar. Son olarak, bir öğede karma ile eşleşecek 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ıklamak, sayfa URL'mizin karma durumunu değiştirir. Daha sonra, yapay bir sınıf I ile yan gezinmeyi gösterir ve gizler:

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

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

CSS Izgarası

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

Gruplar

#sidenav-container birincil düzen öğesi, her biri stack olarak adlandırılmış 1 satır ve 2 sütun oluşturan bir ızgaradır. Alan kısıtlı olduğunda CSS, <main> öğesinin tüm alt öğelerini aynı ızgara adına atar ve tüm öğeler aynı alana yerleştirilerek 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 gezinmeyi içeren animasyon öğesidir. 2 alt öğesi vardır: [nav] adlı gezinme kapsayıcısı ve menüyü kapatmak için kullanılan [escape] adlı bir arka plan <a>.<nav>

#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ğına dair bir demo.

CSS 3D dönüştürmeleri ve geçişleri

Düzenimiz artık mobil görüntü alanı boyutunda üst üste dizilmiş. Ben yeni stiller ekleyene kadar varsayılan olarak makalemiz yer paylaşımlı olarak gösterilir. Sonraki bölümde ele aldığım kullanıcı deneyiminden bazıları şunlar:

  • Açıp kapatarak animasyon
  • Yalnızca kullanıcı buna izin veriyorsa hareketle animasyon kullan
  • Klavye odağının ekran dışı öğeye girmemesi için visibility animasyonu canlandırılır

Hareket animasyonlarını uygulamaya başladığımda, öncelikle erişilebilirlikle başlamak istiyorum.

Erişilebilir hareket

Herkes kayan hareket deneyimi istemez. Çözümümüzde bu tercih, medya sorgusu içindeki bir --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 uygulanan ve uygulanmayan etkileşimin demosu.

Yan gezinme panelimiz açılıp kapanıyorken, kullanıcı daha az hareketi tercih ederse öğeyi anında görünüme taşıyarak hareket olmadan durumunu koruyorum.

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

Yana gezinme (varsayılan)

Mobil cihazda kenar gezinmemizin varsayılan durumunu ekran dışı duruma ayarlamak için öğeyi transform: translateX(-110vw) ile konumlandırıyorum.

Not: Yan gezinme bölmesinin box-shadow (gizlendiğinde) ana görüntü alanına göz atmaması için -100vw adlı tipik ekran dışı koduna 10vw bir tane daha ekledim.

@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);
  }
}
Yandaki bölmede

#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 "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 bir görünürlük geçişi ayarlayarak yapıyorum.

  • İçeri girerken görünürlüğü değiştirmeyin; hemen görünür olmalıdır. Böylece öğenin kaydığını ve odağı kabul edebileceğim.
  • Dışarı çıkarken görünürlüğü değiştirin ancak geçişi geciktirin. Böylece, geçişin sonunda hidden dönüşür hale gelir.

Erişilebilirlikle ilgili kullanıcı deneyimi geliştirmeleri

Bu çözüm, durumun yönetilmesi için URL'nin değiştirilmesine dayanır. Doğal olarak <a> öğesi burada kullanılmalıdır. Ayrıca bu öğeye ücretsiz olarak bazı güzel erişilebilirlik özellikleri de sahip olur. 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şimi kullanıcı deneyimini gösteren demo.

Artık birincil etkileşim düğmelerimiz hem fare hem de klavye için kullanım amacı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'e ekleme

Kapatmak için escape tuşlarına basın

Klavyenizdeki Escape tuşu menüyü kapatmalı, doğru mu? Bunu kabloyla açalım.

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

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

Açık ve kapalı etkileşiminin birden fazla girişi tarayıcı geçmişine yığmasını önlemek için kapat düğmesine aşağıdaki satır içi JavaScript'i ekleyin:

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

Bu işlem, kapanışta URL geçmişi girişini kaldırır ve menüyü hiç açılmamış gibi gösterir.

Kullanıcı deneyimine odaklanma

Sonraki snippet, açılıp kapatıldıktan sonra açma ve kapatma düğmelerine odaklanmamıza yardımcı olur. Geçişi 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 paneli açıldığında kapat düğmesine odaklanın. Yan gezinme paneli kapandığında aç düğmesine odaklanın. Bunu, JavaScript'teki öğede focus() yöntemini çağırarak yapıyorum.

Sonuç

Şimdi bunu nasıl yaptığımı biliyorsun, şimdi nasıl yaparsın? Bu da eğlenceli bir bileşen mimarisi oluşturur. Slotlarla ilk sürümü kim yapacak? 🙂

Gelin, yaklaşımlarımızı çeşitlendirelim ve web üzerinde geliştirme yapmanın tüm yollarını öğrenelim. Oluşturduğunuz Glitch'i tweet'le paylaşabilirsiniz. Yayınladığınız videoyu aşağıdaki Topluluk remiksleri bölümüne eklerim.

Topluluk remiksleri