Estados e feedback visual
Um botão que não muda de cara enquanto processa engana a pessoa a clicar de novo — e ela clica mesmo.
Um componente interativo nunca existe em um único estado visual. Ele muda de cara conforme a situação: parado, sob o mouse, focado pelo teclado, sendo clicado, desabilitado, carregando, com erro. Ignorar esses estados — codar só a aparência "parada" e nada mais — é a diferença entre um componente que parece confiável e um que parece quebrado, mesmo funcionando por baixo.
Os estados essenciais
:hover— sinaliza "isso é interativo" quando o mouse passa por cima, antes de qualquer clique.:focus-visible— sinaliza qual elemento está ativo pra navegação via teclado.:active— feedback imediato de que o clique foi registrado, no instante em que ele acontece.disabled— sinaliza que a ação não está disponível agora (não que ela nunca vai estar).- loading — sinaliza que uma ação está em andamento e ainda não terminou.
- erro — sinaliza que algo deu errado e, idealmente, o que fazer a respeito.
.botao {
background: #2563eb;
transition: background 150ms ease;
}
.botao:hover { background: #1d4ed8; }
.botao:active { background: #1e40af; transform: translateY(1px); }
.botao:disabled { opacity: 0.5; cursor: not-allowed; }
.botao:focus-visible { outline: 2px solid #93c5fd; outline-offset: 2px; }Por que o estado de loading não é opcional
Considere um botão "Salvar" que dispara uma requisição de rede e demora 800ms pra responder. Se o botão não muda de aparência nesse intervalo, a pessoa que clicou não tem nenhuma confirmação de que algo está acontecendo — o cérebro dela conclui "não funcionou" e ela clica de novo. Se a ação não for idempotente (por exemplo, criar um registro), isso pode disparar a mesma ação duas vezes.
.botao.is-loading {
pointer-events: none; /* impede novo clique enquanto processa */
opacity: 0.7;
}
.botao.is-loading::after {
content: "";
/* spinner ou indicador visual entra aqui */
}O ponto central: o feedback visual não é sobre deixar a interface "mais viva" — é sobre a pessoa nunca ficar na dúvida entre "não funcionou" e "está processando". Essas duas situações exigem reações opostas (clicar de novo vs. esperar), e sem feedback visual claro é impossível saber qual delas é a verdadeira.
Exercício
Um formulário de cadastro envia os dados ao clicar em "Criar conta", mas o botão continua com a mesma aparência durante os 2 segundos que o servidor demora pra responder. Que classe de estado está faltando, e o que ela deveria fazer visualmente e funcionalmente?