Codelab: Sidenav 구성요소 빌드

이 Codelab에서는 웹에서 반응형 슬라이드 아웃 측면 탐색 레이아웃 구성요소를 빌드하는 방법을 알아봅니다. HTML부터 시작하여 CSS, JavaScript 순으로 구성요소를 빌드해 보겠습니다.

이 구성요소를 빌드하기 위해 선택한 CSS 웹 플랫폼 기능에 관한 자세한 내용은 제 블로그 게시물 Sidenav 구성요소 빌드를 참조하세요.

설정

  1. 리믹스하여 수정을 클릭하여 프로젝트를 수정할 수 있도록 합니다.
  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>

이 콘텐츠와 길이는 페이지가 표시 영역 높이를 초과할 때 페이지를 스크롤할 수 있게 합니다.

지금까지 탐색, 링크, 사이드나브 닫기 방법이 포함된 aside 요소를 추가했습니다. 또한 기본 요소에 헤더, 사이드바를 여는 방법, 기사를 추가했습니다. 이미 깔끔하고 의미 있으며 시대를 초월하지만 모든 사용자에게 더 깔끔하고 명확하게 만들 수 있습니다. 사이드바의 열려 있는 링크를 더 명확하게 표시할 수 있습니다.

헤더의 열린 링크 요소에 titlearia-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">

사이드바의 닫기 링크가 더 명확하게 표시될 수 있습니다. 사이드나브 닫기 링크 요소에 titlearia-label 속성을 추가합니다.

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

CSS

이제 요소를 배치할 차례입니다. 기본 콘텐츠와 sidenav는 <body> 태그의 직접 하위 요소이므로 시작하는 것이 좋습니다.

<body> 요소가 하위 요소를 배치하도록 다음 CSS를 css/sidenav.css에 추가합니다.

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

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

이 레이아웃은 기본적으로 다음을 나타냅니다. 모든 항목이 포함된 이름이 지정된 행 stack을 만들고 이 행에 두 개의 열을 만들되 두 번째 열의 이름도 stack입니다. 첫 번째 열은 최소 콘텐츠 요구사항에 따라 크기를 조정해야 하며 두 번째 열은 나머지를 차지할 수 있습니다. 그런 다음 제약된 뷰포인트가 540px 이하인 경우 사이드나브와 기본 콘텐츠 요소를 동일한 행과 열에 배치하여 1x1 그리드에서 서로 겹치게 합니다.

이제 이 반응형 스태킹 기능을 기본으로 사용하여 URL 표시줄의 상태를 활용하여 sidenav의 공개 상태 및 전환 스타일을 전환할 수 있습니다.

app/index.html에서 <aside> 요소를 다시 업데이트합니다.

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

이렇게 하면 CSS가 요소와 URL 해시를 함께 일치시킬 수 있습니다. 이는 :target 사용 시 중요합니다. 이제 요소의 ID가 <a> 태그로 설정할 URL 해시와 일치할 수 있습니다.

또한 JavaScript 타겟팅을 더 쉽게 하려면 사이드나비를 제어하는 주요 요소의 ID를 추가합니다. 먼저 측면 탐색 열기 링크에 ID를 추가합니다.

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

그런 다음 사이드바 닫기 링크에 ID를 추가합니다.

<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>와 URL을 #로 설정하는 닫는 <a> 링크 요소입니다. 링크는 종이 슬라이드 아웃 탐색 메뉴의 오른쪽에 표시되지 않습니다. 사용자가 시각적 구성요소를 '클릭하여' 닫을 수 있도록 하기 위해서입니다.

css/sidenav.css에 다음 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 */
  }
}

이러한 스타일은 사이드나브가 표시 영역 높이이고 세로로 스크롤하며 스크롤을 포함하도록 합니다. 중요한 점은 요소를 숨긴다는 것입니다. 기본적으로 표시 영역이 540px 이하이면 사이드나브를 숨깁니다. 아니면

#sidenav-open 요소에 :target 가상 선택기를 추가합니다.

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

해당 요소와 URL 표시줄의 ID가 동일하면 visibilityvisible로 설정합니다. 페이지를 스크롤한 후 사이드 메뉴를 열거나 사이드바가 열려 있는 동안 페이지를 스크롤해 보세요. 어떻게 생각하시나요?

app/sidenav.css 하단에 다음 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/sidenav.css에 다음 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;
  }
}
`prefers-reduced-motion` 미디어 쿼리를 기반으로 적용된 시간 유무와 관계없는 상호작용의 데모입니다.

JavaScript를 뿌리기

Escape 키를 누르면 메뉴가 닫힙니다. js/index.js에 다음 JS를 추가합니다.

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

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

이렇게 하면 사이드나브 요소에서 키 이벤트를 수신합니다. Escape인 경우 URL 해시를 비워 사이드나브가 전환되도록 합니다.

UX JS의 다음 부분은 포커스 관리입니다. 열기와 닫기를 쉽게 만들고 싶으므로 사이드바가 어떤 종류의 전환을 완료할 때까지 기다린 다음 URL 해시와 교차 확인하여 사이드바가 열려 있는지 닫혀 있는지 확인합니다. 그런 다음 JavaScript를 사용하여 방금 누른 버튼과 상호 보완되는 버튼에 포커스를 설정합니다.

js/index.js에 다음 JavaScript를 추가합니다.

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

사용해 보기

  • 사이트를 미리 보려면 앱 보기를 누른 다음 전체 화면전체 화면을 누릅니다.

결론

구성요소와 관련된 요구사항을 마무리했습니다. 이 샘플을 기반으로 하여 URL 대신 JavaScript 상태로 구동하고 원하는 대로 수정하세요. 항상 추가할 사항이나 다룰 사용 사례가 더 있습니다.

css/brandnav.css를 열어 이 구성요소에 적용한 레이아웃과 관련 없는 스타일을 확인합니다. 주력하고 있는 기능 세트가 중요하다고 생각하지 않았고, 레이아웃에서 스타일을 분리하면 복사 및 붙여넣기가 늘어나기를 바랐습니다. 더 많은 것을 배울 수 있습니다.

슬라이드 아웃 반응형 사이드나브 구성요소를 만드는 방법 양쪽에 하나씩 등 1보다 많은 경우도 있나요? YouTube 동영상에 해결 방법을 소개하고자 합니다. 트윗을 보내거나 YouTube에 코드와 함께 댓글을 남겨주세요. 모든 사용자에게 도움이 될 것입니다.