Tabs 구성요소 빌드

iOS 및 Android 앱과 유사한 탭 구성요소를 빌드하는 방법에 관한 기본적인 개요입니다.

이 게시물에서는 반응성이 뛰어나고 여러 기기 입력을 지원하며 여러 브라우저에서 작동하는 웹용 탭 구성요소를 빌드하는 방법을 공유하고자 합니다. 데모 사용해 보기

데모

동영상을 선호하는 경우 이 게시물의 YouTube 버전을 확인하세요.

개요

탭은 디자인 시스템의 일반적인 구성요소이지만 다양한 도형과 형태를 취할 수 있습니다. 처음에는 <frame> 요소를 기반으로 빌드된 데스크톱 탭이 있었지만 이제는 물리 속성을 기반으로 콘텐츠에 애니메이션을 적용하는 부드러운 모바일 구성요소가 있습니다. 모두 공간을 절약하기 위해 동일한 작업을 시도하고 있습니다.

오늘날 탭 사용자 환경의 핵심은 디스플레이 프레임에서 콘텐츠의 표시 여부를 전환하는 버튼 탐색 영역입니다. 다양한 콘텐츠 영역이 동일한 공간을 공유하지만 탐색에서 선택한 버튼에 따라 조건부로 표시됩니다.

웹에서 구성요소 개념에 적용한 스타일의 다양성이 매우 크므로 콜라주가 매우 혼란스럽습니다.
지난 10년간의 탭 구성요소 웹 디자인 스타일 콜라주

웹 전술

몇 가지 중요한 웹 플랫폼 기능 덕분에 이 구성요소를 빌드하는 것이 매우 간단했습니다.

  • 적절한 스크롤 중지 위치에서 우아한 스와이프 및 키보드 상호작용을 위한 scroll-snap-points
  • 브라우저에서 처리되는 인페이지 스크롤 고정 및 공유 지원을 위한 URL 해시를 통한 딥 링크
  • <a>id="#hash" 요소 마크업을 사용한 스크린 리더 지원
  • 크로스페이드 전환 및 즉시 페이지 내 스크롤을 사용 설정하기 위한 prefers-reduced-motion
  • 선택한 탭의 밑줄을 동적으로 표시하고 색상을 변경하는 초안 내 @scroll-timeline 웹 기능

HTML

기본적으로 여기서 UX는 링크를 클릭하고 URL이 중첩된 페이지 상태를 나타내도록 한 다음 브라우저가 일치하는 요소로 스크롤할 때 콘텐츠 영역이 업데이트되는 것을 확인하는 것입니다.

여기에는 링크와 :target와 같은 몇 가지 구조적 콘텐츠 멤버가 있습니다. <nav>에 적합한 링크 목록과 <section>에 적합한 <article> 요소 목록이 필요합니다. 각 링크 해시는 섹션과 일치하므로 브라우저가 고정을 통해 스크롤할 수 있습니다.

링크 버튼을 클릭하면 포커스가 지정된 콘텐츠가 슬라이드합니다.

예를 들어 링크를 클릭하면 Chrome 89에서 :target 도움말에 자동으로 포커스가 설정되며 JS가 필요하지 않습니다. 그러면 사용자는 평소와 같이 입력 장치로 기사 콘텐츠를 스크롤할 수 있습니다. 마크업에 표시된 대로 무료 콘텐츠입니다.

다음 마크업을 사용하여 탭을 구성했습니다.

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

다음과 같이 hrefid 속성을 사용하여 <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 ipsum으로 채우고 링크를 다양한 길이의 제목과 이미지 세트로 채웠습니다. 이제 작업할 콘텐츠가 있으므로 레이아웃을 시작할 수 있습니다.

스크롤 레이아웃

이 구성요소에는 다음과 같은 3가지 유형의 스크롤 영역이 있습니다.

  • 탐색(분홍색)을 가로로 스크롤할 수 있습니다.
  • 콘텐츠 영역 (파란색)을 가로로 스크롤할 수 있습니다.
  • 각 기사 항목(녹색)은 세로로 스크롤할 수 있습니다.
스크롤 영역을 윤곽선으로 표시하고 스크롤 방향을 보여주는 방향 화살표와 색상이 일치하는 3개의 다채로운 상자

스크롤과 관련된 두 가지 요소 유형이 있습니다.


  1. overflow 속성 스타일이 있는 정의된 크기가 있는 상자입니다.
  2. 대형 노출 영역
    이 레이아웃에서는 목록 컨테이너(탐색 링크, 섹션 도움말, 도움말 콘텐츠)입니다.

<snap-tabs> 레이아웃

선택한 최상위 레이아웃은 flex (Flexbox)입니다. 방향을 column로 설정하여 헤더와 섹션이 세로로 정렬됩니다. 첫 번째 스크롤 창이며, 오버플로가 숨겨진 모든 항목을 숨깁니다. 헤더와 섹션이 곧 개별 영역으로 오버스크롤을 사용합니다.

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  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 against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

다채로운 3단 스크롤 다이어그램을 다시 살펴보겠습니다.

  • 이제 <header>(분홍색) 스크롤 컨테이너가 될 준비가 되었습니다.
  • <section>(파란색) 스크롤 컨테이너가 될 준비가 되었습니다.

아래에서 VisBug로 강조 표시한 프레임은 스크롤 컨테이너가 만든 을 볼 수 있도록 도와줍니다.

헤더 및 섹션 요소에 핫핑크 오버레이가 있으며 구성요소에서 차지하는 공간의 개요를 표시합니다.

<header> 레이아웃

다음 레이아웃은 거의 동일합니다. flex를 사용하여 세로 순서를 만듭니다.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

.snap-indicator는 링크 그룹과 함께 가로로 이동해야 하며 이 헤더 레이아웃은 이 단계를 설정하는 데 도움이 됩니다. 절대 위치로 배치된 요소가 없습니다.

nav 및 span.indicator 요소에 구성요소에서 차지하는 공간을 윤곽선으로 표시하는 hotpink 오버레이가 있습니다.

다음은 스크롤 스타일입니다. 두 개의 가로 스크롤 영역(헤더 및 섹션) 간에 스크롤 스타일을 공유할 수 있으므로 유틸리티 클래스 .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에는 Swift가 작동합니다.

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

각 링크는 자체 스타일과 크기를 지정하므로 탐색 레이아웃은 방향과 흐름만 지정하면 됩니다. 탐색 항목의 고유한 너비는 표시기가 너비를 새 타겟으로 조정할 때 탭 간에 재미있는 전환을 만듭니다. 여기에 있는 요소의 수에 따라 브라우저에서 스크롤바를 렌더링할지 여부가 결정됩니다.

탐색의 요소에 핫핑크 오버레이가 있어 구성요소에서 차지하는 공간 및 오버플로되는 위치를 간략히 설명합니다.

<section> 레이아웃

이 섹션은 플렉스 항목이며 공간을 가장 많이 사용하는 항목이어야 합니다. 또한 도움말을 배치할 열을 만들어야 합니다. CSS 2021을 위해 다시 한번 신속하게 작업해 주세요. block-size: 100%는 이 요소를 늘려 상위 요소를 최대한 채운 다음 자체 레이아웃의 경우 상위 요소의 너비의 100%배인 일련의 열을 만듭니다. 상위에 강력한 제약 조건을 작성했기 때문에 여기서는 비율이 잘 작동합니다.

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

마치 '세로로 최대한 확장하되 과도하게'(flex-shrink: 0로 설정된 헤더를 기억하세요. 이 확장 푸시를 방어하기 위한 조치입니다)라고 말하는 것처럼 전체 높이 열 세트의 행 높이를 설정합니다. auto-flow 스타일은 그리드에 항상 하위 요소를 가로선으로 배치하도록 지시합니다. 래핑은 없으며, 바로 이것이 상위 창을 오버플로시키기 위해 원하는 바입니다.

기사 요소에 핫핑크 오버레이가 적용되어 구성요소에서 차지하는 공간과 오버플로하는 위치를 보여줍니다.

때로는 이해하기 어렵습니다. 이 섹션 요소는 상자에 들어맞지만 상자 세트를 만들었습니다. 시각 자료와 설명이 도움이 되었기를 바랍니다.

<article> 레이아웃

사용자가 기사 콘텐츠를 스크롤할 수 있어야 하며 스크롤바는 오버플로가 있는 경우에만 표시되어야 합니다. 이러한 도움말 요소는 깔끔하게 배치되어 있습니다. 스크롤 상위 요소이자 스크롤 하위 요소입니다. 브라우저가 여기서 몇 가지 까다로운 터치, 마우스, 키보드 상호작용을 처리합니다.

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

부모 스크롤러 내에서 기사가 스냅되도록 선택했습니다. 탐색 링크 항목과 기사 요소가 각 스크롤 컨테이너의 인라인 시작에 스냅되는 방식이 마음에 듭니다. 조화로운 관계처럼 보이고 느껴집니다.

article 요소와 그 하위 요소에 구성요소에서 차지하는 공간과 오버플로 방향을 윤곽선으로 표시하는 핫핑크 오버레이가 있습니다.

기사는 그리드 하위 요소이며, 기사의 크기는 스크롤 UX를 제공하려는 표시 영역 영역으로 미리 결정됩니다. 즉, 여기에는 높이나 너비 스타일이 필요하지 않으며 오버플로 방식만 정의하면 됩니다. overflow-y를 auto로 설정한 다음 편리한 overscroll-behavior 속성으로 스크롤 상호작용을 트랩합니다.

스크롤 영역 3개 요약

아래에서는 시스템 설정에서 '스크롤바 항상 표시'를 선택했습니다. 이 설정이 사용 설정된 상태에서 레이아웃이 작동하는 것이 두 배로 중요하다고 생각합니다. 레이아웃과 스크롤 조정을 검토해야 하기 때문입니다.

스크롤바 3개가 표시되도록 설정되어 이제 레이아웃 공간을 사용하고 있지만 구성요소는 여전히 멋지게 보입니다.

이 구성요소에서 스크롤바 여백을 확인하면 스크롤 영역의 위치, 지원하는 방향, 서로 상호작용하는 방식을 명확하게 보여줄 수 있습니다. 이러한 각 스크롤 창 프레임이 레이아웃의 flex 또는 그리드 상위 요소가 되는 방식을 고려해 보세요.

DevTools를 사용하면 다음을 시각화할 수 있습니다.

스크롤 영역에 그리드 및 flexbox 도구 오버레이가 있어 구성요소에서 차지하는 공간과 오버플로 방향을 보여줍니다.
앵커 요소로 가득 찬 flexbox 탐색 요소 레이아웃, 기사 요소로 가득 찬 그리드 섹션 레이아웃, 단락과 제목 요소로 가득 찬 기사 요소를 보여주는 Chromium Devtools

스크롤 레이아웃이 완성되었습니다. 스냅, 딥 링크 가능, 키보드 액세스 가능 UX 개선, 스타일, 만족감을 위한 강력한 기반

주요 기능 소개

스크롤된 하위 요소는 크기를 조절하는 동안 고정된 위치를 유지합니다. 즉, JavaScript는 기기 회전이나 브라우저 크기 조절 시 아무것도 표시할 필요가 없습니다. Chromium DevTools 기기 모드에서 Responsive가 아닌 모드를 선택한 다음 기기 프레임의 크기를 조절하여 사용해 보세요. 요소가 계속 표시되고 콘텐츠와 함께 고정된 것을 볼 수 있습니다. 이는 Chromium이 사양에 맞게 구현을 업데이트한 이후 사용할 수 있습니다. 관련 블로그 게시물을 참고하세요.

애니메이션

여기서 애니메이션 작업의 목표는 상호작용을 UI 피드백과 명확하게 연결하는 것입니다. 이렇게 하면 사용자가 모든 콘텐츠를 원활하게 탐색할 수 있도록 안내하거나 지원할 수 있습니다. 목적에 따라 조건부로 모션을 추가합니다 이제 사용자는 운영체제에서 모션 환경설정을 지정할 수 있으며, 저는 인터페이스에서 사용자의 환경설정에 응답하는 것을 매우 즐깁니다.

탭 밑줄을 기사 스크롤 위치와 연결하겠습니다. 스냅은 보기 좋게 정렬하는 것뿐만 아니라 애니메이션의 시작과 끝을 고정하는 것입니다. 이렇게 하면 미니 지도처럼 작동하는 <nav>가 콘텐츠에 연결된 상태로 유지됩니다. CSS와 JS에서 사용자의 모션 환경설정을 확인합니다. 다음과 같은 몇 가지 좋은 예가 있습니다.

스크롤 동작

:targetelement.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가 맞춤 타임라인, 즉 섹션의 스크롤을 기반으로 애니메이션되도록 하는 것이 중요하다고 생각합니다. 이렇게 하면 연결이 완료되지만 애니메이션을 적용할 상태 저장 포인트(키프레임이라고도 함)라는 마지막 요소가 누락됩니다.

동적 키프레임

@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 오버레이와 함께 표시되며, 두 탭 모두 통과 대비 점수를 보여줍니다.

사용자는 상호작용으로 애니메이션을 제어하며, 표시기의 너비와 위치가 섹션마다 변경되는 것을 보고 스크롤과 함께 완벽하게 추적합니다.

눈치채지 못하셨을 수도 있지만 강조 표시된 탐색 항목이 선택될 때 색상이 전환되는 것을 자랑스럽게 생각합니다.

강조 표시된 항목의 대비가 더 높을수록 선택되지 않은 밝은 회색이 더 뒤로 밀려 보입니다. 마우스 오버 시나 선택 시와 같이 텍스트의 색상을 전환하는 것은 일반적이지만 스크롤 시 색상을 전환하고 이 작업을 밑줄 표시기와 동기화하는 것은 한 단계 높은 수준입니다.

제가 취한 조치는 다음과 같습니다.

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)인 키프레임에서는 링크를 강조표시하며, 그 외의 경우에는 표준 텍스트 색상이 됩니다. 중첩 루프를 사용하면 비교적 간단합니다. 외부 루프는 각 탐색 항목이고 내부 루프는 각 탐색 항목의 개인 키프레이프이기 때문입니다. 외부 루프 요소가 내부 루프 요소와 동일한지 확인하고 이를 사용하여 선택되었는지 확인합니다.

이 글을 작성하는 것이 즐거웠습니다. 최고죠.

더 많은 JavaScript 개선사항

여기서 보여드리는 핵심은 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를 만들고 내 버전을 트윗하여 아래 커뮤니티 리믹스 섹션에 추가합니다.

커뮤니티 리믹스