Slider
범위 위에서 값을 고릅니다. 배열을 넘기면 범위 슬라이더가 됩니다 — 그것을 위한 별도 prop은 없습니다. 값의 모양이 이미 어느 쪽인지 말해 주기 때문입니다.
tsx
import { Slider } from 'neba';
<Slider label="볼륨" defaultValue={65} showValue />;Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 높이와 타입 스케일 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| orientation공통 | 'horizontal' | 'vertical' | 'horizontal' | 트랙이 놓이는 방향. 세로는 길이를 직접 정해 주세요 |
| value | number | number[] | — | 현재 값. 배열이면 thumb이 값마다 하나씩 생깁니다 |
| defaultValue | number | number[] | — | 초기 값 |
| onValueChange | (value, details) => void | — | 끄는 동안 계속 호출됩니다 |
| onValueCommitted | (value, details) => void | — | 값이 확정될 때 한 번만. 네트워크 요청은 이쪽에 거세요 |
| min | number | 0 | 최솟값 |
| max | number | 100 | 최댓값 |
| step | number | 1 | 값이 움직이는 단위 |
| label | ReactNode | — | 트랙 위의 라벨 |
| description | ReactNode | — | 트랙 아래 보조 설명 |
| showValue | boolean | ((formatted, values) => ReactNode) | false | 라벨 옆에 현재 값을 보여 줍니다. 함수를 넘기면 표시 형식을 직접 정할 수 있습니다 |
| disabled | boolean | false | 사용 불가 |
onValueChange는 끄는 동안 계속 호출되고, onValueCommitted는 값이 확정될 때 한 번만 호출됩니다. 네트워크 요청은 두 번째에 거세요.
예시
범위
tsx
import { useState } from 'react';
import { Slider } from 'neba';
export default function SliderRange() {
const [range, setRange] = useState<number[]>([200, 800]);
return (
<div className="w-full max-w-md">
<Slider
label="Price"
min={0}
max={1000}
step={50}
value={range}
onValueChange={(next) => setRange(next as number[])}
showValue={(formatted) => `$${formatted[0]} – $${formatted[1]}`}
/>
</div>
);
}크기
thumb은 트랙보다 의도적으로 훨씬 큽니다. 실제로 손이 닿는 곳은 여기뿐이고, 6px 레일에 맞춘 thumb은 터치스크린에서 아무도 잡지 못하는 thumb입니다.
tsx
import { Slider } from 'neba';
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl'] as const;
export default function SliderSizes() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
{SIZES.map((size) => (
<Slider key={size} size={size} label={size} defaultValue={50} />
))}
</div>
);
}세로
세로 슬라이더에는 자기 길이가 없습니다. 높이를 직접 주세요. 기본값은 규칙이 아니라 출발점입니다.
tsx
import { Slider } from 'neba';
const CHANNELS = [
{ label: 'Low', value: 70, color: 'primary' as const },
{ label: 'Mid', value: 45, color: 'info' as const },
{ label: 'High', value: 30, color: 'success' as const }
];
export default function SliderVertical() {
return (
<div className="flex items-end gap-8">
{CHANNELS.map((channel) => (
<div key={channel.label} className="flex flex-col items-center gap-2">
<Slider
orientation="vertical"
color={channel.color}
aria-label={channel.label}
defaultValue={channel.value}
/>
<span className="text-[0.75rem] text-[var(--neba-muted-fg)]">{channel.label}</span>
</div>
))}
</div>
);
}thumb은 커지지 않습니다
호버와 드래그는 thumb을 확대하는 대신 그 둘레에 링을 그립니다. 라이브러리의 나머지가 따르는 transform 금지 규칙과 같은 것이고, 이 부품에 글자가 없다는 이유로 느슨해지지 않습니다. 커서 아래에서 부품 크기가 변하는 컨트롤이야말로 싸구려로 읽히는 그것입니다.
레일과 인디케이터가 알약 모양인 이유는 Switch의 트랙과 같습니다 — 이것은 무언가가 지나가는 홈이지 시트가 아닙니다.
접근성
- 각 thumb은 진짜
<input type="range">이므로 방향키, Home/End, PageUp/PageDown이 여기 코드 없이 그대로 동작합니다. label이 접근성 이름이 됩니다. 없다면aria-label을 주세요.showValue는<output>으로 렌더링되어 값이 바뀔 때 읽힙니다.