Membuat komponen breadcrumb

Ringkasan dasar tentang cara membuat komponen breadcrumb yang responsif dan dapat diakses agar pengguna dapat 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 breadcrumbs menunjukkan posisi pengguna dalam hierarki situs. Nama ini berasal dari Hansel dan Gretel, yang menjatuhkan remah roti di belakang mereka di hutan gelap dan dapat menemukan jalan pulang dengan menelusuri remah roti ke belakang.

Breadcrumb dalam postingan ini bukan breadcrumb standar, melainkan menyerupai breadcrumb. Mereka menawarkan fungsi tambahan dengan menempatkan halaman turunan langsung ke navigasi dengan <select>, sehingga memungkinkan akses bertingkat.

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 harus memungkinkan pengguna berpindah melalui hierarki informasi ini; melompat ke cabang dan memilih halaman dengan cepat dan akurat.

Arsitektur informasi

Sebaiknya pikirkan dalam hal koleksi dan item.

Koleksi

Koleksi adalah serangkaian 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 menjadi item jika mewakili koleksi lain. Misalnya, RPG adalah item dan koleksi yang valid. Jika berupa item, pengguna berada di halaman koleksi tersebut. Misalnya, mereka berada di halaman RPG, yang menampilkan daftar game RPG, termasuk subkategori tambahan AAA, Indie, dan Self Published.

Dalam istilah ilmu komputer, komponen breadcrumb ini merepresentasikan 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 kami harap konsep halaman landing koleksi dan penelusuran hierarki juga dapat masuk ke breadcrumb Anda.

Tata letak

Markup

Komponen yang baik dimulai dengan HTML yang tepat. Di bagian berikutnya, saya akan membahas pilihan markup dan pengaruhnya terhadap keseluruhan komponen.

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. Breadcrumb contoh tidak menyertakan CSS untuk skema warna ini, sehingga breadcrumb akan menggunakan warna default yang disediakan oleh browser.

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

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

Ikon

Jika ikon diulang di halaman, elemen SVG <use> 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 bungkus 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, menempatkan informasi ikon ke dalam memori, dan melanjutkan dengan bagian halaman lainnya yang mereferensikan ID untuk penggunaan ikon tambahan, 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 yang menampilkan elemen penggunaan SVG yang dirender.

Tentukan sekali, gunakan sebanyak yang Anda inginkan, dengan dampak performa halaman yang minimal dan gaya yang fleksibel. Perhatikan bahwa aria-hidden="true" ditambahkan ke elemen SVG. Ikon tidak berguna bagi seseorang yang menjelajah dan hanya mendengar konten, sehingga menyembunyikannya dari pengguna tersebut akan menghentikan penambahan gangguan yang tidak perlu.

Di sinilah breadcrumb tradisional dan breadcrumb dalam komponen ini berbeda. Biasanya, ini hanya berupa link <a>, tetapi saya telah menambahkan UX penelusuran dengan pemilihan yang disamarkan. Class .crumb bertanggung jawab untuk menata link dan ikon, sedangkan .crumbicon bertanggung jawab untuk menumpuk ikon dan elemen pilihan bersama-sama. 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 tidaklah istimewa, tetapi menambahkan lebih banyak fungsi ke breadcrumb sederhana. Menambahkan title ke elemen <select> akan membantu pengguna pembaca layar, karena memberi mereka informasi tentang tindakan tombol. Namun, karena memberikan bantuan yang sama kepada orang lain, Anda akan melihatnya di bagian depan dan tengah di iPad. Satu atribut memberikan konteks tombol kepada banyak pengguna.

Screenshot dengan elemen pilihan yang tidak terlihat saat kursor diarahkan ke elemen tersebut dan tooltip kontekstualnya ditampilkan.

Dekorasi pemisah

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

Pemisah bersifat opsional, menambahkan satu pemisah juga sudah cukup (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 selanjutnya, membuat penspasian ini menjadi mudah.

Gaya

Karena warna menggunakan warna sistem, sebagian besar gaya berupa kesenjangan dan tumpukan.

Arah dan alur tata letak

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

Elemen navigasi utama nav.breadcrumbs menetapkan properti kustom yang tercakup untuk digunakan turunan, dan menetapkan tata letak yang diratakan vertikal secara horizontal. Hal ini memastikan bahwa elemen jejak, pemisah, dan ikon sejajar.

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

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

Satu breadcrumb ditampilkan sejajar secara vertikal dengan overlay flexbox.

Setiap .crumb juga membuat tata letak yang disejajarkan secara vertikal dan horizontal dengan beberapa jarak, tetapi secara khusus menargetkan turunan link-nya dan menentukan gaya white-space: nowrap. Hal ini sangat penting untuk breadcrumb multi-kata karena kita tidak ingin breadcrumb tersebut menjadi multi-baris. Di bagian selanjutnya dalam postingan ini, kita akan menambahkan gaya untuk menangani luapan 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 terlihat berbeda dari link lainnya. Pengguna pembaca layar tidak hanya akan memiliki indikator yang jelas bahwa link tersebut adalah untuk halaman saat ini, kami juga telah menata gaya elemen secara visual untuk membantu pengguna yang dapat melihat mendapatkan pengalaman pengguna yang serupa.

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

DevTools petak ditampilkan di atas tombol tempat baris dan kolom diberi nama stack.

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

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

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

Elemen <select> adalah yang terakhir dalam DOM, sehingga berada di atas tumpukan, dan interaktif. Tambahkan gaya opacity: .01 sehingga elemen masih dapat digunakan, dan hasilnya adalah kotak pilihan yang sangat pas dengan bentuk ikon. Ini adalah cara yang bagus untuk menyesuaikan tampilan elemen <select> sambil 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 dapat merepresentasikan jalur yang sangat panjang. Saya suka membiarkan sesuatu keluar dari layar secara horizontal, jika sesuai, dan saya merasa komponen breadcrumbs 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 penampungan overscroll.
  • Padding scroll horizontal.
  • Satu titik penempelan pada crumb terakhir. Artinya, saat pemuatan halaman, breadcrumb pertama dimuat dengan cepat dan terlihat.
  • Menghapus titik paskan dari Safari, yang mengalami masalah dengan kombinasi efek paskan dan scroll horizontal.

Kueri media

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

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

Perbandingan berdampingan dari breadcrumb dengan dan tanpa label beranda.

Aksesibilitas

Gerakan

Tidak banyak gerakan dalam komponen ini, tetapi dengan membungkus 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 mengarahkan kursor dan fokus sangat bagus dan bermakna tanpa transition, tetapi jika gerakan tidak masalah, kita akan menambahkan transisi halus ke interaksi.

JavaScript

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

Dua pengukuran pengalaman pengguna penting yang harus ditangani oleh JavaScript: pemilihan telah berubah dan pencegahan penyiapan peristiwa perubahan <select> yang bersemangat.

Pencegahan peristiwa yang ingin segera terjadi diperlukan karena penggunaan elemen <select>. Di Windows Edge, dan mungkin juga browser lain, peristiwa changed select diaktifkan saat pengguna menjelajahi opsi dengan keyboard. Itulah sebabnya saya menyebutnya bersemangat, karena pengguna hanya memilih opsi secara semu, seperti mengarahkan kursor atau memfokuskan, tetapi belum mengonfirmasi pilihan dengan enter atau click. Peristiwa langsung membuat fitur perubahan kategori komponen ini tidak dapat diakses, karena membuka kotak pilihan dan hanya menjelajahi item akan mengaktifkan peristiwa dan mengubah halaman, sebelum pengguna siap.

Perubahan acara <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
  })
})

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

Kesimpulan

Sekarang setelah Anda tahu cara saya melakukannya, bagaimana Anda‽ 🙂

Mari kita diversifikasi pendekatan kita dan pelajari semua cara untuk membangun di web. Buat demo, tweet linknya kepada saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah.

Remix komunitas