prompt inicial do projeto
This commit is contained in:
186
docs/04-DESIGN-SYSTEM-UX.md
Normal file
186
docs/04-DESIGN-SYSTEM-UX.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# 04 — Design system e UX
|
||||
|
||||
> Material 3 Expressive nos apps Compose; os mesmos tokens exportados como CSS custom properties nos webs. Uma linguagem visual, duas implementações.
|
||||
|
||||
## 1. Princípios
|
||||
|
||||
**1. O visitante está no sol, com pressa, e não vai instalar nada.** Cada tela dele tem uma ação. Tipografia grande, contraste alto, área de toque generosa.
|
||||
|
||||
**2. Espera precisa ter narração.** Um spinner de 35 segundos é indistinguível de um app quebrado. O visitante sempre lê o que está acontecendo: "Chamando o morador", "Tentando outros moradores", "Transferindo para a portaria".
|
||||
|
||||
**3. O morador decide em um toque.** A maior parte das decisões acontece na notificação, sem abrir o app.
|
||||
|
||||
**4. Densidade é para o admin, não para o campo.** Painel administrativo pode ter tabela densa e filtro complexo. Tela de portaria, nunca.
|
||||
|
||||
## 2. Tokens
|
||||
|
||||
Fonte única em `web/shared-ui/tokens.json`, gerada para os dois mundos no build:
|
||||
|
||||
```
|
||||
tokens.json ──► tokens.css (custom properties) → web/visitor, web/admin
|
||||
└─► Theme.kt (Material 3 Color) → apps Compose
|
||||
```
|
||||
|
||||
### Cor
|
||||
|
||||
```
|
||||
brand/primary #2563EB ações principais, foco
|
||||
brand/on-primary #FFFFFF
|
||||
semantic/success #15803D autorizado, dentro do geofence
|
||||
semantic/danger #B91C1C negado, expirado
|
||||
semantic/warning #B45309 fora do geofence, degradado
|
||||
semantic/info #0369A1 fila, estado transitório
|
||||
neutral/0..1000 escala de superfície e texto
|
||||
```
|
||||
|
||||
**Contraste mínimo 4.5:1 em tema claro e escuro** — verificado em CI, não no olho. A tela do visitante frequentemente é lida sob sol direto, onde o contraste efetivo despenca; por isso ela usa apenas os extremos da escala neutra, nunca tons médios.
|
||||
|
||||
Decisão nunca depende só de cor: autorizado é **verde + ícone de check + a palavra "Autorizado"**. Daltonismo e sol forte quebram codificação puramente cromática.
|
||||
|
||||
### Tipografia
|
||||
|
||||
`Inter` (web) / `Roboto Flex` (Compose), variáveis, com fallback de sistema.
|
||||
|
||||
| Papel | Tamanho | Uso |
|
||||
|---|---|---|
|
||||
| `display` | 32–40 | Estado da chamada na tela do visitante |
|
||||
| `headline` | 24–28 | Título de tela |
|
||||
| `title` | 18–20 | Nome do morador, unidade |
|
||||
| `body` | 16 | **Mínimo absoluto no fluxo do visitante** |
|
||||
| `label` | 14 | Rótulos de admin |
|
||||
| `caption` | 12 | Só metadados no admin. Nunca na portaria |
|
||||
|
||||
### Espaço, raio, elevação
|
||||
|
||||
Escala 4px (`0,1,2,3,4,6,8,12,16,24` × 4px). Raios: `sm 8` · `md 12` · `lg 16` · `full 999`. Elevação por sombra suave no web e `tonalElevation` no Compose — nunca sombra dura.
|
||||
|
||||
**Alvo de toque mínimo 48×48dp** em todo o fluxo do visitante, sem exceção. Mão trêmula, luva de entregador, celular escorregando.
|
||||
|
||||
## 3. Telas do visitante
|
||||
|
||||
Cinco telas, uma ação cada.
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Res. Bela Vista │ │ ┌───────────┐ │ │ Quem procura? │
|
||||
│ │ │ │ câmera │ │ │ │
|
||||
│ Este acesso │ │ │ frontal │ │ │ Seu nome │
|
||||
│ registra sua │ │ └───────────┘ │ │ [____________] │
|
||||
│ imagem e │ │ │ │ │
|
||||
│ localização │ │ Centralize o │ │ Bloco Unidade │
|
||||
│ para segurança. │ │ rosto │ │ [__] [_____] │
|
||||
│ │ │ │ │ │
|
||||
│ [Como funciona] │ │ ( ◎ tirar ) │ │ [ Continuar ] │
|
||||
│ [ Continuar ] │ │ │ │ │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
1. Aviso LGPD 3. Foto 4. Destino
|
||||
(2 = permissões, (após permissão) (busca cega)
|
||||
nativa do browser)
|
||||
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ ◐ │ │ ✓ │
|
||||
│ │ │ │
|
||||
│ Chamando o │ │ Autorizado │
|
||||
│ morador... │ │ │
|
||||
│ │ │ Mostre o código│
|
||||
│ ▓▓▓▓▓▓░░░░ 12s │ │ │
|
||||
│ │ │ 4 8 2 9 1 7 │
|
||||
│ Aguarde na │ │ │
|
||||
│ entrada │ │ válido 4:52 │
|
||||
│ │ │ │
|
||||
│ [ Cancelar ] │ │ [QR grande] │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
5. Espera narrada 6. Resultado
|
||||
```
|
||||
|
||||
**Tela 1** não é um modal de consentimento com checkbox. A base legal é legítimo interesse — o que se deve é **informar com clareza**, não colher aceite. Ver `06-LGPD-E-SEGURANCA.md`.
|
||||
|
||||
**Tela 5** troca o texto conforme o estado real (`TOCANDO` → `ESCALONADA` → `FILA_OPERADOR`), com barra de progresso ligada ao tempo restante. É a tela onde o produto se ganha ou se perde.
|
||||
|
||||
### Entrega — dois toques a menos
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ O que você faz │
|
||||
│ aqui hoje? │
|
||||
│ │
|
||||
│ ┌─────────────┐ │
|
||||
│ │ 📦 Entrega │ │ ← primeiro, é a maioria
|
||||
│ └─────────────┘ │
|
||||
│ ┌─────────────┐ │
|
||||
│ │ 👤 Visita │ │
|
||||
│ └─────────────┘ │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
Entrega pula a foto do rosto (fotografa o pacote) e vai direto ao destino. Meta de 10s do QR à resposta.
|
||||
|
||||
## 4. App do morador
|
||||
|
||||
### A notificação é a interface principal
|
||||
|
||||
```
|
||||
Android — CallStyle iOS — CallKit (tela cheia)
|
||||
┌──────────────────────────┐ ┌──────────────────────────┐
|
||||
│ 📦 Entrega · Apto 101-A │ │ Portaria │
|
||||
│ [foto do pacote] │ │ Entrega · Apto 101-A │
|
||||
│ │ │ │
|
||||
│ [Autorizar] [Chamar] │ │ [Recusar] [Aceitar] │
|
||||
└──────────────────────────┘ └──────────────────────────┘
|
||||
```
|
||||
|
||||
No iOS, chamada de **visita** usa PushKit + CallKit e ocupa a tela como ligação telefônica — o único caminho para tocar de verdade com o app fechado. **Entrega** usa notificação comum com ações, porque CallKit para uma entrega seria intrusivo e a Apple pode reprovar o uso.
|
||||
|
||||
### Tela de chamada
|
||||
|
||||
Vídeo do visitante em tela cheia; auto-preview pequeno no canto. Sobreposto: nome informado, unidade de destino, e **selo de geofence** — `✓ Na entrada` (verde) ou `⚠ A 340m da portaria` (âmbar). Esse selo é a informação de segurança mais útil da tela: alguém tentando entrar de longe é sinal claro.
|
||||
|
||||
Ações: `Autorizar` (verde, primária), `Negar` (contorno vermelho), `Mudo`, `Encerrar`.
|
||||
|
||||
## 5. App do operador
|
||||
|
||||
Densidade maior, feito para várias visitas em paralelo. Fila à esquerda com foto, unidade, tempo de espera e por que escalonou; contexto à direita antes de assumir — histórico da unidade, visitas recentes, regra de entrega. **O operador nunca atende sem contexto.**
|
||||
|
||||
## 6. Painel admin
|
||||
|
||||
Layout de aplicação: navegação lateral, conteúdo denso, filtros persistentes na URL.
|
||||
|
||||
A tela mais importante é a **auditoria de visitas**: tabela virtualizada, filtro por período/unidade/estado/tipo/geofence, e detalhe com foto, mapa (Leaflet) do ponto do visitante contra o raio do portão, timeline de tentativas (`visit_attempts`), e player do recado. Todo acesso a mídia gera linha em `audit_log` e usa URL assinada de 5 minutos.
|
||||
|
||||
## 7. Movimento
|
||||
|
||||
| Transição | Duração | Curva |
|
||||
|---|---|---|
|
||||
| Troca de tela do visitante | 280ms | `emphasized` |
|
||||
| Estado de chamada | 200ms | `standard` |
|
||||
| Entrada de card na fila | 180ms | `decelerate` |
|
||||
| Feedback de toque | 100ms | `linear` |
|
||||
|
||||
Compose usa `spring(dampingRatio = 0.8f)`; web usa `cubic-bezier(0.2, 0, 0, 1)`.
|
||||
|
||||
**`prefers-reduced-motion` respeitado em todos os webs** e `Settings.Global.ANIMATOR_DURATION_SCALE` no Android. Pulso e transição viram fade.
|
||||
|
||||
## 8. Acessibilidade
|
||||
|
||||
- Contraste ≥ 4.5:1 verificado em CI, claro e escuro
|
||||
- Alvos ≥ 48×48dp no fluxo do visitante
|
||||
- Rótulos semânticos em todo controle (`contentDescription` / `aria-label`)
|
||||
- Foco visível e ordem de tabulação correta no web
|
||||
- Suporte a fonte ampliada até 200% sem quebra de layout
|
||||
- Nenhuma informação transmitida só por cor
|
||||
- `lang="pt-BR"` e textos prontos para i18n desde o início (visitante estrangeiro é caso real)
|
||||
|
||||
## 9. Estados vazios, de erro e degradado
|
||||
|
||||
Todo erro exibido ao visitante vem do campo `detail` do `problem+json`, em português e acionável: **"Aproxime-se da entrada e tente novamente"**, nunca "OUTSIDE_GEOFENCE".
|
||||
|
||||
Modo degradado é comunicado, não escondido:
|
||||
|
||||
```
|
||||
┌──────────────────────────────┐
|
||||
│ ⚠ Vídeo indisponível │
|
||||
│ Continuando por áudio. │
|
||||
└──────────────────────────────┘
|
||||
```
|
||||
|
||||
Esconder a degradação faz o usuário achar que o produto está quebrado. Nomear a degradação faz o produto parecer resiliente — que é o que ele é.
|
||||
Reference in New Issue
Block a user