Yan gezinme bileşeni oluşturma

Dışa kayan duyarlı bir yan gezinme alanının nasıl oluşturulacağına ilişkin temel bir genel bakış

Bu yayında, web için duyarlı, durum bilgili, klavyeyle gezinmeyi destekleyen, JavaScript ile ve JavaScript olmadan çalışan ve tarayıcılarda çalışan bir Sidenav bileşeninin prototipini nasıl oluşturduğumu sizinle 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ır, bazıları güçlü masaüstü bilgisayarlara, bazıları da küçük bir mobil cihazdan ziyaret eder. Ziyaret eden herkes menüyü açıp kapatabilmelidir.

Masaüstünden mobile duyarlı düzen demosu
iOS ve Android'de açık ve koyu temayı azaltın

Web Taktikleri

Bu bileşen keşfinde, kritik web platformu özelliklerinden birkaçını bir araya getirmekten memnun oldum:

  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ümün bir kenar çubuğu var ve yalnızca 540px veya daha düşük bir "mobil" görüntü alanında geçiş yapıyor. 540px, mobil etkileşimli düzen ile statik masaüstü düzeni arasında geçiş yapmak için ayrılma noktamız olacaktır.

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

Bir <a> bağlantısı, URL karmasını #sidenav-open ve diğeri boş ('') olarak ayarlar. Son olarak, bir öğede karmayla 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, ardından sahte bir sınıfla kenar gezinme çubuğunu gösterir ve gizlerim:

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

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

CSS Tablosu

Geçmişte, yalnızca mutlak veya sabit konumlu yan gezinme düzenleri ve bileşenleri kullanıyordum. Bununla birlikte ızgara, grid-area söz dizimiyle aynı satır veya sütuna birden fazla öğe atamamıza olanak tanır.

Gruplar

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

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

Menü yer paylaşımı ve negatif alan kapatma düğmesi için istediğiniz oranı bulmak üzere 2fr ve 1fr öğelerini ayarlayın.

Oranı değiştirdiğinizde ne olduğuna dair bir demo.

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

Düzenimiz artık mobil görüntü alanı boyutunda yığılmış durumda. Birkaç yeni stil ekleyene kadar, varsayılan olarak makalemizin üzerine yazılır. Bir sonraki bölümde ele almakta olduğum bazı kullanıcı deneyimi şunlar:

  • Açılış ve kapanış animasyonu
  • Animasyon yalnızca kullanıcının buna onay verdiği durumlarda hareketli
  • Klavye odağının ekran dışı öğeye girmemesi için visibility animasyonu gösterilir

Hareket animasyonlarını uygulamaya başlarken, önceliği erişilebilirlikle başlamak istiyorum.

Erişilebilir hareket

Herkes kaydırmalı 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 hareketle ilgili 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.

Şimdi yan gezinme çubuğumuz açılıp kapandığında, kullanıcı daha az hareket etmeyi tercih ederse öğeyi anında görüntüye taşıyorum ve hareketsiz haldeki durumunu koruyorum.

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

Yan taraftaki gezinme çubuğu (varsayılan)

Mobildeki yan gezinme çubuğumuzun varsayılan durumunu ekran dışı durumuna ayarlamak için öğeyi transform: translateX(-110vw) ile konumlandırıyorum.

Yan gezinme bölmesinin box-shadow öğesinin, gizlendiğinde ana görüntü alanına göz atmaması için -100vw adlı tipik ekran dışı koda 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);
  }
}
Kenar gezinme çubuğu

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

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

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

Amaç, bu menü kapalıyken ekran okuyuculardan gizlenmesini sağlamak. Böylece sistemler odağı ekran dışı 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. Öğenin içine kaydığı ve odağı kabul edebilmem için hemen görünür olun.
  • Dışarı çıkarken görünürlüğü değiştirin ancak erteleyin, bu nedenle geçişin sonunda hidden olarak dönüyor.

Erişilebilirlik kullanıcı deneyimi iyileştirmeleri

Bu çözüm, durumun yönetilebilmesi için URL'nin değiştirilmesine dayanır. Doğal olarak, <a> öğesi burada kullanılmalıdır. Bazı güzel erişilebilirlik özellikleri ücretsiz olarak sunulur. Etkileşimli öğelerimizi, niyeti açıkça belirten 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ı deneyiminin demosu.

Artık birincil etkileşim düğmelerimiz fare ve klavye işlevlerini açıkça belirtiyor.

:is(:hover, :focus)

Bu kullanışlı CSS işlevsel sözde seçici, fareyle üzerine gelme stillerimizi odakla da paylaşarak hızlı bir şekilde kapsayıcı olmamızı sağlıyor.

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

JavaScript üzerine yerleştir

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

Klavyenizdeki Escape tuşu menüyü kapatmalı, değil mi? Bunu kabloyla 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çık ve kapalı etkileşimin, tarayıcı geçmişinde birden fazla girişi yığmasını önlemek için kapat düğmesine aşağıdaki JavaScript satır içi kodunu 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ırarak menü hiç açılmamış gibi görünür.

Kullanıcı deneyimine odaklanın

Sonraki snippet, aç veya kapandıktan sonra aç ve kapat 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 çubuğu açıldığında kapat düğmesine odaklanın. Yan gezinme bölmesi kapandığında, açık düğmeye odaklanın. Bunu JavaScript'teki öğede focus() çağrısı yaparak yapıyorum.

Sonuç

Nasıl yaptığımı artık bildiğine göre sen ne yaparsın? Bu da bileşen mimarisini eğlenceli hale getiriyor. Slot'larla 1. sürümü kim yapacak? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de geliştirme yapmanın tüm yollarını öğrenelim. Bir Glitch oluşturup versiyonunu tweet'le bana gönder, ardından bunu aşağıdaki Topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri