Input
Campo de texto de uma linha — estiliza qualquer tipo HTML nativo (text, email, password, search, file…).
Overview
Input é um <input> HTML nativo estilizado com tokens semânticos do
KDS. Aceita qualquer type válido — text, email, password,
number, search, tel, url, file — e adapta o teclado mobile
quando aplicável (ex: email mostra @ no teclado iOS).
Estados visuais não são controlados por className: focus vem do browser
(focus-visible), erro vem de aria-invalid (atributo ARIA), disabled
do prop nativo. Esse desacoplamento permite que FormControl
(@kalvner/kds/forms/form) ligue tudo automaticamente quando o input
está dentro de um FormField.
Anatomy
<Input
type="text"
├─ [data-slot="input"]
└─ [aria-invalid] (set by FormControl quando há erro)
/>Input é um único elemento — não é compound. Ícones laterais e botões
de limpar são composições externas (wrapper relative + absolute),
não props do próprio Input. Esse padrão simplifica a API e mantém o
componente substituível.
Usage
import { Input } from "@kalvner/kds/forms/input";
import { Label } from "@kalvner/kds/forms/label";
export function EmailField() {
return (
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="voce@exemplo.com" />
</div>
);
}Props
| Prop | Tipo | Default | Descrição |
|---|---|---|---|
type | 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url' | 'file' | ... | 'text' | Qualquer type HTML nativo. |
disabled | boolean | false | Bloqueia interação. |
readOnly | boolean | false | Permite seleção/cópia mas bloqueia edição. |
placeholder | string | — | Hint dentro do campo (não substitui Label). |
aria-invalid | boolean | — | Aciona ring vermelho — atribuído por FormControl. |
...rest | React.ComponentProps<'input'> | — | Tudo de <input>: id, value, onChange, pattern, min, max. |
Variants
States
Composition
When to use
- Qualquer entrada de uma linha — nome, e-mail, busca, slug, número.
- Em formulários, em filtros, em campos de busca em headers.
- Quando o valor cabe naturalmente em uma linha. Para textos longos
(mais de ~80 caracteres ou múltiplas frases), use
Textarea.
When not to use
- Para textos multi-linha —
Textarea. - Para escolha entre opções —
Select,RadioGroup, ou Combobox. - Para alternar uma flag —
SwitchouCheckbox. - Para upload com preview e drag-and-drop, considere uma composição
custom além do
type="file"nativo.
Best practices
- Sempre pareie com Label. Mesmo quando o desenho usa placeholder
como label flutuante, mantenha um
<Label>visível. Placeholder some quando o usuário começa a digitar — sem Label, ele esquece o que estava preenchendo. - Mensagem de erro abaixo do input, nunca dentro. Substituir o placeholder por mensagem de erro confunde — o usuário precisa do contexto do que digitou.
- Use
inputModequando otypenão cobre o teclado ideal. Ex:<Input type="text" inputMode="numeric" pattern="[0-9]*" />para CEP, código de verificação. - Evite reservar layout só com altura. O
Inputjá tem altura fixa (h-9), o que evita CLS. Mensagens de erro devem aparecer em slots reservados via gridgap-2no FormItem — ver [[ui/layout/cumulative-layout-shift]].
Accessibility
| Concern | Comportamento |
|---|---|
| Label | Sempre vincule via htmlFor + id ou wrap o Input dentro de <Label>. |
| Erro | Use aria-invalid="true" + aria-describedby apontando para a mensagem. FormControl faz isso automaticamente. |
| Disabled state | disabled é nativo — bloqueia foco e interação. Para apenas indicar visualmente, use aria-disabled. |
| Autocomplete | Defina autoComplete apropriado (email, current-password, cc-number) para password managers e iOS autofill. |
| Required | Use required HTML; combine com asterisco visual no Label. |
| Touch target | Altura padrão é 36px. Para mobile crítico, considere uma classe h-10 ou maior. |