Tworzenie komponentu menu nawigacyjnego

Podstawowe informacje o tworzeniu elastycznego i łatwo dostępnego elementu menu nawigacyjnego, które umożliwia użytkownikom poruszanie się po witrynie.

W tym poście przedstawię sposoby tworzenia komponentów menu nawigacyjnego. Wypróbuj wersję demonstracyjną

Demonstracja

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

Przegląd

Komponent menu nawigacyjnego pokazuje, gdzie w hierarchii witryny znajduje się użytkownik. Jego nazwa pochodzi od Hansela i Gretel, którzy ukryli menu nawigacyjne w ciemnym lesie i udało się znaleźć drogę do domu, śledząc okruchy w tył.

Menu nawigacyjne w tym poście nie jest standardowym menu nawigacyjnym, tylko menu nawigacyjne. Zapewniają dodatkową funkcjonalność, ponieważ umieszczają strony równorzędne bezpośrednio w obszarze nawigacyjnym za pomocą elementu <select>, co umożliwia wielopoziomowy dostęp.

UX w tle

W powyższym filmie demonstracyjnym kategorie zastępcze to gatunki gier wideo. Ten szlak tworzy się: home » rpg » indie » on sale, jak pokazano poniżej.

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

Architektura informacji

Warto myśleć w kategoriach kolekcji i elementów.

Kolekcje

Kolekcja to tablica opcji do wyboru. Na stronie głównej prototypu menu nawigacyjnego tego posta znajdują się kolekcje gier FPS, RPG, brawler, lochów, sportu i łamigłówek.

Elementy

Gra wideo to element, a konkretna kolekcja może też być nim, jeśli reprezentuje inną kolekcję. Może to być np. przedmiot i prawidłowa kolekcja. Jeśli to dany element, użytkownik wyświetla się na tej stronie kolekcji. Przykład: znajdują się na stronie RPG, która zawiera listę gier RPG oraz dodatkowe podkategorie AAA, Indie i Self Publish.

W rozumieniu informatyki ten komponent menu nawigacyjnego reprezentuje tablicę wielowymiarową:

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

Twoja aplikacja lub witryna będzie miała niestandardową architekturę informacji (IA) tworzącą inną wielowymiarową tablicę. Mam jednak nadzieję, że strony docelowe kolekcji i poruszanie się po hierarchii również znajdą się w menu nawigacyjnym.

Układy

Markup

Dobre komponenty zaczynają się od odpowiedniego kodu HTML. W następnej sekcji omówię wybrane znaczniki i ich wpływ na ogólny komponent.

Schemat ciemnego i jasnego

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

Metatag color-scheme w powyższym fragmencie kodu informuje przeglądarkę, że ta strona chce korzystać z trybu jasnego i ciemnego. Przykładowe menu nawigacyjne nie zawiera żadnego kodu CSS w przypadku tych schematów kolorów, dlatego będzie korzystać z kolorów domyślnych dostarczonych przez przeglądarkę.

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

Do nawigacji po witrynie można użyć elementu <nav>, który ma domyślnie rolę nawigacji ARIA. Podczas testów zauważyłem, że atrybut role zmienia sposób interakcji czytnika ekranu z elementem, co zapowiedziano jako nawigację, więc zdecydowałam się go dodać.

Ikony

Gdy ikona jest powtórzona na stronie, element SVG <use> oznacza, że możesz raz zdefiniować element path i używać go we wszystkich wystąpieniach tej ikony. Zapobiega to powtarzaniu tych samych informacji o ścieżce, powodując większe dokumenty i potencjalnie niespójności w ścieżce.

Aby skorzystać z tej metody, dodaj do strony ukryty element SVG i zapakuj ikony w element <symbol> o unikalnym identyfikatorze:

<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, zapisuje informacje o ikonie w pamięci i kontynuuje wyszukiwanie w pozostałej części strony, odwołując się do identyfikatora, aby użyć ikony w taki 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 z wyrenderowanym elementem SVG.

Zdefiniuj raz i używaj ich dowolną liczbę razy z minimalnym wpływem na wydajność strony i elastycznym stylem. Zwróć uwagę na to, że do elementu SVG dodano element aria-hidden="true". Ikony nie są przydatne dla osób przeglądających treść, które tylko słyszą treść. Jeśli je ukryjesz, nie będą dodawać zbędnego szumu.

Tutaj znajdziesz różnice między tradycyjnym menu nawigacyjnym a elementami tego komponentu. Normalnie byłby to tylko link <a>, ale został przeze mnie dodany interfejs dotyczący przemierzania z ukrytym wyborem. Klasa .crumb odpowiada za rozmieszczenie linku i ikony, a klasa .crumbicon odpowiada za ułożenie ikony i wybranie elementu. Nazwaliśmy go linkiem podzielonym, ponieważ jego funkcje są podobne do przycisku podziału, 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ększają funkcjonalność menu nawigacyjnego. Dodanie atrybutu title do elementu <select> jest przydatne dla użytkowników czytników ekranu, ponieważ daje im informacje o działaniu przycisku. Jest on jednak tak samo pomocny dla wszystkich użytkowników, a na iPadzie jest jak na pierwszym planie. Jeden atrybut dostarcza kontekstu przycisku wielu użytkownikom.

Zrzut ekranu z ustawionym niewidocznym elementem wyboru i wyświetloną etykietką kontekstową.

Dekoracje separatorów

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

Separatory są opcjonalne, dodanie tylko jednego z nich też się sprawdza (zobacz trzeci przykład filmu powyżej). Następnie przyznaję każdemu aria-hidden="true", ponieważ pełnią one funkcję dekoracyjną i nie są czymś, co czytnik ekranu musi informować.

Omówiona poniżej właściwość gap ułatwia stosowanie odstępów.

Style

Ponieważ kolor używa kolorów systemowych, w stylach są przede wszystkim luki i stosy.

Kierunek układu i przepływ

Narzędzia deweloperskie z wyrównaniem menu nawigacyjnego za pomocą funkcji nakładki Flexbox.

Główny element nawigacji nav.breadcrumbs ustawia zakres właściwości niestandardowej, z której będą korzystać dzieci, oraz określa układ wyrównany w poziomie w pionie. Dzięki temu elementy nawigacyjne, 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);
}

Jedno menu nawigacyjne wyświetlane pionowo z nakładkami Flexbox.

Każdy element .crumb ustawia też poziomy układ w pionie z pewną luką, ale kieruje specjalnie elementy podrzędne linków i określa styl white-space: nowrap. Jest to konieczne w przypadku wielowyrazowych menu nawigacyjnego, ponieważ nie powinny być wielowierszowe. W dalszej części tego postu dodamy style, które obsługują przepełnienie poziome spowodowane 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 atrybut aria-current="page", aby link do bieżącej strony wyróżniał się na tle pozostałych. Użytkownicy czytników ekranu nie tylko będą mieli wyraźny znak, że link prowadzi do bieżącej strony, ale także spersonalizowaliśmy element, aby ułatwić wzrokowym użytkownikom korzystanie z niej w podobny sposób.

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

Narzędzia deweloperskie w siatce mają nakładkę z przyciskiem, w którym wiersz i kolumna są stosem nazw.

.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 górze stosu i jest interaktywny. Dodaj styl opacity: .01, aby element był nadal użyteczny, a w rezultacie powstało pole wyboru 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 */
}

Rozszerzone menu

Menu nawigacyjne powinien reprezentować bardzo długi szlak. Jestem zachwycony tym, że elementy menu mogą wychodzić z ekranu w poziomie tam, gdzie to konieczne, i uważam, że ten element menu nawigacyjnego dobrze się sprawdza.

.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 dodatkowe konfigurują następujący interfejs użytkownika:

  • Przewijanie w poziomie z zablokowanym przewijaniem.
  • Dopełnienie przewijania w poziomie.
  • Jeden punkt skupienia na ostatnim menu. Oznacza to, że podczas wczytywania strony pierwsze menu jest przyciągane i widoczne.
  • Usuwa punkt przyciągania z Safari, który ma problemy z kombinacjami przewijania w poziomie i efektów przyciągania.

Zapytania o multimedia

W przypadku mniejszych widocznych obszarów możesz ukryć etykietę „Główna” i pozostawić tylko ikonę:

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

Menu nawigacyjne obok siebie z etykietą strony głównej i bez niej – w celu porównania.

Ułatwienia dostępu

Ruch

Ten komponent nie zawiera za dużo ruchu, ale gdy dodasz przejście za pomocą znacznika prefers-reduced-motion, możemy zapobiec niechcianemu ruchowi.

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

Żaden inny styl nie musi się zmieniać. Efekty najechania kursorem i ostrości są świetne i znaczą bez transition. Jeśli ruch jest w porządku, dodamy subtelne przejście do interakcji.

JavaScript

Po pierwsze, niezależnie od typu routera używanego w witrynie lub aplikacji, gdy użytkownik zmieni menu nawigacyjne, musi zostać zaktualizowany URL, a użytkownik wyświetli odpowiednią stronę. Po drugie, aby ujednolicić wrażenia użytkownika, zadbaj o to, aby podczas przeglądania opcji <select> nie pojawiały się nieoczekiwane elementy nawigacyjne.

2 krytyczne środki związane z wrażeniami użytkownika obsługiwane przez JavaScript: wybór zmienił się i chcemy zapobiec uruchamianiu zdarzeń zmiany <select>.

Jest to konieczne ze względu na użycie elementu <select>. W Windows Edge i prawdopodobnie także innych przeglądarkach wybrane zdarzenie changed jest uruchamiane, gdy użytkownik przegląda opcje za pomocą klawiatury. Właśnie dlatego tak nazywam to chętnymi. Użytkownik pseudonimowo wybrał tę opcję (najechanie kursorem lub zaznaczenie), ale jeszcze nie potwierdził swojego wyboru za pomocą enter ani click. Niecierpliwe zdarzenie powoduje, że funkcja zmiany kategorii komponentu staje się niedostępna, ponieważ otwarcie pola wyboru i po prostu przejrzenie elementu spowoduje uruchomienie zdarzenia i zmianę strony, zanim użytkownik będzie gotowy.

Lepsze zdarzenie <select> zostało zmienione

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 wykrywaniu zdarzeń przejścia z klawiatury w każdym elemencie <select> i określaniu, czy naciśnięty klawisz to potwierdzenie nawigacji (Tab lub Enter) czy nawigacja przestrzenna (ArrowUp lub ArrowDown). Na podstawie tych informacji komponent może czekać lub przejść do chwili uruchomienia zdarzenia dotyczącego elementu <select>.

Podsumowanie

Wiesz już, jak to zrobiłem, więc jak to zrobisz 🙂

Stwórzmy różne metody i nauczmy się wszystkiego, jak rozwijać się w internecie. Utwórz demonstrację i udostępnię mi linki na Twitterze, a dodam ją do sekcji remiksów w ramach społeczności poniżej.

Remiksy społeczności