Skip to content
정보 블로그 배우기 탐구하다 패턴 Case studies
이 페이지에서
  • 개요
  • 웹 전술
    • CSS :target 의사 클래스
    • CSS 그리드
    • CSS 3D 변환 및 전환
    • 접근성 UX 개선 사항
    • 결론
  • 커뮤니티 리믹스
  • Home
  • All articles

sidenav 구성요소 빌드

반응형 슬라이드 아웃 sidenav를 구축하는 방법에 대한 기본 개요

Jan 21, 2021
Available in: English, Español, Русский 및 日本語
Adam Argyle
Adam Argyle
TwitterGitHubGlitchHomepage
이 페이지에서
  • 개요
  • 웹 전술
    • CSS :target 의사 클래스
    • CSS 그리드
    • CSS 3D 변환 및 전환
    • 접근성 UX 개선 사항
    • 결론
  • 커뮤니티 리믹스

이 게시물에서 반응형, 상태 저장형, 키보드 탐색 지원, JavaScript 유무에 관계없이 작동하고 여러 브라우저에서 작동하는 웹용 sidenav 구성 요소의 프로토타입을 만든 방법을 공유합니다. 데모를 사용해 보세요.

비디오를 선호하는 경우 이 게시물의 YouTube 버전이 있습니다.

개요 #

반응형 내비게이션 시스템을 구축하는 것은 어렵습니다. 일부 사용자는 키보드를 사용하고 일부는 강력한 데스크톱을 사용하며 일부는 소형 모바일 장치에서 방문합니다. 방문하는 모든 사람이 메뉴를 열고 닫을 수 있어야 합니다.

데스크톱과 모바일에 반응하는 레이아웃 데모
iOS 및 Android에서 작동하는 밝고 어두운 테마

웹 전술 #

이번 구성 요소 탐색에서 몇 가지 중요한 웹 플랫폼 기능을 결합할 수 있었습니다.

  1. CSS :target
  2. CSS 그리드
  3. CSS 변환
  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의 해시 상태가 변경되고 의사 클래스를 사용하여 sidenav를 표시하거나 숨깁니다.

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

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

CSS 그리드 #

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

스택 #

기본 레이아웃 요소인 #sidenav-container는 1개의 행과 2개의 열을 생성하는 그리드이며, 각각 1개는 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>는 sidenav를 포함하는 애니메이션 요소입니다. 여기에는 2개의 자식이 있습니다. [nav]로 명명된 탐색 컨테이너 <nav>와 메뉴를 닫는 데 사용되며 [escape]으로 명명된 백드롭 <a>입니다.

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

2fr 및 1fr을 조정하여 메뉴 오버레이와 음수 공간 닫기 버튼에 대해 원하는 비율을 찾습니다.

비율을 변경하면 어떻게 되는지를 보여주는 데모입니다.

CSS 3D 변환 및 전환 #

이제 레이아웃이 모바일 뷰포트 크기로 쌓입니다. 몇 가지 새로운 스타일을 추가할 때까지는 기본적으로 기사를 오버레이합니다. 다음은 이 다음 섹션에서 목표로 하는 몇 가지 UX입니다.

  • 열기 및 닫기 애니메이션
  • 사용자가 동의하는 경우에만 모션으로 애니메이션을 적용
  • visibility 애니메이션을 적용하여 키보드 포커스가 오프스크린 요소에 들어가지 않게 하기

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

접근 가능한 모션 #

모든 사람이 슬라이드 아웃 모션 경험을 원하는 것은 아닙니다. 우리 솔루션에서 이 기본 설정은 미디어 쿼리 내에서 --duration CSS 변수를 조정하여 적용됩니다. 미디어 쿼리 값은 동작에 대한 사용자의 운영 체제 기본 설정을 나타냅니다(사용 가능한 경우).

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

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

이제 sidenav가 열리고 닫힐 때 사용자가 감소된 움직임을 선호하는 경우 움직임이 없는 상태를 유지하면서 요소를 즉시 보기로 이동합니다.

전환, 변형, 번역 #

sidenav out(기본값) #

모바일에서 sidenav의 기본 상태를 오프스크린 상태로 설정하려면 transform: translateX(-110vw)을 적용하여 요소를 배치합니다.

sidenav의 box-shadow가 숨겨져 있을 때 기본 뷰포트를 들여다보지 않도록 하기 위해 일반적인 오프스크린 코드인 -100vw에 또 다른 10vw를 추가했습니다.

@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 in #

#sidenav 요소가 :target과 일치하면 translateX() 위치를 homebase 0으로 설정하고 URL 해시가 변경될 때 CSS가 요소를 -110vw 출력 위치에서 "in" 위치인 0으로 var(--duration) 동안 슬라이드 아웃하는 것을 지켜보세요.

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

JavaScript에 뿌리기 #

닫으려면 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();
})

sidenav가 열리면 닫기 버튼에 초점을 맞춥니다. sidenav가 닫히면 열기 버튼에 초점을 맞춥니다. JavaScript의 요소에서 focus()를 호출하여 이 작업을 수행합니다.

결론 #

이제 제가 어떻게했는지 알아보았습니다. 여러분이라면 어떻게 하시겠습니까? 이것은 재미있는 구성 요소 아키텍처입니다! 슬롯이 있는 1차 버전은 누가 만들까요? 🙂

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

커뮤니티 리믹스 #

  • @_developit의 사용자 정의 요소: 데모 및 코드
  • @mayeedwin1의 HTML/CSS/JS: 데모 및 코드
  • @a_nurella의 Glitch Remix: 데모 및 코드
  • @EvroMalarkey의 HTML/CSS/JS: 데모 및 코드
CSSDOMJavaScript레이아웃모바일UX
마지막 업데이트: Jan 21, 2021 — 기사 개선하기
Codelabs

See it in action

Learn more and put this guide into action.

  • Codelab: Building a Sidenav component
Return to all articles
공유
구독

Contribute

  • 버그 신고
  • 소스 보기

관련된 콘텐츠

  • developer.chrome.com
  • Chrome 업데이트
  • 사례 연구
  • 팟캐스트
  • 쇼

연결

  • Twitter
  • YouTube
  • Google Developers
  • Chrome
  • Firebase
  • Google Cloud Platform
  • 전체 제품
  • 약관 및 개인정보 보호
  • 커뮤니티 가이드라인

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies.