Codelab: Membuat komponen Sidenav

Codelab ini mengajarkan cara mem-build komponen tata letak navigasi samping geser keluar yang responsif di web. Kita akan mem-build komponen seiring berjalannya waktu, dimulai dengan HTML, lalu CSS, lalu JavaScript.

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

Penyiapan

  1. Klik Remix to Edit agar project dapat diedit.
  2. Buka app/index.html.

HTML

Pertama, dapatkan dasar-dasar penyiapan HTML sehingga ada konten dan beberapa kotak yang dapat digunakan.

Masukkan 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> berfungsi dengan baik di sidebar <aside>. Namun, masih ada hal lain yang dapat kita lakukan untuk meningkatkan kualitas.

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 yang terbuka. Bagian samping memiliki tombol tutup. Kita akan segera menampilkan dan menyembunyikan elemen berdasarkan ukuran area pandang.

Di elemen <article>, kita menempelkan kalimat placeholder. Ganti `` dengan konten Anda sendiri, atau tempelkan lorem yang disediakan di bawah:

<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, adalah yang akan menyebabkan halaman dapat di-scroll jika melebihi tinggi area pandang Anda.

Sejauh ini Anda telah menambahkan elemen samping, dengan navigasi, tautan, dan cara menutup navigasi samping. Anda juga menambahkan header, cara membuka sidenav, dan artikel ke elemen utama. Hal ini sudah bersih, semantik, dan sudah tidak lekang waktu, tetapi kita dapat membuatnya lebih bersih dan lebih jelas untuk semua orang. Tautan terbuka di panel navigasi 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 terbuka juga dapat ditandai dengan lebih jelas. Tambahkan atribut berikut ke SVG di dalam elemen link terbuka:

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

Link tutup di menu 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

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

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

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 menyatakan: Buat baris bernama stack dengan semua isinya, dan 2 kolom di baris tersebut, yang ke-2 juga bernama stack. Kolom ke-1 harus disesuaikan dengan kebutuhan konten minimalnya, dan kolom ke-2 dapat mengisi sisanya. Kemudian, jika berada di area tampilan terbatas 540px atau kurang, tempatkan elemen sidenav dan elemen konten utama ke dalam baris dan kolom yang sama, sehingga keduanya saling tumpang-tindih dalam petak 1x1.

Dengan fungsi penumpukan responsif ini sebagai dasar, sekarang kita dapat memanfaatkan status kolom URL untuk mengalihkan visibilitas dan gaya transisi sidenav.

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

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

Hal ini memungkinkan CSS 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 sidenav. Pertama, tambahkan ID ke link buka sidenav:

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

Berikutnya, tambahkan ID ke tautan tutup sidenav:

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

Tindakan ini akan menggabungkan tata letak penumpukan responsif <body> makro, serta menghubungkan kita ke kolom URL. Mari kita lanjutkan!

<aside> juga memiliki tata letak yang rapi. Elemen ini memiliki 2 turunan, <nav> yang merupakan komponen yang terlihat seperti kertas yang bergeser keluar, dan elemen link <a> penutup yang menetapkan URL harus #. Link tidak terlihat di sebelah kanan navigasi slide keluar kertas; sehingga orang dapat "mengklik nonaktif" 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, tempat petak dapat bersinar dan memberi desainer banyak kontrol.

Selanjutnya, saya perlu menempatkan konten utama secara kondisional dan mempertahankan posisi saya melalui scroll dokumen apa pun. Ini adalah pekerjaan yang bagus untuk position: sticky dan beberapa overscroll-behavior.

Tambahkan gaya berikut untuk sidenav:

#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 sidenav adalah tinggi area tampilan, men-scroll secara vertikal dan berisi scroll. Yang sangat penting, tindakan ini akan menyembunyikan elemen. Secara default, saat area pandang adalah 540px atau lebih kecil, sembunyikan sidenav 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. Buka menu samping setelah men-scroll halaman, atau coba scroll halaman saat sidenav 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 tampilan berukuran 540px atau lebih besar.

Untuk menambahkan sedikit 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 durasi yang diterapkan 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 = '';
  }
});

Ini memproses peristiwa utama pada elemen sidenav. Jika Escape, hash URL akan ditetapkan ke kosong, sehingga sidenav akan bertransisi keluar.

Bagian UX JS berikutnya adalah pengelolaan fokus. Saya ingin mempermudah pembukaan dan penutupan, jadi saya menunggu hingga sidenav menyelesaikan semacam transisi, lalu memeriksanya dengan hash URL untuk menentukan apakah sidenav terbuka atau tertutup. Saya kemudian menggunakan {i>JavaScript<i} untuk mengatur fokus tombol menjadi bebas dari fokus 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 View App. Lalu, tekan Fullscreen layar penuh.

Kesimpulan

Itulah rangkuman kebutuhan saya dengan komponen. Jangan ragu untuk membangunnya, dorong dengan status JavaScript, bukan URL, dan secara umum buatlah menjadi milik Anda. Selalu ada hal lain yang dapat ditambahkan atau kasus penggunaan lain yang dapat dicakup.

Buka css/brandnav.css untuk melihat gaya terkait non-tata letak yang saya terapkan ke komponen ini. Saya merasa hal ini tidak penting untuk kumpulan fitur yang saya fokuskan, dan saya berharap bahwa memisahkan gaya dari tata letak akan mendorong penyalinan dan penempelan. Mungkin ada lebih banyak pembelajaran untuk Anda di sana.

Bagaimana cara membuat komponen sidenav responsif yang dapat di-slide? Apakah Anda pernah memiliki lebih dari 1, seperti satu di kedua sisi? Kami ingin menampilkan solusi Anda dalam video YouTube. Pastikan untuk mengirim tweet kepada kami atau memberi komentar di YouTube dengan kode Anda. Hal ini akan membantu semua orang yang menonton.