Tworzenie komponentu menu nawigacyjnego

Podstawowe informacje o tworzeniu elastycznych i łatwo dostępnych komponentów menu nawigacyjnego, które ułatwią użytkownikom poruszanie się po witrynie.

W tym poście chcę podzielić się z Tobą sposobem tworzenia komponentów ścieżki nawigacyjnej. Wypróbuj wersję demonstracyjną

Demonstracja

Jeśli wolisz film, oto wersja tego posta w YouTube:

Omówienie

Komponent ścieżki menu nawigacyjnego wskazuje, w którym miejscu w hierarchii witryny znajduje się użytkownik. Nazwa pochodzi od Jaś i Małgosia, którzy w ciemnym lesie zostawili za sobą okruchy chleba, dzięki czemu mogli odnaleźć drogę do domu.

Menu nawigacyjne w tym poście nie jest standardowym menu nawigacyjnym, tylko przypomina je. Oferuje ona dodatkowe funkcje, umieszczając strony siostrza bezpośrednio w menu nawigacyjnym (<select>), co umożliwia dostęp na wielu poziomach.

Tłogowisko

W powyższym filmie z prezentacją komponentu kategorie zastępcze to gatunki gier wideo. Ten szlak tworzysz, korzystając z tej ścieżki: home » rpg » indie » on sale, jak pokazano poniżej.

Ten element ścieżki nawigacyjnej powinien umożliwiać użytkownikom poruszanie się po tej hierarchii informacji, czyli szybkie i bezbłędne przełączanie się między gałęziami i wybieranie stron.

Architektura informacji

Warto myśleć o kolekcjach i przedmiotach.

Kolekcje

Kolekcja to zbiór opcji do wyboru. Na stronie głównej prototypu tego posta znajdują się kolekcje gier FPS, RPG, brawler, dungeon crawler, sportowych i logicznych.

Elementy

Gra wideo jest elementem, a konkretna kolekcja może być elementem, jeśli reprezentuje inną kolekcję. Na przykład RPG to element i poprawna kolekcja. Jeśli jest to produkt, użytkownik znajduje się na stronie kolekcji. Na przykład na stronie z grami RPG, która zawiera listę gier RPG, w tym dodatkowe podkategorie AAA, Indie i Self Published.

W terminologii informatycznej ten element ścieżki stanowi wielowymiarową tablicę:

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

Twoja aplikacja lub witryna będzie mieć niestandardową architekturę informacji (IA), co utworzy inną wielowymiarową tablicę, ale mam nadzieję, że koncepcja stron docelowych kolekcji i przemierzania hierarchii również znajdzie się w menu nawigacyjnym.

Układy

Markup

Dobre komponenty zaczynają się od odpowiedniego kodu HTML. W następnej sekcji omówię wybór znaczników i jego wpływ na cały komponent.

Schemat ciemny i jasny

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

Metatag color-scheme w powyższym fragmencie kodu informuje przeglądarkę, że ta strona chce używać jasnego i ciemnego stylu przeglądarki. Przykładowe elementy nawigacyjne nie zawierają kodu CSS dla tych schematów kolorów, więc będą używać domyślnych kolorów udostępnionych przez przeglądarkę.

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

Element <nav> należy używać do nawigacji po witrynie, ponieważ ma on domyślną rolę ARIA nawigacji. Podczas testów zauważyliśmy, że atrybut role zmienia sposób interakcji czytnika ekranu z elementem. W fakcie był on ogłaszany jako element nawigacji, dlatego zdecydowaliśmy się go dodać.

Ikony

Gdy ikona jest powtarzana na stronie, element SVG <use> oznacza, że możesz zdefiniować path raz i używać go we wszystkich wystąpieniach ikony. Dzięki temu informacje o tej samej ścieżce nie będą się powtarzać, co spowoduje większe dokumenty i potencjalnie niespójności w ścieżkach.

Aby użyć tej techniki, dodaj do strony ukryty element SVG i owiń ikony w element <symbol> z unikalnym identyfikatorem:

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

Przeglądarka odczytuje kod HTML SVG, zapisze informacje o ikonie w pamięci i przejdzie do dalszej części strony, która odwołuje się do identyfikatora ikony w celu jej dodatkowego użycia.

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

Narzędzia dla programistów pokazujące wyrenderowany element SVG.

Zdefiniuj raz, używaj dowolną liczbę razy, z minimalnym wpływem na wydajność strony i elastycznym stylizowaniem. Zwróć uwagę, że do elementu SVG dodano aria-hidden="true". Ikony nie są przydatne dla osób, które przeglądają treści tylko przy użyciu dźwięku. Ukrywanie ich przed tymi użytkownikami zapobiega dodawaniu przez nich niepotrzebnego hałasu.

Właśnie w tym miejscu tradycyjne menu nawigacyjne różni się od tego w tym komponencie. Zwykle jest to tylko link <a>, ale dodałem interfejs użytkownika z ukrytym elementem select. Klasa .crumb odpowiada za rozmieszczenie linku i ikony, a klasa .crumbicon – za ułożenie ikony i elementu select obok siebie. Nazwałem go „linkiem podzielonym”, ponieważ jego funkcje są bardzo podobne do przycisku podzielonego, ale służy do nawigacji po stronach.

<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 i kilka opcji to nic specjalnego, ale zwiększają funkcjonalność prostego menu nawigacyjnego. Dodanie elementu title do elementu <select> jest przydatne dla użytkowników czytników ekranu, ponieważ dostarcza im informacji o działaniu przycisku. Jednak ten sam tryb jest dostępny dla innych użytkowników – na iPadzie znajduje się on z przodu. Jeden atrybut zapewnia wielu użytkownikom kontekst dla przycisku.

Zrzut ekranu z najeżdżającym niewidocznym elementem wyboru i etykietą kontekstową.

Dekoracje separatora

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

Separatory są opcjonalne, ale dodanie tylko jednego też się sprawdzi (patrz trzeci przykład w filmie powyżej). Potem podaję każdemu elementowi aria-hidden="true", ponieważ są one ozdobne i czytnik ekranu nie ma nic do ogłoszenia.

Właściwość gap, o której opowiemy w następnym punkcie, ułatwia prawidłowe rozmieszczanie tych elementów.

Style

Ponieważ kolory są kolorami systemowymi, w większości są to luki i zbiory dla stylów.

Kierunek i przepływ układu

Narzędzia deweloperskie pokazujące wyrównanie menu nawigacyjnego z funkcją nakładki Flexbox.

Główny element nawigacji nav.breadcrumbs ustawia ograniczoną właściwość niestandardową do użycia przez elementy podrzędne, a także określa układ poziomy z wyrównaniem pionowym. Dzięki temu elementy ścieżki, separatory i ikony będą wyrównane.

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

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

Element nawigacji pokazany w układaniu pionowym z nakładkami flexbox.

Każdy element .crumb tworzy też poziomy układ wyrównany pionowo z pewnymi lukami, ale specjalnie kieruje elementy podrzędne linków i określa styl white-space: nowrap. Jest to bardzo ważne w przypadku menu nawigacyjnego z wielu słów, ponieważ nie chcemy, aby zajmowało ono kilka wierszy. W dalszej części tego posta dodamy style, które umożliwią obsłużenie poziomego przepełnienia przez usługę white-space.

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

  & > a {
    white-space: nowrap;

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

Dodajemy aria-current="page", aby wyróżnić link do bieżącej strony na tle reszty. Użytkownicy czytników ekranu będą mieć wyraźny wskaźnik, że link prowadzi do bieżącej strony, ale także wizualnie wyróżniliśmy element, aby ułatwić widzącym korzystanie z usługi.

Komponent .crumbicon używa siatki do ułożenia ikony SVG z „prawie niewidocznym” elementem <select>.

Nakładka DevTools na przycisk, w którym zarówno wiersz, jak i kolumna mają nazwę zdefiniowaną w zbiorze.

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

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

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

Element <select> znajduje się na końcu modelu DOM, więc jest na szczycie stosu i jest interaktywny. Dodaj styl opacity: .01, aby element był nadal użyteczny. W efekcie pole wyboru będzie idealnie dopasowane do kształtu ikony. To dobry sposób na dostosowanie wyglądu elementu <select> przy zachowaniu wbudowanych funkcji.

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

Rozwijany

Ścieżka powinna być w stanie reprezentować bardzo długi ślad. Lubię pozwolić, by elementy w odpowiednich momentach wyświetlały się poza ekran w poziomie. Uważam, że ten element menu nawigacyjnego spełnia swoje zadanie.

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

Style przepełnienia umożliwiają takie działanie:

  • Przewijanie poziome z zatrzymaniem po przekroczeniu krawędzi.
  • Dopełnienie przesunięcia poziomego.
  • Jeden punkt dopasowania na ostatnim fragmencie ścieżki. Oznacza to, że po wczytaniu strony pierwszy elementCrumb wczytuje się w ramach widoku.
  • Usunięcie punktu zazębiania z Safari, który ma problemy z kombinacją przewijania poziomego i punktów zazębiania.

Zapytania multimedialne

Jednym z subtelnych dostosowań w przypadku mniejszych widoków jest ukrycie etykiety „Strona główna” i pozostawienie tylko ikony:

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

Porównanie elementów nawigacji z etykietą strony głównej i bez niej.

Ułatwienia dostępu

Ruch

W tym komponencie nie ma dużo ruchu, ale owinięcie przejścia w mechanizmie kontroli prefers-reduced-motion możemy zapobiec niepożądanym ruchom.

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

Żaden z innych stylów nie wymaga zmiany, efekty najechania kursorem i wyświetlenia są świetne i znaczące bez transition, ale jeśli animacja jest w porządku, dodamy subtelną animację do interakcji.

JavaScript

Po pierwsze, niezależnie od typu routera używanego w witrynie lub aplikacji, gdy użytkownik zmieni informacje w śladach, adres URL musi zostać zaktualizowany, a użytkownik musi zobaczyć odpowiednią stronę. Po drugie, aby ujednolicić wrażenia użytkowników, zadbaj o to, aby podczas przeglądania opcji w <select> nie doszło do nieoczekiwanej nawigacji.

2 kluczowe wskaźniki dotyczące wrażeń użytkowników, które są obsługiwane przez JavaScript: select has changed i eager <select> zapobieganie wywoływaniu zdarzeń zmiany.

Pośpieszna zapobieganie zdarzeniom jest konieczne ze względu na użycie elementu <select>. W Windows Edge, a także prawdopodobnie w innych przeglądarkach, zdarzenie wyboru changed jest uruchamiane, gdy użytkownik przegląda opcje za pomocą klawiatury. Dlatego nazywam je „niecierpliwe”, ponieważ użytkownik wybrał opcję tylko pozornie, np. przez najechanie kursorem lub skupienie się na niej, ale nie potwierdził wyboru za pomocą przycisku enter ani click. Zdarzenie o szybkim działaniu powoduje, że funkcja zmiany kategorii komponentu jest niedostępna, ponieważ otwarcie pola wyboru i zwykłe przeglądanie produktu powoduje wywołanie zdarzenia i zmianę strony, zanim użytkownik będzie gotowy.

Lepsze <select> zmienione zdarzenie

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

Strategia polega na sprawdzaniu zdarzeń naciśnięcia klawisza w przypadku każdego elementu <select> i określaniu, czy naciśnięty klawisz był potwierdzeniem nawigacji (Tab lub Enter) czy nawigacją przestrzenną (ArrowUp lub ArrowDown). Dzięki temu komponent może zdecydować, czy zaczekać, czy wykonać działanie, gdy zostanie wywołane zdarzenie elementu <select>.

Podsumowanie

Teraz, gdy już wiesz, jak to zrobić, jak Ty to zrobisz? 🙂

Zróżnicujemy nasze podejścia i poznamy wszystkie sposoby tworzenia stron internetowych. Utwórz wersję demonstracyjną, wyślij mi linki, a ja dodam je do sekcji z remiksami społeczności.

Remiksy społeczności