Membangun komponen sidenav

Gambaran dasar tentang cara membuat slide out sidenav yang responsif

Dalam posting ini saya ingin berbagi dengan Anda bagaimana saya membuat prototipe komponen Sidenav untuk web yang responsif, stateful, mendukung navigasi keyboard, bekerja dengan dan tanpa Javascript, dan bekerja lintas browser. Coba demo.

Jika Anda lebih suka video, inilah versi YouTube dari posting ini:

Gambaran #

Sulit membangun sistem navigasi yang responsif. Beberapa pengguna akan menggunakan keyboard, beberapa memiliki desktop yang kuat, dan beberapa akan mengunjungi 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 turun di iOS dan Android

Taktik Web #

Dalam eksplorasi komponen ini, saya memiliki kegembiraan dalam menggabungkan beberapa fitur platform web penting:

  1. CSS :target
  2. Kisi CSS
  3. Transformasi CSS
  4. Kueri Media CSS untuk area pandang dan preferensi pengguna
  5. JS untuk focus Peningkatan UX

Solusi saya memiliki satu bilah sisi dan beralih hanya saat berada di area pandang “seluler” dari 540px atau kurang. 540px akan menjadi titik jeda kami untuk beralih antara tata letak interaktif seluler dan tata letak desktop statis.

CSS :target kelas semu #

Satu <a> tautan menyetel hash url ke #sidenav-open dan yang lainnya untuk dikosongkan (''). Terakhir, elemen memiliki id untuk mencocokkan 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>

Mengklik setiap link ini mengubah status hash dari URL halaman kita, kemudian dengan pseudo-class saya menampilkan dan menyembunyikan sidenav:

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

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

Kisi CSS #

Di masa lalu, saya hanya menggunakan tata letak dan komponen sidenav posisi absolut atau tetap. Grid, dengan nya grid-area sintaks, memungkinkan kita menetapkan beberapa elemen ke baris atau kolom yang sama.

Tumpukan #

Elemen tata letak utama #sidenav-container adalah kisi yang membuat 1 baris dan 2 kolom, masing-masing 1 diberi nama stack. Saat ruang dibatasi, CSS menetapkan semua <main> anak elemen ke nama petak yang sama, menempatkan semua elemen ke dalam ruang yang sama, membuat tumpukan.

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

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

Itu <aside> adalah elemen animasi yang berisi navigasi samping. Ini memiliki 2 anak: wadah navigasi <nav> bernama [nav] dan latar belakang <a> bernama [escape], yang digunakan untuk menutup menu.

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

Menyesuaikan 2fr & 1fr untuk menemukan rasio yang Anda suka untuk tampilan menu dan tombol tutup spasi negatifnya.

Demo tentang apa yang terjadi jika Anda mengubah rasio.

Transformasi & Transisi 3D CSS #

Tata letak kami sekarang ditumpuk pada ukuran area pandang seluler. Sampai saya menambahkan beberapa gaya baru, itu menutupi artikel kita secara default. Inilah beberapa UX yang saya bidik di bagian selanjutnya ini:

  • Menganimasikan buka dan tutup
  • Hanya menganimasikan dengan gerakan jika pengguna setuju dengan itu
  • Menghidupkan visibility jadi fokus keyboard tidak memasuki elemen di luar layar

Saat saya mulai menerapkan animasi gerak, saya ingin memulai dengan mengutamakan aksesibilitas.

Gerakan yang dapat diakses #

Tidak semua orang menginginkan pengalaman gerakan geser keluar. Dalam solusi kami, preferensi ini diterapkan dengan menyesuaikan a --duration Variabel CSS 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 sidenav kita geser terbuka dan tertutup, jika pengguna lebih suka gerakan yang dikurangi, saya langsung memindahkan elemen ke tampilan, mempertahankan status tanpa gerakan.

Transisi, ubah, terjemahkan #

Sidenav keluar (default) #

Untuk menyetel status default sidenav kami di seluler ke status di luar layar, saya memposisikan elemen dengan transform: translateX(-110vw).

Catatan, saya menambahkan yang lain 10vw ke kode di luar layar biasa dari -100vw, untuk memastikan box-shadow dari sidenav tidak mengintip ke viewport utama saat tersembunyi.

@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);
}
}
Sidenav di #

Ketika #sidenav elemen cocok sebagai :target, mengatur translateX() posisi ke basis 0, dan perhatikan saat CSS menggeser elemen dari posisi keluarnya -110vw, ke posisi “dalam” dari 0 lebih var(--duration) ketika 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 menyembunyikan menu dari pembaca layar saat keluar, sehingga sistem tidak menempatkan fokus ke menu di luar layar. Saya mencapai ini dengan mengatur transisi visibilitas saat :target perubahan.

  • Saat masuk, jangan transisi visibilitas; langsung terlihat sehingga saya bisa melihat elemen meluncur masuk dan menerima fokus.
  • Saat keluar, transisikan visibilitas tetapi tunda, sehingga membalik ke hidden di akhir transisi keluar.

Peningkatan aksesibilitas UX #

Solusi ini bergantung pada perubahan URL agar negara dapat dikelola. Secara alami, file <a> elemen harus digunakan di sini, dan mendapat beberapa fitur aksesibilitas yang bagus secara gratis. Mari menghiasi elemen interaktif kita dengan label yang secara jelas mengartikulasikan maksud.

<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 sulih suara dan interaksi keyboard UX.

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

:is(:hover, :focus) #

Pseudo-selector fungsional CSS yang berguna ini memungkinkan kita dengan cepat menjadi inklusif dengan gaya hover kita dengan membagikannya dengan fokus juga.

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

Taburkan di Javascript #

tekan escape untuk menutup #

Itu Escape tombol pada keyboard Anda harus menutup menu kan? Mari kita pasang itu.

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

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

Fokus UX #

Cuplikan berikutnya membantu kita memfokuskan pada tombol buka dan tutup setelah tombol buka atau tutup. Saya ingin membuat toggling menjadi mudah.

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. Saat sidenav tertutup, fokuskan tombol buka. Saya melakukan ini dengan menelepon focus() pada elemen di JavaScript.

Kesimpulan #

Sekarang Anda tahu bagaimana saya melakukannya, bagaimana Anda ?! Ini membuat beberapa arsitektur komponen menyenangkan! Siapa yang akan membuat versi pertama dengan slot? ?

Mari mendiversifikasi 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.

Terakhir diperbarui: Perbaiki artikel