콘텐츠를 깔끔하게 분류하는 탭 컴포넌트. 언더라인 탭, 알약형 탭, 카드형 탭까지 — 3가지 스타일을 직접 클릭해서 확인하고 코드를 복사해가세요.
같은 공간에 여러 콘텐츠를 넣되, 탭 버튼으로 하나씩 전환하는 UI예요. 스크롤 없이 많은 정보를 정돈된 방식으로 보여줄 수 있어요.
탭 동작 원리: 탭 버튼을 클릭하면 ① 모든 패널을 숨기고 (display:none) ② 선택한 패널만 표시 (display:block) ③ 탭 버튼의 active 클래스 이동. 이 세 단계가 전부예요.
제품 상세 페이지(설명/리뷰/배송), 프로필 페이지(게시글/팔로워/좋아요), 대시보드(개요/통계/설정) 등 카테고리별로 콘텐츠를 분리할 때 최적이에요.
탭 버튼에 role="tab", aria-selected를 넣고, 패널에 role="tabpanel"을 추가해요. 키보드 화살표 키로도 탭을 이동할 수 있어야 해요.
#tab-review처럼 해시(#)를 URL에 저장하면 공유 링크로 특정 탭을 바로 열 수 있어요. 뒤로가기 버튼도 자연스럽게 동작해 UX가 좋아져요.
탭이 2개 이하면 그냥 두 섹션으로 나누는 게 나아요. 탭이 6개 이상이면 화면이 작을 때 넘쳐요. 검색 엔진은 탭 안의 콘텐츠를 잘 못 읽을 수 있어요.
프로젝트 분위기에 맞는 스타일을 골라 쓸 수 있어요. 아래 데모에서 직접 클릭해보세요.
활성 탭 아래 선이 그어지는 심플한 스타일이에요. GitHub, Google 등 많은 사이트에서 쓰는 클래식 패턴입니다. 콘텐츠 위주의 사이트에 잘 어울려요.
활성 탭이 둥근 배경으로 강조돼요. 현대적이고 모바일 친화적이에요. 앱처럼 느껴지는 디자인에 잘 어울려요. 탭 수가 많아도 스크롤 가능해요.
탭이 파일 탭처럼 생겼어요. 활성 탭이 아래 콘텐츠 박스와 연결된 느낌이에요. 관리자 패널, 설정 페이지 같은 도구형 UI에 잘 어울려요.
탭 UI의 핵심 코드예요. HTML 구조, CSS 상태 스타일, JavaScript 전환 로직 순서로 살펴봐요.
data-tab 속성으로 탭 버튼과 패널을 연결해요. aria 속성으로 접근성도 챙겨요.
<div class="tab-wrap" role="tablist"> <!-- 탭 버튼 --> <div class="tab-nav"> <button class="tab-btn active" data-tab="intro" role="tab" aria-selected="true">소개</button> <button class="tab-btn" data-tab="features" role="tab" aria-selected="false">기능</button> <button class="tab-btn" data-tab="pricing" role="tab" aria-selected="false">가격</button> </div> <!-- 탭 패널 --> <div class="tab-panel active" id="panel-intro" role="tabpanel">소개 내용</div> <div class="tab-panel" id="panel-features" role="tabpanel">기능 내용</div> <div class="tab-panel" id="panel-pricing" role="tabpanel">가격 내용</div> </div>
/* 패널: 기본 숨김 */ .tab-panel { display: none; } .tab-panel.active { display: block; animation: fadeTab .2s ease both; } @keyframes fadeTab { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } /* 탭 버튼 — 언더라인 스타일 */ .tab-btn { border: none; background: none; cursor: pointer; padding: 12px 20px; font-weight: 500; color: #6b7280; position: relative; } .tab-btn::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: #4f46e5; transform: scaleX(0); transition: transform .2s; } .tab-btn.active { color: #4f46e5; font-weight: 700; } .tab-btn.active::after { transform: scaleX(1); }
이벤트 위임으로 탭 버튼 클릭을 처리해요. data-tab 값으로 패널을 찾아요.
// 탭 초기화 함수 (여러 탭 그룹에 재사용 가능) function initTabs(wrapper) { const buttons = wrapper.querySelectorAll('[data-tab]'); const panels = wrapper.querySelectorAll('.tab-panel'); buttons.forEach(function(btn) { btn.addEventListener('click', function() { const target = this.dataset.tab; // 모든 버튼 비활성화 buttons.forEach(function(b) { b.classList.remove('active'); b.setAttribute('aria-selected', 'false'); }); // 모든 패널 숨기기 panels.forEach(function(p) { p.classList.remove('active'); }); // 선택한 탭/패널 활성화 this.classList.add('active'); this.setAttribute('aria-selected', 'true'); wrapper.querySelector(`#panel-${target}`) ?.classList.add('active'); }); }); } // 사용: 탭 그룹마다 호출 document.querySelectorAll('.tab-wrap').forEach(initTabs);
재사용성 팁: initTabs 함수를 wrapper 요소에 인자로 넘기면 페이지에 탭 그룹이 여러 개 있어도 각각 독립적으로 동작해요. document.querySelectorAll('.tab-wrap').forEach(initTabs) 한 줄로 모든 탭을 초기화할 수 있어요.
탭을 클릭해서 각 스타일을 확인해보세요. 코드는 위 섹션에서 복사할 수 있어요.
① 언더라인 탭
클래식하고 깔끔한 스타일이에요. 활성 탭 아래에 보라색 선이 생겨요. GitHub, Notion 같은 사이트에서 자주 볼 수 있는 패턴이에요.
::after 가상 요소로 언더라인 구현scaleX() 트랜지션으로 자연스러운 등장 효과바이브툴킷의 모든 코드는 무료로 복사해서 사용하실 수 있어요. 상업적 이용도 가능해요.
"탭 UI 만드는 방법을 몰랐는데 이 페이지 덕분에 바로 사이트에 적용했어요!"
② 알약형(Pill) 탭
모든 분류의 도구를 한 번에 볼 수 있어요. 알약형 탭은 선택된 항목이 배경색으로 명확하게 강조돼요. 모바일 앱에서 자주 보이는 스타일이에요.
③ 카드형 탭
카드형 탭은 파일 탭처럼 생겨서 관리자 패널, 설정 페이지에 잘 어울려요. 활성 탭이 아래 패널 콘텐츠와 연결된 느낌을 줍니다.
설정 페이지처럼 탭마다 독립적인 설정 항목이 있을 때 카드형이 직관적이에요.
방문자 수, 클릭 수 등 다양한 통계를 탭으로 분리해서 보여줄 수 있어요.
탭 UI를 AI에게 요청할 때 구체적인 스타일과 탭 이름을 알려주면 완성도 높은 코드를 받을 수 있어요.
아래 프롬프트를 복사해서 AI에 붙여넣어 보세요.
내 제품 상세 페이지에 탭 UI를 추가해줘. 탭 목록: - 상품 설명 (기본 활성) - 사용 방법 - 리뷰 (23개) - 배송/교환 스타일: 언더라인 탭 (활성 탭 아래 파란 선) 전환 효과: 아래에서 위로 페이드인 (0.2s) 요구사항: - 탭 클릭 시 해당 패널만 표시 - 키보드 접근성 (aria-selected 포함) - 모바일에서도 가로 스크롤로 탭 표시 - 순수 HTML + CSS + Vanilla JS (라이브러리 없이)
탭 UI를 구현할 때 자주 맞닥뜨리는 질문들이에요.
display:none으로 숨겨진 콘텐츠는 구글이 읽을 수 있지만, 페이지 랭킹에서 덜 중요하게 처리할 수 있어요. SEO가 중요한 콘텐츠(상품 설명, 리뷰 등)는 탭 대신 모두 표시된 섹션으로 구성하거나, 탭 URL을 별도 페이지로 만드는 것을 고려해봐요.
window.location.hash = '#' + tabId로 해시를 업데이트하고, 페이지 로드 시 window.location.hash를 읽어 해당 탭을 활성화하면 돼요. 이렇게 하면 특정 탭 URL을 공유하거나 뒤로가기 버튼으로 탭 이동이 가능해요.
keydown 이벤트에서 ArrowLeft, ArrowRight를 감지해 이전/다음 탭 버튼으로 포커스를 이동하고 클릭 이벤트를 트리거하면 돼요.
overflow-x: auto를 추가하면 수평 스크롤이 생겨요. 탭이 6개 이상이면 "더보기" 드롭다운으로 넘치는 탭을 숨기는 패턴도 있어요. 혹은 탭 대신 사이드바 네비게이션이나 셀렉트박스로 전환하는 것도 좋은 UX예요.
useState로 활성 탭 ID를 관리하고, 탭 버튼 클릭 시 상태를 업데이트해요. 조건부 렌더링({activeTab === 'intro' && <IntroPanel />})으로 패널을 표시해요. Radix UI의 Tabs 컴포넌트나 shadcn/ui를 쓰면 접근성까지 포함된 완성된 컴포넌트를 바로 사용할 수 있어요.