KDS
Forms

Button

Click target primário — seis variantes visuais e quatro presets de tamanho.

Overview

Button é o click target mais elementar do KDS. Toda interação que dispara uma ação — submit de formulário, abrir um Dialog, confirmar um item da lista — passa por ele. Internamente é um <button> estilizado por class-variance-authority, com seis variantes visuais (default, destructive, outline, secondary, ghost, link) e oito presets de tamanho — quatro textuais (xs a lg) e quatro icon-only para botões quadrados em toolbars e linhas de tabela.

A escolha da variante é uma escolha de hierarquia visual antes de ser estética. Cada superfície carrega no máximo uma ação primária — o default. Tudo mais é secundário (outline, secondary, ghost) ou intencionalmente cauteloso (destructive).

Preview
Variante padrão com tamanho default.

Anatomy

<Button variant="default" size="default">
  ├─ [data-slot="button"]
  ├─ [data-variant="default"]
  ├─ [data-size="default"]
  └─ children (texto + ícones lucide opcionais)
</Button>

Atributos de dado (data-variant, data-size) estão expostos para CSS custom downstream sem hack de classes. asChild permite renderizar como um <a>, <Link> do Next, ou qualquer outro elemento — útil para que um link se comporte visualmente como botão sem perder semântica de navegação.

Usage

import { Button } from "@kalvner/kds/forms/button";

export function Example() {
  return <Button onClick={() => save()}>Salvar</Button>;
}
Default
O caminho feliz — texto, click handler.

Props

PropTipoDefaultDescrição
variant'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link''default'Variante visual.
size'default' | 'xs' | 'sm' | 'lg' | 'icon' | 'icon-xs' | 'icon-sm' | 'icon-lg''default'Altura + padding.
asChildbooleanfalseRenderiza o único filho em vez de um <button> (Radix Slot).
disabledbooleanfalseEstado nativo disabled do botão.
...restReact.ComponentProps<'button'>Tudo que <button> aceita (type, onClick, aria-*).

Variants

Default
Ação primária — uma por superfície.
Destructive
Ações irreversíveis — sempre confirme antes.
Outline
Secundário com peso visual médio.
Secondary
Suporte sólido — fill suave.
Ghost
Terciário — só hover state.
Link
Texto sublinhado — semântica de botão.

States

Disabled
Sem pointer events, opacidade reduzida.
Loading
Spinner + texto — desabilitado para evitar duplo envio.
Leading icon
Ícone reforça a ação.
Trailing icon
Indica continuidade — Próximo, Avançar.
Icon-only
Toolbar e ações de tabela. Inclua aria-label.
Full width
Útil em mobile e dialogs estreitos.

Composition

Form footer
Cancelar à esquerda, ação primária à direita — convenção ocidental.
Destructive confirmation
Saída segura à esquerda, destrutivo à direita.

Para ações de alto impacto — Excluir conta, Apagar workspace — não basta um destructive: peça que o usuário digite o nome do recurso em um Dialog antes de habilitar o botão. O Dialog ainda não está no KDS, mas o padrão de stakes (low / high) ditará o gating.

When to use

  • Disparar uma ação imediata (submit, salvar, abrir, navegar via JS).
  • Apresentar uma escolha primária + alternativas (form footer, dialog).
  • Comandos em toolbars — use size="icon" com aria-label claro.

When not to use

  • Para navegação pura entre páginas, prefira <a> ou <Link> — só troque para <Button asChild> se precisar do estilo de botão na navegação.
  • Para alternar uma configuração de efeito imediato (notificações on/off), use Switch.
  • Para selecionar entre múltiplos valores, use RadioGroup ou Select.

Best practices

  • Uma ação primária por superfície. Se você está com dois botões default no mesmo card, um deles deveria ser outline ou ghost.
  • Ordem ocidental nos rodapés. Cancelar à esquerda, confirmar à direita. O olho percorre da esquerda para a direita até o commit.
  • Destrutivo nunca é a escolha mais fácil. Posicione longe da ação primária, sempre confirme em um Dialog para itens críticos.
  • Loading desabilita. Trocar texto por spinner sem desabilitar abre brecha para duplo envio.
  • Não use cores fora do token. Botões coloridos out-of-token quebram a hierarquia. Cite [[ui/colors/pure-white-black-avoid]] — nunca um bg-white puro mesmo em contraste.

Accessibility

ConcernComportamento
Roles<button> nativo. Quando asChild é usado, garanta que o componente filho gere semântica equivalente (<a> ganha role="link" automaticamente).
KeyboardTab navega; Enter e Space ativam. disabled remove do tab order.
Focus ringfocus-visible:ring-[3px] ring-ring/50 — visível só no foco via teclado.
Screen readerAnunciado como "button" + texto interno. Para icon-only, exija aria-label.
Disabled vs aria-disabledUse disabled (HTML nativo) — bloqueia clique e keyboard. aria-disabled apenas anuncia mas não bloqueia.
Loading stateCombine disabled + spinner. Considere aria-live="polite" no container quando o estado mudar dinamicamente.
Touch targetdefault é 36px de altura — borderline para mobile. Em telas táteis, prefira lg (40px) ou wraps com padding.
  • Form — wrappers de react-hook-form que combinam Button no rodapé.
  • Input — frequente companheiro nos formulários.
  • Switch — para efeito imediato em vez de submit.

On this page