Carousel
가로 스크롤 캐러셀 — scroll-snap 트랙 + 이전/다음 컨트롤, multiBrowse·hero·fullScreen 레이아웃(ADR-012 Track C).
마지막 업데이트 2026-06-12
한눈에#
scroll-snap 트랙 + 이전/다음 컨트롤로 항목을 가로로 훑는 캐러셀입니다 — multiBrowse·hero·fullScreen 레이아웃을 제공합니다.
- scroll-snap 가로 트랙
- multiBrowse·hero·fullScreen
- 이전/다음 컨트롤
- overlay 글래스 컨트롤
items는 { id, content } 배열 — 트랙은 항목 경계에 스냅되고 버튼이 한 항목씩 스크롤
items는 { id, content } 배열입니다 — id가 안정적인 key가 됩니다. 트랙은 scroll-snap으로
항목 경계에 맞춰지고, 이전/다음 버튼이 한 항목씩 스크롤합니다.
사용 시점#
추천 항목·미디어·배너를 가로로 훑는 컬렉션이면 Carousel — 동등 패널을 한 번에 하나씩 보는 건 다른 컴포넌트입니다.
추천 항목·미디어 갤러리·배너처럼 가로로 스크롤하며 훑는 컬렉션
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
import { Carousel } from '@wds/ui-web';
<Carousel ariaLabel="추천 항목" items={[{ id: 'a', content: '카드 A' }, /* … */]} />변형#
variant가 항목 너비(레이아웃)를 가릅니다 — multiBrowse(기본, 여러 항목)·hero(큰 포컬
항목 + 다음 항목 살짝)·fullScreen(한 화면 한 항목).
오버레이 컨트롤#
controls="overlay"는 이전/다음 버튼을 슬라이드 위로 띄웁니다 — 미디어·이미지 위에 컨트롤이
떠 있는 패턴입니다. 컨트롤엔 중립 surface 계약 data-wds-surface="floating"이 붙어,
liquid-glass 머티리얼 테마 스코프 안에서는 clear 글래스(콘텐츠
위 변형)로 칠해집니다 — 기본 테마에선 일반 불투명 버튼입니다. 아래 데모는
data-wds-material="liquid-glass"로 감싸 glass 컨트롤을 보여줍니다.
prefers-reduced-transparency에서는 테마 레이어가 자동으로 불투명 surface로 폴백하고 blur를
제거합니다 — 컨트롤 가독성이 우선됩니다.
compact 폭(<600px, ADR-012 적응형 경계 BP.compact)에서는 띄우기를
자동으로 꺼서 컨트롤을 트랙 옆 inline 흐름으로 폴백합니다 — 좁은 화면에서 40px 컨트롤 2개가
슬라이드의 약 1/3을 가려 미디어 가장자리를 잠식하지 않도록 합니다. 태블릿·데스크톱(≥600px,
overlay의 의도된 미디어 갤러리 용처)에서만 슬라이드 위로 띄웁니다.
크기#
레이아웃이 variant로 정해지므로 별도 size 축은 없습니다 — 항목 너비는 트랙 폭에 대한
비율로 결정됩니다. 트랙 폭은 부모 컨테이너가 정합니다.
상태#
스크롤 위치에 따라 이전/다음 버튼이 자동으로 비활성화됩니다 — 시작에서는 이전이,
끝에서는 다음이 disabled. 모션은 prefers-reduced-motion을 존중해
허용될 때만 부드럽게 스크롤합니다.
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
items | ReadonlyArray<{ id: string; content: ReactNode }> | — | 슬라이드 배열 — id가 안정적 key |
variant | 'multiBrowse' | 'hero' | 'fullScreen' | 'multiBrowse' | 레이아웃 — 항목 너비를 결정 |
ariaLabel | string | — | 캐러셀 영역의 접근성 이름 — 필수 |
previousLabel | string | '이전' | 이전 버튼 aria-label — 현지화용 |
nextLabel | string | '다음' | 다음 버튼 aria-label — 현지화용 |
controls | 'inline' | 'overlay' | 'inline' | 컨트롤 배치 — 'overlay'는 슬라이드 위로 띄우고 data-wds-surface='floating' 계약을 부착(liquid-glass에서 clear 글래스) |
gap | string | — | 항목 간격 — 기본은 토큰 간격 |
className | string | — | 루트에 병합 |
접근성#
| 계약 | 구현 |
|---|---|
| 영역 | 루트 role="group" + aria-roledescription="carousel" + aria-label(=ariaLabel) |
| 슬라이드 | 각 항목 role="group" + aria-roledescription="slide" + aria-label="N / total" |
| 컨트롤 | 이전/다음 <button> aria-label(기본 "이전"/"다음", previousLabel·nextLabel로 현지화), 경계에서 disabled |
| 키보드 | 트랙 포커스(tabindex=0) + ArrowLeft/ArrowRight 스크롤 |
| 모션 | scroll-behavior: smooth는 prefers-reduced-motion: no-preference에서만 |
토큰#
component 토큰 없이 semantic을 직접 소비합니다(신설 기준 §4 미충족).
| 속성 | 토큰 |
|---|---|
| 컨트롤 버튼 | color.surface · shadow.sm · radius.full · hover color.surface-hover → 눌림 color.surface-pressed(:focus-visible 2px color.focus-ring) |
| 간격 | space.* 토큰 |
| 모션 | duration.normal + ease.standard (reduced-motion 존중) |