跳到内容

原生选择

针对表单、筛选器和国家选择器重新设计的原生 HTML 下拉列表——由操作系统渲染的下拉菜单,带有新粗野主义风格的边框和阴影。

import {
  NativeSelect,
  NativeSelectOption,

原生选择器是浏览器自带 <select> 元素 上的一层薄样式——没有基元、没有 portal、没有 JavaScript。收起态控件套上新粗野主义配方——粗边框、硬阴影、加粗字体——展开后的列表仍由操作系统选择器呈现。

适合以下场景:

  • 国家/地区、时区与货币选择——选项很长时,系统渲染比任何自定义弹出层都快。
  • 移动端为主的表单——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 {
  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>

组合

简单

将选项直接放在 NativeSelect 之下(不使用 NativeSelectOptGroup)。

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

带分组

使用 NativeSelectOptGroup 将选项按类别组织。

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

示例

分组

使用 NativeSelectOptGroup 将选项按类别组织。

import {
  NativeSelect,
  NativeSelectOptGroup,

禁用

NativeSelect 组件上添加 disabled 属性以禁用该选择框。

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 仅选择 Combobox 模式 正是为了模拟它。无需 ARIA 接线;只需通过 <Label htmlFor>aria-label 给它可访问名称。

键盘行为由浏览器提供,跨操作系统与浏览器会略有差异。常见按键如下:

按键操作
Space / Alt + ArrowDown打开选项列表
ArrowDown / ArrowUp高亮下一项 / 上一项
Home / End跳到第一项 / 最后一项
Enter确认高亮项并关闭列表
Escape关闭列表且不改变取值
可打印字符键入即定位——跳到下一个匹配所输内容的选项

API 参考

NativeSelect

包裹原生 HTML select 元素的主选择组件。

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

NativeSelectOption

表示选择框中的单个选项。

PropTypeDefault
valuestring
disabledbooleanfalse

NativeSelectOptGroup

将相关选项组合在一起,使结构更清晰。

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