Progress Bar

Pronto

Indicador linear de progresso (determinate ou indeterminate). Comunica % completo de uma operação que tem fim conhecido.

Usar quando

Upload com bytes restantes, importação em lote, processo multi-etapa, instalação. Onboarding com X de Y passos.

Não usar quando

Operação sem progresso medível (use Spinner). Loading curto (use Spinner). Stepper visual (use Stepper component).

Variantes

Determinate (com %)
Importando contatos
42%
Upload de PDF· 3.4 MB de 4.5 MB
75%
Concluído
100%
Indeterminate (sem progresso conhecido)
Sincronizando com tribunal...
Tamanhos
60%
60%
60%
Animado (anima de 0 a value)
Importando processos· 0 de 470
0%
Stacked (multi-segmentos)
Concluídos 45Em andamento 30Atrasados 15

Microinterações

MicrointeraçãoDisparada porComportamentoTiming
Width transitionvalue mudaWidth 0→target com easing500ms cubic-bezier(0.32,0.72,0,1)
Indeterminate slideindeterminate=truePílula brass desliza esquerda↔direita continuamente1500ms ease loop
Complete pulsevalue chega a 100Bar pisca verde 1x300ms ease
Reduced motionprefers-reduced-motionSem animação, só width finalinstant

Acessibilidade

Acessibilidade — checklist

ARIA esperado
  • role="progressbar"
  • aria-valuenow="42" aria-valuemin="0" aria-valuemax="100"
  • aria-label="Progresso: 42%"
  • Indeterminate: omit aria-valuenow
Notas
  • Always include text label visível (não confie só na barra).
  • Indeterminate sem aria-valuenow.
  • Em mobile: full-width, height 4-6px (não vire ruído).
  • Reduced-motion: pular animação de width.

Código

'use client';
import { T, RADIUS } from '@/lib/tokens';

export function ProgressBar({ value, label }: { value: number; label?: string }) {
  return (
    <div>
      {label && <p style={{ fontSize: 12, color: T.ink2, marginBottom: 4 }}>{label}</p>}
      <div role="progressbar" aria-valuenow={value} aria-valuemin={0} aria-valuemax={100}
        style={{ height: 6, background: T.surface3, borderRadius: 980, overflow: 'hidden' }}>
        <div style={{
          width: `${value}%`, height: '100%',
          background: T.ink, borderRadius: 980,
          transition: 'width 500ms cubic-bezier(0.32,0.72,0,1)',
        }} />
      </div>
    </div>
  );
}

Regras

Faça

  • Sempre exiba label textual + % numérico.
  • Determinate quando souber bytes/items totais.
  • Indeterminate só quando sincronização opaca.
  • Height 4-8px (não vire ruído visual).
  • Cor padrão ink; success quando complete.

Não faça

  • Não use indeterminate quando você SABE o progresso.
  • Não use cor brass como default (cor é sinal).
  • Não esconda label textual.
  • Não force animação rápida (>1500ms = lento, agressivo).
  • Não tenha múltiplas progressbars empilhadas (caos).

Tokens usados

TokenValorPapel
T.ink#0A0A0Acor da barra preenchida
T.surface3#F3F0E6background da track
T.brass#A47C2Bindeterminate slider
T.verde#2F6B3Ctone=success
RADIUS.pill980borderRadius