Select
Stable미리 정의한 옵션 중 하나를 선택하는 필드입니다.
Overview
Select는 미리 정의된 목록에서 한 가지 값을 선택할 때 사용합니다. 현재 선택과 변경 가능 여부를 명확하게 전달합니다.
여행할 도시 한 곳을 선택해 주세요.
현재 값: 없음
<label htmlFor="destination">여행지</label>
<Select id="destination" size="md"
options={[{ value: 'seoul', label: '서울' }, { value: 'busan', label: '부산' }]}
placeholder="여행지를 선택하세요"
aria-describedby="destination-help"
/>
<p id="destination-help">한 곳을 선택해 주세요.</p>이 가이드의 Select는 네이티브 select로 독립 구현했습니다. 트리거는 원본 site 토큰을 사용하고, 열린 목록의 외형과 키보드 동작은 브라우저·운영체제를 따릅니다.
Anatomy
5. 도움말 또는 오류 메시지
| 번호 | 구성 요소 | 역할 |
|---|---|---|
| 1 | Label | 필드 이름. htmlFor와 id로 연결 |
| 2 | Container | 크기·경계·배경·포커스 영역 |
| 3 | Selected value / Placeholder | 현재 선택 값 또는 선택 안내 |
| 4 | Indicator / Options | 열림 가능성을 알리는 표시와 선택 목록 |
| 5 | Description / Error | aria-describedby로 연결한 도움말·오류 메시지 |
번호는 구성 요소를 식별하며 배치 순서를 고정하지 않습니다. 레이블은 위나 옆에 배치할 수 있습니다. 옵션의 순서는 선택 맥락에 맞게 정합니다.
Variants
원본 Select는 하나의 기본 스타일을 사용합니다. 필수 선택 여부, 초기 선택과 비활성 옵션을 조합합니다. 이 구현은 단일 선택 전용입니다.
필수 항목입니다. “준비 중” 옵션은 선택할 수 없습니다.
Sizes
원본 공통 Component size 9개를 사용합니다. 기본 md는 40px입니다. 터치 영역이 필요한 화면에서는 lg(44px) 이상을 선택합니다.
………………………| Size | Height | Padding X | Font size |
|---|---|---|---|
| xxxs | … | … | … |
| xxs | … | … | … |
| xs | … | … | … |
| sm | … | … | … |
| md | … | … | … |
| lg | … | … | … |
| xl | … | … | … |
| xxl | … | … | … |
| xxxl | … | … | … |
States
Default, Focus, Error, Readonly, Disabled 상태를 지원합니다. 위 Live Preview에서 상태를 바꾸고 Tab으로 실제 포커스를 확인하세요.
Tab으로 포커스를 이동해 보세요.
입력한 값을 확인해 주세요.
값을 확인할 수 있지만 변경할 수 없습니다.
현재 사용할 수 없습니다.
네이티브 select에는 readonly 속성이 없습니다. readOnly에서는 선택된 레이블을 읽기 전용 텍스트 필드로 표시하고, name이 있으면 hidden input으로 실제 값을 전송합니다. 이때 목록은 열리지 않습니다. disabled 상태는 폼 전송에서 제외됩니다.
Select 상태 토큰 전체 · 원본 이름과 매핑
| Token | 정의 / 매핑 | 현재 값 |
|---|---|---|
--site-select-default-bg | var(--site-bg-base) | … |
--site-select-default-border | var(--site-line-default) | … |
--site-select-default-text | var(--site-text-primary) | … |
--site-select-default-icon | var(--site-icon-default-color) | … |
--site-select-focus-border | var(--site-line-focus) | … |
--site-select-focus-text | var(--site-text-primary) | … |
--site-select-focus-icon | var(--site-icon-strong-color) | … |
--site-select-error-border | var(--site-line-error) | … |
--site-select-error-text | var(--site-text-error) | … |
--site-select-readonly-bg | var(--site-bg-surface-light) | … |
--site-select-readonly-border | var(--site-line-default) | … |
--site-select-readonly-text | var(--site-text-primary) | … |
--site-select-readonly-icon | var(--site-icon-muted-color) | … |
--site-select-disabled-bg | var(--site-bg-disabled) | … |
--site-select-disabled-border | var(--site-line-default) | … |
--site-select-disabled-text | var(--site-text-on-disabled) | … |
--site-select-disabled-icon | var(--site-icon-muted-color) | … |
Guidelines
권장해요
항상 보이는 레이블과 구체적인 도움말을 제공합니다.
오류에는 aria-invalid와 수정 방법을 설명하는 메시지를 함께 제공합니다.
사용자가 이해하는 이름으로 옵션을 표시하고, 실제 값은 고유한 문자열로 관리합니다.
피해주세요
placeholder만으로 레이블을 대신하지 않습니다.
값을 읽기만 해야 할 때 disabled로 정보를 숨기지 않습니다.
검색·다중 선택·비동기 옵션 로딩을 이 단일 Select에 포함하지 않습니다.
원본 색상 값을 보존합니다. 오류·placeholder 등의 대비는 사용하는 배경과 글자 크기에 맞게 확인합니다.
API
이 레포의 API입니다. 원본 Radix의 SelectTrigger/SelectItem 합성 API 대신 options 배열과 네이티브 onChange 이벤트를 사용합니다. className은 컨테이너에 적용됩니다.
| Prop | Type | Default / 설명 |
|---|---|---|
| size | xxxs | xxs | xs | sm | md | lg | xl | xxl | xxxl | md |
| options | readonly SelectOption[] | 필수 · value / label / disabled? |
| placeholder | string | 선택 안내 · 값은 빈 문자열 |
| value / defaultValue | string | 제어 / 초기 값 · 둘 중 하나 사용 |
| disabled | boolean | false · 조작 및 폼 전송 제외 |
| readOnly | boolean | false · 값 확인만 허용 |
| aria-invalid | boolean | "true" | "false" | 오류 표시 |
| aria-describedby | string | 도움말·오류 요소의 id |
| id / name | string | 레이블 연결 / 폼 필드 이름 |
| onChange | ChangeEventHandler<HTMLSelectElement> | 변경 이벤트 |
| …native props | 네이티브 단일 select 속성 (ref 제외) | required, autoComplete 등 |
Code Example
'use client';
import { useState } from 'react';
import { Select } from '@/components/select';
export default function DestinationField() {
const [destination, setDestination] = useState('');
return (
<div>
<label htmlFor="destination">여행지</label>
<Select id="destination" name="destination"
value={destination}
onChange={e => setDestination(e.target.value)}
placeholder="여행지를 선택하세요"
options={[
{ value: 'seoul', label: '서울' },
{ value: 'busan', label: '부산' },
]}
aria-describedby="destination-help" />
<p id="destination-help">여행할 도시 한 곳을 선택하세요.</p>
</div>
);
}