İçerik haritası bileşeni oluşturma

Kullanıcıların sitenizde gezinmesi için duyarlı ve erişilebilir bir içerik haritası bileşeni oluşturmaya dair temel bir genel bakış.

Bu yayında, içerik haritası bileşenlerini oluşturmanın bir yolunu paylaşmak istiyorum. Demoyu deneyin.

Demo

Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:

Genel Bakış

İçerik haritası bileşeni, kullanıcının site hiyerarşisindeki yerini gösterir. Bu terim, karanlık ormanda arkalarına ekmek kırıntıları bırakan ve geriye doğru izleyerek eve giden Hansel ile Gretel masalından gelir.

Bu yayındaki içerik haritası, standart içerik haritası değil, içerik haritasına benzer bir içerik haritasıdır. Kardeş sayfaları <select> ile doğrudan gezinme menüsüne ekleyerek ek işlevler sunar ve çok katmanlı erişimi mümkün kılar.

Arka plan kullanıcı deneyimi

Yukarıdaki bileşen demo videosunda yer tutucu kategoriler video oyunu türleridir. Bu iz, aşağıdaki yol izlenerek oluşturulur: home » rpg » indie » on sale, aşağıda gösterildiği gibi.

Bu içerik haritası bileşeni, kullanıcıların bu bilgi hiyerarşisinde gezinmesine, dallara atlamasına ve sayfaları hızlı ve doğru bir şekilde seçmesine olanak tanımalıdır.

Bilgi mimarisi

Koleksiyonlar ve öğeler açısından düşünmenin faydalı olduğunu düşünüyorum.

Koleksiyonlar

Koleksiyon, aralarından seçim yapabileceğiniz bir dizi seçenektir. Bu makalenin içerik haritası prototipinin ana sayfasında FPS, RPG, dövüş, zindan keşif, spor ve bulmaca koleksiyonları yer alıyor.

Öğe sayısı

Video oyunları öğedir. Belirli bir koleksiyon, başka bir koleksiyonu temsil ediyorsa öğe olarak da kabul edilebilir. Örneğin, RPG bir öğe ve geçerli bir koleksiyondur. Öğe olduğunda kullanıcı ilgili koleksiyon sayfasındadır. Örneğin, AAA, bağımsız ve kendi kendine yayınlanan ek alt kategoriler de dahil olmak üzere RPG oyunlarının listesini gösteren RPG sayfasında yer alırlar.

Bilgisayar bilimi terimleriyle bu içerik haritası bileşeni bir çok boyutlu dizi temsil eder:

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

Uygulamanız veya web siteniz farklı bir çok boyutlu dizi oluşturan özel bilgi mimarisine (IA) sahip olacaktır. Ancak koleksiyon açılış sayfaları ve hiyerarşi geçişi konseptinin de içerik haritalarına dahil edilmesini umuyorum.

Düzenler

Brüt kar

İyi bileşenler uygun HTML ile başlar. Bir sonraki bölümde, işaretleme seçeneklerimi ve bunların bileşeni nasıl etkilediğini ele alacağım.

Koyu ve açık şema

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

Yukarıdaki snippet'teki color-scheme meta etiketi, tarayıcıyı bu sayfanın açık ve koyu tarayıcı stillerini istediğini bilgilendirir. Örnek içerik haritaları bu renk şemaları için herhangi bir CSS içermez. Bu nedenle, içerik haritaları tarayıcı tarafından sağlanan varsayılan renkleri kullanır.

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

Site gezinme için <nav> öğesini kullanmak uygundur. Bu öğe, örtülü bir ARIA gezinme rolüne sahiptir. Test sırasında, role özelliğinin ekran okuyucunun öğeyle etkileşim şeklini değiştirdiğini fark ettim. Aslında gezinme olarak duyuruluyordu. Bu nedenle, bu özelliği eklemeyi tercih ettim.

Simgeler

Bir simge bir sayfada tekrarlandığında SVG <use> öğesi, path öğesini bir kez tanımlayıp simgenin tüm örnekleri için kullanabileceğiniz anlamına gelir. Bu, aynı yol bilgilerinin tekrarlanmasını önler. Böylece, daha büyük belgeler oluşur ve yol tutarsızlığı olasılığı ortaya çıkar.

Bu tekniği kullanmak için sayfaya gizli bir SVG öğesi ekleyin ve simgeleri benzersiz bir kimliğe sahip bir <symbol> öğesine sarmalayın:

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

Tarayıcı SVG HTML'sini okur, simge bilgilerini belleğe yerleştirir ve sayfanın geri kalanında simgenin diğer kullanımları için kimliğe referans vererek devam eder. Örneğin:

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

Oluşturulan bir SVG kullanım öğesini gösteren Geliştirici Araçları.

Bir kez tanımlayın, sayfa performansını en aza indirecek ve esnek bir stil sunacak şekilde istediğiniz kadar kullanın. SVG öğesine aria-hidden="true" eklendiğini fark edin. Simgeler, içeriğe göz atan ve yalnızca içeriği dinleyen kullanıcılar için yararlı değildir. Bu kullanıcılardan gizlendiğinde, gereksiz gürültü eklemelerini engeller.

Geleneksel içerik haritası ile bu bileşendeki içerik haritaları arasındaki fark burada ortaya çıkar. Normalde bu yalnızca bir <a> bağlantısı olurdu ancak gizlenmiş bir seçimle gezinme kullanıcı deneyimi ekledim. .crumb sınıfı, bağlantının ve simgesinin düzenlenmesinden sorumludur. .crumbicon sınıfı ise simgeyi ve seçili öğeyi birlikte yığmaktan sorumludur. İşlevleri bölünmüş düğmeye çok benzediği için bu bağlantıyı sayfa gezinme için bölünmüş bağlantı olarak adlandırdık.

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

Bağlantı ve bazı seçenekler, basit bir içerik haritasına daha fazla işlevsellik katan özel bir şey değildir. <select> öğesine title eklemek, ekran okuyucu kullanıcılarına düğmenin işlevi hakkında bilgi vererek onlara yardımcı olur. Ancak bu özellik diğer herkese de aynı yardımı sağlar. iPad'de bu özelliğin ön planda olduğunu göreceksiniz. Tek bir özellik, birçok kullanıcıya düğme bağlamı sağlar.

Fareyle üzerine gelindiğinde görünen görünmez seçim öğesinin ve içeriğe dayalı ipucunun gösterildiği ekran görüntüsü.

Ayırıcı süslemeleri

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

Ayırıcı eklemek isteğe bağlıdır. Yalnızca bir ayırıcı eklemek de işe yarar (yukarıdaki videodaki üçüncü örneği inceleyin). Ardından, ekran okuyucunun okuması gerekmediği için her birine aria-hidden="true" değeri veriyorum.

Aşağıda açıklanan gap özelliği, bu öğelerin aralığını kolaylaştırır.

Stiller

Renk, sistem renklerini kullandığından stiller için çoğunlukla boşluklar ve yığınlardan oluşur.

Düzen yönü ve akışı

Flexbox yer paylaşımı özelliğiyle içerik haritası gezinme hizalamasını gösteren Geliştirici Araçları.

Birincil gezinme öğesi nav.breadcrumbs, alt öğelerin kullanabileceği kapsamlı bir özel mülk ayarlar ve aksi takdirde yatay, dikey olarak hizalanmış bir düzen oluşturur. Bu sayede kırıntılar, bölücüler ve simgeler hizalanır.

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

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

Flexbox yer paylaşımlarıyla dikey olarak hizalanmış bir içerik haritası gösteriliyor.

Her .crumb, belirli bir boşlukla yatay olarak dikey hizalanmış bir düzen de oluşturur ancak özellikle bağlantı alt öğelerini hedefler ve stili white-space: nowrap belirtir. Çok kelimeli içerik haritası yollarının birden fazla satıra sığdırılmasını istemediğimiz için bu, içerik haritası yolları için çok önemlidir. Bu gönderinin ilerleyen bölümlerinde, bu white-space özelliğinde neden olduğu yatay taşma sorununu ele almak için stiller ekleyeceğiz.

.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", geçerli sayfa bağlantısının diğerlerinden öne çıkmasına yardımcı olmak için eklenir. Ekran okuyucu kullanıcıları, bağlantının geçerli sayfaya ait olduğunu net bir şekilde görebilir. Görme engeli olmayan kullanıcıların da benzer bir kullanıcı deneyimi yaşamalarına yardımcı olmak için öğenin görsel stilini de düzenledik.

.crumbicon bileşeni, bir SVG simgesini "neredeyse görünmez" bir <select> öğesiyle yığmak için ızgara kullanır.

Satır ve sütunun adının stack olduğu bir düğmenin üzerine yerleştirilmiş Grid DevTools gösteriliyor.

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

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

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

<select> öğesi, DOM'de son sırada olduğu için yığının üstündedir ve etkileşimlidir. Öğenin hâlâ kullanılabilir olması için bir opacity: .01 stili ekleyin. Böylece, simgenin şekline mükemmel şekilde uyan bir seçim kutusu elde edersiniz. Bu, yerleşik işlevleri korurken <select> öğesinin görünümünü özelleştirmenin iyi bir yoludur.

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

Taşma

İçerik haritaları çok uzun bir yolu temsil edebilmelidir. Uygun durumlarda, öğelerin ekrandan yatay olarak çıkarılmasına izin vermenin hayranıyım ve bu içerik haritası bileşeninin iyi değerlendirildiğini düşünüyorum.

.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;
  }}
}

Taşma stilleri aşağıdaki kullanıcı deneyimini oluşturur:

  • Aşırı kaydırma sınırlaması olan yatay kaydırma.
  • Yatay kaydırma dolgusu.
  • Son kırıntıda bir sabitleme noktası. Bu, sayfa yüklemesinde, tutturulan ve görüntülenen ilk kırıltma yüklemelerinin olacağı anlamına gelir.
  • Yatay kaydırma ve tutturma efekti kombinasyonlarında sorun yaşayan Safari'deki tutturma noktasını kaldırır.

Medya sorguları

Daha küçük ekran görüntüleri için yapabileceğiniz ince ayarlardan biri, "Ana Sayfa" etiketini gizleyerek yalnızca simgeyi bırakmaktır:

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

Karşılaştırma için, içerik haritalarının yanında ana sayfa etiketi olan ve olmayan,

Erişilebilirlik

Hareket

Bu bileşende çok fazla hareket yoktur ancak geçişi bir prefers-reduced-motion işaretiyle sarmalayarak istenmeyen hareketi engelleyebiliriz.

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

Diğer stillerin hiçbirinin değiştirilmesi gerekmez. Fareyle üzerine gelme ve odaklanma efektleri, transition olmadan da harika ve anlamlı. Ancak hareket uygunsa etkileşime ince bir geçiş ekleriz.

JavaScript

Öncelikle, sitenizde veya uygulamanızda kullandığınız yönlendiricinin türünden bağımsız olarak, bir kullanıcı içerik haritalarını değiştirdiğinde URL'nin güncellenmesi ve kullanıcıya uygun sayfayı göstermesi gerekir. İkinci olarak, kullanıcı deneyimini normalleştirmek için kullanıcılar <select>seçeneklerine göz atarken beklenmedik gezinmelerin yapılmadığından emin olun.

JavaScript tarafından yönetilecek iki kritik kullanıcı deneyimi ölçümü: select has changed ve istekli <select> değişiklik etkinliği tetikleme önleme.

<select> öğesi kullanıldığı için zorunlu etkinlik önleme özelliği gerekir. Windows Edge'de ve muhtemelen diğer tarayıcılarda, kullanıcı klavyeyle seçeneklere göz atarken select changed etkinliği tetiklenir. Kullanıcı, fareyle üzerine gelme veya odaklanma gibi seçenekleri yalnızca sözde seçtiğinden ve seçimi enter veya click ile onaylamadığından bu seçeneği istekli olarak nitelendiriyorum. Eyeger etkinliği, bu bileşen kategorisi değişikliği özelliğini erişilemez hale getirir. Çünkü seçim kutusunu açıp yalnızca bir öğeye göz atmak, etkinliği tetikler ve kullanıcı daha hazır olmadan sayfayı değiştirir.

Daha iyi bir <select> changed etkinliği

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

Bunun stratejisi, her <select> öğesindeki klavye aşağısındaki etkinlikleri izlemek ve basılan tuşun gezinme onayı mı (Tab veya Enter) yoksa üç boyutlu gezinme mi (ArrowUp ya da ArrowDown) olduğunu belirlemektir. Bu tespitle bileşen, <select> öğesine ilişkin etkinlik etkinleştiğinde beklemeye veya gitmeye karar verebilir.

Sonuç

Bunu nasıl yaptığımı öğrendiğinize göre, siz ne yapardınız? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de uygulama geliştirmenin tüm yollarını öğrenelim. Bir demo oluşturun, bağlantılarını bana tweetleyin. Ardından, aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri