반응형 슬라이드 아웃 사이드나비를 빌드하는 방법에 관한 기본 개요
이 게시물에서는 반응형이고 상태가 있으며 키보드 탐색을 지원하고 JavaScript 유무와 관계없이 작동하며 여러 브라우저에서 작동하는 웹용 사이드나브 구성요소의 프로토타입을 만드는 방법을 공유하고자 합니다. 데모 사용해 보기
동영상을 선호하는 경우 이 게시물의 YouTube 버전을 확인하세요.
개요
반응형 탐색 시스템을 빌드하는 것은 쉽지 않습니다. 어떤 사용자는 키보드를 사용하고, 어떤 사용자는 강력한 데스크톱을 사용하고, 어떤 사용자는 작은 휴대기기에서 방문합니다. 방문하는 모든 사용자가 메뉴를 열고 닫을 수 있어야 합니다.
웹 전술
이 구성요소 탐색에서는 몇 가지 중요한 웹 플랫폼 기능을 결합하는 즐거움을 누렸습니다.
솔루션에는 사이드바가 하나 있으며 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개를 만드는 그리드이며, 각각 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>
는 측면 탐색 메뉴가 포함된 애니메이션 요소입니다. 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;
}
}
이제 사이드나브가 열리고 닫힐 때 사용자가 움직임을 줄이고 싶다면 요소를 즉시 뷰로 이동하여 움직임 없이 상태를 유지합니다.
전환, 변환, 변환
측면 탐색 바깥쪽 (기본값)
휴대기기에서 사이드나브의 기본 상태를 오프스크린 상태로 설정하려면 transform: translateX(-110vw)
를 사용하여 요소를 배치합니다.
사이드바의 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
요소가 :target
과 일치하면 translateX()
위치를 홈베이스 0
로 설정하고 CSS가 URL 해시가 변경될 때 요소를 -110vw
의 외부 위치에서 var(--duration)
위의 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>
이제 기본 상호작용 버튼은 마우스와 키보드 모두에 대한 의도를 명확하게 설명합니다.
: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();
})
사이드나브가 열리면 닫기 버튼에 포커스를 맞춥니다. 사이드나브가 닫히면 열기 버튼에 포커스를 맞춥니다. JavaScript에서 요소에 focus()
를 호출하여 이 작업을 실행합니다.
결론
이제 내가 어떻게 했는지 알았으니 어떻게 하면 어떨까?! 이를 통해 재미있는 구성요소 아키텍처를 만들 수 있습니다. 슬롯이 있는 첫 번째 버전은 누가 만들까요? 🙂
접근 방식을 다양화하고 웹에서 빌드하는 모든 방법을 알아보겠습니다. Glitch를 만들고 내 버전을 트윗해 주시면 아래의 커뮤니티 리믹스 섹션에 추가해 드리겠습니다.
커뮤니티 리믹스
- 커스텀 요소를 사용한 @_developit: 데모 및 코드
- HTML/CSS/JS를 사용하는 @mayeedwin1: 데모 및 코드
- 글리치 리믹스: @a_nurella 데모 및 코드
- @EvroMalarkey 및 HTML/CSS/JS: 데모 및 코드