Membuat komponen sidenav

Ringkasan dasar tentang cara membuat sidenav slide responsif

Dalam postingan ini saya ingin berbagi dengan Anda bagaimana saya membuat prototipe komponen Sidenav untuk web yang responsif, stateful, mendukung navigasi keyboard, berfungsi dengan dan tanpa JavaScript, serta berfungsi di berbagai browser. Coba demo.

Jika Anda lebih suka menonton video, berikut versi YouTube untuk postingan ini:

Ringkasan

Membangun sistem navigasi yang responsif itu sulit. Beberapa pengguna akan menggunakan {i>keyboard<i}, beberapa akan memiliki desktop yang canggih, dan beberapa akan berkunjung dari perangkat seluler kecil. Setiap orang yang berkunjung harus dapat membuka dan menutup menu.

Demo tata letak responsif desktop ke seluler
Tema terang dan gelap di bawah di iOS dan Android

Taktik Web

Dalam eksplorasi komponen ini, saya senang menggabungkan beberapa fitur platform web penting:

  1. CSS :target
  2. Petak CSS
  3. transforms CSS
  4. Kueri Media CSS untuk area pandang dan preferensi pengguna
  5. JS untuk focus peningkatan UX

Solusi saya memiliki satu sidebar dan beralih hanya saat berada di area tampilan "seluler" dengan ukuran 540px atau kurang. 540px akan menjadi titik henti sementara untuk beralih antara tata letak interaktif seluler dan tata letak desktop statis.

Class semu :target CSS

Satu link <a> menetapkan hash URL ke #sidenav-open dan link lainnya ke kosong (''). Terakhir, elemen memiliki id untuk dicocokkan dengan hash:

<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>

Dengan mengklik setiap link ini, status hash URL halaman akan berubah, lalu dengan kelas semu, saya menampilkan dan menyembunyikan sidenav:

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

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

Petak CSS

Dahulu, saya hanya menggunakan tata letak dan komponen sidenav posisi absolut atau tetap. Namun, dengan sintaksis grid-area, kita dapat menetapkan beberapa elemen ke baris atau kolom yang sama.

Stack

Elemen tata letak utama #sidenav-container adalah petak yang membuat 1 baris dan 2 kolom, 1 di antaranya diberi nama stack. Saat ruang terbatas, CSS akan menetapkan semua turunan elemen <main> ke nama petak yang sama, sehingga menempatkan semua elemen ke dalam ruang yang sama, sehingga menghasilkan stack.

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

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

<aside> adalah elemen animasi yang berisi navigasi samping. File ini memiliki 2 turunan: penampung navigasi <nav> bernama [nav] dan tampilan latar <a> bernama [escape], yang digunakan untuk menutup menu.

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

Sesuaikan 2fr & 1fr guna menemukan rasio yang Anda inginkan untuk overlay menu dan tombol tutup ruang negatifnya.

Demo apa yang akan terjadi jika Anda mengubah rasio.

Transformasi & transisi 3D CSS

Tata letak kita sekarang ditumpuk pada ukuran area pandang seluler. Sampai saya menambahkan beberapa gaya baru, artikel kita akan ditempatkan secara {i>default<i}. Berikut adalah beberapa UX yang saya tetapkan di bagian berikutnya:

  • Menganimasikan buka dan tutup
  • Hanya animasikan dengan gerakan jika pengguna tidak keberatan dengan hal tersebut
  • Animasikan visibility agar fokus keyboard tidak memasuki elemen offscreen

Saat saya mulai mengimplementasikan animasi {i>motion<i}, saya ingin memulai dengan prioritas utama aksesibilitas.

Gerakan yang dapat diakses

Tidak semua orang menginginkan pengalaman gerakan geser. Dalam solusi kami, preferensi ini diterapkan dengan menyesuaikan variabel CSS --duration di dalam kueri media. Nilai kueri media ini mewakili preferensi sistem operasi pengguna untuk gerakan (jika tersedia).

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

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
Demo interaksi dengan dan tanpa durasi diterapkan.

Sekarang saat panel navigasi bergeser terbuka dan tertutup, jika pengguna lebih menyukai gerakan yang dikurangi, saya akan langsung memindahkan elemen ke tampilan, mempertahankan status tanpa gerakan.

Transisi, transformasi, terjemahan

Sidenav keluar (default)

Untuk menyetel status default sidenav di perangkat seluler ke status offscreen, saya memosisikan elemen dengan transform: translateX(-110vw).

Catatan, saya menambahkan 10vw lain ke kode offscreen standar -100vw, untuk memastikan box-shadow sidenav tidak muncul ke area pandang utama saat disembunyikan.

@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);
  }
}
Navigasi samping di

Saat elemen #sidenav cocok sebagai :target, tetapkan posisi translateX() ke homebase 0, lalu perhatikan saat CSS menggeser elemen dari posisi luar -110vw, ke posisi "dalam" 0 dari var(--duration) saat hash URL diubah.

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

Visibilitas transisi

Tujuannya sekarang adalah untuk menyembunyikan menu dari pembaca layar saat dikeluarkan, sehingga sistem tidak menempatkan fokus ke menu di luar layar. Saya melakukannya dengan menyetel transisi visibilitas saat :target berubah.

  • Saat masuk, jangan mengubah visibilitas; segera terlihat sehingga saya dapat melihat elemen bergeser masuk dan menerima fokus.
  • Saat keluar, visibilitas transisi tetapi menundanya, sehingga beralih ke hidden di akhir transisi keluar.

Peningkatan UX aksesibilitas

Solusi ini bergantung pada perubahan URL agar status dapat dikelola. Tentunya, elemen <a> harus digunakan di sini, dan elemen tersebut mendapatkan beberapa fitur aksesibilitas yang bagus secara gratis. Mari hias elemen interaktif kita dengan label yang menjabarkan maksud dengan jelas.

<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>
Demo UX interaksi keyboard dan voiceover.

Sekarang tombol interaksi utama kita dengan jelas menyatakan maksudnya untuk mouse dan keyboard.

:is(:hover, :focus)

Pemilih semu fungsional CSS yang praktis ini memungkinkan kita secara cepat inklusif dengan gaya pengarahan kursor dengan membagikannya dengan fokus.

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

Tambahkan fitur di JavaScript

Tekan escape untuk menutup

Tombol Escape pada keyboard akan menutup menu, bukan? Mari kita hubungkan.

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

sidenav.addEventListener('keyup', event => {
  if (event.code === 'Escape') document.location.hash = '';
});
Histori browser

Untuk mencegah interaksi terbuka dan tertutup agar tidak menumpuk beberapa entri ke dalam histori browser, tambahkan JavaScript berikut secara inline ke tombol tutup:

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

Tindakan ini akan menghapus entri histori URL saat penutupan, sehingga menu seolah-olah tidak pernah dibuka.

Fokus pada UX

Cuplikan berikutnya membantu kita menempatkan fokus pada tombol buka dan tutup setelah membuka atau menutup. Saya ingin mempermudah peralihan.

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

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

Saat sidenav terbuka, fokuskan tombol tutup. Ketika {i>sidenav<i} ditutup, fokus tombol {i>open<i}. Saya melakukannya dengan memanggil focus() pada elemen di JavaScript.

Kesimpulan

Sekarang setelah Anda tahu cara saya melakukannya, bagaimana Anda akan melakukannya?! Hal ini menghasilkan arsitektur komponen yang menyenangkan. Siapa yang akan membuat versi pertama dengan slot? 🙂

Mari kita diversifikasi pendekatan kita dan mempelajari semua cara untuk membangun di web. Buat Glitch, tweet saya versi Anda, dan saya akan menambahkannya ke bagian Remix komunitas di bawah.

Remix komunitas