Carousel
여러 슬라이드를 한 장씩 넘겨 보여 줍니다. 스와이프와 키보드 이동, RTL을 모두 지원합니다.
import { Carousel } from 'neba';
<Carousel label="제품 소개">
<img src="/one.jpg" alt="" />
<img src="/two.jpg" alt="" />
</Carousel>;최상위 자식 하나가 슬라이드 하나가 됩니다. 별도의 슬라이드 컴포넌트는 없고, snap 지점과 폭, role="group" · aria-roledescription="slide"는 컴포넌트가 붙입니다.
Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| locale | string | — | BCP 47 태그. region 이름, 화살표, 각 슬라이드 이름을 이 언어로 씁니다 |
| variant공통 | 'solid' | 'outline' | 'text' | 'outline' | 프레임의 무게. 컨테이너의 방식대로 시트에 색을 들이지 않습니다. 캐러셀은 남의 사진을 담습니다. 사진에 이미 테두리가 있으면 text |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 프레임의 모서리, 화살표의 크기와 안쪽 여백, 점의 크기 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| density공통 | 'default' | 'compact' | 'default' | 여백만 바꿉니다. 높이와 글자 크기는 그대로 |
| elevation공통 | 0 | 1 | 2 | 3 | 0 | 그림자 깊이. 0은 그림자 없음 |
| value | number | — | 보이는 슬라이드. 0부터 |
| defaultValue | number | 0 | 처음 보이는 슬라이드 |
| onValueChange | (index: number) => void | — | 슬라이드가 바뀔 때. 손가락으로 밀어서 바뀐 경우에도 불립니다 |
| loop | boolean | true | 끝에서 처음으로 돌아갈지. 끄면 양 끝에서 화살표가 죽습니다. 시작과 끝이 있는 묶음에는 그쪽이 정직합니다 |
| autoPlay | boolean | false | 스스로 넘어갑니다. 기본값은 꺼짐이며, 켜면 hover·포커스·백그라운드 탭에서 멈추고 프레임 아래에 멈춤 버튼이 그려집니다. 모션을 줄이도록 설정한 환경에서는 시작하지 않습니다 |
| interval | number | 5000 | 슬라이드 한 장을 붙잡는 시간(ms) |
| arrows | boolean | true | 이전/다음 버튼 |
| indicators | boolean | true | 프레임 아래 위치 점들 |
| label | string | — | 캐러셀의 접근성 이름. 선택이 아니라 기본값입니다. 이름 없는 region은 건너뛸 수도 없습니다 |
| previousLabel | string | — | 이전 버튼의 이름 |
| nextLabel | string | — | 다음 버튼의 이름 |
| pauseLabel · playLabel | string | — | 멈춤 버튼의 두 이름, 슬라이드가 넘어가는 동안, 그리고 멈춘 뒤. 기본값은 locale의 표현 |
| slideLabel | (index: number, count: number) => string | — | 슬라이드 하나를 스크린 리더에게 어떻게 부를지, 그리고 그 점의 라벨. 기본값은 locale의 표현 |
| children | ReactNode | — | 슬라이드들. 최상위 자식 하나가 슬라이드 하나가 됩니다. 스냅 지점, 폭, role을 감싸는 쪽이 붙이므로 사진 한 장에 그것들을 붙일 일이 없습니다 |
<div>의 native 속성은 그대로 전달됩니다.
내부 구현은 CSS scroll snap이 걸린 스크롤 컨테이너입니다. 그래서 스와이프가 브라우저 기본 동작으로 처리되고, RTL에서 방향이 자동으로 뒤집히며, 전환은 scroll-behavior: smooth를 씁니다. prefers-reduced-motion에서는 같은 경로로 즉시 전환됩니다.
예시
loop · arrows · indicators
loop를 끄면 화살표가 양 끝에서 비활성화됩니다. 처음과 끝이 있는 묶음에 적합합니다. arrows와 indicators는 각각 좌우 화살표와 하단 인디케이터를 표시합니다.
화살표는 프레임 위에 그려집니다. 가장자리 근처에 텍스트가 있는 슬라이드는 화살표를 피할 만큼 안쪽 여백을 두세요. size="md"에서 3.5rem 정도입니다.
사진
사진은 프레임을 가득 채우므로 안쪽 여백을 둘 것이 없고, 화살표는 사진 위에 놓입니다. 슬라이드마다 ratio를 준 Image 하나씩이라, 파일이 오는 동안에도 스트립의 높이가 변하지 않습니다.
value와 onValueChange
controlled로 쓰면 페이지의 다른 컨트롤로 슬라이드를 옮길 수 있습니다. 사용자가 스와이프해서 슬라이드가 바뀐 경우에도 onValueChange가 호출됩니다.
autoPlay와 interval
autoPlay의 기본값은 꺼짐입니다. 켜더라도 hover, 내부 focus, 백그라운드 탭에서 멈추고, prefers-reduced-motion에서는 시작하지 않습니다. 자동 재생 중에는 현재 슬라이드를 알리는 live region도 침묵하고, 멈추면 다시 알립니다.
켜면 회전을 멈추는 버튼이 프레임 아래 점 줄 옆에 그려집니다. 이 버튼을 없애는 prop은 없습니다. hover와 focus는 휴대폰을 든 독자나 화면 확대를 쓰는 독자에게는 멈출 방법이 되지 못하기 때문입니다. 이름은 pauseLabel과 playLabel로 지정합니다.
모든 슬라이드가 반드시 읽혀야 하는 내용이라면 Tabs나 세로 나열을 고려하세요.
접근성
label이 캐러셀의 accessible name이 됩니다.previousLabel·nextLabel·pauseLabel·playLabel·slideLabel로 컨트롤 이름을 지정합니다.autoPlay는 회전을 멈추는 버튼을 프레임 아래에 함께 그립니다. hover도 tab도 쓰지 않는 독자가 멈출 수 있어야 하기 때문입니다.- 각 슬라이드는
role="group"과aria-roledescription="slide"를 갖습니다.
제공하지 않는 것
- 한 화면에 여러 장:
overflow-x-auto를 얹은 Grid를 쓰세요. - 세로 방향: 스크롤되는 목록이면 충분합니다.
- fade 전환: 스크롤 기반 구현과 함께 쓸 수 없습니다.
- region 이름, 화살표, 각 슬라이드의 이름을
locale이 정합니다.label과slideLabel로 직접 쓸 수도 있습니다.