Ir ao conteúdo
SEED engenhariaDesign System

Componentes

7. Select

estávelseed-componentes.md v1.43 · §07seção 9 de 9807-select-estavel-validado-pelo-rafael-em-2026-08.md · MD5 9c026ae0

Título completo no canon: Select — estável · validado pelo Rafael em 2026-08-02 (v0.11, mock do custom corrigido na validação)

Consome o form-field (§2). Abre a família de ESCOLHA do bloco (select → combobox → checkbox → radio → switch) e carrega a régua que governa todos.

Base de evidência (3 rodadas encadeadas, 2026-08-02): R1 (canon) — GOV.UK ("último recurso" em serviço público, com pesquisa), NN/g dropdowns (limiares <5 radios / >15 combobox), USWDS (<7), M3 (6), Carbon (3), CMS.gov (janela 7–15 + regras satélites), Parliament/Bristol/Pelican. R2 (herda: nativo × custom + mobile) — a própria stack: shadcn mantém DOIS selects (custom Radix e Native Select), comunidade documenta o trade-off e a regra de não misturar. R3 (herda: o custom é seguro o bastante?) — Sarah Higley (Microsoft): teste de usabilidade real com leitores — recriar o <select> nativo é impossível (semântica por plataforma, teclado inconsistente, mobile inteiramente diferente); ARIA 1.2 foi o padrão menos bugado (= o que o Radix implementa); Angular Material recomenda nativo p/ a11y. Decisões G1–G7 aprovadas pelo Rafael em 2026-08-02.

7.1 A régua unificada da família de escolha (G1 — governa 5 componentes)#

Opções Componente Fonte da faixa
1 escolha binária de AÇÃO com efeito imediato switch (§8) Fluent: switch = ação
1 escolha binária de STATUS confirmada no submit (consentimento, "li e aceito") checkbox único (§10) — nunca switch Fluent: checkbox = status · emenda v0.12
2–6, uma escolha radio (§9) NN/g <5 · M3 6 · USWDS <7 → adotamos 6
2–6, várias escolhas checkbox (§10) CMS/NN/g: multi em dropdown é proibido (G5)
7–15 (teto ~20 no ERP denso — Fiori) select janela CMS 7–15
>15, ou qualquer volume com busca necessária combobox (§11) NN/g >15 · Fiori 20–200

Supersede: esta régua ATUALIZA a linha de volume do §3.1 (que citava só o Fiori). No formulário público vale o espírito GOV.UK (G7): antes de aceitar um select, reformule a pergunta para caber em radios; no ERP denso, o select curto é ferramenta legítima de densidade — o "último recurso" nasceu do one-thing-per-page de governo, não de toolbars com 12 filtros.

7.2 As 7 decisões (G1–G7, aprovadas 2026-08-02)#

# Decisão Porquê Descartado
G1 Régua unificada (§7.1) 6 fontes harmonizadas; uma régua, cinco componentes, zero sobreposição Réguas por componente (a do §3.1 já divergia da NN/g)
G2 Nativo estilizado = PADRÃO (site, público, TODO toque); custom Radix permitido no produto desktop quando a opção exige conteúdo rico (ícone de status, metadado). NUNCA misturar os dois no mesmo formulário; híbrido POR PLATAFORMA (nativo no toque, custom no desktop) é recomendado — coerência com o dispositivo, não mistura Higley: recriar o nativo é impossível; Angular Material: nativo = melhor a11y; no toque o nativo abre o picker do OS (roda iOS/lista Android) com zero JS; Radix implementa ARIA 1.2 — o padrão MENOS bugado no teste da Higley, o que torna o custom aceitável onde o conteúdo justifica Custom universal (assume o risco Higley sem necessidade); nativo universal (opção rica do ERP não cabe em <option> — só texto)
G3 Rótulo sempre; placeholder "Selecione…" (option disabled selected hidden) SÓ sem default sensato; default inteligente quando a maioria escolhe a mesma opção CMS: nunca substituir o rótulo pelo placeholder; default correto poupa a interação inteira "Selecione…" universal (esconde o default útil); primeiro item como default acidental
G4 Selecionar NUNCA muda contexto (não navega, não submete, não recarrega) CMS + WCAG 3.2.2 On Input Select-como-navegação (o anti-padrão clássico)
G5 Multi-seleção em dropdown PROIBIDA → checkboxes; optgroup/grupos só com categorias reais CMS: usuários não entendem multi em dropdown <select multiple> (ctrl+clique: ninguém descobre)
G6 Fronteiras formais armadas: binário→switch · 2–6→radio · >15/busca→combobox (que herdará a semântica ARIA 1.2 testada) A família nasce coordenada, não em disputa Cada item redescobrir seus limites
G7 "Último recurso" contextualizado por superfície (§7.1) O contexto da pesquisa GOV.UK é serviço público Importar o dogma sem o contexto (mataria o select no ERP sem ganho)

7.3 Anatomia e estados (variante nativa — a padrão)#

<select> nativo com appearance:none + chevron SVG próprio (o nativo não estiliza a seta) sobre a moldura do form-field: mesma altura (44px md), mesma borda (4.74:1), mesmos 9 estados NO TRIGGER (o painel de opções aberto é do OS/browser — não estilizamos, e isso é a FEATURE: é o painel que o usuário do dispositivo conhece). Dark: o trigger segue os tokens; o painel nativo segue o OS — documentado como comportamento esperado, não bug. Variante custom (Radix): trigger idêntico; painel usa surface-overlay + shadow-overlay (tokens de elevação v1.1); opção rica = ícone 20px + texto + metadado opcional em --seed-field-help-text.

7.4 Contrato HTML#

<select> + <option> com value estável (nunca o texto como valor) · placeholder: <option value="" disabled selected hidden>Selecione…</option> · grupos: <optgroup label> · autocomplete quando aplicável (country, address-level1…) · form-field completo em volta (rótulo/ajuda/erro herdados).

7.5 Microcopy#

Rótulo nomeia a coisa ("Concessionária"), placeholder instrui a ação ("Selecione…") — nunca o contrário. Opções em sentence case, ordenadas por LÓGICA do domínio (frequência de uso > alfabética > alfabética como fallback); nunca "Escolha uma opção acima" como erro — o erro do form-field diz o quê: "Selecione a concessionária".

7.6 Código — essência#

.seed-select{appearance:none;-webkit-appearance:none;
  height:var(--seed-field-height-md);padding:0 40px 0 12px;width:100%;
  font-family:var(--seed-font-sans);font-size:14px;color:var(--seed-field-value);
  background:var(--seed-field-bg);border:1px solid var(--seed-field-border);
  border-radius:var(--seed-field-radius);cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16'%3E%3Cpath fill='%234D606C' d='M4.3 6.3 8 10l3.7-3.7 1 1L8 12 3.3 7.3l1-1Z'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center}
.seed-select:hover{border-color:var(--seed-field-border-hover)}
.seed-select:focus-visible{outline:none;box-shadow:var(--seed-focus-ring)}
.seed-select:invalid{color:var(--seed-field-placeholder)} /* placeholder-option selecionada */
[data-theme="dark"] .seed-select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16'%3E%3Cpath fill='%23ACBECA' d='M4.3 6.3 8 10l3.7-3.7 1 1L8 12 3.3 7.3l1-1Z'/%3E%3C/svg%3E")}
/* Nativo (padrão — G2): */
export function NativeSelect(props: React.SelectHTMLAttributes<HTMLSelectElement> & { placeholder?: string }) {
  const { id, helpId, errId, invalid } = useField();
  return (
    <select id={id} aria-invalid={invalid || undefined}
      aria-describedby={cn(invalid && errId, helpId) || undefined}
      className="seed-select" {...props}>
      {props.placeholder && <option value="" disabled hidden>{props.placeholder}</option>}
      {props.children}
    </select>
  );
}
/* Custom (produto desktop, opção rica): shadcn <Select> (Radix, ARIA 1.2) com
   SelectTrigger reestilizado nos mesmos tokens do trigger nativo e
   SelectContent em surface-overlay + shadow-overlay. Regra G2: nunca os dois
   no mesmo formulário; no toque, o custom degrada para o nativo. */

7.7 Aplicação dos 7 testes (§1.12)#

  1. Container 3:1: trigger herda (4.74/4.50); chevron 4.66:1 (cinza-700 sobre branco, decorativo com redundância do próprio trigger). ✅ 2. Mecanismos: placeholder ≠ valor por COR (4.74 vs 13.86 — mesmo mecanismo do input); aberto/fechado pelo painel, não por tom. ✅ 3. Grayscale: chevron + moldura carregam o papel sem matiz. ✅ 4. Par: nativo × custom lado a lado no preview — triggers idênticos (é o objetivo: o usuário não deve notar a diferença fechado). ✅ 5. Regra do um: uma escolha por select (multi é proibido — G5). ✅ 6. Estado atual: valor selecionado sempre visível no trigger; placeholder denuncia o não-preenchido pela cor. ✅ 7. Polegar 360px: nativo abre o picker do OS — a melhor experiência de toque possível por definição; trigger 44px full-width. ✅

BancadaSEED DS v2 — Bloco 2 · Preview v0.13 (13/13)abrir em página própria ↗
Esc