sidenav 구성요소 빌드

반응형 슬라이드 아웃 사이드 탐색 빌드 방법에 관한 기본 개요

이 게시물에서는 반응형, 스테이트풀(Stateful)이며, 키보드 탐색을 지원하고, JavaScript와 함께 또는 사용하지 않고 작동하고, 여러 브라우저에서 작동하는 웹용 Sidenav 구성요소의 프로토타입을 어떻게 프로토타입했는지 공유하고자 합니다. 데모 사용해 보기

동영상을 선호한다면 이 게시물의 YouTube 버전을 참조하세요.

개요

반응형 탐색 시스템을 빌드하기는 어렵습니다. 어떤 사용자는 키보드를 사용하고, 어떤 사용자는 강력한 데스크톱을 사용하고, 어떤 사용자는 작은 휴대기기를 사용하여 방문합니다. 방문하는 모든 사용자가 메뉴를 열고 닫을 수 있어야 합니다.

데스크톱-모바일 반응형 레이아웃 데모
iOS 및 Android에서 밝은 테마와 어두운 테마 다운

웹 전략

이 구성요소 탐색에서 저는 다음과 같은 몇 가지 중요한 웹 플랫폼 기능을 결합하는 즐거움을 느꼈습니다.

  1. CSS :target
  2. CSS 그리드
  3. CSS transforms
  4. 표시 영역 및 사용자 환경설정에 대한 CSS 미디어 쿼리
  5. focus UX 개선사항을 위한 JS

내 솔루션에는 사이드바가 하나 있으며 540px 이하의 '모바일' 표시 영역에 있을 때만 전환됩니다. 540px은 모바일 대화형 레이아웃과 정적 데스크톱 레이아웃 간 전환을 위한 중단점입니다.

CSS :target 의사 클래스

<a> 링크는 URL 해시를 #sidenav-open로 설정하고 다른 하나를 비어 있음 ('')으로 설정합니다. 마지막으로 요소에 해시와 일치하는 id가 있습니다.

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

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

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

이러한 각 링크를 클릭하면 페이지 URL의 해시 상태가 변경되고 유사 클래스를 사용하여 측면 탐색을 표시하고 숨깁니다.

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
  }

  #sidenav-open:target {
    visibility: visible;
  }
}

CSS 그리드

이전에는 절대 위치 또는 고정 위치 사이드 탐색 레이아웃 및 구성요소만 사용했습니다. 하지만 그리드를 사용하면 grid-area 문법을 통해 동일한 행이나 열에 여러 요소를 할당할 수 있습니다.

스택

기본 레이아웃 요소 #sidenav-container는 행 1개와 열 2개를 만드는 그리드로, 각 열의 이름을 stack로 지정합니다. 공간이 제한되면 CSS는 <main> 요소의 모든 하위 요소를 동일한 그리드 이름에 할당하고 모든 요소를 동일한 공간에 배치하여 스택을 만듭니다.

#sidenav-container {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;
  min-height: 100vh;
}

@media (max-width: 540px) {
  #sidenav-container > * {
    grid-area: stack;
  }
}

<aside>는 측면 탐색 메뉴가 포함된 애니메이션 요소입니다. 여기에는 2개의 하위 요소가 있는데 하나는 [nav] 탐색 컨테이너 <nav>이고 다른 하나는 메뉴를 닫는 데 사용되는 <a> [escape] 백드롭입니다.

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

2fr1fr를 조정하여 메뉴 오버레이와 네거티브 스페이스 닫기 버튼의 비율을 찾습니다.

비율을 변경하면 어떤 일이 발생하는지 보여주는 데모입니다.

CSS 3D 변환 및 전환

이제 레이아웃이 모바일 표시 영역 크기로 누적됩니다. 새 스타일을 추가할 때까지는 기사가 기본적으로 오버레이됩니다. 이 다음 섹션에서 살펴볼 UX는 다음과 같습니다.

  • 열기 및 닫기 애니메이션
  • 사용자가 괜찮은 경우에만 모션을 사용하여 애니메이션을 적용합니다.
  • 키보드 포커스가 오프스크린 요소에 들어가지 않도록 visibility에 애니메이션을 적용합니다.

저는 모션 애니메이션을 구현하기 시작할 때 접근성을 최우선으로 생각하고 싶습니다.

액세스 가능한 모션

누구나 슬라이드 아웃 모션 경험을 원하지는 않을 것입니다. 이 솔루션에서는 미디어 쿼리 내에서 --duration CSS 변수를 조정하여 이러한 환경설정을 적용합니다. 이 미디어 쿼리 값은 모션에 대한 사용자의 운영체제 환경설정을 나타냅니다 (있는 경우).

#sidenav-open {
  --duration: .6s;
}

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
지속 시간을 적용한 상호작용과 사용하지 않은 상호작용의 데모입니다.

이제 측면 탐색 메뉴가 슬라이딩 및 닫힐 때 사용자가 축소된 모션을 선호한다면 요소를 즉시 뷰로 이동하여 모션 없이 상태를 유지합니다.

전환, 변환, 변환

측면 탐색 외부 (기본값)

모바일에서 측면 탐색 메뉴의 기본 상태를 오프스크린 상태로 설정하기 위해 transform: translateX(-110vw)로 요소를 배치합니다.

참고로, 일반적인 오프스크린 코드 -100vw에 다른 10vw를 추가했습니다. 이는 측면 탐색 메뉴의 box-shadow가 숨겨져 있을 때 기본 표시 영역을 엿보지 않도록 하기 위함입니다.

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);
  }
}
측면 탐색 메뉴

#sidenav 요소가 :target와 일치하면 translateX() 위치를 홈베이스 0로 설정하고 CSS에서 URL 해시가 변경될 때 CSS가 요소를 바깥쪽 -110vw에서 '인' 위치로 var(--duration) 위에 'in' 위치로 슬라이드하는 것을 볼 수 있습니다.0

@media (max-width: 540px) {
  #sidenav-open:target {
    visibility: visible;
    transform: translateX(0);
    transition:
      transform var(--duration) var(--easeOutExpo);
  }
}

전환 공개 상태

이제 스크린 리더가 메뉴를 벗어났을 때 해당 메뉴를 숨겨 시스템이 오프스크린 메뉴에 포커스를 두지 않도록 하는 것이 목표입니다. :target가 변경될 때 공개 상태 전환을 설정하여 이 작업을 실행합니다.

  • 들어가면 공개 상태를 전환하지 마세요. 요소가 슬라이드 인하고 포커스를 받을 수 있도록 즉시 표시되도록 합니다.
  • 밖으로 나가면 표시 상태를 전환하지만 지연되므로 전환 종료 시 hidden로 전환됩니다.

접근성 UX 개선사항

이 솔루션은 상태를 관리하기 위해 URL을 변경해야 합니다. 물론 여기에서 <a> 요소를 사용해야 하며 멋진 접근성 기능을 무료로 제공합니다. 인텐트를 명확하게 전달하는 라벨로 상호작용 요소를 장식해 보겠습니다.

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

<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
  <svg>...</svg>
</a>
음성 해설 및 키보드 상호작용 UX의 데모입니다.

이제 기본 상호작용 버튼에서 마우스와 키보드 모두에 관한 인텐트를 명확하게 표시합니다.

:is(:hover, :focus)

이 편리한 CSS 기능인 의사 선택기를 사용하면 마우스 오버 스타일을 포커스와 공유하여 신속하게 포용할 수 있습니다.

.hamburger:is(:hover, :focus) svg > line {
  stroke: hsl(var(--brandHSL));
}

자바스크립트 기반 Sprinkle

닫으려면 escape 키를 누르세요

키보드의 Escape 키를 누르면 메뉴가 닫힙니다. 이것을 연결합시다.

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

sidenav.addEventListener('keyup', event => {
  if (event.code === 'Escape') document.location.hash = '';
});
브라우저 방문 기록

열기 및 닫기 상호작용이 브라우저 기록에 여러 항목을 쌓지 않도록 하려면 닫기 버튼에 다음 JavaScript 인라인을 추가합니다.

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>

이렇게 하면 닫을 때 URL 기록 항목이 삭제되어 메뉴가 열린 적이 없는 것처럼 됩니다.

UX 중심

다음 스니펫은 열기 또는 닫기 버튼을 연 후 버튼에 포커스를 두는 데 도움이 됩니다. 쉽게 전환하고 싶어.

sidenav.addEventListener('transitionend', e => {
  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
      ? document.querySelector('#sidenav-close').focus()
      : document.querySelector('#sidenav-button').focus();
})

측면 탐색 메뉴가 열리면 닫기 버튼에 포커스를 맞춥니다. 측면 탐색 메뉴가 닫히면 열기 버튼에 포커스를 맞춥니다. JavaScript의 요소에서 focus()를 호출하면 됩니다.

결론

이제 내가 어떻게 했는지 알았으니 어떻게 할 건가요? 이를 통해 재미있는 구성요소 아키텍처를 만들 수 있습니다. 누가 슬롯이 있는 첫 번째 버전을 만들까요? 🙂

접근 방식을 다양화하고 웹에서 빌드하는 모든 방법을 알아보겠습니다 Glitch를 만들고 내 버전을 트윗하면 아래의 커뮤니티 리믹스 섹션에 추가합니다.

커뮤니티 리믹스