İç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 yönelik temel bir genel bakış.

Bu yayında, içerik haritası bileşenlerini oluşturmanın bir yolu üzerine düşünmek istiyorum. Demoyu deneyin.

Tanıtım

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

Genel bakış

İçerik haritaları bileşeni, kullanıcının site hiyerarşisinin neresinde olduğunu gösterir. Adı, karanlık bir ormanda arkalarına bırakan ve kırıntıları geriye doğru izleyerek eve giden yolunu bulan Hansel ve Gratel'e aittir.

Bu yayındaki içerik haritaları standart içerik haritası değil, benzerdir. Bunlar, <select> ile birlikte kardeş sayfaları doğrudan gezinmeye yerleştirerek 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 kategorileri video oyunu türleridir. Bu patika, aşağıda gösterildiği gibi home » rpg » indie » on sale yolunda gidilerek oluşturulur.

Bu içerik haritası bileşeni, kullanıcıların bu bilgi hiyerarşisinde ilerlemesini, dalları atlayıp sayfaları hızlı ve doğru bir şekilde seçebilmesini sağlamalıdır.

Bilgi mimarisi

Koleksiyonlar ve öğeler açısından düşünmek yararlı oluyor.

Koleksiyonlar

Koleksiyon, seçim yapabileceğiniz bir dizi seçenektir. Bu yayının içerik haritası prototipinin ana sayfasından FPS, RPG, dövüş, zindan tarayıcısı, spor ve bulmaca koleksiyonları bulunuyor.

Öğe sayısı

Video oyunu bir öğedir. Başka bir koleksiyonu temsil eden belirli bir koleksiyon da öğe olabilir. Örneğin, RYO bir öğe ve geçerli bir koleksiyondur. Öğe söz konusu olduğunda kullanıcı, koleksiyon sayfasındadır. Örneğin, bunlar RYO sayfasında yer alır. Bu sayfada AAA, Bağımsız ve Kendi Kendine Yayınlanmış ek alt kategorileri de içeren bir RYO oyunları listesi görüntülenir.

Bilgisayar bilimi terimlerinde, bu içerik haritası bileşeni çok boyutlu bir diziyi temsil eder:

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

Uygulamanızın veya web sitenizin farklı bir çok boyutlu dizi oluşturan özel bilgi mimarisi (IA) olacaktır. Ancak, koleksiyon açılış sayfaları ve hiyerarşi geçişi kavramının, içerik haritalarınıza da dahil edileceğini umuyorum.

Düzenler

Markup

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

Koyu ve açık şema

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

Yukarıdaki snippet'teki color-scheme meta etiketi, tarayıcıya bu sayfanın açık ve koyu tarayıcı stillerini istediği bilgisini verir. Örnek içerik haritaları bu renk şemaları için herhangi bir CSS içermediğinden, içerik haritalarında tarayıcı tarafından sağlanan varsayılan renkler kullanılır.

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

Gizli bir ARIA gezinme rolüne sahip olan, site gezinmesi için <nav> öğesinin kullanılması uygundur. Test sırasında role özelliğinin ekran okuyucunun öğeyle etkileşim kurma biçimini değiştirdiğini fark ettim. Bu özellik, gezinme olarak duyuruldu ve bu yüzden bu özelliği eklemeye karar verdim.

Simgeler

Bir simge bir sayfada tekrarlandığında, SVG <use> öğesi path öğesini bir kez tanımlayabileceğiniz ve simgenin tüm örnekleri için kullanabileceğiniz anlamına gelir. Bu, aynı yol bilgilerinin tekrarlanmasını engelleyerek daha büyük belgelere ve yol tutarsızlığı potansiyeline yol açar.

Bu tekniği kullanmak için sayfaya gizli bir SVG öğesi ekleyin ve simgeleri bir <symbol> öğesinin içinde benzersiz bir kimlikle 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 simgenin ek kullanımları için kimliğe referans vererek sayfanın geri kalanıyla 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ı.

Sayfa performansını minimum düzeyde etkileyip esnek stil özelliklerini uygulayarak bir kez tanımlayın, istediğiniz kadar kullanın. SVG öğesine aria-hidden="true" bildirimi eklendi. Simgeler, göz atan ve yalnızca içeriği duyanlar için yararlı değildir. Bunları bu kullanıcılardan gizlemek, gereksiz sesler eklemelerini önler.

Geleneksel içerik haritası ile bu bileşendekiler birbirinden ayrılır. Normalde bu yalnızca bir <a> bağlantısıdır ama gizlenmiş bir seçimle geçiş kullanıcı deneyimi ekledim. .crumb sınıfı bağlantının ve simgenin yerleştirilmesinden, .crumbicon ise simgenin ve seçili öğenin birlikte yerleştirilmesinden sorumludur. İşlevleri bölünmüş düğmeye çok benzediği için ancak sayfada gezinmeye yönelik olduğu için bunu bölünmüş bağlantı olarak adlandırdım.

<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 özel bir şey değildir, sadece basit bir içerik haritasına daha fazla işlevsellik katar. <select> öğesine bir title eklenmesi, ekran okuyucu kullanıcılarına düğmenin işlemi hakkında bilgi sağlayarak yardımcı olur. Ancak, diğer herkese de aynı yardımı sağladığından, iPad'de önde ve ortada olduğunu göreceksiniz. Bir özellik, birçok kullanıcıya düğme bağlamı sağlar.

Görünmez seçim öğesinin üzerine getirildiği 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ılar isteğe bağlıdır ve yalnızca bir tane eklemek de işe yarar (yukarıdaki videoda üçüncü örneğe bakın). Daha sonra her aria-hidden="true", dekoratif öğeler olduğu ve ekran okuyucunun duyurmak zorunda kalacağı bir şey olmadığı için onlara veriyorum.

Bir sonraki bölümde ele alınacak gap özelliği, bu alanların aralıklarını basit bir şekilde belirler.

Stiller

Renkte sistem renkleri kullanıldığından, renkler çoğunlukla boşluklardan 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 kullanması için kapsamlı bir özel özellik ayarlar ve aksi takdirde yatay olarak dikey olarak hizalanmış bir düzen oluşturur. Bu işlem, küçük resimlerin, ayırıcıların ve simgelerin hizalanmasını sağlar.

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

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

Flexbox yer paylaşımları ile dikey olarak hizalanmış bir içerik haritası.

Her .crumb, belirli bir boşlukla yatay olarak dikey olarak hizalanmış bir düzen oluşturur ancak özel olarak bağlantı alt öğelerini hedefler ve white-space: nowrap stilini belirtir. Birden çok satırlı olmasını istemediğimizden, birden fazla kelimeden oluşan içerik haritaları için bu çok önemlidir. Bu yayının ilerleyen bölümlerinde, bu white-space özelliğinin 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;
    }
  }
}

Mevcut sayfa bağlantısının diğerlerinden farklı olarak öne çıkmasına yardımcı olmak için aria-current="page" eklendi. Ekran okuyucu kullanıcıları, bağlantının geçerli sayfa için olduğuna dair net bir göstergeye sahip olacak

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

Izgara Geliştirici Araçları, satır ve sütunun &quot;stack&quot; olarak adlandırıldığı bir düğmeyle yer paylaşımlı olarak 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'da en sonda yer aldığı için yığının üzerindedir ve etkileşimlidir. Öğenin hâlâ kullanılabilmesi için opacity: .01 stili ekleyin. Böylece, simgenin şekline mükemmel bir şekilde uyan bir seçim kutusu elde edilir. Bu, yerleşik işlevselliği koruyarak <select> öğesinin görünümünü özelleştirmenin güzel 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. Bir şeylerin, uygun olduğunda yatay olarak ekran dışına çıkmasına izin vermeyi seviyorum ve bu içerik haritası bileşeninin uygun olduğunu hissettim.

.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 ayarlar:

  • Fazla kaydırma yaparak yatay kaydırma.
  • Yatay kaydırma dolgusu.
  • Son kırıntıda bir tutturma noktası. Diğer bir deyişle, sayfa yükleme sırasında ilk kırıntı yüklemeleri sabitlenmiş ve görünür durumdadır.
  • Yatay kaydırma ve tutturma efekti kombinasyonlarında sorun yaşayan Safari'deki yapışma noktasını kaldırır.

Medya sorguları

Küçük görüntü alanlarında yapılan küçük ayarlamalardan biri, "Ana Ekran" etiketini gizlemek ve geriye yalnızca simgeyi bırakmaktır:

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

Karşılaştırma yapmak için içerik haritalarının, ana sayfa etiketiyle birlikte ve etiketsiz olarak yan yana gösterilmesi.

Erişilebilirlik

Hareket

Bu bileşende çok fazla hareket yok ancak geçişi bir prefers-reduced-motion kontrolüyle sarmalayarak istenmeyen hareketi önleyebiliriz.

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

Diğer stillerin hiçbirinin değişmesine gerek yoktur, fareyle üzerine gelme ve odaklama efektleri transition olmadan harika ve anlamlıdır, ancak hareket uygunsa etkileşime hafif 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 haritası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 yalnızca <select> seçeneklerine göz atarken beklenmedik gezinmelerin olmadığından emin olun.

JavaScript tarafından ele alınacak iki kritik kullanıcı deneyimi önlemi: Seçili olan seçenek değişti ve <select> değişiklik etkinliğini tetiklemeyi önleme konusunda istekli.

Hızlı etkinlik önleme, <select> öğesi kullanılması nedeniyle gereklidir. Windows Edge'de ve muhtemelen diğer tarayıcılarda, kullanıcı klavyeyle seçeneklere göz atarken changed seçme etkinliği tetiklenir. Kullanıcı, fareyle üzerine gelme veya odaklama gibi yalnızca sözde seçeneği belirlediği ancak enter veya click ile yapılan seçimi onaylamadığı için bunu hevesli olarak adlandırdım. İstekli etkinliği, bu bileşen kategori değişikliği özelliğini erişilemez hale getirir. Çünkü, seçim kutusunun açılması ve bir öğeye göz atmak, etkinliği tetikler ve kullanıcı hazır olmadan sayfayı değiştirir.

Daha iyi bir <select> değiştirilmiş etkinlik

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 bir <select> öğesinde klavyeyi aşağı çekme etkinliklerini izlemek ve tuşa basılan tuşun gezinme onayı mı (Tab veya Enter) yoksa uzamsal gezinme mi (ArrowUp veya ArrowDown) olduğunu belirlemektir. Bu belirlemeyle bileşen, <select> öğesi etkinliği etkinleştiğinde beklemeye ya da devam etmeye karar verebilir.

Sonuç

Nasıl yaptığımı artık bildiğine göre siz de nasıl yapardınız? 🙂

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

Topluluk remiksleri