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, podążając za tymi okruchami.

Menu nawigacyjne w tym poście jest niestandardowe menu nawigacyjnego, wygląda jak menu nawigacyjne. Zapewniają dodatkowe funkcje dzięki połączeniu elementów równorzędnych bezpośrednio do nawigacji dzięki funkcji <select>, dzięki czemu dostęp wielopoziomowy jak to tylko możliwe.

Tłogowisko

W filmie demonstracyjnym komponentu powyżej kategorie zastępcze to gatunki: gier wideo. Ścieżka ta jest tworzona po wybraniu 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, szybkie przechodzenie między gałęziami i dokładne wybieranie stron.

Architektura informacji

Warto myśleć o kolekcjach i przedmiotach.

Kolekcje

Kolekcja to zbiór opcji do wyboru. Na stronie głównej usługi to prototyp menu nawigacyjnego. Są to kolekcje FPS, RPG, brawler robot do lochów, sport i łamigłówki.

Elementy

Gra wideo to przedmiot, a konkretna kolekcja może być również przedmiotem, jeśli reprezentuje kolejną kolekcję. Na przykład RPG to element i poprawna kolekcja. Gdy jest to element, użytkownik jest na tej 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, w wielowymiarowej tablicy, ale mam nadzieję, że koncepcja strony docelowej kolekcji stron i przemierzania hierarchii mogą być również uwzględniane w menu nawigacyjnym.

Układy

Markup

Dobre komponenty zaczynają się od odpowiedniego kodu HTML. W następnej sekcji omówię wybrane przeze mnie opcje znaczników i ich wpływ na cały komponent.

Schemat jasny i ciemny

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

Metatag color-scheme w powyższym informuje przeglądarkę, że strona chce korzystać z przeglądarki jasnej i ciemnej stylów. Przykładowe elementy nawigacyjne nie zawierają kodu CSS dla tych schematów kolorów, dlatego będą używać domyślnych kolorów udostępnionych przez przeglądarkę.

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

Użycie parametru Element <nav> do nawigacji w witrynie, która ma domyślnie przypisaną 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. Zapobiega to powtarzaniu informacji o tej samej ścieżce, przez co większe dokumenty i ryzyko niespójności ścieżek.

Aby skorzystać z tej metody, dodaj do strony ukryty element SVG i zapakuj ikony w elemencie <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, 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 tyle razy, ile chcesz, 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 ikonę, a .crumbicon odpowiada za układanie ikony w stosy i zaznaczanie elementu. 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 niektóre opcje nie są wyjątkami, ale zwiększają funkcjonalność proste menu nawigacyjne. Dodanie elementu title do elementu <select> jest przydatne dla użytkowników czytników ekranu, ponieważ dostarcza im informacji o działaniu przycisku. Jednak ta sama pomoc jest dostępna dla wszystkich użytkowników, a na iPadzie jest widoczna na pierwszym planie. Jeden atrybut zapewnia wielu użytkownikom kontekst dla przycisku.

Zrzut ekranu przedstawiający najechany niewidoczny element wyboru i jego
kontekstowa etykietka.

Dekoracje separatora

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

Użycie separatorów jest opcjonalne. Świetnie sprawdza się też dodanie tylko jednego z nich (zobacz trzeci przykład powyżej). Następnie dodaję aria-hidden="true", ponieważ są one dekoracyjne i nie muszą być odczytywane przez czytnik ekranu.

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 stylów.

Kierunek i przepływ układu

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

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 pionie z nakładkami flexboxa.

Każdy element .crumb tworzy też poziomy układ z niewielkim odstępem, ale kieruje się na elementy podrzędne linku 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 zmieniliśmy styl wizualny elementu, aby ułatwić korzystanie z niego użytkownikom ze wzrokiem.

Komponent .crumbicon używa siatki do nakładania ikony SVG z „niemal niewidoczne" <select>.

Siatka z Narzędziami deweloperskimi z nałożonym przyciskiem, tam gdzie wiersz i kolumna są jednocześnie
stos o nazwie.

.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ę nad stosem, i interaktywny. Dodaj styl opacity: .01, aby element nadal mógł być użyteczny, w rezultacie powstanie pole zaznaczenia, które idealnie pasuje do kształtu ikony. To dobry sposób na dostosowanie wyglądu elementu <select> podczas i obsługuje wbudowane funkcje.

.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. Jestem zwolennikiem że w odpowiednich sytuacjach przesuną się poza ekran w poziomie. .

.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 ograniczeniem przewijania.
  • 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

Subtelną korektą w mniejszych widocznych obszarach jest ukrycie ekranu głównego. label, opuszczając tylko ikonę:

@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 zbyt wielu ruchów, ale dzięki umieszczeniu przejścia w ramach kontroli prefers-reduced-motion możemy zapobiec niechcianym ruchom.

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

Żaden inny styl nie wymaga zmiany. Efekty najechania kursorem i zaznaczenia są świetne. i wartościowe bez transition. Jeśli ruch jest prawidłowy, dodajemy subtelny 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 nie było nieoczekiwanych przejść, gdy użytkownicy przeglądają <select>opcje.

Dwa kluczowe wskaźniki wrażeń użytkownika, które mają być obsługiwane przez JavaScript: zmienił się i chętniej <select> ustawił blokadę uruchamiania zdarzenia zmiany.

Pośpieszna zapobieganie zdarzeniom jest konieczne ze względu na użycie elementu <select>. W przeglądarce Edge na Windowsie (i prawdopodobnie też w innych przeglądarkach) zdarzenie select changedwyzwala się, 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. Pragniący sprawia, że funkcja zmiany kategorii komponentu jest niedostępna, ponieważ otwarcie pola wyboru i przeszukanie elementu spowoduje uruchomienie zdarzenia, zmienić stronę, 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
  })
})

W tym celu należy zwracać uwagę na przypadki braku klawiatury w każdym <select> i określić, czy naciśnięty klawisz to potwierdzenie nawigacji (Tab lub Enter) lub nawigacji przestrzennej (ArrowUp lub ArrowDown). W związku z tym determinacja, komponent może zdecydować, czy zaczekać, czy też zakończyć, gdy zdarzenie dla Uruchomiono <select> element.

Podsumowanie

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

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