Ringkasan dasar tentang cara membuat sidenav slide responsif
Dalam posting ini saya ingin berbagi dengan Anda bagaimana saya membuat prototipe komponen Sidenav untuk web yang responsif, stateful, mendukung navigasi {i>keyboard<i}, dapat digunakan dengan dan tanpa JavaScript, dan berfungsi di seluruh 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 {i>desktop<i} yang canggih, dan beberapa lainnya mengunjunginya dari perangkat seluler kecil. Setiap orang yang berkunjung harus dapat membuka dan menutup menu.
Taktik Web
Dalam eksplorasi komponen ini, saya senang menggabungkan beberapa fitur platform web penting:
- CSS
:target
- Petak CSS
- Transformasi CSS
- Kueri Media CSS untuk area pandang dan preferensi pengguna
- JS untuk
focus
peningkatan UX
Solusi saya memiliki satu sidebar dan beralih hanya saat berada di "seluler" area pandang 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
yang cocok 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>
Mengklik setiap tautan ini akan mengubah status {i>hash <i}dari URL laman kita, kemudian dengan kelas semu, saya menampilkan dan menyembunyikan sidenav:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
Petak CSS
Sebelumnya, saya hanya menggunakan posisi absolut atau tetap
komponen dan tata letak panel navigasi. Namun, dengan sintaksis grid-area
,
memungkinkan kita 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 dari setiap gambar diberi nama stack
. Saat ruang terbatas, CSS akan menetapkan semua elemen <main>
turunan dengan nama {i>grid<i} 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;
}
}
Tampilan latar menu
<aside>
adalah elemen animasi yang berisi navigasi samping. 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
untuk menemukan rasio yang Anda sukai untuk overlay menu dan tombol tutup ruang negatifnya.
Transformasi 3D CSS & transisi
Tata letak kita sekarang ditumpuk pada ukuran area pandang seluler. Sampai saya menambahkan beberapa gaya baru, ini menempatkan artikel kita 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 {i>motion<i} (jika tersedia).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Sekarang ketika panel navigasi bergeser terbuka dan tertutup, jika pengguna lebih suka mengurangi gerakan, Saya langsung memindahkan elemen ke dalam tampilan, mempertahankan status tanpa gerakan.
Transisi, transformasi, terjemahan
Sidenav keluar (default)
Untuk mengatur status {i>default<i} dari navigasi
samping kita ke keadaan {i>offscreen<i},
Saya memosisikan elemen dengan transform: translateX(-110vw)
.
Catatan, saya menambahkan 10vw
lain ke kode offscreen standar -100vw
,
untuk memastikan box-shadow
dari sidenav tidak masuk 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
Jika elemen #sidenav
cocok sebagai :target
, setel posisi translateX()
ke homebase 0
,
dan perhatikan saat CSS menggeser elemen dari posisi keluar -110vw
, ke "in"
posisi 0
di atas 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 {i>screen reader<i} ketika keluar,
agar sistem tidak menempatkan
fokus di menu di luar layar. Saya melakukan ini
dengan menyetel
transisi visibilitas saat :target
berubah.
- Saat masuk, jangan mengalihkan visibilitas; langsung 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
Link
Solusi ini bergantung pada perubahan URL agar status dapat dikelola.
Tentunya, elemen <a>
harus digunakan di sini, dan elemen ini mendapatkan beberapa aksesibilitas yang bagus
fitur baru 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>
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 dengan cepat menyertakan dengan gaya mengambang dengan membagikannya melalui 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 riwayat browser, tambahkan baris JavaScript berikut ke tombol {i>close<i}:
<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 mereka 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,
memfokuskan 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 mendiversifikasi pendekatan dan mempelajari segala cara untuk membangun di web. Buat Glitch, tweet saya versi Anda, dan saya akan menambahkannya ke Bagian Remix komunitas di bawah.
Remix komunitas
- @_developit dengan elemen kustom: demo & kode
- @mayeedwin1 dengan HTML/CSS/JS: demo & kode
- @a_nurella dengan Remix Glitch: demo & kode
- @EvroMalarkey dengan HTML/CSS/JS: demo & kode