Ringkasan dasar tentang cara membuat slide sidenav yang responsif
Dalam postingan ini, saya ingin berbagi dengan Anda cara membuat prototipe komponen Sidenav untuk web yang responsif, stateful, mendukung navigasi keyboard, berfungsi dengan dan tanpa JavaScript, serta berfungsi di seluruh browser. Coba demonya.
Jika Anda lebih suka video, berikut versi YouTube postingan ini:
Ringkasan
Membangun sistem navigasi yang responsif itu sulit. Sebagian pengguna akan menggunakan {i>keyboard<i}, sebagian lagi menggunakan {i>desktop<i} yang canggih, dan sebagian lagi akan mengunjungi {i>website<i} dari perangkat seluler yang kecil. Setiap orang yang mengunjungi harus dapat membuka dan menutup menu.
Taktik Web
Dalam eksplorasi komponen ini, saya merasa senang dapat menggabungkan beberapa fitur platform web yang penting:
- CSS
:target
- Petak CSS
- transforms CSS
- Kueri Media CSS untuk area pandang dan preferensi pengguna
- JS untuk
focus
peningkatan UX
Solusi saya memiliki satu sidebar dan beralih hanya ketika berada di area pandang "seluler" 540px
atau kurang.
540px
akan menjadi titik henti sementara untuk beralih antara tata letak interaktif seluler dan tata letak desktop statis.
Pseudo-class :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>
Mengklik setiap link ini akan mengubah status hash URL halaman kita, lalu dengan kelas pseudo, saya tampilkan dan sembunyikan panel samping:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
Petak CSS
Sebelumnya, saya hanya menggunakan tata letak dan komponen
sidenav posisi absolut atau tetap. Namun, Grid, dengan sintaksis grid-area
-nya,
memungkinkan kita menetapkan beberapa elemen ke baris atau kolom yang sama.
Tumpukan
Elemen tata letak utama #sidenav-container
adalah petak yang membuat 1 baris dan 2 kolom,
1 barisnya masing-masing bernama stack
. Jika ruang dibatasi, CSS akan menetapkan semua turunan elemen
<main>
ke nama petak yang sama, sehingga menempatkan semua elemen ke dalam ruang yang sama, sehingga menghasilkan 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. Menu 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
untuk menemukan rasio yang Anda inginkan untuk overlay menu dan tombol tutup ruang negatifnya.
Transformasi & transisi 3D CSS
Tata letak kita sekarang ditumpuk pada ukuran area pandang seluler. Sampai saya menambahkan beberapa gaya baru, itu akan menghamparkan artikel kami secara {i>default<i}. Berikut adalah beberapa UX yang saya cari di bagian selanjutnya:
- Menganimasikan buka dan tutup
- Hanya animasikan dengan gerakan jika pengguna setuju dengan itu
- Menganimasikan
visibility
agar fokus keyboard tidak memasuki elemen di balik layar
Saat saya mulai menerapkan animasi gerak, saya ingin memulai dengan aksesibilitas sebagai prioritas.
Gerakan yang dapat diakses
Tidak semua orang menginginkan pengalaman gerakan {i>slide out<i}. 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;
}
}
Sekarang, ketika panel samping kita bergeser terbuka dan tertutup, jika pengguna lebih memilih gerakan yang dikurangi, saya akan langsung memindahkan elemen ke tampilan, mempertahankan status tanpa gerakan.
Transisi, transformasi, terjemahkan
Sidenav keluar (default)
Untuk menetapkan status default sidebar di perangkat seluler ke status offscreen,
saya memosisikan elemen dengan transform: translateX(-110vw)
.
Perhatikan, saya menambahkan 10vw
lain ke kode offscreen standar -100vw
,
untuk memastikan box-shadow
sidenav tidak mengintip 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);
}
}
Sidenav di
Saat elemen #sidenav
cocok dengan :target
, tetapkan posisi translateX()
ke homebase 0
,
dan perhatikan saat CSS menggeser elemen dari posisi keluar -110vw
, ke posisi "dalam" 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,
sehingga sistem tidak menempatkan fokus ke dalam menu {i>offscreen<i}. Saya melakukannya dengan menetapkan
transisi visibilitas saat :target
berubah.
- Saat masuk, jangan mentransisikan visibilitas; langsung terlihat sehingga saya dapat melihat elemen bergeser masuk dan menerima fokus.
- Saat keluar, lakukan transisi visibilitas tetapi tunda, sehingga akan beralih ke
hidden
di akhir transisi keluar.
Peningkatan aksesibilitas UX
Link
Solusi ini bergantung pada perubahan URL agar status dapat dikelola.
Tentu saja, elemen <a>
harus digunakan di sini, dan mendapatkan beberapa fitur aksesibilitas yang bagus
secara gratis. Mari kita hiasi elemen interaktif kita dengan label yang mengutarakan maksud yang 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 menyatakan dengan jelas tujuannya untuk {i>mouse<i} dan {i>keyboard<i}.
:is(:hover, :focus)
Pemilih semu fungsional CSS yang praktis ini memungkinkan kita untuk cepat inklusif dengan gaya pengarahan kursor dengan membagikannya dengan fokus.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
Tambahkan di JavaScript
Tekan escape
untuk menutup
Tombol Escape
pada keyboard akan menutup menu bukan? Mari kita menghubungkannya.
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 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 UX
Cuplikan berikutnya membantu kita memfokuskan pada tombol buka dan tutup setelah tombol terbuka atau tertutup. Saya ingin beralih mudah.
sidenav.addEventListener('transitionend', e => {
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? document.querySelector('#sidenav-close').focus()
: document.querySelector('#sidenav-button').focus();
})
Saat panel samping terbuka, fokuskan tombol tutup. Saat {i>sidenav<i} ditutup,
fokus pada tombol {i>open<i}. Saya melakukannya dengan memanggil focus()
pada elemen di JavaScript.
Kesimpulan
Sekarang Anda tahu bagaimana saya melakukannya, bagaimana Anda akan?! Inilah beberapa arsitektur komponen yang menyenangkan! Siapa yang akan membuat versi pertama dengan slot? 🙂
Mari kita lakukan diversifikasi pendekatan dan mempelajari semua cara untuk membangun di web. Buat Glitch, tweet me 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