Chip
ProntoTag clicável e/ou removível. Diferente de Badge: chip é interativo (click filtra, X remove).
Usar quando
Tags em processo/contato, filtros aplicados (com X para remover), seleção múltipla compacta, tokens de busca, áreas vinculadas.
Não usar quando
Selo de status estático (use Badge). Botão de ação principal (use Button). Texto longo (use Card).
Variantes
Microinterações
| Microinteração | Disparada por | Comportamento | Timing |
|---|---|---|---|
| Mount in | chip aparece (após filter add) | opacity 0→1 + scale 0.85→1 | 200ms cubic-bezier(0.32,0.72,0,1) |
| Remove out | click X | scale 1→0.85 + opacity 1→0 + width collapse | 200ms ease |
| Hover | mouseenter | Background +5% saturated, X aparece destacado | 150ms ease |
| Toggle select | click no chip | Borda + background invertem (ink ↔ surface) | 150ms ease |
| Tap haptic (mobile) | tap | Vibration 10ms + visual scale 0.95 | 100ms |
Acessibilidade
Acessibilidade — checklist
Teclado
| Tab | Foco no chip |
| Enter / Space | Ativa (toggle ou click) |
| Backspace / Delete | Remove (se removível) |
| Tab → X | Foco no X |
ARIA esperado
- Chip selecionável: <button aria-pressed="true|false" />
- Chip removível: <span> + <button aria-label="Remover X" />
- Action chip: <button> normal
- Em filtros: aria-label="Filtro: <X> aplicado, clique para remover"
Notas
- Backspace/Delete deve remover quando focado.
- Em filtros aplicados, anuncie via aria-live="polite" qual foi removido.
- Combine bem com Tags Input — chips são output dele.
- Cor é sinal, mas sempre acompanha de texto.
Código
'use client';
import { X } from 'lucide-react';
import { T, RADIUS, MOTION, transition } from '@/lib/tokens';
interface ChipProps {
children: React.ReactNode;
removable?: boolean;
selected?: boolean;
onClick?: () => void;
onRemove?: () => void;
color?: 'neutral' | 'brass' | 'selo' | 'verde' | 'carmim';
}
export function Chip({ children, removable, selected, onClick, onRemove, color = 'neutral' }: ChipProps) {
return (
<span
onClick={onClick}
style={{
display: 'inline-flex', alignItems: 'center', gap: 4,
background: selected ? T.ink : T[`${color}Tint`] ?? T.surface3,
color: selected ? T.surface : T[color] ?? T.ink2,
border: `1px solid ${selected ? T.ink : T.border}`,
borderRadius: RADIUS.pill,
padding: '4px 12px',
fontSize: 12,
fontWeight: 500,
cursor: onClick ? 'pointer' : 'default',
transition: transition('all', 'fast'),
}}
>
{children}
{removable && (
<button onClick={(e) => { e.stopPropagation(); onRemove?.(); }} aria-label="Remover">
<X size={11} />
</button>
)}
</span>
);
}Regras
Faça
- ✓Use removável (X) em filtros aplicados.
- ✓Use selecionável (toggle) em multi-seleção compacta.
- ✓Cor por sinal — brass para destaque, neutral default.
- ✓Mount/remove com animação (300ms max).
- ✓Combine bem com Tags Input (chips são saída dele).
Não faça
- ✗Não use chip como botão primário de página (use Button).
- ✗Não use texto longo (>20 caracteres — use Card ou paragraph).
- ✗Não force cor brass em chips genéricos (brass é sinal).
- ✗Não esconda o X (sempre visível em hover, opcional em rest).
- ✗Não esqueça keyboard delete.
Tokens usados
| Token | Valor | Papel |
|---|---|---|
T.ink | #0A0A0A | background quando selected |
T.surface3 | #F3F0E6 | background neutral |
T.brassTint | rgba(164,124,43,0.08) | background brass |
T.border | #E3DFD2 | borda default |
RADIUS.pill | 980 | forma |
MOTION.fast | 150ms | transition |