iOS 및 Android 앱에 있는 것과 유사한 탭 구성요소를 빌드하는 방법에 관한 기본적인 개요입니다.
이 게시물에서는 반응형이고, 여러 기기 입력을 지원하며, 여러 브라우저에서 작동하는 웹용 Tabs 구성요소를 빌드하는 생각을 공유하고자 합니다. 데모 사용해 보기
동영상을 선호한다면 이 게시물의 YouTube 버전을 참조하세요.
개요
탭은 디자인 시스템의 일반적인 구성요소이지만 다양한 모양과 형태를 취할 수 있습니다. 먼저 <frame>
요소를 기반으로 빌드된 데스크톱 탭이 있었지만 이제는 물리 속성을 기반으로 콘텐츠에 애니메이션을 적용하는 버터형 모바일 구성요소가 있습니다.
공간 절약이라는 동일한 일을 하려고 합니다.
오늘날 탭 사용자 환경의 핵심은 디스플레이 프레임의 콘텐츠 공개 상태를 전환하는 버튼 탐색 영역입니다. 다양한 콘텐츠 영역이 동일한 공간을 공유하지만 탐색에서 선택한 버튼을 기반으로 조건부로 표시됩니다.
![웹이 구성요소 개념에 적용한 스타일이 매우 다양하여 콜라주가 매우 혼란스러울 수 있습니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-collage-is-quite-chao-b6f8cd38f8ddd.png?authuser=7&hl=ko)
웹 전략
이 구성요소는 대체로 다음과 같은 몇 가지 중요한 웹 플랫폼 기능 덕분에 빌드할 수 있습니다.
scroll-snap-points
: 적절한 스크롤 중지 위치와 함께 우아한 스와이프 및 키보드 상호작용- 브라우저의 URL 해시를 통한 딥 링크가 인페이지 스크롤 앵커링 및 공유 지원을 처리함
<a>
및id="#hash"
요소 마크업을 사용한 스크린 리더 지원prefers-reduced-motion
: 크로스페이드 전환과 즉각적인 인페이지 스크롤을 사용 설정합니다.- 선택한 탭에 동적으로 밑줄을 치고 색상을 변경하는 초안
@scroll-timeline
웹 기능
HTML
기본적으로 UX에서 링크를 클릭하고 URL이 중첩된 페이지 상태를 나타내도록 한 다음 브라우저에서 일치하는 요소로 스크롤할 때 콘텐츠 영역이 업데이트됩니다.
여기에는 구조적 콘텐츠 멤버인 링크와 :target
가 있습니다. <nav>
가 적합한 링크 목록과 <section>
가 적합한 <article>
요소 목록이 필요합니다. 각 링크 해시는 섹션과 일치하므로 브라우저에서 앵커링을 통해 스크롤할 수 있습니다.
예를 들어 링크를 클릭하면 Chrome 89의 :target
도움말로 자동 이동되며 자바스크립트가 필요하지 않습니다. 그러면 사용자는 평소대로 입력 장치를 사용하여 기사 콘텐츠를 스크롤할 수 있습니다. 마크업에 표시된 대로 무료 콘텐츠입니다.
다음 마크업을 사용하여 탭을 구성했습니다.
<snap-tabs>
<header>
<nav>
<a></a>
<a></a>
<a></a>
<a></a>
</nav>
</header>
<section>
<article></article>
<article></article>
<article></article>
<article></article>
</section>
</snap-tabs>
다음과 같이 href
및 id
속성을 사용하여 <a>
와 <article>
요소 간의 연결을 설정할 수 있습니다.
<snap-tabs>
<header>
<nav>
<a href="#responsive"></a>
<a href="#accessible"></a>
<a href="#overscroll"></a>
<a href="#more"></a>
</nav>
</header>
<section>
<article id="responsive"></article>
<article id="accessible"></article>
<article id="overscroll"></article>
<article id="more"></article>
</section>
</snap-tabs>
그런 다음 기사에 lorem을 혼합하고 긴 길이와 이미지 세트를 혼합하여 링크를 채웠습니다. 작업할 콘텐츠로 레이아웃을 시작할 수 있습니다.
스크롤 레이아웃
이 구성요소에는 세 가지 유형의 스크롤 영역이 있습니다.
- 탐색 창(분홍색)을 가로로 스크롤할 수 있음
- 콘텐츠 영역(파란색)을 가로로 스크롤할 수 있음
- 각 기사 항목(녹색)은 세로로 스크롤할 수 있습니다.
![다채로운 색상의 상자 3개에 색상 일치 화살표가 표시되어 있습니다. 이 상자는 스크롤 영역의 윤곽을 잡고 스크롤 방향을 보여줍니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/3-colorful-boxes-color-m-da0de23b13551.png?authuser=7&hl=ko)
스크롤과 관련된 두 가지 유형의 요소가 있습니다.
- 창
overflow
속성 스타일을 가진 정의된 측정기준이 있는 상자입니다. - 대형 표면
이 레이아웃에서는 목록 컨테이너(탐색 링크, 섹션 기사, 기사 콘텐츠)입니다.
<snap-tabs>
레이아웃
선택한 최상위 수준 레이아웃은 Flex (Flexbox)였습니다. 방향을 column
로 설정했으므로 헤더와 섹션이 세로로 정렬됩니다. 이는 첫 번째 스크롤 창이며, 오버플로가 숨겨진 모든 항목을 숨깁니다. 헤더와 섹션은 곧 개별 영역으로 오버스크롤을 사용합니다.
<snap-tabs> <header></header> <section></section> </snap-tabs>
snap-tabs { display: flex; flex-direction: column; /* establish primary containing box */ overflow: hidden; position: relative; & > section { /* be pushy about consuming all space */ block-size: 100%; } & > header { /* defend againstneeding 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }
다채로운 색상의 3스크롤 다이어그램을 다시 가리키기
- 이제
<header>
가 (분홍색) 스크롤 컨테이너가 될 준비가 되었습니다. <section>
는 (파란색) 스크롤 컨테이너로 준비됩니다.
아래에 VisBug와 함께 강조 표시한 프레임을 사용하면 스크롤 컨테이너가 만든 창을 확인할 수 있습니다.
![헤더와 섹션 요소에 핫핑크 오버레이가 있어서 구성요소에서 차지하는 공간을 알 수 있습니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-header-section-eleme-dd313d7eb1453.png?authuser=7&hl=ko)
탭 <header>
레이아웃
다음 레이아웃은 거의 동일합니다. Flex를 사용하여 세로 순서를 생성합니다.
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
header { display: flex; flex-direction: column; }
.snap-indicator
는 링크 그룹과 함께 가로로 이동해야 하며 이 헤더 레이아웃은 이 단계를 설정하는 데 도움이 됩니다. 여기에는 절대 위치로 배치된 요소가 없습니다.
![nav 및 span.indicator 요소에 핫핑크 오버레이가 있어서 구성요소에서 차지하는 공간을 대략적으로 보여줍니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-nav-spanindicator-e-eddaf5b04df39.png?authuser=7&hl=ko)
다음은 스크롤 스타일입니다. 두 개의 가로 스크롤 영역 (헤더 및 섹션) 간에 스크롤 스타일을 공유할 수 있으므로 유틸리티 클래스 .scroll-snap-x
를 만들었습니다.
.scroll-snap-x {
/* browser decide if x is ok to scroll and show bars on, y hidden */
overflow: auto hidden;
/* prevent scroll chaining on x scroll */
overscroll-behavior-x: contain;
/* scrolling should snap children on x */
scroll-snap-type: x mandatory;
@media (hover: none) {
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
}
각각은 x축의 오버플로우, 오버스크롤 트랩을 위한 스크롤 포함, 터치 기기의 숨겨진 스크롤바, 마지막으로 콘텐츠 프레젠테이션 영역을 잠그기 위한 스크롤 스냅이 필요합니다. 키보드 탭 순서에 액세스할 수 있으며 모든 상호작용 가이드가 자연스럽게 초점을 맞춥니다. 스크롤 스냅 컨테이너도 키보드에서 캐러셀 스타일로 상호작용합니다.
탭 헤더 <nav>
레이아웃
탐색 링크는 줄바꿈 없이 한 줄로 배치하고 세로로 가운데 정렬해야 하며 각 링크 항목은 스크롤-스냅 컨테이너에 맞춰야 합니다. 2021년 CSS도 훌륭하게 구성되었습니다.
<nav> <a></a> <a></a> <a></a> <a></a> </nav>
nav { display: flex; & a { scroll-snap-align: start; display: inline-flex; align-items: center; white-space: nowrap; } }
각 링크 스타일과 크기 자체가 조정되므로 탐색 레이아웃에서는 방향과 흐름만 지정하면 됩니다. 탐색 항목의 고유한 너비를 사용하면 표시기가 너비를 새 타겟에 맞춰 조정하므로 탭 간 전환이 재미있어집니다. 여기에 포함된 요소 수에 따라 브라우저가 스크롤바를 렌더링하거나 렌더링하지 않습니다.
![탐색의 a 요소에는 핫핑크 오버레이가 있어, 구성요소에서 차지하는 공간 및 오버플로우 위치를 표시합니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-elements-the-nav-h-c8efe2b10b412.png?authuser=7&hl=ko)
탭 <section>
레이아웃
이 섹션은 유연한 항목이며 공간을 가장 많이 소비해야 합니다. 또한 기사를 배치할 열을 만들어야 합니다. CSS 2021에서도
신속하게 대응하시기 바랍니다. block-size: 100%
는 이 요소를 확장하여 상위 요소를 최대한 채운 다음 자체 레이아웃의 경우 상위 요소의 너비가 100%
인 일련의 열을 만듭니다. 여기서는 상위 항목에 강력한 제약 조건을 작성했기 때문에
백분율이 효과적입니다.
<section> <article></article> <article></article> <article></article> <article></article> </section>
section { block-size: 100%; display: grid; grid-auto-flow: column; grid-auto-columns: 100%; }
마치 '최대한 세로로 밀면서'라고 말하는 것과 같습니다(flex-shrink: 0
로 설정한 헤더: 이 확장 푸시를 방지하는 방어임). 그러면 전체 높이 열 집합의 행 높이를 설정합니다. auto-flow
스타일은 그리드에 항상 원하는 대로 정확히 원하는 대로 하위 요소를 가로선에 배치하도록(줄바꿈 없이) 상위 창을 오버플로하도록 지시합니다.
![기사 요소에 핫핑크 오버레이가 있어, 구성요소에서 차지하는 공간과 넘쳐나는 위치를 대략적으로 보여줍니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-article-elements-hot-924ae524ee8fb.png?authuser=7&hl=ko)
가끔 문제가 복잡해질 때가 있어요! 이 섹션 요소는 상자에 맞지만 상자 집합을 만들기도 합니다. 시각적 요소와 설명이 도움이 되었길 바랍니다.
탭 <article>
레이아웃
사용자는 기사 콘텐츠를 스크롤할 수 있어야 하며 스크롤바는 오버플로가 있는 경우에만 표시되어야 합니다. 이러한 기사 요소가 깔끔하게 배치되어 있습니다. 두 요소는 동시에 스크롤 상위 요소와 스크롤 하위 요소입니다. 이 경우 브라우저는 실제로 까다로운 터치, 마우스 및 키보드 상호작용을 처리합니다.
<article> <h2></h2> <p></p> <p></p> <h2></h2> <p></p> <p></p> ... </article>
article { scroll-snap-align: start; overflow-y: auto; overscroll-behavior-y: contain; }
상위 스크롤러 내에서 기사가 스냅되도록 선택했습니다. 탐색 링크 항목과 기사 요소가 각 스크롤 컨테이너의 인라인 시작에 맞춰지는 방식이 정말 마음에 듭니다. 그것은 조화로운 관계처럼 보이고 느껴집니다.
![기사 요소와 그 하위 요소에는 핫핑크 오버레이가 있어, 구성요소에서 차지하는 공간과 넘침하는 방향을 대략적으로 표시합니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-article-element-its-8afcb8a6817ff.png?authuser=7&hl=ko)
기사는 그리드 하위 요소이며 크기는 스크롤 UX를 제공하려는 표시 영역 영역으로 미리 결정됩니다. 즉, 여기에는 높이 또는 너비 스타일이 필요하지 않으며 오버플로 방식만 정의하면 됩니다. overflow-y를 auto로 설정한 다음 편리한 오버스크롤 동작 속성으로 스크롤 상호작용도 트랩합니다.
스크롤 영역 3개 요약
시스템 설정에서 '항상 스크롤바를 표시'하도록 선택했습니다. 레이아웃과 스크롤 조정을 검토해야 하므로 이 설정이 켜져 있는 상태에서 레이아웃이 작동하는 것이 더 중요하다고 생각합니다.
![스크롤바 3개가 표시되도록 설정되어 이제 레이아웃 공간을 차지하지만 구성요소는 여전히 멋지게 보입니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-3-scrollbars-are-set-ca02ac51f046d.png?authuser=7&hl=ko)
이 구성요소의 스크롤바 여백을 보면 스크롤 영역의 위치, 스크롤 영역이 지원하는 방향, 서로 상호작용하는 방식을 명확하게 표시하는 데 도움이 됩니다. 이러한 각 스크롤 창 프레임이 어떻게 레이아웃의 플렉스 또는 그리드 상위 요소인지 고려하세요.
DevTools는 이를 시각화하는 데 도움을 줄 수 있습니다.
![스크롤 영역에는 그리드 및 플렉스박스 도구 오버레이가 있어 구성요소에서 차지하는 공간과 오버랩되는 방향을 대략적으로 표시합니다.](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/the-scroll-areas-grid-f-79f0ae5839bcc.png?authuser=7&hl=ko)
스크롤 레이아웃이 완성되었습니다. 맞추기, 딥 링크 가능, 키보드 액세스가 가능합니다. UX 개선, 스타일, 즐거움을 위한 튼튼한 기반
주요 기능
스크롤 스냅된 하위 요소는 크기 조절 중에 고정된 위치를 유지합니다. 즉, JavaScript는 기기 회전이나 브라우저 크기 조절 시 아무것도 표시할 필요가 없습니다. Chromium DevTools 기기 모드에서 반응형 이외의 모드를 선택한 다음 기기 프레임의 크기를 조절해 사용해 보세요. 요소가 뷰에 유지되며 콘텐츠와 함께 잠겨 있습니다. 이 기능은 Chromium이 사양과 일치하도록 구현을 업데이트한 이후 사용할 수 있습니다. 자세한 내용은 블로그 게시물을 참조하세요.
애니메이션
여기서 애니메이션 작업의 목표는 상호작용을 UI 의견과 명확하게 연결하는 것입니다. 이렇게 하면 사용자가 모든 콘텐츠를 원활하게 검색할 수 있도록 안내하거나 지원할 수 있습니다. 목적에 맞게 모션을 조건부로 추가하겠습니다. 이제 사용자가 운영체제에서 모션 환경설정을 지정할 수 있으므로 인터페이스에서 사용자의 환경설정에 응답하는 것이 즐거워집니다.
탭 밑줄을 기사 스크롤 위치에 연결하겠습니다. 맞추기는 멋진 정렬일 뿐만 아니라 애니메이션의 시작과 끝을 고정하는 역할도 합니다.
이렇게 하면 미니 지도처럼 작동하는 <nav>
가
콘텐츠에 연결된 상태로 유지됩니다.
CSS와 JS 모두에서 사용자의 모션 환경설정을 확인할 예정입니다. 배려할 만한 멋진 장소가 몇 군데 있습니다!
스크롤 동작
:target
와 element.scrollIntoView()
의 모션 동작을 모두 개선할 수 있습니다. 기본적으로 즉시 반영됩니다. 브라우저는 스크롤 위치만
설정합니다. 스크롤 위치를 깜박이지 않고 스크롤 위치로 전환하려면 어떻게 해야 할까요?
@media (prefers-reduced-motion: no-preference) {
.scroll-snap-x {
scroll-behavior: smooth;
}
}
여기서는 모션과 스크롤처럼 사용자가 제어할 수 없는 모션을 도입하므로 사용자가 운영체제에서 감소된 모션을 선호하지 않는 경우에만 이 스타일을 적용합니다. 이런 식으로 스크롤 모션에 문제가 없는 분들을 위해서만 스크롤 모션을 소개합니다
탭 표시기
이 애니메이션의 목적은 표시기를 콘텐츠 상태와 연결하는 데 도움이 됩니다. 움직임을 줄이고 싶은 사용자에게는 border-bottom
스타일의 색상을 크로스페이드하고, 모션에 문제가 없는 사용자에게는 스크롤 링크 슬라이딩 + 색상 페이드 애니메이션을 사용하기로 결정했습니다.
Chromium Devtools에서 환경설정을 전환하여 두 가지 전환 스타일을 시연할 수 있습니다. 이걸 만드는 과정이 굉장히 즐거웠습니다.
@media (prefers-reduced-motion: reduce) {
snap-tabs > header a {
border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
transition: color .7s ease, border-color .5s ease;
&:is(:target,:active,[active]) {
color: var(--text-active-color);
border-block-end-color: hsl(var(--accent));
}
}
snap-tabs .snap-indicator {
visibility: hidden;
}
}
사용자가 감소된 모션을 선호하면 더 이상 필요하지 않으므로 .snap-indicator
를 숨깁니다. 그런 다음 border-block-end
스타일과 transition
로 바꿉니다. 또한 탭 상호작용에서 활성 탐색 항목에 브랜드 밑줄 강조 표시뿐만 아니라 텍스트 색상도 더 어두워집니다. 활성 요소는 텍스트 색상 대비가 높고 밝은 언더라이트 강조가 있습니다.
사용자의 모션 환경설정을 신중하게 준수한다는 의미에서 CSS를 몇 줄만 더 추가하여 사용자가 있다는 느낌을 줄 수 있습니다. 나는 그것을 사랑합니다.
@scroll-timeline
위 섹션에서는 감소된 모션 크로스페이드 스타일을 처리하는 방법을 살펴보았고, 이 섹션에서는 표시기와 스크롤 영역을 함께 연결한 방법을 보여드리겠습니다. 다음은 재미있는 실험 기능입니다. 여러분도 저처럼 흥분하셨기를 바랍니다.
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
);
먼저 JavaScript에서 사용자의 모션 환경설정을 확인합니다. 그 결과가 false
이고 사용자가 줄어든 모션을 선호한다는 의미라면 스크롤 연결 모션 효과를 실행하지 않습니다.
if (motionOK) {
// motion based animation code
}
이 문서 작성 시점을 기준으로 @scroll-timeline
의 브라우저 지원은 없습니다. 실험용 구현만 포함된 초안 사양입니다. 하지만 이 데모에서 사용하는 폴리필이 있습니다.
ScrollTimeline
CSS와 JavaScript 모두 스크롤 타임라인을 만들 수 있지만, 애니메이션에 실시간 요소 측정을 사용할 수 있도록 JavaScript를 선택했습니다.
const sectionScrollTimeline = new ScrollTimeline({
scrollSource: tabsection, // snap-tabs > section
orientation: 'inline', // scroll in the direction letters flow
fill: 'both', // bi-directional linking
});
한 항목이 다른 항목의 스크롤 위치를 따라가기를 원하고 ScrollTimeline
를 만들어 스크롤 링크의 드라이버인 scrollSource
를 정의합니다.
일반적으로 웹의 애니메이션은 전역 시간 틱을 기준으로 실행되지만 메모리의 맞춤 sectionScrollTimeline
를 사용하면 이를 모두 변경할 수 있습니다.
tabindicator.animate({
transform: ...,
width: ...,
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
애니메이션의 키프레임을 살펴보기 전에 스크롤의 팔로어인 tabindicator
가 섹션의 스크롤인 맞춤 타임라인에 따라 애니메이션 처리됩니다. 이렇게 하면 연결이 완료되지만 애니메이션화할 최종 요소인 스테이트풀(Stateful) 포인트(키프레임이라고도 함)가 누락되었습니다.
동적 키프레임
@scroll-timeline
로 애니메이션을 적용하는 매우 강력한 순수 선언적 CSS 방법이 있지만, 제가 선택한 애니메이션은 너무 동적입니다. auto
너비 간에 전환하는 방법은 없으며 하위 요소 길이를 기반으로 여러 키프레임을 동적으로 만들 수 없습니다.
JavaScript는 이 정보를 얻는 방법을 알고 있으므로 하위 요소를 직접 반복하고 런타임 시 계산된 값을 가져옵니다.
tabindicator.animate({
transform: [...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`),
width: [...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
각 tabnavitem
의 경우 offsetLeft
위치의 구조를 해체하고 이를 translateX
값으로 사용하는 문자열을 반환합니다. 그러면 애니메이션의 변환 키프레임 4개가 생성됩니다. 너비도 마찬가지입니다. 각각에 동적 너비를 요청한 다음 키프레임 값으로 사용됩니다.
다음은 내 글꼴 및 브라우저 환경설정을 기준으로 한 출력 예입니다.
TranslateX 키프레임:
[...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`)
// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]
너비 키프레임:
[...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]
전략을 요약하기 위해 이제 탭 표시기가 섹션 스크롤러의 스크롤 맞추기 위치에 따라 4개의 키프레임에서 애니메이션 처리됩니다. 맞추기 지점은 키프레임 사이에 명확한 묘사를 만들고 애니메이션의 동기화된 느낌을 더해줍니다.
![활성 탭과 비활성 탭은 VisBug 오버레이와 함께 표시되고,](https://web.developers.google.cn/static/articles/building/a-tabs-component/image/active-tab-inactive-tab-9604191ea7007.png?authuser=7&hl=ko)
사용자는 상호작용과 함께 애니메이션을 구동하여 표시기의 너비와 위치가 한 섹션에서 다음 섹션으로 변경되는 것을 확인하고 스크롤로 완벽하게 추적합니다.
눈치채지 못했을 수도 있지만, 강조 표시된 탐색 항목이 선택될 때 색상이 전환된다는 점이 무척 자랑스럽습니다.
강조 표시된 항목의 대비가 높을수록 선택 해제된 연한 회색이 더 뒤로 푸시백됩니다. 마우스 오버 시 및 선택 시와 같이 텍스트의 색상을 전환하는 것이 일반적이지만, 다음 단계는 밑줄 표시기와 동기화되어 스크롤 시 색상을 전환하는 것입니다.
방법은 다음과 같습니다.
tabnavitems.forEach(navitem => {
navitem.animate({
color: [...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
});
각 탭 탐색 링크에는 밑줄 표시기와 동일한 스크롤 타임라인을 추적하는 이 새로운 색상 애니메이션이 필요합니다. 이전과 동일한 타임라인을 사용합니다. 스크롤 시 틱을 내보내는 것이기 때문에 원하는 모든 유형의 애니메이션에서 이 틱을 사용할 수 있습니다. 앞서 했던 것처럼 루프에 4개의 키프레임을 만들고 색상을 반환합니다.
[...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
// results in 4 array items, which represent 4 keyframe states
// [
"var(--text-active-color)",
"var(--text-color)",
"var(--text-color)",
"var(--text-color)",
]
var(--text-active-color)
색상의 키프레임은 링크를 강조 표시하고, 그렇지 않으면 표준 텍스트 색상으로 강조 표시합니다. 외부 루프는 각 탐색 항목이고 내부 루프는 각 navitem의 개인 키프레임이므로 중첩 루프는 비교적 직관적입니다. 외부 루프 요소가 내부 루프 요소와 동일한지
확인하고 이를 사용하여 선택 시점을 확인합니다.
이 글을 쓰는 것이 정말 즐거웠습니다. 최고죠.
훨씬 더 많은 자바스크립트 개선사항
여기서 보여 드리는 핵심 내용은 JavaScript 없이도 작동합니다. 이제 JS를 사용할 수 있을 때 어떻게 개선할 수 있는지 살펴보겠습니다.
딥 링크
딥 링크는 모바일 용어에 더 가깝습니다. 하지만 딥 링크의 의도는 탭 콘텐츠로 연결되는 URL을 직접 공유할 수 있다는 점에서 탭이라고 생각합니다. 브라우저는 URL 해시에서 일치하는 ID로 인페이지로 이동합니다. 이 onload
핸들러가 여러 플랫폼에 영향을 미치는 것을 발견했습니다.
window.onload = () => {
if (location.hash) {
tabsection.scrollLeft = document
.querySelector(location.hash)
.offsetLeft;
}
}
스크롤 종료 동기화
사용자가 항상 키보드를 클릭하거나 사용하지는 않으며, 때로는 가능한 한 자유롭게 스크롤할 수도 있습니다. 섹션 스크롤러가 스크롤을 멈추면 상단 탐색 메뉴에서 스크롤되는 위치와 일치해야 합니다.
스크롤 끝을 기다리는 방법은 다음과 같습니다.
js
tabsection.addEventListener('scroll', () => {
clearTimeout(tabsection.scrollEndTimer);
tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100);
});
섹션을 스크롤할 때마다 섹션 제한시간을 지우고 새 제한 시간을 시작합니다. 섹션 스크롤이 중지되면 시간 제한을 삭제하지 말고 휴식 후 100ms를 실행합니다. 이벤트가 실행되면 사용자가 중지한 위치를 파악하는 함수를 호출합니다.
const determineActiveTabSection = () => {
const i = tabsection.scrollLeft / tabsection.clientWidth;
const matchingNavItem = tabnavitems[i];
matchingNavItem && setActiveTab(matchingNavItem);
};
스크롤이 맞춰졌다고 가정할 때 현재 스크롤 위치를 스크롤 영역의 너비로 나누면 소수점이 아닌 정수가 됩니다. 그런 다음 계산된 색인을 통해 캐시에서 navitem을 가져오려고 합니다. 그런 다음 무언가를 찾으면 일치 항목을 활성으로 설정합니다.
const setActiveTab = tabbtn => {
tabnav
.querySelector(':scope a[active]')
.removeAttribute('active');
tabbtn.setAttribute('active', '');
tabbtn.scrollIntoView();
};
활성 탭을 설정하려면 먼저 현재 활성 탭을 지운 다음 수신되는 탐색 항목에 활성 상태 속성을 지정합니다. scrollIntoView()
호출은 주목할 만한 흥미로운 CSS 상호작용입니다.
.scroll-snap-x {
overflow: auto hidden;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
가로 스크롤 맞추기 유틸리티 CSS에서 사용자가 모션을 허용하는 경우 smooth
스크롤을 적용하는 미디어 쿼리를 중첩했습니다. JavaScript는 요소를 뷰로 스크롤하는 호출을 자유롭게 만들 수 있으며 CSS는 UX를 선언적으로 관리할 수 있습니다.
가끔씩 장난스럽게 작은 상대를 만들어 주기도 합니다.
결론
이제 내가 어떻게 했는지 알았으니 어떻게 할 건가요? 이렇게 하면 재미있는 구성요소 아키텍처가 만들어집니다. 누가 가장 좋아하는 프레임워크에 슬롯이 있는 첫 번째 버전을 만들까요? 🙂
접근 방식을 다양화하고 웹에서 빌드하는 모든 방법을 알아보겠습니다. Glitch를 만들고 내 버전을 트윗하면 아래 커뮤니티 리믹스 섹션에 추가합니다.
커뮤니티 리믹스
- 웹 구성요소가 포함된 @devnook, @rob_dodson, @DasSurma: 기사
- @jhvanderschee(버튼: Codepen)