Codelab: Membuat komponen Sidenav

Codelab ini mengajarkan cara membuat komponen tata letak navigasi samping slide yang responsif di web. Kita akan membuat komponen sambil berjalan, mulai dengan HTML, lalu CSS, lalu JavaScript.

Lihat postingan blog saya yang berjudul Membangun komponen Sidenav untuk mempelajari fitur platform web CSS yang dipilih untuk mem-build komponen ini.

Penyiapan

  1. Klik Remix untuk Mengedit agar project dapat diedit.
  2. Buka app/index.html.

HTML

Pertama, dapatkan dasar-dasar pengaturan HTML sehingga ada konten dan beberapa kotak untuk dikerjakan.

Letakkan HTML berikut ke dalam tag <body>.

<aside></aside>
<main></main>

<aside> menyimpan menu navigasi sebagai elemen pelengkap untuk <main>, yang menyimpan konten halaman utama.

Selanjutnya, kita akan mengisi elemen semantik tersebut dengan konten halaman lainnya.

Tambahkan elemen navigasi, beberapa link navigasi, dan link tutup di dalam elemen <aside>.

<aside>
  <nav>
    <h4>My</h4>
    <a href="#">Dashboard</a>
    <a href="#">Profile</a>
    <a href="#">Preferences</a>
    <a href="#">Archive</a>

    <h4>Settings</h4>
    <a href="#">Accessibility</a>
    <a href="#">Theme</a>
    <a href="#">Admin</a>
  </nav>

  <a href="#"></a>
</aside>

Link berfungsi dengan baik di dalam elemen <nav>, dan elemen <nav> terlihat bagus di sidebar <aside>. Namun, masih ada hal yang dapat kami lakukan untuk meningkatkannya.

Di elemen konten utama, tambahkan header dan artikel untuk menyimpan konten tata letak secara semantik.

<main>
  <header>
    <a href="#sidenav-open" class="hamburger">
      <svg viewBox="0 0 50 40">
        <line x1="0" x2="100%" y1="10%" y2="10%" />
        <line x1="0" x2="100%" y1="50%" y2="50%" />
        <line x1="0" x2="100%" y1="90%" y2="90%" />
      </svg>
    </a>
    <h1>Site Title</h1>
  </header>

  <article>
    {put some placeholder content here}
  </article>
</main>

Header memiliki link menu terbuka. Bagian samping memiliki tombol tutup. Kita akan segera menampilkan dan menyembunyikan elemen berdasarkan ukuran area pandang.

Dalam elemen <article>, kita menempelkan kalimat placeholder. Ganti `` dengan barang Anda sendiri, atau tempel {i>lorem<i} yang disediakan di bawah ini:

<h2>Totam Header</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum consectetur, necessitatibus velit officia ut impedit veritatis temporibus soluta? Totam odit cupiditate facilis nisi sunt hic necessitatibus voluptatem nihil doloribus! Enim.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead Totam Odit</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

Konten ini, dan panjangnya, akan menyebabkan halaman dapat di-scroll jika melebihi tinggi area pandang Anda.

Sejauh ini Anda telah menambahkan elemen samping, dengan navigasi, tautan, dan cara untuk menutup panel samping. Anda juga menambahkan header, cara untuk membuka panel samping, dan artikel ke elemen utama. Ini sudah bersih, semantik, dan sudah lama sekali, tapi kita bisa membuatnya lebih bersih dan lebih jelas bagi semua orang. Tautan yang terbuka di {i>sidenav<i} dapat ditandai dengan lebih jelas.

Tambahkan atribut title dan aria-label ke elemen link terbuka header:

<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">

Ikon SVG yang terbuka juga dapat ditandai dengan lebih jelas. Tambahkan atribut berikut ke SVG di dalam elemen open link:

<svg viewBox="0 0 50 40">
<svg viewBox="0 0 50 40" role="presentation" focusable="false" aria-label="trigram for heaven symbol">

Tautan tutup di panel samping dapat ditandai dengan lebih jelas. Tambahkan atribut title dan aria-label ke elemen link tutup sidenav:

<a href="#"></a>
<a href="#" title="Close Menu" aria-label="Close Menu"></a>

CSS

Waktunya mengatur tata letak elemen. Konten utama dan sidenav adalah turunan langsung dari tag <body>, jadi ini adalah tempat yang baik untuk memulai.

Tambahkan CSS berikut ke dalam css/sidenav.css sehingga elemen <body> menata letak turunannya.

body {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;

  @media (max-width: 540px) {
    & > :matches(aside, main) {
      grid-area: stack;
    }
  }
}

Tata letak ini pada dasarnya mengatakan: Buat baris bernama stack dengan semua yang ada di dalamnya, dan 2 kolom di baris tersebut, yang ke-2 juga bernama stack. Kolom pertama harus disesuaikan dengan kebutuhan konten minimal, dan kolom ke-2 dapat mengisi sisanya. Kemudian, jika dalam area tampilan terbatas 540px atau kurang, tempatkan elemen sidenav dan konten utama ke dalam baris dan kolom yang sama, sehingga keduanya berada di atas satu sama lain dalam petak 1x1.

Dengan fungsi tumpukan responsif ini sebagai dasar, kini kita dapat memanfaatkan status kolom URL untuk mengganti gaya visibilitas dan transisi sidebar.

Update elemen <aside> kembali di app/index.html:

<aside>
<aside id="sidenav-open">

Hal ini memungkinkan CSS untuk mencocokkan elemen dan hash URL secara bersamaan. Hal ini penting untuk penggunaan :target. Sekarang ID elemen dapat cocok dengan hash URL yang akan kita tetapkan dengan tag <a>.

Selain itu, untuk memudahkan penargetan JavaScript, tambahkan ID untuk elemen utama yang mengontrol sidebar. Pertama, tambahkan ID ke link buka sidebar:

<a href="#sidenav-open" class="hamburger" title="Open Menu" aria-label="Open Menu">
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">

Selanjutnya, tambahkan ID ke link tutup panel samping:

<a href="#" title="Close Menu" aria-label="Close Menu"></a>
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

Ini mencakup tata letak tumpukan responsif <body> makro, dan mengikat kita ke dalam kolom URL. Mari kita lanjutkan!

<aside> juga memiliki tata letak yang rapi. Dependensi ini memiliki 2 turunan, <nav> yang merupakan komponen seperti kertas yang dapat bergeser keluar, dan elemen link <a> penutup yang menetapkan URL ke #. Tautan tidak terlihat di sebelah kanan navigasi geser ke luar kertas; karena pengguna dapat "mengeklik" komponen visual untuk menutupnya.

Tambahkan CSS berikut ke css/sidenav.css:

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

Saya pikir rasio dan nama adalah sentuhan yang sangat bagus di sini, di mana {i>grid<i} dapat bersinar dan memberi desainer banyak kontrol.

Selanjutnya, saya perlu menempatkan konten utama secara bersyarat dan mempertahankan posisi saya di antara scroll dokumen. Ini adalah tugas yang bagus untuk position: sticky dan beberapa overscroll-behavior.

Tambahkan gaya berikut untuk panel samping:

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

  @media (max-width: 540px) {
    position: sticky;
    top: 0;
    max-height: 100vh;
    overflow: hidden auto;
    overscroll-behavior: contain;

    visibility: hidden; /* not keyboard accessible when closed */
  }
}

Gaya tersebut memastikan panel samping sesuai dengan tinggi area pandang, di-scroll secara vertikal dan berisi scroll. Yang terpenting, ia menyembunyikan elemen. Secara default, jika area tampilan berukuran 540px atau lebih kecil, sembunyikan panel samping tersebut. Kecuali!

Tambahkan pemilih pseudo :target ke elemen #sidenav-open:

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

Jika ID elemen tersebut dan kolom URL sama, tetapkan visibility ke visible. Lanjutkan dan buka menu samping setelah men-scroll halaman, atau coba scroll halaman saat panel samping terbuka. Bagaimana menurut Anda?

Tambahkan CSS berikut ke bagian bawah app/sidenav.css:

#sidenav-button,
#sidenav-close {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;
  touch-action: manipulation;

  @media (min-width: 540px) {
    display: none;
  }
}

Gaya ini menargetkan tombol buka dan tutup, menentukan gaya ketuk dan sentuh, serta menyembunyikannya saat area pandang berukuran 540px atau lebih besar.

Untuk beberapa gaya, mari kita tambahkan transformasi CSS dengan aksesibilitas yang baik. Tambahkan CSS berikut ke css/sidenav.css:

#sidenav-open {
  --easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1);
  --duration: .6s;

  ...

  @media (max-width: 540px) {
    ...

    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);

    &:target {
      visibility: visible;
      transform: translateX(0);
      transition: transform var(--duration) var(--easeOutExpo);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    --duration: 1ms;
  }
}
Demo interaksi dengan dan tanpa penerapan durasi berdasarkan kueri media `prefers-Reduced-motion`.

Menambahkan beberapa JavaScript

Tombol Escape akan menutup menu. Tambahkan JS ini ke js/index.js:

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

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

Tindakan ini akan memproses peristiwa tombol pada elemen sidenav. Jika ditetapkan ke Escape, kode ini akan menetapkan hash URL ke kosong, sehingga transisi sidenav keluar.

Bagian UX JS berikutnya adalah {i>focus management<i} (manajemen fokus). Saya ingin membuka dan menutup dengan mudah, jadi saya menunggu hingga panel navigasi selesai melakukan transisi, lalu memeriksanya dengan hash URL untuk menentukan apakah panel masuk atau keluar. Saya menggunakan JavaScript untuk menyetel fokus secara gratis ke tombol yang baru saja ditekan.

Tambahkan JavaScript berikut ke js/index.js:

const closenav = document.querySelector('#sidenav-close');
const opennav = document.querySelector('#sidenav-button');

sidenav.addEventListener('transitionend', e => {
  if (e.propertyName !== 'transform') {
    return;
  }

  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
    ? closenav.focus()
    : opennav.focus();
});

Cobalah

  • Untuk melihat pratinjau situs, tekan Lihat Aplikasi. Lalu tekan Layar Penuh layar penuh.

Kesimpulan

Itulah rangkuman dari kebutuhan yang saya miliki dengan komponen. Jangan ragu untuk mengembangkannya, menguasainya dengan status JavaScript, bukan URL, dan secara umum, jadikan URL tersebut milik Anda. Selalu ada lebih banyak hal untuk ditambahkan atau lebih banyak kasus penggunaan.

Buka css/brandnav.css untuk melihat gaya yang tidak terkait dengan tata letak yang saya terapkan ke komponen ini. Saya tidak merasa hal ini penting bagi set fitur yang saya fokuskan, dan saya berharap bahwa memisahkan gaya dari tata letak akan mendorong salin dan tempel. Mungkin ada lebih banyak pelajaran untuk Anda di sana!

Bagaimana cara membuat slide keluar komponen sidenav responsif? Pernahkah Anda memiliki lebih dari 1, seperti salah satunya di kedua sisi? Saya ingin menampilkan solusi Anda dalam video YouTube. Pastikan untuk menyampaikan komentar Anda atau memberikan komentar di YouTube mengenai kode Anda, hal itu akan membantu semua orang.