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 przedstawię sposoby tworzenia komponentów menu nawigacyjnego. Wypróbuj wersję demonstracyjną.

Prezentacja

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

Przegląd

Komponent menu nawigacyjnego wskazuje, gdzie w hierarchii witryny znajduje się użytkownik. Nazwa pochodzi od Hansela i Gretela, którzy umieszczeli za nimi menu nawigacyjne w ciemnym lesie, aby znaleźć drogę do domu, śledząc menu wstecz.

Menu nawigacyjne w tym poście nie jest standardowym menu nawigacyjnym, tylko przypomina je. Oferują dodatkowe funkcje, ponieważ umieszczają strony równorzędne bezpośrednio w obszarze nawigacyjnym za pomocą tagu <select>, co umożliwia dostęp wielopoziomowy.

Wrażenia użytkowników w tle

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

Ten komponent menu nawigacyjnego powinien umożliwiać użytkownikom poruszanie się po tej hierarchii informacyjnej: przeskakiwanie gałęzi i wybieranie stron z szybkością i dokładnością.

Architektura informacji

Warto myśleć o kolekcjach i przedmiotach.

Kolekcje

Kolekcja to tablica opcji do wyboru. Na stronie głównej prototypu menu nawigacyjnego tego posta znajdują się kolekcje FPS, RPG, brawler, lochy, sport i łamigłówki.

Elementy

Gra wideo to element, a konkretna kolekcja może być również przedmiotem, jeśli reprezentuje inną kolekcję. Na przykład RPG to przedmiot i prawidłowa kolekcja. Gdy jest to produkt, użytkownik jest na tej stronie kolekcji. Np. są one na stronie RPG, która zawiera listę gier RPG wraz z dodatkowymi podkategoriami: AAA, Niezależne i Wydane samodzielnie.

W kontekście informatycznym ten komponent menu nawigacyjnego reprezentuje 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ę wybrane opcje i ich wpływ na ogólny komponent.

Schemat jasny i ciemny

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

Metatag color-scheme w powyższym fragmencie informuje przeglądarkę, że strona chce korzystać z jasnego i ciemnego stylu. W przykładowym menu nawigacyjnym nie ma kodu CSS dla tych schematów kolorów, więc będą w nim używane domyślne kolory z przeglądarki.

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

Do nawigacji w witrynie można użyć elementu <nav>, który ma domniemaną rolę nawigacji ARIA. Podczas testów zauważyłem, że atrybut role zmienił sposób interakcji czytnika ekranu z elementem, a element został ogłoszony jako nawigacja, więc postanowiłem go dodać.

Ikony

Gdy ikona powtarza się na stronie, element SVG <use> oznacza, że możesz raz zdefiniować element path i użyć 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ść ścieżek.

Aby skorzystać z tej metody, dodaj do strony ukryty element SVG i zapakuj ikony w elemencie <symbol> 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, umieszcza informacje o ikony w pamięci i kontynuuje, ponieważ reszta strony odwołuje się do identyfikatora, aby jej użyć w inny sposób:

<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 deweloperskie pokazujące wyrenderowany element użycia SVG.

Możesz go określić raz i zastosować dowolną liczbę razy przy minimalnym wpływie na wydajność strony i elastycznym stylu. Zwróć uwagę, że do elementu SVG dodano aria-hidden="true". Ikony nie są przydatne dla osób przeglądających tylko treść, które słyszą. Zasłanianie ikon przed nimi uniemożliwia dodawanie niepotrzebnych szumów.

Tutaj tradycyjne menu nawigacyjne i elementy tego komponentu się różnią. Normalnie byłby to tylko link <a>, ale dodałem UX przemierzania z ukrytą opcją wyboru. Klasa .crumb odpowiada za rozmieszczenie linku i ikony, a klasa .crumbicon odpowiada za ułożenie ikony i wybranego elementu razem. Nazwałem go „dzielonym linkiem”, ponieważ jego funkcje są bardzo podobne do przycisku podzielonego przycisku, ale do poruszania się po stronie.

<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 niektóre opcje to nic specjalnego, ale zwiększa możliwości prostego menu nawigacyjnego. Dodanie elementu title do elementu <select> jest pomocne dla użytkowników czytników ekranu, ponieważ przekazuje im informacje 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 separatorów

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

Separatory są opcjonalne. Świetnie sprawdza się też dodanie tylko jednego z nich (patrz trzeci przykład z filmu powyżej). Następnie podaję każdemu elementowi aria-hidden="true", ponieważ są one ozdobne i czytnik ekranu nie ma nic do ogłoszenia.

Właściwość gap (opisana poniżej) sprawia, że odstępy między nimi są proste.

Style

Ponieważ w kolorach są używane kolory systemowe, są to głównie luki i stosy 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 nawigacyjny nav.breadcrumbs ustawia niestandardową właściwość o zakresie ograniczonym do wykorzystania przez elementy podrzędne i w inny sposób określa układ wyrównany w pionie w poziomie. Dzięki temu elementy składowe, separatory i ikony są wyrównane.

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

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

1 menu nawigacyjne wyświetlane pionowo 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 kluczowe w przypadku wielowyrazowego menu nawigacyjnego, ponieważ nie chcemy, aby były wielowierszowe. W dalszej części tego posta dodamy style do obsługi przepełnienia poziomego spowodowanego przez tę właściwość 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;
    }
  }
}

Dodano aria-current="page", aby link do bieżącej strony wyróżniał się na tle innych. Użytkownicy czytników ekranu będą wyraźnie informowali o tym, że link prowadzi do bieżącej strony, a elementy mają styl wizualny, aby użytkownicy widzący mogli w podobny sposób korzystać z treści.

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

Widoczna jest siatka narzędzi deweloperskich z nakładką przycisku, w którym wiersz i kolumna są nazwanymi stosami.

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

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

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

Element <select> jest ostatni w DOM, więc znajduje się na stosie i jest interaktywny. Dodaj styl opacity: .01, aby element nadal był użyteczny, a w efekcie powstanie pole wyboru 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

Menu nawigacyjne powinno reprezentować bardzo długi szlak. 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 nadmiarowe umożliwiają skonfigurowanie takiego interfejsu użytkownika:

  • Przewijanie poziome z ograniczeniem przewijania.
  • Dopełnienie przewijania w poziomie.
  • Jeden punkt przyciągania do ostatniego elementu. Oznacza to, że podczas wczytywania strony pierwsze okrągłe menu zostanie przyciągnięte i widoczne.
  • Usuwa punkt przyciągania z Safari, który ma problemy z kombinacją efektów przewijania w poziomie i efektu przyciągania.

Zapytania o multimedia

Subtelną korektą w mniejszych widocznych obszarach jest ukrycie etykiety „Strona główna”, pozostawiając tylko ikonę:

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

Obok menu nawigacyjnego z etykietą „Home” lub bez niej, w celu porównania.

Ułatwienia dostępu

Poruszone

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

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

Żaden z pozostałych stylów nie wymaga zmiany. Efekty najechania kursorem i zaznaczenia są świetne bez elementu transition, ale jeśli ruch jest prawidłowy, dodamy do interakcji subtelne przejście.

JavaScript

Po pierwsze, niezależnie od typu routera używanego w witrynie lub aplikacji, gdy użytkownik zmieni menu nawigacyjne, trzeba zaktualizować URL i wyświetlić mu 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.

Dwie kluczowe wskaźniki zadowolenia użytkowników, którymi powinien być obsługiwany JavaScript: funkcja Select zmieniła się i często też funkcję blokowania uruchamiania zdarzeń zmiany przez <select>.

Użycie elementu <select> wymaga zapobiegania zdarzeniom. 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 nazwaliśmy to „chętni”, ponieważ użytkownik wybrał tylko pseudoopcję, np. najechanie kursorem czy zaznaczenie, ale nie potwierdził go przy użyciu elementu enter ani click. Zdarzenie powoduje, że funkcja zmiany kategorii komponentu jest niedostępna, ponieważ otwarcie pola wyboru i zwykłe przeglądanie elementu powoduje uruchomienie zdarzenia i zmianę strony, zanim użytkownik będzie gotowy.

Lepszy zmieniony <select>

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

W tym celu należy zwracać uwagę na przypadki naciśnięcia klawisza w poszczególnych elementach <select> i ustalić, czy naciśnięty klawisz to potwierdzenie nawigacji (Tab lub Enter) czy nawigacja przestrzenna (ArrowUp lub ArrowDown). Dzięki temu komponent może zdecydować, czy chce czekać, czy odejdzie, gdy nastąpi uruchomienie zdarzenia dla elementu <select>.

Podsumowanie

Wiesz już, jak to zrobiłem. Jak Ty? 🙂

Stosujmy różne podejścia i poznajmy sposoby budowania obecności w internecie. Przygotuj wersję demonstracyjną, a potem dodam linki do tweetów, a ja dodam ją do poniższej sekcji na temat remiksów na karcie Społeczność.

Remiksy utworzone przez społeczność