Spinner
ProntoLoader circular indeterminate. Indica que algo está acontecendo sem comunicar progresso medível.
Usar quando
Loading curto (300ms-3s). Async sem progresso conhecido (fetch, save). Inline em botão durante mutation.
Não usar quando
Loading com progresso medível (use Progress Bar). Loading inicial de listas (use Skeleton). Loading >5s (use mensagem ou estimativa).
Variantes
Microinterações
| Microinteração | Disparada por | Comportamento | Timing |
|---|---|---|---|
| Continuous spin | mount | rotate 0deg → 360deg infinite linear | 600ms linear loop |
| Reduced motion | prefers-reduced-motion | Pulse opacity 1→0.5→1 | 1500ms ease loop |
| Mount fade | render | opacity 0→1 | 150ms ease |
Acessibilidade
Acessibilidade — checklist
ARIA esperado
- role="status" + aria-label="Carregando"
- aria-live="polite" no container pai
- aria-hidden="true" se decorativo (label visível ao lado)
Notas
- Sempre tenha texto adjacente ("Carregando...") — spinner sozinho não fala.
- Em reduced-motion: pulse em vez de spin.
- Tamanho proporcional ao contexto (xs em chip, lg em página inteira).
- Cor: ink default, brass para destaque, surface em fundo escuro.
Código
'use client';
import { T } from '@/lib/tokens';
interface SpinnerProps { size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; color?: 'ink' | 'brass' | 'surface' | 'muted' }
const SIZES = { xs: 12, sm: 14, md: 18, lg: 28, xl: 40 };
export function Spinner({ size = 'md', color = 'ink' }: SpinnerProps) {
const px = SIZES[size];
return (
<span role="status" aria-label="Carregando" style={{
display: 'inline-block', width: px, height: px,
border: `${px > 20 ? 2.5 : 1.5}px solid rgba(0,0,0,0.1)`,
borderTopColor: T[color] ?? T.ink, borderRadius: '50%',
animation: 'lb-spinner-spin 600ms linear infinite',
}} />
);
}Regras
Faça
- ✓Spin contínuo 600ms linear loop.
- ✓Ring com border-top-color (apenas 1/4 do círculo colorido).
- ✓Sempre tenha texto label adjacente.
- ✓Reduced-motion: pulse em vez de spin.
- ✓Tamanho proporcional (xs em badge, xl em loading de página).
Não faça
- ✗Não use spin para loading >5s (frustra — use estimativa).
- ✗Não tenha múltiplos spinners visíveis ao mesmo tempo.
- ✗Não use cor decorativa (ink padrão, brass se contexto editorial).
- ✗Não esqueça aria-label.
- ✗Não tenha spinner sem context (sempre acompanhe de texto).
Tokens usados
| Token | Valor | Papel |
|---|---|---|
T.ink | #0A0A0A | cor default do ring colorido |
T.brass | #A47C2B | variante brass |
T.surface | #FFFFFF | spinner sobre fundo escuro |
T.inkMuted | #5A5A5E | variante muted |