Yan gezinme bileşeni oluşturma

Duyarlı bir kayar yan gezinme çubuğu oluşturma hakkında temel bilgiler

Bu yayında, web için duyarlı, durum bilgisi olan, klavye ile gezinmeyi destekleyen, JavaScript ile ve JavaScript olmadan çalışan ve tarayıcılar arası uyumluluğa sahip bir Sidenav bileşeninin prototipini nasıl oluşturduğumu sizinle paylaşmak istiyorum. Demoyu deneyin.

Video tercih ediyorsanız bu yayının YouTube versiyonunu aşağıda bulabilirsiniz:

Genel Bakış

Duyarlı bir gezinme sistemi oluşturmak zordur. Bazı kullanıcılar klavye kullanır, bazıları güçlü masaüstü bilgisayarlara sahiptir ve bazıları da 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

Web Taktikleri

Bu bileşen keşfinde, birkaç önemli web platformu özelliğini birleştirme keyfini yaşadım:

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

Çözümümde bir kenar çubuğu var ve yalnızca 540px veya daha küçük bir görüntü alanında "mobil" olarak değiştiriliyor. 540px, mobil etkileşimli düzen ile statik masaüstü düzeni arasında geçiş yaparken kullanacağımız kesme noktası olacak.

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

Bir <a> bağlantısı URL karmasını #sidenav-open, diğeri ise boş ('') olarak ayarlar. Son olarak, bir öğe karmayla eşleşecek id değerine sahiptir:

<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ıf kullanarak yan gezinme çubuğunu gösterir ve gizlerim:

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

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

CSS Izgarası

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

Şekil grupları

Birincil düzen öğesi #sidenav-container, 1 satır ve 2 sütun oluşturan bir ızgaradır. Bunlardan 1'ine stack adı verilir. Alan sınırlı olduğunda CSS, <main> öğesinin tüm alt öğelerini aynı ızgara adına atar. Böylece tüm öğeler aynı alana yerleştirilerek bir yığın oluşturulur.

#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 animasyonlu öğedir. Bu öğenin 2 alt öğesi vardır: <nav> adlı gezinme kapsayıcısı [nav] ve menüyü kapatmak için kullanılan [escape] adlı arka plan <a>.

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

Menü yerleşimi ve negatif alan kapatma düğmesi için istediğiniz oranı bulmak üzere 2fr ve 1fr değerlerini ayarlayın.

Oranı değiştirdiğinizde ne olacağına dair bir demo.

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

Düzenimiz artık mobil görüntü alanı boyutunda yığılmış durumda. Until I add some new styles, it's overlaying our article by default. Bu sonraki bölümde hedeflediğim bazı kullanıcı deneyimleri şunlardır:

  • Açılış ve kapanış animasyonu
  • Yalnızca kullanıcı kabul ediyorsa hareketle animasyon yapın
  • Klavye odağının ekran dışındaki öğeye girmemesi için visibility öğesine animasyon ekleyin.

Hareket animasyonlarını uygulamaya başlarken erişilebilirliği ön planda tutmak istiyorum.

Erişilebilir hareket

Herkes kaydırma hareketli deneyim istemeyebilir. Çözümümüzde bu tercih, medya sorgusunun 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) gösterir.

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

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
Süre uygulanan ve uygulanmayan etkileşim demosu.

Artık yan gezinme çubuğumuz açılıp kapanırken kullanıcı hareket azaltmayı tercih ediyorsa öğeyi anında görünür hale getiriyorum ve durumu hareket olmadan koruyorum.

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

Sidenav out (varsayılan)

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

Yan gezinme çubuğunun box-shadow gizlendiğinde ana görünüm penceresine sızmamasını sağlamak için normalde -100vw olan 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);
  }
}
Yan gezinme çubuğu

#sidenav öğesi :target ile eşleştiğinde translateX() konumunu ana üs 0 olarak ayarlayın. URL karması değiştirildiğinde CSS'nin öğeyi -110vw olan dış konumundan var(--duration) süresince 0 olan "iç" 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üğü

Şimdi amaç, menü gösterildiğinde ekran okuyuculardan gizlemek. Böylece sistemler, ekran dışındaki bir menüye odaklanmaz. Bunu, :target değiştiğinde görünürlük geçişi ayarlayarak yapıyorum.

  • Görünürlüğe geçiş yapmayın. Öğenin kayarak gelmesini ve odaklanmasını görebilmem için hemen görünür olun.
  • Dışarı çıkarken görünürlüğü geçişli hale getirin ancak geçişi geciktirin. Böylece geçişin sonunda hidden simgesine döner.

Erişilebilirlik kullanıcı deneyimi geliştirmeleri

Bu çözüm, durumun yönetilmesi için URL'nin değiştirilmesine dayanır. Burada doğal olarak <a> öğesi kullanılmalıdır ve bu öğe, ücretsiz olarak bazı güzel erişilebilirlik özellikleri sunar. Etkileşimli öğelerimizi, amacı net bir şekilde 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ı 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çici, üzerine gelme stillerini odaklanma stiliyle de paylaşarak hızlı bir şekilde kapsayıcı olmamızı sağlar.

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

JavaScript'i ekleyin

Kapatmak için escape düğmesine basın

Klavyenizdeki Escape tuşu menüyü kapatmalıdır, değil mi? Şimdi bu sorunu çözelim.

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

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

Açma ve kapatma etkileşiminin tarayıcı geçmişine birden fazla giriş eklemesini önlemek için kapatma 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, kapatıldığında URL geçmişi girişini kaldırarak menü hiç açılmamış gibi görünmesini sağlar.

Focus UX

Aşağıdaki snippet, açma ve kapatma düğmeleri açıldıktan veya kapatıldıktan sonra bu düğmelere odaklanmamıza yardımcı olur. Geçişleri 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. Sidenav kapandığında, aç düğmesine odaklanın. Bunu, JavaScript'te öğe üzerinde focus() çağırarak yapıyorum.

Sonuç

Benim nasıl yaptığımı öğrendiğinize göre, siz nasıl yapardınız? Bu, eğlenceli bir bileşen mimarisi oluşturur. Who's going to make the 1st version with slots? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de içerik oluşturmanın tüm yollarını öğrenelim. Bir Glitch oluşturun, sürümünüzü bana tweet atın. Ben de sürümünüzü aşağıdaki Topluluk remiksleri bölümüne ekleyeyim.

Topluluk remiksleri