Forms
Select
Dropdown de seleção única — listbox portalada com keyboard navigation completa.
Overview
Select é o componente compound do Radix Select com tokens KDS. O
trigger fica no fluxo da página; o listbox é portalado e animado
(slide / fade) quando aberto. Suporta agrupamento (SelectGroup +
SelectLabel) e separação (SelectSeparator) — use isso para listas
longas que se beneficiam de seções nomeadas (Américas, Europa, Ásia).
Para listas curtas onde ver todas as opções facilita a decisão (≤5
opções), RadioGroup é melhor — Radio mostra tudo
visualmente. Select esconde até abrir, o que penaliza decisões rápidas
mas economiza espaço vertical.
Preview
Select básico com placeholder.
Anatomy
<Select>
<SelectTrigger> ← visível, no fluxo
<SelectValue /> ← mostra valor ou placeholder
</SelectTrigger>
<SelectContent> ← portalado, animado
<SelectGroup> ← opcional
<SelectLabel />
<SelectItem value="a">A</SelectItem>
...
</SelectGroup>
<SelectSeparator /> ← opcional, entre grupos
</SelectContent>
</Select>Subcomponents
| Componente | Descrição |
|---|---|
Select | Raiz que controla open/closed e value. |
SelectTrigger | Botão visível que abre o listbox. |
SelectValue | Renderiza o valor atual ou placeholder. |
SelectContent | Container portalado com a lista. |
SelectGroup | Agrupa items relacionados. |
SelectLabel | Título de um SelectGroup. |
SelectItem | Cada opção individual. |
SelectSeparator | Linha divisória entre grupos. |
SelectScrollUpButton / ScrollDownButton | Botões de scroll quando a lista é maior que o popover. |
Usage
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem
} from "@kalvner/kds/forms/select";
export function FrameworkPicker() {
return (
<Select>
<SelectTrigger className="w-60">
<SelectValue placeholder="Selecione" />
</SelectTrigger>
<SelectContent>
<SelectItem value="next">Next.js</SelectItem>
<SelectItem value="remix">Remix</SelectItem>
</SelectContent>
</Select>
);
}Props
Select (Root)
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
value | string | — | Valor selecionado (controlado). |
defaultValue | string | — | Não controlado. |
onValueChange | (v: string) => void | — | Callback. |
disabled | boolean | false | Bloqueia trigger. |
name | string | — | Nome do form, para FormData. |
SelectTrigger
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
size | 'sm' | 'default' | 'default' | Altura. |
aria-invalid | boolean | — | Aciona ring vermelho. |
States
With placeholder
Texto muted enquanto vazio.
Disabled
Trigger sem pointer events.
Sizes
sm vs default.
Error
aria-invalid no trigger + mensagem abaixo.
Selecione um país.
Composition
Grouped countries
Listas longas se beneficiam de SelectGroup + SelectLabel + SelectSeparator.
When to use
- Escolha única entre 5–~30 opções fixas.
- Quando o espaço vertical importa (forms longos, sidebars).
- Para listas onde agrupamento facilita (continentes, categorias).
When not to use
- ≤5 opções —
RadioGroupdeixa visíveis e acelera escolha. - Listas com >50 opções ou onde o usuário sabe o que digitar — use Combobox (Cmdk) com busca.
- Múltipla seleção — Select aqui é single. Use uma lista de Checkboxes ou um MultiSelect dedicado.
- Ações (Salvar, Excluir) — use
Button, não Select.
Best practices
- Sempre tenha placeholder ou defaultValue. Trigger vazio sem texto fica confuso.
- Itens devem ser short, single-line. Se o texto vai quebrar, o Select não é o componente certo.
- Agrupe quando há >10 opções. SelectGroup + SelectLabel reduz carga cognitiva.
- Não use ícones decorativos sem affordance. Ícones devem diferenciar items, não decorar.
Accessibility
| Concern | Comportamento |
|---|---|
| Roles | Radix expõe role="combobox" no trigger, role="listbox" no content, role="option" em cada item. |
| Keyboard | Tab foca trigger; Space ou Enter ou ↓ abre; ↑↓ navega; Enter seleciona; Esc fecha; type-ahead funciona (digite "B" para pular pra Brasil). |
| Focus management | Foco volta ao trigger ao fechar. |
| Anúncio | "Combobox, [valor atual], collapsed/expanded, [N de M]". |
| Erro | aria-invalid no trigger; mensagem com aria-describedby. |
| Touch target | Trigger 36px default — borderline mobile. Para mobile crítico, considere h-10. |
Related
RadioGroup— listas curtas visíveis.Form— wrappers de RHF + Zod.- Combobox (próxima fase) — quando busca ajuda.