Membuat komponen breadcrumb

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

Dalam postingan ini saya ingin berbagi pemikiran tentang cara membangun komponen breadcrumb. Coba demo.

Demo

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

Ringkasan

Komponen breadcrumb akan menampilkan posisi pengguna dalam hierarki situs. Namanya berasal dari Hansel dan Gretel, yang turun rebah di belakang mereka di hutan gelap dan dapat menemukan jalan pulang dengan melacak remah-remahnya ke belakang.

Breadcrumb dalam postingan ini bukan standar breadcrumb, modelnya seperti breadcrumb. Mereka menawarkan fungsi tambahan dengan menempatkan saudara halaman langsung ke navigasi dengan <select>, sehingga membuat akses multi-tingkat sebaik mungkin.

UX Latar Belakang

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

Komponen breadcrumb ini akan memungkinkan pengguna menelusuri hierarki informasi; melompati cabang dan memilih halaman dengan cepat dan tingkat akurasi.

{i>Information architecture<i} (arsitektur informasi)

Menurut saya, memikirkan koleksi dan barang memberikan manfaat.

Koleksi

Koleksi adalah array opsi yang dapat dipilih. Dari beranda prototipe breadcrumb dari postingan ini, koleksinya adalah FPS, RPG, brawler, dungeon crawler, olahraga, dan puzzle.

Item

Video game adalah item, koleksi tertentu juga bisa menjadi item jika mewakili koleksi lainnya. Misalnya, RPG adalah item dan pengumpulan data. Jika berupa item, pengguna akan berada di halaman koleksi tersebut. Misalnya, mereka berada di halaman RPG, yang menampilkan daftar game RPG, termasuk sub-kategori tambahan AAA, Indie, dan Dipublikasikan Sendiri.

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

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

Aplikasi atau {i>website<i} Anda akan memiliki arsitektur informasi (IA) kustom yang membuat array multidimensi berbeda, tapi saya berharap konsep halaman landing pengumpulan halaman dan hierarki traversal juga dapat masuk ke dalam breadcrumb Anda.

Tata letak

Markup

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

Skema gelap dan terang

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

Tag meta color-scheme di atas cuplikan memberi tahu browser bahwa halaman ini menginginkan browser terang dan gelap gaya. 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>

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

Ikon

Ketika sebuah ikon diulang pada halaman, SVG Elemen <use> berarti Anda bisa mendefinisikan path sekali, dan menggunakannya untuk semua ikon. Hal ini mencegah informasi jalur yang sama terulang, menyebabkan dokumen yang lebih besar dan potensi inkonsistensi jalur.

Untuk menggunakan teknik ini, tambahkan elemen SVG tersembunyi ke laman dan kurung ikon-ikon tersebut 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 berlanjut dengan bagian laman 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.

Tetapkan sekali, gunakan sebanyak yang Anda inginkan, dengan dampak performa halaman 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, bersembunyi mereka dari pengguna tersebut menghentikan mereka menambah derau yang tidak perlu.

Di sinilah breadcrumb tradisional dan breadcrumb di komponen ini berbeda. Biasanya, link ini hanya akan menjadi link <a>, tetapi saya telah menambahkan UX traversal dengan {i>sguised select<i}. Class .crumb bertanggung jawab untuk mengatur tata letak link dan ikon, sedangkan .crumbicon bertanggung jawab untuk menumpuk ikon dan memilih elemen bersama-sama. Saya menyebutnya {i>split-link<i} karena fungsinya sangat mirip dengan split-button, tapi 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>

Tautan dan beberapa opsi tidak istimewa tetapi menambahkan lebih banyak fungsi ke breadcrumb yang sederhana. Menambahkan title ke elemen <select> berguna untuk layar pengguna pembaca, memberi mereka informasi tentang tindakan tombol. Bagaimanapun juga memberikan bantuan yang sama kepada orang lain juga, Anda akan melihatnya di posisi terdepan iPad. Satu atribut memberikan konteks tombol kepada banyak pengguna.

Screenshot dengan elemen pilih yang tidak terlihat diarahkan ke atasnya beserta elemen
menampilkan tooltip kontekstual.

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). Saya kemudian memberikan setiap aria-hidden="true" karena bersifat dekoratif dan tidak sesuatu yang perlu dibacakan oleh {i>screen reader<i}.

Properti gap, yang dibahas berikutnya, membuat jaraknya menjadi mudah.

Gaya

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

Arah dan alur tata letak

DevTools menunjukkan perataan navigasi breadcrumb dengan overlay flexbox-nya
aplikasi baru.

Elemen navigasi utama nav.breadcrumbs menetapkan properti kustom cakupan untuk digunakan oleh anak-anak, dan jika tidak membuat garis horizontal yang disejajarkan secara vertikal tata letak. Ini memastikan bahwa remah-remah, {i>divider<i}, 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 secara vertikal sejajar dengan overlay flexbox.

Setiap .crumb juga membuat tata letak sejajar vertikal horizontal dengan beberapa gap, namun secara khusus menargetkan turunan tautannya dan menetapkan gaya white-space: nowrap. Hal ini sangat penting untuk breadcrumb multi-kata karena kami mereka ingin menggunakan {i>multi-line<i}. 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 lebih menarik dari beristirahat. Pengguna pembaca layar tidak hanya akan memiliki indikator yang jelas bahwa tautan untuk laman saat ini, kita telah menata gaya elemen secara visual untuk membantu pengguna yang mampu melihat mendapatkan pengalaman pengguna yang serupa.

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

Grid DevTools ditampilkan yang menempatkan tombol di tempat baris dan kolom berada
yang dinamai.

.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 pilihan yang sangat pas dengan bentuk ikon. Ini adalah cara yang bagus untuk menyesuaikan tampilan elemen <select> sekaligus dan mempertahankan fungsionalitas 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 sekali hal-hal menjadi keluar layar secara horizontal, jika diperlukan, dan saya merasakan hal ini breadcrumb yang 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 tambahan menyiapkan UX berikut:

  • Scroll horizontal dengan pembatasan overscroll.
  • Padding scroll horizontal.
  • Satu titik di remah terakhir. Ini berarti bahwa pada saat pemuatan laman, hal pertama pemuatan {i>crumb <i}terputus dan terlihat.
  • Menghapus titik snap dari Safari, yang berjuang dengan bidang horizontal kombinasi efek scroll dan snap.

Kueri media

Satu penyesuaian kecil untuk area pandang yang lebih kecil adalah dengan menyembunyikan menu "Beranda" label, menyisakan hanya ikon:

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

Berdampingan dari breadcrumb dengan dan tanpa label beranda, untuk
perbandingan.

Aksesibilitas

Gerakan

Tidak ada banyak {i>motion <i}dalam komponen ini, namun 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 pengarahan kursor dan fokus akan sangat bagus dan bermakna tanpa transition, tetapi jika gerakan dapat digunakan, kita akan menambahkan transisi ke interaksi.

JavaScript

Pertama, terlepas dari jenis {i>router<i} 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 diharapkan yang terjadi saat pengguna menjelajahi <select> lainnya.

Dua ukuran pengalaman pengguna yang penting harus ditangani oleh JavaScript: {i>select<i} memiliki yang berubah dan ingin mencegah pengaktifan peristiwa perubahan <select>.

Pencegahan peristiwa yang segera diperlukan karena penggunaan <select> . Di Windows Edge, dan mungkin juga browser lain, pilih changed dipicu saat pengguna menjelajahi opsi dengan keyboard. Inilah sebabnya saya disebut ingin, karena pengguna hanya memilih semu untuk memilih opsi, seperti mengarahkan kursor atau fokus, tetapi belum mengonfirmasi pilihan dengan enter atau click. Bersemangat membuat fitur perubahan kategori komponen ini tidak dapat diakses, karena membuka kotak pilihan dan menelusuri item akan mengaktifkan acara 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 ini adalah memantau peristiwa keyboard bawah pada setiap <select> dan menentukan apakah tombol yang ditekan adalah konfirmasi navigasi (Tab atau Enter) atau navigasi spasial (ArrowUp atau ArrowDown). Dengan ini determinasi, komponen dapat memutuskan untuk menunggu atau pergi, ketika peristiwa untuk Elemen <select> diaktifkan.

Kesimpulan

Sekarang setelah Anda tahu bagaimana saya melakukannya, bagaimana Anda akan 🙂

Mari kita diversifikasi pendekatan kami dan mempelajari semua cara untuk membangun di web. Buat demo, link tweet saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah ini.

Remix komunitas