Forms
Checkbox
Controle de seleção binária ou indeterminada — usado em formulários a serem submetidos.
Overview
Checkbox é o Checkbox.Root do Radix com a indicator do KDS. Suporta
três estados — unchecked, checked, indeterminate — via o prop
checked que aceita boolean | "indeterminate". O ícone interno é o
CheckIcon da lucide-react.
A diferença entre Checkbox e Switch é semântica, não
visual: Checkbox é parte de uma form que será submetida (o usuário
marca várias opções e depois clica Salvar). Switch tem efeito
imediato (a configuração é persistida assim que o switch muda). Use o
componente certo — confunde menos o usuário e simplifica o backend.
Preview
Checkbox com Label inline.
Anatomy
<Checkbox id="...">
├─ [data-slot="checkbox"]
├─ [data-state="checked" | "unchecked" | "indeterminate"]
└─ <CheckIcon /> (renderizado quando checked ou indeterminate)
</Checkbox>Usage
import { Checkbox } from "@kalvner/kds/forms/checkbox";
import { Label } from "@kalvner/kds/forms/label";
export function TermsField() {
return (
<div className="flex items-center gap-2">
<Checkbox id="terms" />
<Label htmlFor="terms">Aceito os termos</Label>
</div>
);
}Props
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
checked | boolean | 'indeterminate' | — | Controlado: estado atual. |
defaultChecked | boolean | 'indeterminate' | — | Não controlado: estado inicial. |
onCheckedChange | (c: boolean | 'indeterminate') => void | — | Callback de mudança. |
disabled | boolean | false | Bloqueia interação. |
required | boolean | false | Marca como obrigatório. |
aria-invalid | boolean | — | Aciona ring vermelho. |
id | string | — | Vincula ao Label via htmlFor. |
States
Unchecked
Estado padrão.
Checked
Background primary, ícone branco.
Indeterminate
Para árvores com filhos parcialmente marcados.
Disabled
Opacidade reduzida, sem pointer events.
Error
Ring vermelho via aria-invalid.
Aceite os termos para continuar.
Composition
Terms of service
Checkbox + texto com link inline.
Multi-select group
Múltipla seleção em um fieldset.
When to use
- Aceite de termos antes de submeter.
- Múltipla seleção dentro de um formulário (notificações, permissões).
- Linhas de tabela selecionáveis em ações em massa.
- Estado tri-state (parcialmente selecionado) em árvores.
When not to use
- Para alternar configuração de efeito imediato — use
Switch. - Para escolher uma única opção entre várias — use
RadioGroup. - Para ações de uma vez (ex: botão submeter), Checkbox não substitui Button.
Best practices
- Tap target inclui o Label. Em mobile, o Checkbox em si é apenas
16px — clicar só nele é difícil. O
<Label>aumenta a área hit porque clicar nele dispara o controle. - Marque obrigatório quando crítico. Asterisco vermelho no Label
requiredno Checkbox. Validação Zod com.refine(v => v === true).
- Não use Checkbox para "ligar/desligar" feature flags. Esse é papel de Switch — tem efeito imediato.
- Indeterminate é um estado, não uma ação. O usuário não clica para "ir pra indeterminate"; ela é setada programaticamente quando só alguns dos children estão marcados.
Accessibility
| Concern | Comportamento |
|---|---|
| Roles | Radix expõe role="checkbox" + aria-checked (true, false, mixed). |
| Keyboard | Tab foca; Space alterna. |
| Label | Vincule via htmlFor + id. Clicar no Label alterna o checkbox. |
| Group | Para grupos relacionados, envolva em <fieldset> + <legend>. |
| Erro | aria-invalid aplica ring vermelho. Mensagem associada via aria-describedby. |
| Touch target | Container clicável (Label + Checkbox) deve atingir 44×44 CSS px. |
Related
Switch— efeito imediato.RadioGroup— escolha mutuamente exclusiva.Form— wrappers de RHF + Zod.