Codelab: Создание компонента Sidenav

Эта лаборатория кода научит вас, как создать адаптивный компонент макета боковой навигации в Интернете. Мы будем создавать компонент по ходу дела, начиная с HTML, затем CSS, затем JavaScript.

Прочтите мою публикацию в блоге «Создание компонента Sidenav» , чтобы узнать о функциях веб-платформы CSS, выбранных для создания этого компонента.

Настраивать

  1. Нажмите Remix to Edit , чтобы сделать проект доступным для редактирования.
  2. Откройте app/index.html .

HTML

Во-первых, разберитесь с основами настройки HTML, чтобы было содержимое и несколько полей для работы.

Поместите следующий HTML-код в тег <body> .

<aside></aside>
<main></main>

<aside> содержит меню навигации как дополнительный элемент <main> , который содержит основное содержимое страницы.

Далее мы заполним эти семантические элементы остальным содержимым страницы.

Добавьте элемент навигации, несколько навигационных ссылок и ссылку закрытия внутри элемента <aside> .

<aside>
  <nav>
    <h4>My</h4>
    <a href="#">Dashboard</a>
    <a href="#">Profile</a>
    <a href="#">Preferences</a>
    <a href="#">Archive</a>

    <h4>Settings</h4>
    <a href="#">Accessibility</a>
    <a href="#">Theme</a>
    <a href="#">Admin</a>
  </nav>

  <a href="#"></a>
</aside>

Ссылки отлично смотрятся внутри элементов <nav> , а элементы <nav> отлично смотрятся на боковых панелях <aside> . Тем не менее, мы можем сделать больше, чтобы улучшиться.

В основной элемент контента добавьте заголовок и статью, чтобы семантически хранить содержимое макета.

<main>
  <header>
    <a href="#sidenav-open" class="hamburger">
      <svg viewBox="0 0 50 40">
        <line x1="0" x2="100%" y1="10%" y2="10%" />
        <line x1="0" x2="100%" y1="50%" y2="50%" />
        <line x1="0" x2="100%" y1="90%" y2="90%" />
      </svg>
    </a>
    <h1>Site Title</h1>
  </header>

  <article>
    {put some placeholder content here}
  </article>
</main>

В шапке есть ссылка на открытие меню. Сбоку имеется кнопка закрытия. Скоро мы будем показывать и скрывать элементы в зависимости от размера области просмотра.

В элемент <article> мы вставили предложение-заполнитель. Замените `` на свой собственный или вставьте лорем, представленный ниже:

<h2>Totam Header</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum consectetur, necessitatibus velit officia ut impedit veritatis temporibus soluta? Totam odit cupiditate facilis nisi sunt hic necessitatibus voluptatem nihil doloribus! Enim.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead Totam Odit</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

Именно этот контент и его длина обеспечивают возможность прокрутки страницы, когда она превышает высоту области просмотра.

На данный момент вы добавили элемент в сторону с навигацией, ссылками и возможностью закрыть боковую навигацию. Вы также добавили заголовок, способ открытия боковой панели и статью к основному элементу. Это уже чисто, семантично и вне времени, но мы можем сделать его чище и понятнее для всех. Открытую ссылку в боковой панели можно было бы обозначить более четко.

Добавьте атрибуты title и aria-label к элементу открытой ссылки заголовка:

<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">

Значок открытого SVG также можно было бы обозначить более четко. Добавьте следующие атрибуты в SVG внутри элемента открытой ссылки:

<svg viewBox="0 0 50 40">
<svg viewBox="0 0 50 40" role="presentation" focusable="false" aria-label="trigram for heaven symbol">

Тесную связь в боковой навигации можно было бы обозначить более четко. Добавьте атрибуты title и aria-label в элемент закрытия ссылки Sidenav:

<a href="#"></a>
<a href="#" title="Close Menu" aria-label="Close Menu"></a>

CSS

Пришло время расположить элементы. Основной контент и Sidenav являются прямыми дочерними элементами тега <body> , так что это хорошее место для начала.

Добавьте следующий CSS в css/sidenav.css , чтобы элемент <body> располагал дочерние элементы.

body {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;

  @media (max-width: 540px) {
    & > :matches(aside, main) {
      grid-area: stack;
    }
  }
}

По сути, этот макет говорит: создайте именованный stack строк со всем, что в нем, и 2 столбца в этой строке, второй из которых также называется именованным stack . Размер первого столбца должен соответствовать минимальным потребностям в содержимом, а второй столбец может занимать все остальное. Затем, если область просмотра ограничена 540px или меньше, поместите элементы Sidenav и основного контента в одну и ту же строку и столбец, в результате чего они окажутся друг над другом в сетке 1x1.

Благодаря этой гибкой функции стекирования в качестве основы мы теперь можем использовать состояние строки URL-адреса для переключения видимости и стиля перехода боковой панели.

Обновите элемент <aside> обратно в app/index.html :

<aside>
<aside id="sidenav-open">

Это позволяет CSS сопоставлять элемент и хэш URL-адреса вместе. Это важно для использования :target . Теперь идентификатор элемента может соответствовать хешу URL-адреса, который мы зададим с помощью тегов <a> .

Кроме того, чтобы упростить таргетинг на JavaScript, добавьте идентификаторы ключевых элементов, управляющих боковой навигацией. Сначала добавьте идентификатор в ссылку открытия Sidenav:

<a href="#sidenav-open" class="hamburger" title="Open Menu" aria-label="Open Menu">
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">

Затем добавьте идентификатор в ссылку закрытия Sidenav:

<a href="#" title="Close Menu" aria-label="Close Menu"></a>
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

На этом завершается адаптивный макет стекирования макроса <body> , а также привязывается нас к строке URL. Давайте продолжать!

<aside> также имеет аккуратный макет. У него есть 2 дочерних элемента: <nav> , который представляет собой выдвигающийся компонент, похожий на бумагу, и закрывающий элемент ссылки <a> , который устанавливает URL-адрес в # . Ссылка невидима справа от навигационной панели выдвижения бумаги; это для того, чтобы люди могли «щелкнуть» по визуальному компоненту, чтобы закрыть его.

Добавьте следующий CSS в css/sidenav.css :

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;
}

Я думаю, что пропорции и имена были здесь действительно приятным штрихом, поскольку сетка могла бы сиять и давать дизайнеру много контроля.

Далее мне нужно условно наложить основное содержимое и сохранить свою позицию при любой прокрутке документа. Это отличная работа для position: sticky и некоторая overscroll-behavior .

Добавьте следующие стили для боковой панели:

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;

  @media (max-width: 540px) {
    position: sticky;
    top: 0;
    max-height: 100vh;
    overflow: hidden auto;
    overscroll-behavior: contain;

    visibility: hidden; /* not keyboard accessible when closed */
  }
}

Эти стили гарантируют, что Sidenav соответствует высоте области просмотра, прокручивается вертикально и содержит прокрутку. Очень важно, что он скрывает элемент. По умолчанию, когда область просмотра составляет 540px или меньше, скройте эту боковую панель. Пока не!

Добавьте псевдоселектор :target к элементу #sidenav-open :

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

Если идентификатор этого элемента и строка URL совпадают, установите для видимости visibility visible . Откройте боковое меню после прокрутки страницы или попробуйте прокрутить страницу, пока открыта боковая навигация. Что вы думаете?

Добавьте следующий CSS в конец файла app/sidenav.css :

#sidenav-button,
#sidenav-close {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;
  touch-action: manipulation;

  @media (min-width: 540px) {
    display: none;
  }
}

Эти стили предназначены для наших кнопок открытия и закрытия, определяют их стили касания и касания, а также скрывают их, когда область просмотра составляет 540px или больше.

Для немного изящества давайте добавим CSS-преобразования с уважительной доступностью. Добавьте следующий CSS в css/sidenav.css :

#sidenav-open {
  --easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1);
  --duration: .6s;

  ...

  @media (max-width: 540px) {
    ...

    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);

    &:target {
      visibility: visible;
      transform: translateX(0);
      transition: transform var(--duration) var(--easeOutExpo);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    --duration: 1ms;
  }
}
Демонстрация взаимодействия с длительностью и без нее, примененная на основе медиа-запроса «предпочитает уменьшенное движение».

Добавьте немного JavaScript

Клавиша Escape должна закрыть меню. Добавьте этот JS в js/index.js :

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', e => {
  if (e.code === 'Escape') {
    document.location.hash = '';
  }
});

Это прослушивает ключевое событие в элементе Sidenav. Если это Escape , хеш URL-адреса становится пустым, что приводит к отключению боковой навигации.

Следующая часть UX JS — управление фокусом. Я хочу, чтобы открытие и закрытие было простым, поэтому я жду, пока Sidenav завершит какой-либо переход, а затем перекрестно проверяю его по хешу URL-адреса, чтобы определить, включен он или нет. Затем я использую JavaScript, чтобы установить фокус на кнопке, дополняющей ту, которую они только что нажали.

Добавьте следующий код JavaScript в js/index.js :

const closenav = document.querySelector('#sidenav-close');
const opennav = document.querySelector('#sidenav-button');

sidenav.addEventListener('transitionend', e => {
  if (e.propertyName !== 'transform') {
    return;
  }

  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
    ? closenav.focus()
    : opennav.focus();
});

Попробуйте это

  • Чтобы просмотреть сайт, нажмите «Просмотреть приложение» . Затем нажмите Полноэкранный режим полноэкранный .

Заключение

Это подведение итогов того, что у меня было с этим компонентом. Не стесняйтесь развивать его, управляйте им с помощью состояния JavaScript вместо URL-адреса и в общем сделайте его своим! Всегда есть что добавить или охватить больше вариантов использования.

Откройте css/brandnav.css чтобы проверить стили, не связанные с макетом, которые я применил к этому компоненту. Я не чувствовал, что это важно для набора функций, на которых я сосредоточился, и надеялся, что отделение стилей от макета будет способствовать копированию и вставке. Там вы могли бы получить больше знаний!

Как сделать выдвижные адаптивные компоненты боковой навигации? У вас когда-нибудь было больше одного, например, по одному с обеих сторон? Я хотел бы представить ваше решение в видео на YouTube. Обязательно напишите мне в Твиттере или оставьте комментарий на YouTube со своим кодом, это всем поможет!