Pular para o conteúdo
Botão

Componentes

Botão

Pílula em todos os tamanhos — botão é a coisa que se TOCA, e no vocabulário de forma isso é rounded-full. Sete variantes, sete tamanhos, e cada um sobe um degrau em aparelho de dedo, sozinho.
src/components/ui/button.tsxcvaradix Slot (asChild)

Variantes

Sete, e a escolha entre elas é de HIERARQUIA, não de gosto: uma tela tem no máximo uma ação primária à vista.
default · ai · outline · secondary · ghost · destructive · link
VarianteQuandoReceita
defaultA ação principal da tela ou do bloco. Uma por cena.bg-primary text-primary-foreground hover:bg-brand-strong
aiAção que dispara o agente. Sempre com Sparkles.bg-ia text-ia-fg hover:bg-ia-strong focus-visible:ring-ia/50
outlineAção secundária, e o gatilho de algo que precisa parecer clicável (o Cancelar de um par, “Aplicar preço”).border bg-surface hover:bg-ink-50
secondaryAção neutra dentro de um bloco que já tem primária.bg-secondary text-secondary-foreground hover:bg-secondary/80
ghostAção de linha e de toolbar, onde a borda seria ruído. Cuidado: sem borda, ele pode não parecer clicável.hover:bg-ink-100
destructiveSó depois de confirmação. Nunca como gatilho direto de exclusão.bg-destructive text-destructive-fg hover:bg-destructive/90
linkNavegação escrita dentro de um texto ou no fim de uma lista.text-primary underline-offset-4 hover:underline

Armadilha já pagaGhost pode esconder o clique

O gatilho de “Aplicar R$ X” era ghost e lia como texto solto — o founder só descobriu que era clicável quando avisado. Virou outline: a borda cinza serve aos dois pesos, e o vermelho continua morando na tarja de confirmação, onde a consequência está escrita.

Tamanhos

Três de texto e três de ícone, mais o lg (que é o default com mais respiro lateral). Não existem tamanhos intermediários.
xs h-6 · sm h-8 (toolbar) · default h-10 (CTA) · lg h-10 com px-6
icon-xs 24 · icon-sm 32 · icon 40 — e o estado desabilitado
sizeAlturaNo dedoOnde
xs24px32pxChip de ação dentro de uma linha densa
sm32px40pxToolbar, linha de lista, rodapé de card
default40px44pxCTA da tela, ação de modal
lg40px44pxIgual ao default com px-6 — CTA que precisa de mais peso
icon-xs24px32pxÍcone solo em linha densa
icon-sm32px40pxKebab, fechar, ações de linha
icon40px44pxÍcone solo como ação principal

O padding lateral encolhe quando há ícone (has-[>svg]:px-3.5): ícone tem menos peso óptico que letra, e sem esse ajuste o botão com ícone fica visualmente mais largo que o irmão sem.

Anatomia e detalhes

A classe base do botão carrega sete decisões. Nenhuma é decorativa.
ui/button.tsx — a base do cva
inline-flex shrink-0 items-center justify-center gap-2
rounded-full text-sm font-medium whitespace-nowrap
transition-[color,background-color,border-color,box-shadow,opacity]
outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50
active:translate-y-px
disabled:pointer-events-none disabled:opacity-50
aria-invalid:border-destructive aria-invalid:ring-destructive/20
[&_svg]:pointer-events-none [&_svg]:shrink-0
[&_svg:not([class*='size-'])]:size-4
DetalhePor quê
transition-[…] em vez de transition-allA lista exclui o transform de propósito: o active:translate-y-px é resposta tátil imediata, e animá-lo atrasa o toque.
active:translate-y-px1px pra baixo no clique. É o único “peso” do botão — não há sombra pressionada.
shrink-0Botão dentro de flex não amassa antes do texto ao lado.
whitespace-nowrapRótulo de botão não quebra em duas linhas: se não cabe, o rótulo é longo demais.
[&_svg]:size-4 condicionalO ícone herda o tamanho do botão, e um className explícito no ícone vence.
disabled:opacity-50 + pointer-events-noneDesabilitado não recebe hover nem clique — e continua legível.
aria-invalidUm botão pode ser inválido (o submit de um form com erro): a borda acompanha.
PropTipoPadrãoO que faz
variant"default" | "ai" | "outline" | "secondary" | "ghost" | "destructive" | "link""default"A hierarquia da ação.
size"xs" | "sm" | "default" | "lg" | "icon" | "icon-xs" | "icon-sm""default"Cresce sozinho em pointer-coarse.
asChildbooleanfalseRenderiza o filho no lugar do <button> mantendo as classes — o jeito certo de fazer um <Link> parecer botão sem aninhar âncora dentro de botão.
…propsReact.ComponentProps<'button'>type, disabled, onClick, form, aria-* — tudo do elemento nativo.
link com cara de botão
<Button asChild>
  <Link href="/cadastros"><Users /> Ver cadastros</Link>
</Button>

Regras de uso

O que a revisão cobra num botão novo.

Sim

Ação que dispara o agente: roxa, com Sparkles, foco roxo.

Não

Ação de IA em verde confunde a convenção “verde = ação direta, roxo = agente”.

Sim

Uma primária por cena, e ela fica por último (mais perto do canto onde o olho termina). Verbo no infinitivo + objeto.

Não

Três primárias na mesma linha: nenhuma é primária. E “Exportar” sem ícone quebra o ritmo do cluster.

RegraDetalhe
Rótulo é verbo no infinitivo + objeto“Confirmar consulta”, “Enviar proposta”, “Tentar de novo”. Nunca “OK”, “Submeter”, “Confirmação De Consulta”.
Sentence caseSó a primeira letra maiúscula, sempre.
Ícone à esquerdaÍcone à direita só quando ele indica direção (a seta de “próximo”).
Botão só de ícone precisa de aria-labelCom o verbo e o objeto: “Excluir Marina Alvez”.
Estado de espera troca o rótulo“Salvando…”, “Excluindo…” — e o botão fica disabled enquanto isso.
Nunca destructive como primeiro cliqueExclusão passa por ConfirmStrip ou ConfirmDialog.
Cancelar antes de ConfirmarNo desktop, lado a lado à direita; no celular empilhados em flex-col-reverse (o principal embaixo, no polegar).