콘텐츠로 건너뛰기

네이티브 선택

양식, 필터 및 국가 선택기를 위해 재설계된 기본 HTML 선택 요소 — 네오브루탈리즘 스타일의 테두리와 그림자가 적용된 OS 기반 드롭다운.

import {
  NativeSelect,
  NativeSelectOption,

Native Select는 브라우저 고유 <select> 요소 위의 얇은 스타일 레이어입니다. 프리미티브도, 포털도, JavaScript도 없습니다. 닫힌 컨트롤에는 두꺼운 테두리, 선명한 그림자, 굵은 서체로 네오브루탈리즘 공식을 입히고, 열린 목록은 OS 피커 그대로입니다.

다음과 같은 경우에 사용합니다.

  • 국가·시간대·통화 선택 — 커스텀 팝오버보다 OS가 더 빠르게 그리는 긴 옵션 목록.
  • 모바일 중심 폼 — iOS와 Android가 네이티브 피커 휠을 쓰므로 작은 화면에서 커스텀 드롭다운보다 낫습니다.
  • 일반 폼 전송 — 실제 <select name="…">라서 폼과 함께 값이 전송되고 하이드레이션 전에도 동작합니다.

설치

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/native-select.json
npx shadcn@latest add https://neobrutalism.com/r/base/native-select.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/native-select.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/native-select.json

다음 코드를 복사해서 프로젝트에 붙여넣습니다.

components/ui/native-select.tsx
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"

import { cn } from "@/lib/utils"

type NativeSelectProps = Omit<React.ComponentProps<"select">, "size"> & {
  size?: "sm" | "default"
}

function NativeSelect({
  className,
  size = "default",
  ...props
}: NativeSelectProps) {
  return (
    <div
      className={cn(
        "group/native-select relative w-fit has-[select:disabled]:opacity-50",
        className
      )}
      data-slot="native-select-wrapper"
      data-size={size}
    >
      <select
        data-slot="native-select"
        data-size={size}
        className="h-8 w-full min-w-0 appearance-none rounded border-2 bg-input py-2 pr-8 pl-3 text-sm shadow-sm transition-colors outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive data-[size=sm]:h-7 data-[size=sm]:rounded data-[size=sm]:py-0.5"
        {...props}
      />
      <ChevronDownIcon
        className="pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none"
        aria-hidden="true"
        data-slot="native-select-icon"
      />
    </div>
  )
}

function NativeSelectOption({
  className,
  ...props
}: React.ComponentProps<"option">) {
  return (
    <option
      data-slot="native-select-option"
      className={cn("bg-[Canvas] text-[CanvasText]", className)}
      {...props}
    />
  )
}

function NativeSelectOptGroup({
  className,
  ...props
}: React.ComponentProps<"optgroup">) {
  return (
    <optgroup
      data-slot="native-select-optgroup"
      className={cn("bg-[Canvas] text-[CanvasText]", className)}
      {...props}
    />
  )
}

export { NativeSelect, NativeSelectOptGroup, NativeSelectOption }

import 경로를 프로젝트 구성에 맞게 수정합니다.

사용법

import {
  NativeSelect,
  NativeSelectOptGroup,
  NativeSelectOption,
} from "@/components/ui/native-select"
<NativeSelect>
  <NativeSelectOption value="">Select a fruit</NativeSelectOption>
  <NativeSelectOption value="apple">Apple</NativeSelectOption>
  <NativeSelectOption value="banana">Banana</NativeSelectOption>
  <NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
  <NativeSelectOption value="pineapple">Pineapple</NativeSelectOption>
</NativeSelect>

구성

간단한 구성

NativeSelectOptGroup 없이 NativeSelect 바로 아래에 옵션을 배치합니다.

NativeSelect
├── NativeSelectOption
├── NativeSelectOption
├── NativeSelectOption
└── NativeSelectOption

그룹이 있는 구성

NativeSelectOptGroup을 사용해서 옵션을 카테고리별로 정리합니다.

NativeSelect
├── NativeSelectOptGroup
│   ├── NativeSelectOption
│   └── NativeSelectOption
└── NativeSelectOptGroup
    ├── NativeSelectOption
    └── NativeSelectOption

예제

그룹

NativeSelectOptGroup을 사용해서 옵션을 카테고리별로 정리합니다.

import {
  NativeSelect,
  NativeSelectOptGroup,

비활성화

NativeSelect 컴포넌트에 disabled prop을 추가하면 select를 비활성화할 수 있습니다.

import {
  NativeSelect,
  NativeSelectOption,

오류 상태

aria-invalid를 사용해서 유효성 검증 오류를 표시하고, 스타일 지정을 위해 Field 컴포넌트에 data-invalid 속성을 추가합니다.

import {
  NativeSelect,
  NativeSelectOption,

Native Select vs Select

  • 네이티브 브라우저 동작, 더 나은 성능, 모바일에 최적화된 드롭다운에는 NativeSelect를 사용하세요.
  • 커스텀 스타일, 애니메이션, 복잡한 상호작용에는 Select를 사용하세요.

RTL

Neobrutalism에서 RTL 지원을 활성화하는 방법은 RTL 설정 가이드를 참고하세요.

"use client"

import * as React from "react"

접근성

플랫폼 고유 <select>입니다. 브라우저가 이미 보조 기술에 노출하며, WAI-ARIA select-only combobox 패턴이 흉내 내려는 바로 그 컨트롤입니다. ARIA 배선은 필요 없고, <Label htmlFor> 또는 aria-label로 접근 가능한 이름만 주세요.

키보드 동작은 브라우저가 제공하므로 OS와 브라우저마다 약간 다릅니다. 공통 집합:

키보드 상호작용:

동작
Space / Alt + ArrowDown옵션 목록 열기
ArrowDown / ArrowUp다음/이전 옵션 강조
Home / End첫/마지막 옵션으로 이동
Enter강조된 옵션을 확정하고 목록 닫기
Escape값을 바꾸지 않고 목록 닫기
인쇄 가능한 문자타입어헤드 — 입력과 맞는 다음 옵션으로 이동

API 참조

NativeSelect

네이티브 HTML select 요소를 감싸는 기본 select 컴포넌트입니다.

<NativeSelect>
  <NativeSelectOption value="option1">Option 1</NativeSelectOption>
  <NativeSelectOption value="option2">Option 2</NativeSelectOption>
</NativeSelect>

NativeSelectOption

select 안의 개별 옵션을 나타냅니다.

PropTypeDefault
valuestring
disabledbooleanfalse

NativeSelectOptGroup

관련된 옵션을 함께 묶어 더 체계적으로 정리합니다.

PropTypeDefault
labelstring
disabledbooleanfalse
<NativeSelectOptGroup label="Fruits">
  <NativeSelectOption value="apple">Apple</NativeSelectOption>
  <NativeSelectOption value="banana">Banana</NativeSelectOption>
</NativeSelectOptGroup>