Tiro
Tiro Design
Components

Select

옵션 중 하나를 선택하는 콤보박스/리스트박스 컴포넌트.

트리거 라벨이 현재 선택값을 반영해야 할 때 사용.

Mobile

모바일에서 Select는 옵션 목록을 BottomSheet로 표시한다. 트리거 자체의 형태(라벨 + chevron)는 동일하나, 탭하면 popover가 아닌 시트가 슬라이드 업한다.

  • 컨테이너 anatomy(detents·grabber·swipe-to-dismiss·safe area)는 Dialog → Mobile Presentation 참조
  • 옵션 행: 세로 스택, 각 행 ≥44pt (터치 타겟), 좌측 라벨 + 우측 selected indicator (checkmark)
  • 옵션 수가 8개를 초과하면 detent를 [auto, 1]로 설정해 사용자가 시트를 끌어올려 확장할 수 있게 함
  • 검색이 필요할 때는 MultiSelect 또는 combobox 패턴 사용
  • 헤더 영역에 현재 라벨(예: "언어 선택") 표시. dismiss는 옵션 선택 시 자동 + 시트 외부 영역 탭

Preview

States

closed · open · selected · disabled

Sizes

지원 사이즈: sm · md · lg

Default: md

Trigger는 Input과 동일 스케일 사용:

SizeHeightpxpyText tokenPrimary useMobile (≥44pt hit area)
sm32px106label3Medium 12/18 MediumDense filter, inline selecthitSlop으로 hit area 44pt 확장 필수
md40px1210label3Medium 12/18 MediumDefault (web). FormhitSlop 4pt 권장
lg48px1612label3Medium 12/18 MediumMobile primary form (default)✓ 기본 충족

Item (dropdown 내부, web): h-7 (28px), label3Medium 12/18 Medium. 옵션 목록은 trigger보다 더 조밀하게 표시해 긴 목록을 빠르게 스캔할 수 있게 한다. 단일 선택이므로 selected indicator 없음.

Item contextRow height (web)Row height (mobile)비고
Dropdown (popover)h-7 (28px)— (모바일은 BottomSheet 변환)Web 한정
BottomSheet option≥44pt모바일 표준 — Select Mobile

Disabled Item (맨 아래 안내 텍스트)

옵션 목록 마지막에 "더 많은 언어가 곧 지원될 예정이에요!"처럼 선택 불가능한 안내 텍스트를 넣을 때는 disabled prop을 사용한다. data-[disabled] 상태에서 text-stone-400으로 표시되어 일반 항목과 구분된다.

<SelectItem value="more" disabled>
  더 많은 언어가 곧 지원될 예정이에요!
</SelectItem>
  • disabled item은 클릭/포커스 불가 (pointer-events-none)
  • 텍스트 색상: stone-400 (muted)
  • 별도 separator 없이 자연스럽게 섞어서 배치

Scroll & Max Visible Items

항목이 많을 때 드롭다운 길이를 제한하고 스크롤을 제공한다.

RuleValueRationale
최대 표시 항목 수6개6 × 28px = 168px. 그 이상은 스크롤
max-height (Content)176px항목 6개 + 상하 padding p-1 포함
스크롤 방식네이티브 우측 스크롤바 (overflow-y-auto)Chevron 버튼 없음, 자연스러운 스크롤
기본 열림 방향side="bottom" (아래 우선)트리거 아래에서 열리는 것이 자연스러움
  • 항목이 6개 이하일 때는 스크롤바 미표시 (내용이 max-height 이내)
  • 항목이 6개 초과 시 우측 네이티브 스크롤바가 자동 표시됨
  • position="popper" + Radix collision detection이 공간 부족 시 위로 뒤집는 것을 방지하기 위해 max-h-[176px]을 항상 적용

Token Mapping

TokenValueDescription
trigger height (sm)h-8 (32px)TextInput size="sm"과 동일
trigger height (md)h-10 (40px)Default
trigger height (lg)h-12 (48px)
trigger border1px solid stone-200Input 패턴
focus ring없음 (focus:outline-none)클릭 시 ring 미표시
placeholderstone-400
text (trigger + item)label3MediumTrigger와 Item 동일 크기 (12/18 Medium)
content bgwhite
content border1px solid stone-200
content radiusrounded-lg
content shadowshadow-md
content paddingp-1
content sideOffset4pxtrigger와 content 간격
item hoverstone-50, rounded-md inset
item heighth-7 (28px)
disabled item textstone-400data-[disabled]:text-stone-400
chevron iconstone-400, h-3.5 w-3.5strokeWidth 2

Props

SelectTriggersize prop을 지원한다. Input의 size prop과 동일한 스케일이므로 같은 Row에 혼합 배치할 때 크기를 맞출 수 있다.

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Trigger 높이 (sm=32px, md=40px, lg=48px)

Usage

{
  /* Default (md, 40px) — 일반 폼 */
}
<Select value={plan} onValueChange={setPlan}>
  <SelectTrigger>
    <SelectValue placeholder="플랜 선택" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="monthly">월간 ($10)</SelectItem>
    <SelectItem value="yearly">연간 ($100)</SelectItem>
    <SelectItem value="team">팀 ($25/user)</SelectItem>
  </SelectContent>
</Select>;

{
  /* sm (32px) — Item 행 안에 인라인으로 배치, TextInput size="sm"과 동일 높이 */
}
<Select value={lang} onValueChange={setLang}>
  <SelectTrigger size="sm" className="w-[200px]">
    <SelectValue />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="ko_KR">한국어 / Korean</SelectItem>
    <SelectItem value="en_US">English / English (US)</SelectItem>
  </SelectContent>
</Select>;

A11y

  • role="combobox" + role="listbox" + role="option" 자동
  • Label 연결 필수 (htmlFor)
  • 키보드 탐색 (Arrow, Home/End, type-to-search)

검색 가능한 콤보박스 또는 다중 선택이 필요하다면 MultiSelect 컴포넌트를 사용하세요.

상황컴포넌트
옵션 5–20개, 선택값이 트리거에 표시Select (기본)
옵션 20개 이상 또는 검색 필요MultiSelect
멀티 선택 + 검색MultiSelect
선택 결과가 트리거에 반영 안 됨DropdownMenu

Table / Row 안에서 사용할 때 (Ghost variant)

테이블 행이나 List Item 내부에 Select를 배치할 경우, 기본 border가 주변 행과 시각적으로 충돌한다. 이때는 border와 배경을 제거한 ghost 스타일을 사용한다.

Variantborderbg주 사용처
default1px solid stone-200white폼, 필터 영역
ghost없음투명테이블 행, List Item 내 인라인 역할 선택
  • hover 시 bg-stone-50 유지 — 인터랙티브임을 시각적으로 전달
  • paid 옵션 badge: caption1Regular text-stone-400으로 $ 표시 — Select.ItemText 바로 뒤에 인라인 배치
  • ghost 트리거는 행 전체 클릭 영역과 겹치지 않도록 클릭 이벤트를 분리해서 배치

Do / Don't

  • Do: 선택값이 UI에 표시되어야 할 때 사용
  • Do: 옵션 5–20개에 적합
  • Don't: 2개 옵션 토글은 Switch 또는 ToggleGroup
  • Don't: 선택 결과가 트리거에 반영되지 않는다면 DropdownMenu 사용
  • Don't: 검색이나 다중 선택이 필요하다면 Select 대신 MultiSelect 사용

On this page