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 — 동등 패널을 한 번에 하나씩 보는 건 다른 컴포넌트입니다.
권장 — 이렇게 쓰세요
지양 — 이러지 마세요
쓴다 — 추천 항목·미디어 갤러리·배너처럼 가로로 스크롤하며 훑는 컬렉션
대신 Tabs — 동등한 콘텐츠 패널을 한 번에 하나씩 전환할 때
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
Carousel를 직접 조작해 보세요
속성·테마·토큰을 바꾸고 React·Flutter 코드를 확인하는 풀스크린 빌더로 엽니다.
플레이그라운드는 넓은 작업 영역이 필요해 웹·태블릿에서 편집할 수 있어요.
변형#
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 | readonly CarouselItem[] | — | 표시할 슬라이드 데이터 — key 안정성을 위해 children 대신 배열을 받는다 |
ariaLabel | string | — | 캐러셀 영역의 접근 가능한 이름 (필수) |
variant | 'multiBrowse' | 'hero' | 'fullScreen' | multiBrowse | 아이템 폭 레이아웃 (M3) — 기본 'multiBrowse' |
previousLabel | string | 이전 | 이전 버튼 aria-label (기본 '이전') — 현지화용 (Pagination 선례) |
nextLabel | string | 다음 | 다음 버튼 aria-label (기본 '다음') — 현지화용 (Pagination 선례) |
controls | 'inline' | 'overlay' | inline | 컨트롤 배치 — 'inline'(기본, 트랙 옆) · 'overlay'(슬라이드 위로 띄움). 'overlay'는 컨트롤에 중립 surface 계약 data-wds-surface="floating"을 부착해, liquid-glass 머티리얼 테마 스코프에서 clear 글래스(콘텐츠/미디어 위) 변형으로 칠해진다. compact 폭(<600px, ADR-012 BP.compact)에서는 띄우기를 끄고 inline 흐름으로 폴백해 컨트롤이 좁은 슬라이드의 미디어 가장자리를 가리지 않는다(아트디렉션 검수 H-2). |
gap | string | — | 슬라이드 사이 간격 — 기본 토큰값. 과도한 매개변수화 금지 |
className | string | — | 루트에 병합 |
ref | Ref<HTMLDivElement> | — | 루트 <div>로 전달되는 ref — 내부 trackRef와 별개 노드 (React 19 ref-as-prop) |
접근성#
| 계약 | 구현 |
|---|---|
| 영역 | 루트 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 존중) |