Membuat komponen breadcrumb

Ringkasan dasar tentang cara membuat komponen breadcrumb yang responsif dan mudah diakses bagi pengguna untuk menavigasi situs Anda.

Dalam postingan ini, saya ingin membagikan pemikiran tentang cara membuat komponen breadcrumb. Coba demo.

Demo

Jika Anda lebih suka video, berikut versi YouTube dari postingan ini:

Ringkasan

Komponen breadcrumb menunjukkan posisi pengguna dalam hierarki situs. Namanya berasal dari kisah Hansel dan Gretel, yang menjatuhkan remah-remah di belakang mereka di hutan gelap dan dapat menemukan jalan pulang dengan melacak remah-remah ke belakang.

Breadcrumb dalam postingan ini bukan breadcrumb standar, tetapi mirip breadcrumb. Fungsi ini menawarkan fungsi tambahan dengan menempatkan halaman sejenis langsung ke navigasi dengan <select>, sehingga memungkinkan akses multi-tingkat.

UX Latar Belakang

Dalam video demo komponen di atas, kategori placeholder adalah genre video game. Jalur ini dibuat dengan menavigasi jalur berikut: home » rpg » indie » on sale, seperti yang ditunjukkan di bawah.

Komponen breadcrumb ini akan memungkinkan pengguna untuk menjelajahi hierarki informasi ini; melompat ke cabang dan memilih halaman dengan cepat dan akurat.

Arsitektur informasi

Menurut saya, sebaiknya Anda memikirkannya dalam hal koleksi dan item.

Koleksi

Koleksi adalah array opsi yang dapat dipilih. Dari halaman beranda prototipe breadcrumb postingan ini, koleksinya adalah FPS, RPG, brawler, dungeon crawler, olahraga, dan teka-teki.

Item

Video game adalah item, koleksi tertentu juga dapat berupa item jika mewakili koleksi lainnya. Misalnya, RPG adalah item dan koleksi yang valid. Jika berupa item, pengguna akan berada di halaman koleksi tersebut. Misalnya, subkategori tersebut berada di halaman RPG, yang menampilkan daftar game RPG, termasuk subkategori tambahan AAA, Indie, dan Self Published.

Dalam istilah ilmu komputer, komponen breadcrumb ini mewakili array multidimensi:

const rawBreadcrumbData = {
  "FPS": {...},
  "RPG": {
    "AAA": {...},
    "indie": {
      "new": {...},
      "on sale": {...},
      "under 5": {...},
    },
    "self published": {...},
  },
  "brawler": {...},
  "dungeon crawler": {...},
  "sports": {...},
  "puzzle": {...},
}

Aplikasi atau situs Anda akan memiliki arsitektur informasi (IA) kustom yang membuat array multidimensi yang berbeda, tetapi saya harap konsep halaman landing koleksi dan penelusuran hierarki juga dapat masuk ke breadcrumb Anda.

Tata letak

Markup

Komponen yang baik dimulai dengan HTML yang sesuai. Pada bagian selanjutnya, saya akan membahas pilihan markup dan bagaimana pengaruhnya terhadap komponen keseluruhan.

Skema gelap dan terang

<meta name="color-scheme" content="dark light">

Tag meta color-scheme dalam cuplikan di atas memberi tahu browser bahwa halaman ini menginginkan gaya browser terang dan gelap. Contoh breadcrumb tidak menyertakan CSS apa pun untuk skema warna ini, sehingga breadcrumb akan menggunakan warna default yang disediakan oleh browser.

<nav class="breadcrumbs" role="navigation"></nav>

Sebaiknya gunakan elemen <nav> untuk navigasi situs, yang memiliki peran navigasi ARIA implisit. Dalam pengujian, saya melihat bahwa memiliki atribut role mengubah cara pembaca layar berinteraksi dengan elemen, yang sebenarnya diumumkan sebagai navigasi, sehingga saya memilih untuk menambahkannya.

Ikon

Saat ikon diulang di halaman, elemen <use> SVG berarti Anda dapat menentukan path sekali, dan menggunakannya untuk semua instance ikon. Hal ini mencegah informasi jalur yang sama diulang, sehingga menyebabkan dokumen yang lebih besar dan potensi inkonsistensi jalur.

Untuk menggunakan teknik ini, tambahkan elemen SVG tersembunyi ke halaman dan gabungkan ikon dalam elemen <symbol> dengan ID unik:

<svg style="display: none;">

  <symbol id="icon-home">
    <title>A home icon</title>
    <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
  </symbol>

  <symbol id="icon-dropdown-arrow">
    <title>A down arrow</title>
    <path d="M19 9l-7 7-7-7"/>
  </symbol>

</svg>

Browser membaca HTML SVG, memasukkan informasi ikon ke dalam memori, dan melanjutkan dengan bagian halaman lainnya yang mereferensikan ID untuk penggunaan tambahan ikon, seperti ini:

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-home" />
</svg>

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-dropdown-arrow" />
</svg>

DevTools menampilkan elemen penggunaan SVG yang dirender.

Tentukan sekali, gunakan sebanyak yang Anda inginkan, dengan dampak performa halaman minimal dan gaya visual yang fleksibel. Perhatikan bahwa aria-hidden="true" ditambahkan ke elemen SVG. Ikon tidak berguna bagi pengguna yang hanya mendengarkan konten saat menjelajahi. Menyembunyikannya dari pengguna tersebut akan mencegah mereka menambahkan derau yang tidak perlu.

Di sinilah breadcrumb tradisional dan breadcrumb di komponen ini berbeda. Biasanya, ini hanya akan menjadi link <a>, tetapi saya telah menambahkan UX traversal dengan pilihan yang disamarkan. Class .crumb bertanggung jawab untuk menata letak link dan ikon, sedangkan .crumbicon bertanggung jawab untuk menumpuk ikon dan memilih elemen secara bersamaan. Saya menyebutnya link terpisah karena fungsinya sangat mirip dengan tombol terpisah, tetapi untuk navigasi halaman.

<span class="crumb">
  <a href="#sub-collection-b">Category B</a>
  <span class="crumbicon">
    <svg>...</svg>
    <select class="disguised-select" title="Navigate to another category">
      <option>Category A</option>
      <option selected>Category B</option>
      <option>Category C</option>
    </select>
  </span>
</span>

Link dan beberapa opsi tidak ada yang istimewa, tetapi menambahkan lebih banyak fungsi ke breadcrumb sederhana. Menambahkan title ke elemen <select> akan membantu pengguna pembaca layar, memberi mereka informasi tentang tindakan tombol. Namun, fitur ini juga memberikan bantuan yang sama kepada semua orang, Anda akan melihatnya di bagian depan dan tengah di iPad. Satu atribut memberikan konteks tombol kepada banyak pengguna.

Screenshot dengan elemen pilih yang tidak terlihat diarahkan kursor dan
tooltip kontekstualnya ditampilkan.

Dekorasi pemisah

<span class="crumb-separator" aria-hidden="true">→</span>

Pemisah bersifat opsional, menambahkan hanya satu juga dapat digunakan (lihat contoh ketiga dalam video di atas). Kemudian, saya memberikan setiap aria-hidden="true" karena bersifat dekoratif dan bukan sesuatu yang perlu diumumkan oleh pembaca layar.

Properti gap, yang akan dibahas berikutnya, membuat spasi ini menjadi mudah.

Gaya

Karena warna menggunakan warna sistem, sebagian besar warna merupakan celah dan tumpukan untuk gaya.

Arah dan alur tata letak

DevTools menampilkan perataan navigasi breadcrumb dengan fitur overlay flexbox-nya.

Elemen navigasi utama nav.breadcrumbs menetapkan properti kustom cakupan untuk digunakan oleh turunan, dan jika tidak, menetapkan tata letak horizontal yang sejajar secara vertikal. Hal ini memastikan bahwa crumbs, pembagi, dan ikon sejajar.

.breadcrumbs {
  --nav-gap: 2ch;

  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  padding: calc(var(--nav-gap) / 2);
}

Satu breadcrumb yang ditampilkan sejajar secara vertikal dengan overlay flexbox.

Setiap .crumb juga menetapkan tata letak horizontal yang sejajar secara vertikal dengan beberapa celah, tetapi secara khusus menargetkan turunan link-nya dan menentukan gaya white-space: nowrap. Hal ini penting untuk breadcrumb multi-kata karena kita tidak ingin breadcrumb tersebut menjadi multi-baris. Nanti dalam postingan ini, kita akan menambahkan gaya untuk menangani overflow horizontal yang disebabkan oleh properti white-space ini.

.crumb {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--nav-gap) / 4);

  & > a {
    white-space: nowrap;

    &[aria-current="page"] {
      font-weight: bold;
    }
  }
}

aria-current="page" ditambahkan untuk membantu link halaman saat ini tampil beda dari yang lainnya. Pengguna pembaca layar tidak hanya akan memiliki indikator yang jelas bahwa link tersebut untuk halaman saat ini, tetapi kami juga telah menata gaya elemen secara visual untuk membantu pengguna yang dapat melihat pengalaman pengguna yang serupa.

Komponen .crumbicon menggunakan petak untuk menumpuk ikon SVG dengan elemen <select> yang "hampir tidak terlihat".

Petak DevTools ditampilkan dengan menempatkan tombol di mana baris dan kolom
bernama stack.

.crumbicon {
  --crumbicon-size: 3ch;

  display: grid;
  grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
  place-items: center;

  & > * {
    grid-area: stack;
  }
}

Elemen <select> berada di urutan terakhir dalam DOM, sehingga berada di atas tumpukan, dan interaktif. Tambahkan gaya opacity: .01 agar elemen masih dapat digunakan, dan hasilnya adalah kotak pilih yang sangat sesuai dengan bentuk ikon. Ini adalah cara yang bagus untuk menyesuaikan tampilan elemen <select> sekaligus mempertahankan fungsi bawaan.

.disguised-select {
  inline-size: 100%;
  block-size: 100%;
  opacity: .01;
  font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}

Tambahan

Breadcrumb harus bisa mewakili jejak yang sangat panjang. Saya suka membiarkan segala sesuatunya keluar dari layar secara horizontal, jika perlu, dan saya merasa komponen breadcrumb ini memenuhi syarat dengan baik.

.breadcrumbs {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: calc(var(--nav-gap) / 2);

  & > .crumb:last-of-type {
    scroll-snap-align: end;
  }

  @supports (-webkit-hyphens:none) { & {
    scroll-snap-type: none;
  }}
}

Gaya overflow menyiapkan UX berikut:

  • Scroll horizontal dengan pembatasan overscroll.
  • Padding scroll horizontal.
  • Satu titik di remah terakhir. Artinya, saat halaman dimuat, crumb pertama akan dimuat dan terlihat.
  • Menghapus titik snap dari Safari, yang mengalami kesulitan dengan kombinasi efek snap dan scroll horizontal.

Kueri media

Salah satu penyesuaian halus untuk area pandang yang lebih kecil adalah menyembunyikan label "Beranda", sehingga hanya meninggalkan ikon:

@media (width <= 480px) {
  .breadcrumbs .home-label {
    display: none;
  }
}

breadcrumb secara berdampingan dengan dan tanpa label beranda, untuk perbandingan.

Aksesibilitas

Gerakan

Tidak ada banyak gerakan dalam komponen ini, tetapi dengan menggabungkan transisi dalam pemeriksaan prefers-reduced-motion, kita dapat mencegah gerakan yang tidak diinginkan.

@media (prefers-reduced-motion: no-preference) {
  .crumbicon {
    transition: box-shadow .2s ease;
  }
}

Tidak ada gaya lain yang perlu diubah, efek pengarahan kursor dan fokus sangat baik dan bermakna tanpa transition, tetapi jika gerakan tidak masalah, kita akan menambahkan transisi halus ke interaksi.

JavaScript

Pertama, terlepas dari jenis router yang Anda gunakan di situs atau aplikasi, saat pengguna mengubah breadcrumb, URL perlu diperbarui dan pengguna menampilkan halaman yang sesuai. Kedua, untuk menormalkan pengalaman pengguna, pastikan tidak ada navigasi yang tidak terduga yang terjadi saat pengguna hanya menjelajahi opsi <select>.

Dua ukuran pengalaman pengguna penting yang akan ditangani oleh JavaScript: pilih telah berubah dan pencegahan pengaktifan peristiwa perubahan <select> yang cepat.

Pencegahan peristiwa yang cepat diperlukan karena penggunaan elemen <select>. Di Windows Edge, dan mungkin juga browser lainnya, peristiwa changed yang dipilih akan diaktifkan saat pengguna menjelajahi opsi dengan keyboard. Itulah sebabnya saya menyebutnya eager, karena pengguna hanya memilih opsi secara pseudo, seperti mengarahkan kursor atau fokus, tetapi belum mengonfirmasi pilihan dengan enter atau click. Peristiwa eager membuat fitur perubahan kategori komponen ini tidak dapat diakses, karena membuka kotak pilih dan hanya menjelajahi item akan memicu peristiwa dan mengubah halaman, sebelum pengguna siap.

Perubahan peristiwa <select> yang lebih baik

const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])

// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
  let ignoreChange = false

  nav.addEventListener('change', e => {
    if (ignoreChange) return
    // it's actually changed!
  })

  nav.addEventListener('keydown', ({ key }) => {
    if (preventedKeys.has(key))
      ignoreChange = true
    else if (allowedKeys.has(key))
      ignoreChange = false
  })
})

Strategi untuk ini adalah dengan memantau peristiwa keyboard down di setiap elemen <select> dan menentukan apakah tombol yang ditekan adalah konfirmasi navigasi (Tab atau Enter) atau navigasi spasial (ArrowUp atau ArrowDown). Dengan determinasi ini, komponen dapat memutuskan untuk menunggu atau melanjutkan, saat peristiwa untuk elemen <select> diaktifkan.

Kesimpulan

Setelah Anda tahu cara saya melakukannya, bagaimana Anda melakukannya‽ 🙂

Mari kita diversifikasi pendekatan dan pelajari semua cara untuk mem-build di web. Buat demo, tweet link-nya, dan saya akan menambahkannya ke bagian remix komunitas di bawah.

Remix komunitas