up-cc 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/up-analista-codigo.md +446 -0
- package/agents/up-auditor-modernidade.md +378 -0
- package/agents/up-auditor-performance.md +426 -0
- package/agents/up-auditor-ux.md +396 -0
- package/agents/up-consolidador-ideias.md +493 -0
- package/agents/up-pesquisador-mercado.md +350 -0
- package/agents/up-sintetizador-melhorias.md +407 -0
- package/bin/lib/core.cjs +3 -3
- package/bin/up-tools.cjs +490 -23
- package/commands/ajuda.md +19 -0
- package/commands/ideias.md +49 -0
- package/commands/melhorias.md +45 -0
- package/package.json +1 -1
- package/references/audit-modernidade.md +1617 -0
- package/references/audit-performance.md +478 -0
- package/references/audit-ux.md +1544 -0
- package/templates/report.md +177 -0
- package/templates/suggestion.md +152 -0
- package/workflows/ideias.md +381 -0
- package/workflows/melhorias.md +409 -0
|
@@ -0,0 +1,1544 @@
|
|
|
1
|
+
<overview>
|
|
2
|
+
Referencia de heuristicas de UX para analise estatica de codigo. Este documento traduz as heuristicas de usabilidade de Nielsen em sinais detectaveis via analise de arquivos de codigo (CSS, SCSS, componentes TSX/JSX/Vue/Svelte, HTML, templates).
|
|
3
|
+
|
|
4
|
+
**Abordagem:** O agente auditor de UX NAO tem acesso visual a interface renderizada. Ele le arquivos fonte e detecta padroes que indicam problemas de usabilidade com alta probabilidade. Cada heuristica neste catalogo foi traduzida para um sinal de codigo verificavel via grep, analise de AST simplificada, ou heuristica de leitura de arquivo.
|
|
5
|
+
|
|
6
|
+
**Limitacoes importantes:**
|
|
7
|
+
- Analise estatica de UX e inerentemente imprecisa -- sinais de codigo sao proxies, nao provas definitivas
|
|
8
|
+
- False positives sao esperados; o campo "Limitacao" de cada heuristica documenta cenarios conhecidos
|
|
9
|
+
- Este reference complementa (nao substitui) testes com usuarios reais
|
|
10
|
+
- Heuristicas puramente visuais (harmonia de cores, equilibrio de layout, estetica subjetiva) estao excluidas por serem indetectaveis via codigo
|
|
11
|
+
|
|
12
|
+
**Como o agente usa este reference:**
|
|
13
|
+
1. Detecta a stack do projeto (secao `stack_detection`)
|
|
14
|
+
2. Ajusta sinais de deteccao conforme framework identificado
|
|
15
|
+
3. Percorre cada categoria aplicando as heuristicas relevantes
|
|
16
|
+
4. Produz sugestoes no formato do template `suggestion.md` com Dimensao = "UX"
|
|
17
|
+
</overview>
|
|
18
|
+
|
|
19
|
+
<stack_detection>
|
|
20
|
+
## Deteccao de Stack
|
|
21
|
+
|
|
22
|
+
Antes de aplicar heuristicas, o agente deve detectar a stack do projeto para ajustar sinais de deteccao. A ordem de deteccao e: CSS framework, component framework, UI library, form library.
|
|
23
|
+
|
|
24
|
+
### CSS Framework
|
|
25
|
+
|
|
26
|
+
| Framework | Sinal de deteccao | Ajuste nas heuristicas |
|
|
27
|
+
|-----------|-------------------|----------------------|
|
|
28
|
+
| Tailwind CSS | `tailwind.config.js/ts` na raiz, ou `@tailwind` em CSS, ou classes `sm:`, `md:`, `lg:` em componentes | Responsividade: procurar classes `sm:`, `md:`, `lg:` em vez de `@media`. Consistencia: procurar `bg-[#xxx]` e `text-[#xxx]` (cores arbitrarias) em vez de `color: #xxx`. Espacamento: verificar mistura inconsistente de classes de spacing |
|
|
29
|
+
| Bootstrap | `bootstrap` em package.json, ou `import 'bootstrap'`, ou classes `col-`, `row`, `container` | Responsividade: procurar grid classes (`col-sm-`, `col-md-`). Consistencia: verificar se usa variaveis Bootstrap (`--bs-*`) vs valores hardcoded |
|
|
30
|
+
| CSS Modules | Arquivos `*.module.css` ou `*.module.scss` | Consistencia: escopo local reduz problemas globais, focar em valores hardcoded dentro dos modulos |
|
|
31
|
+
| Styled Components | `import styled from 'styled-components'` ou `import { css } from 'styled-components'` | Consistencia: verificar se usa theme provider vs valores hardcoded nos templates |
|
|
32
|
+
| CSS puro | Nenhum dos acima detectado, arquivos `.css` ou `.scss` presentes | Aplicar todas as heuristicas CSS padrao (media queries, variaveis CSS, etc.) |
|
|
33
|
+
|
|
34
|
+
### Component Framework
|
|
35
|
+
|
|
36
|
+
| Framework | Sinal de deteccao | Ajuste nas heuristicas |
|
|
37
|
+
|-----------|-------------------|----------------------|
|
|
38
|
+
| React | Arquivos `.tsx` ou `.jsx`, ou `react` em package.json | Formularios: procurar `onChange`, `onSubmit`, `useState`. Feedback: procurar `useState` para loading/error states. Erros: procurar `ErrorBoundary` |
|
|
39
|
+
| Vue | Arquivos `.vue`, ou `vue` em package.json | Formularios: procurar `v-model`, `@submit`. Feedback: procurar `ref()` ou `reactive()` para loading states. Estrutura: verificar `<template>`, `<script>`, `<style>` |
|
|
40
|
+
| Svelte | Arquivos `.svelte`, ou `svelte` em package.json | Formularios: procurar `bind:value`, `on:submit`. Feedback: procurar `{#if loading}` patterns |
|
|
41
|
+
| Next.js | `next` em package.json, pasta `app/` ou `pages/` | Navegacao: verificar `not-found.tsx`, `error.tsx`, `loading.tsx`. Metadata: verificar `metadata` export ou `<Head>` |
|
|
42
|
+
| Vanilla HTML | Arquivos `.html` sem framework detectado | Aplicar heuristicas HTML puro (labels, form validation attributes, semantic HTML) |
|
|
43
|
+
|
|
44
|
+
### UI Library
|
|
45
|
+
|
|
46
|
+
| Library | Sinal de deteccao | Ajuste nas heuristicas |
|
|
47
|
+
|---------|-------------------|----------------------|
|
|
48
|
+
| shadcn/ui | `@/components/ui/` imports, ou `components.json` com `"style"` | Feedback: Toast, Alert, Dialog ja disponiveis -- verificar se sao usados apos acoes. Formularios: Form components disponiveis -- verificar uso |
|
|
49
|
+
| Radix UI | `@radix-ui/` em package.json ou imports | Feedback: Dialog, Toast primitives disponiveis. Acessibilidade: Radix e acessivel por padrao, focar em uso correto |
|
|
50
|
+
| Material UI | `@mui/material` em package.json | Feedback: Snackbar, Alert, Dialog disponiveis. Consistencia: verificar se usa theme vs estilos inline |
|
|
51
|
+
| Ant Design | `antd` em package.json | Feedback: message, notification, Modal disponiveis. Formularios: Form component com validacao integrada |
|
|
52
|
+
| Chakra UI | `@chakra-ui/react` em package.json | Feedback: useToast, Alert disponiveis. Consistencia: verificar se usa theme tokens |
|
|
53
|
+
| Nenhuma | Nenhuma UI library detectada | Verificar implementacao manual de feedback (alert, modal customizado). Maior probabilidade de problemas de consistencia |
|
|
54
|
+
|
|
55
|
+
### Form Library
|
|
56
|
+
|
|
57
|
+
| Library | Sinal de deteccao | Ajuste nas heuristicas |
|
|
58
|
+
|---------|-------------------|----------------------|
|
|
59
|
+
| React Hook Form | `react-hook-form` em package.json ou `useForm` import | Formularios: validacao integrada, verificar se `errors` object e exibido ao usuario |
|
|
60
|
+
| Formik | `formik` em package.json ou `useFormik`/`<Formik>` | Formularios: validacao integrada, verificar se `errors`/`touched` sao usados no JSX |
|
|
61
|
+
| Zod + form | `zod` em package.json com form library | Formularios: schema validation presente, verificar se erros do schema sao exibidos |
|
|
62
|
+
| VeeValidate | `vee-validate` em package.json (Vue) | Formularios: validacao integrada para Vue |
|
|
63
|
+
| Nenhuma | Nenhuma form library detectada | Maior probabilidade de formularios sem validacao client-side, verificar validacao manual |
|
|
64
|
+
|
|
65
|
+
</stack_detection>
|
|
66
|
+
|
|
67
|
+
<category name="feedback-status">
|
|
68
|
+
## Feedback e Visibilidade do Status
|
|
69
|
+
|
|
70
|
+
Heuristicas relacionadas a Heuristica #1 de Nielsen: "Visibilidade do status do sistema". O sistema deve manter o usuario informado sobre o que esta acontecendo por meio de feedback apropriado em tempo razoavel.
|
|
71
|
+
|
|
72
|
+
Em analise estatica, detectamos a ausencia de feedback verificando se handlers de acoes e operacoes assincronas incluem gerenciamento de estados visuais (loading, sucesso, erro).
|
|
73
|
+
|
|
74
|
+
### LOADING-SUBMIT
|
|
75
|
+
|
|
76
|
+
**Heuristica de Nielsen:** #1 - Visibilidade do status do sistema
|
|
77
|
+
**Frameworks:** React, Vue, Svelte
|
|
78
|
+
**Impacto tipico:** M
|
|
79
|
+
**Sinal de deteccao:**
|
|
80
|
+
```bash
|
|
81
|
+
# Procurar handlers de submit sem estado de loading
|
|
82
|
+
# Em React: onSubmit handler que nao referencia isLoading/isPending/isSubmitting
|
|
83
|
+
grep -rn "onSubmit" src/ --include="*.tsx" --include="*.jsx"
|
|
84
|
+
# Verificar se o mesmo componente tem useState com loading/pending/submitting
|
|
85
|
+
# Se handler existe mas nao ha estado de loading no componente -> problema
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Problema em codigo:**
|
|
89
|
+
```tsx
|
|
90
|
+
// Usuario clica "Salvar" e nao sabe se a acao esta sendo processada
|
|
91
|
+
function handleSubmit(data: FormData) {
|
|
92
|
+
await api.saveProfile(data); // Nenhum feedback visual durante a espera
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return <button onClick={handleSubmit}>Salvar</button>;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Solucao:**
|
|
99
|
+
```tsx
|
|
100
|
+
// Usuario ve indicacao de que a acao esta sendo processada
|
|
101
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
102
|
+
|
|
103
|
+
async function handleSubmit(data: FormData) {
|
|
104
|
+
setIsLoading(true);
|
|
105
|
+
try {
|
|
106
|
+
await api.saveProfile(data);
|
|
107
|
+
toast.success("Perfil salvo com sucesso");
|
|
108
|
+
} finally {
|
|
109
|
+
setIsLoading(false);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return <button onClick={handleSubmit} disabled={isLoading}>
|
|
114
|
+
{isLoading ? "Salvando..." : "Salvar"}
|
|
115
|
+
</button>;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Limitacao:** Componentes que usam libraries como React Query/SWR gerenciam loading automaticamente via hooks (useMutation). Verificar se o componente importa essas libraries antes de reportar.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### LOADING-FETCH
|
|
123
|
+
|
|
124
|
+
**Heuristica de Nielsen:** #1 - Visibilidade do status do sistema
|
|
125
|
+
**Frameworks:** React, Vue, Svelte
|
|
126
|
+
**Impacto tipico:** M
|
|
127
|
+
**Sinal de deteccao:**
|
|
128
|
+
```bash
|
|
129
|
+
# Procurar useEffect com fetch/axios sem estado de loading
|
|
130
|
+
grep -rn "useEffect" src/ --include="*.tsx" --include="*.jsx"
|
|
131
|
+
# No mesmo componente, verificar se existe useState para loading
|
|
132
|
+
# Alternativamente: procurar fetch/axios.get sem isLoading associado
|
|
133
|
+
grep -rn "fetch\|axios\.get\|axios\.post" src/ --include="*.tsx" --include="*.jsx"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Problema em codigo:**
|
|
137
|
+
```tsx
|
|
138
|
+
// Pagina fica em branco enquanto dados carregam -- usuario nao sabe se esta funcionando
|
|
139
|
+
const [users, setUsers] = useState([]);
|
|
140
|
+
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
fetch("/api/users").then(r => r.json()).then(setUsers);
|
|
143
|
+
}, []);
|
|
144
|
+
|
|
145
|
+
return <UserList users={users} />;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Solucao:**
|
|
149
|
+
```tsx
|
|
150
|
+
// Usuario ve skeleton/spinner enquanto dados carregam
|
|
151
|
+
const [users, setUsers] = useState([]);
|
|
152
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
153
|
+
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
fetch("/api/users")
|
|
156
|
+
.then(r => r.json())
|
|
157
|
+
.then(setUsers)
|
|
158
|
+
.finally(() => setIsLoading(false));
|
|
159
|
+
}, []);
|
|
160
|
+
|
|
161
|
+
if (isLoading) return <UserListSkeleton />;
|
|
162
|
+
return <UserList users={users} />;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Limitacao:** Componentes usando React Query (`useQuery`), SWR (`useSWR`), ou Next.js `loading.tsx` gerenciam loading automaticamente. Verificar imports antes de reportar.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### DESTRUTIVA-SEM-CONFIRMACAO
|
|
170
|
+
|
|
171
|
+
**Heuristica de Nielsen:** #1 - Visibilidade do status do sistema / #5 - Prevencao de erros
|
|
172
|
+
**Frameworks:** All
|
|
173
|
+
**Impacto tipico:** G
|
|
174
|
+
**Sinal de deteccao:**
|
|
175
|
+
```bash
|
|
176
|
+
# Procurar handlers de delete/remove sem dialog/confirm/modal
|
|
177
|
+
grep -rn "delete\|remove\|destroy\|reset" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.svelte"
|
|
178
|
+
# No mesmo componente ou handler, verificar se existe confirm(), Dialog, Modal, AlertDialog
|
|
179
|
+
# Se handler de delecao existe sem confirmacao -> problema
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Problema em codigo:**
|
|
183
|
+
```tsx
|
|
184
|
+
// Clicar "Excluir" remove o item imediatamente sem confirmacao
|
|
185
|
+
function handleDelete(id: string) {
|
|
186
|
+
await api.deleteProject(id);
|
|
187
|
+
router.refresh();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return <button onClick={() => handleDelete(project.id)}>Excluir</button>;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Solucao:**
|
|
194
|
+
```tsx
|
|
195
|
+
// Usuario confirma antes de acao irreversivel
|
|
196
|
+
const [showConfirm, setShowConfirm] = useState(false);
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<>
|
|
200
|
+
<button onClick={() => setShowConfirm(true)}>Excluir</button>
|
|
201
|
+
<AlertDialog open={showConfirm} onOpenChange={setShowConfirm}>
|
|
202
|
+
<AlertDialogContent>
|
|
203
|
+
<AlertDialogTitle>Excluir projeto?</AlertDialogTitle>
|
|
204
|
+
<AlertDialogDescription>
|
|
205
|
+
Esta acao nao pode ser desfeita.
|
|
206
|
+
</AlertDialogDescription>
|
|
207
|
+
<AlertDialogAction onClick={() => api.deleteProject(project.id)}>
|
|
208
|
+
Confirmar exclusao
|
|
209
|
+
</AlertDialogAction>
|
|
210
|
+
<AlertDialogCancel>Cancelar</AlertDialogCancel>
|
|
211
|
+
</AlertDialogContent>
|
|
212
|
+
</AlertDialog>
|
|
213
|
+
</>
|
|
214
|
+
);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Limitacao:** Nem toda acao "delete" e destrutiva para o usuario (ex: remover item de carrinho de compras e reversivel). Avaliar contexto do handler. Soft deletes com undo sao alternativa valida.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### SUCESSO-SEM-FEEDBACK
|
|
222
|
+
|
|
223
|
+
**Heuristica de Nielsen:** #1 - Visibilidade do status do sistema
|
|
224
|
+
**Frameworks:** All
|
|
225
|
+
**Impacto tipico:** M
|
|
226
|
+
**Sinal de deteccao:**
|
|
227
|
+
```bash
|
|
228
|
+
# Procurar handlers de API (POST/PUT/PATCH) que nao mostram feedback de sucesso
|
|
229
|
+
grep -rn "\.post\|\.put\|\.patch\|fetch.*POST\|fetch.*PUT" src/ --include="*.tsx" --include="*.jsx"
|
|
230
|
+
# No mesmo handler, verificar se existe toast/alert/message/notification apos chamada
|
|
231
|
+
# Se chamada API de mutacao existe sem feedback de sucesso ao usuario -> problema
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Problema em codigo:**
|
|
235
|
+
```tsx
|
|
236
|
+
// Usuario salva configuracoes mas nao recebe confirmacao de que funcionou
|
|
237
|
+
async function handleSave(settings: Settings) {
|
|
238
|
+
await api.updateSettings(settings);
|
|
239
|
+
// Nenhum feedback -- usuario fica na duvida se salvou
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Solucao:**
|
|
244
|
+
```tsx
|
|
245
|
+
// Usuario recebe confirmacao visual de sucesso
|
|
246
|
+
async function handleSave(settings: Settings) {
|
|
247
|
+
await api.updateSettings(settings);
|
|
248
|
+
toast.success("Configuracoes salvas com sucesso");
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Limitacao:** Algumas acoes nao precisam de feedback explicito (ex: auto-save que mostra indicador sutil). Verificar se o componente tem algum mecanismo de feedback mesmo que nao seja toast (ex: icone de check, texto "Salvo").
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### EMPTY-STATE
|
|
257
|
+
|
|
258
|
+
**Heuristica de Nielsen:** #1 - Visibilidade do status do sistema
|
|
259
|
+
**Frameworks:** React, Vue, Svelte
|
|
260
|
+
**Impacto tipico:** M
|
|
261
|
+
**Sinal de deteccao:**
|
|
262
|
+
```bash
|
|
263
|
+
# Procurar listas renderizadas com .map() sem condicional para array vazio
|
|
264
|
+
grep -rn "\.map(" src/ --include="*.tsx" --include="*.jsx"
|
|
265
|
+
# Verificar se antes do .map() existe check para length === 0 ou array vazio
|
|
266
|
+
# Se .map() direto sem empty state check -> problema
|
|
267
|
+
grep -rn "\.length === 0\|\.length == 0\|!.*\.length" src/ --include="*.tsx" --include="*.jsx"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Problema em codigo:**
|
|
271
|
+
```tsx
|
|
272
|
+
// Lista vazia mostra area em branco -- usuario nao sabe se e bug ou nao tem dados
|
|
273
|
+
return (
|
|
274
|
+
<div>
|
|
275
|
+
<h2>Seus Projetos</h2>
|
|
276
|
+
{projects.map(p => <ProjectCard key={p.id} project={p} />)}
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Solucao:**
|
|
282
|
+
```tsx
|
|
283
|
+
// Lista vazia mostra mensagem contextual com acao
|
|
284
|
+
return (
|
|
285
|
+
<div>
|
|
286
|
+
<h2>Seus Projetos</h2>
|
|
287
|
+
{projects.length === 0 ? (
|
|
288
|
+
<EmptyState
|
|
289
|
+
title="Nenhum projeto ainda"
|
|
290
|
+
description="Crie seu primeiro projeto para comecar"
|
|
291
|
+
action={<Button onClick={onCreateProject}>Criar projeto</Button>}
|
|
292
|
+
/>
|
|
293
|
+
) : (
|
|
294
|
+
projects.map(p => <ProjectCard key={p.id} project={p} />)
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Limitacao:** Componentes que recebem dados ja filtrados pelo pai podem ter empty state no componente pai. Verificar hierarquia de componentes antes de reportar.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### SKELETON-AUSENTE
|
|
305
|
+
|
|
306
|
+
**Heuristica de Nielsen:** #1 - Visibilidade do status do sistema
|
|
307
|
+
**Frameworks:** React, Vue, Svelte
|
|
308
|
+
**Impacto tipico:** P
|
|
309
|
+
**Sinal de deteccao:**
|
|
310
|
+
```bash
|
|
311
|
+
# Procurar condicionais de loading que mostram apenas texto simples
|
|
312
|
+
grep -rn "loading.*?.*Carregando\|loading.*?.*Loading\|isLoading.*?.*\.\.\." src/ --include="*.tsx" --include="*.jsx"
|
|
313
|
+
# Se loading state existe mas feedback e apenas texto ("Carregando...") sem skeleton/spinner -> problema
|
|
314
|
+
# Verificar se componente importa Skeleton, Spinner, ou tem CSS de shimmer
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Problema em codigo:**
|
|
318
|
+
```tsx
|
|
319
|
+
// Texto "Carregando..." causa layout shift quando dados chegam
|
|
320
|
+
if (isLoading) return <p>Carregando...</p>;
|
|
321
|
+
return <ComplexDashboard data={data} />;
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Solucao:**
|
|
325
|
+
```tsx
|
|
326
|
+
// Skeleton preserva layout e da impressao de velocidade
|
|
327
|
+
if (isLoading) return <DashboardSkeleton />;
|
|
328
|
+
return <ComplexDashboard data={data} />;
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Limitacao:** Para areas pequenas ou acoes rapidas (<200ms tipicamente), texto simples ou spinner e aceitavel. Skeletons sao mais importantes para conteudo principal da pagina e listas longas.
|
|
332
|
+
|
|
333
|
+
</category>
|
|
334
|
+
|
|
335
|
+
<category name="consistencia">
|
|
336
|
+
## Consistencia e Padroes Visuais
|
|
337
|
+
|
|
338
|
+
Heuristicas relacionadas a Heuristica #4 de Nielsen: "Consistencia e padroes". Usuarios nao devem ter que se perguntar se diferentes palavras, situacoes ou acoes significam a mesma coisa. Seguir convencoes da plataforma.
|
|
339
|
+
|
|
340
|
+
Em analise estatica, detectamos inconsistencia verificando uso de valores hardcoded em vez de tokens de design, e variacao sem padrao em propriedades visuais entre componentes.
|
|
341
|
+
|
|
342
|
+
### CORES-HARDCODED
|
|
343
|
+
|
|
344
|
+
**Heuristica de Nielsen:** #4 - Consistencia e padroes
|
|
345
|
+
**Frameworks:** All
|
|
346
|
+
**Impacto tipico:** M
|
|
347
|
+
**Sinal de deteccao:**
|
|
348
|
+
```bash
|
|
349
|
+
# CSS puro: procurar cores hex/rgb repetidas em multiplos arquivos
|
|
350
|
+
grep -rn "color:\s*#\|background:\s*#\|background-color:\s*#\|border.*:\s*#" src/ --include="*.css" --include="*.scss"
|
|
351
|
+
# Tailwind: procurar cores arbitrarias (brackets) repetidas
|
|
352
|
+
grep -rn "bg-\[#\|text-\[#\|border-\[#" src/ --include="*.tsx" --include="*.jsx" --include="*.vue"
|
|
353
|
+
# Se mesma cor hex aparece em 3+ arquivos sem ser variavel CSS / token -> problema
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Problema em codigo:**
|
|
357
|
+
```css
|
|
358
|
+
/* Mesma cor azul hardcoded em 5 arquivos diferentes -- trocar requer mudar todos */
|
|
359
|
+
.header { background-color: #3b82f6; }
|
|
360
|
+
.button-primary { background-color: #3b82f6; }
|
|
361
|
+
.link { color: #3b82f6; }
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Solucao:**
|
|
365
|
+
```css
|
|
366
|
+
/* Design token centralizado -- mudar a marca requer mudar um valor */
|
|
367
|
+
:root { --color-primary: #3b82f6; }
|
|
368
|
+
.header { background-color: var(--color-primary); }
|
|
369
|
+
.button-primary { background-color: var(--color-primary); }
|
|
370
|
+
.link { color: var(--color-primary); }
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Limitacao:** Cores usadas uma unica vez (ex: gradiente decorativo) nao precisam ser tokens. Focar em cores que aparecem em 3+ locais. Tailwind com config customizado ja centraliza cores.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
### ESPACAMENTO-INCONSISTENTE
|
|
378
|
+
|
|
379
|
+
**Heuristica de Nielsen:** #4 - Consistencia e padroes
|
|
380
|
+
**Frameworks:** All
|
|
381
|
+
**Impacto tipico:** P
|
|
382
|
+
**Sinal de deteccao:**
|
|
383
|
+
```bash
|
|
384
|
+
# CSS puro: verificar variedade de valores de padding/margin sem padrao
|
|
385
|
+
grep -rn "padding:\|margin:" src/ --include="*.css" --include="*.scss" | sort
|
|
386
|
+
# Tailwind: verificar mistura inconsistente de classes de spacing em componentes similares
|
|
387
|
+
# Ex: componente A usa p-4 e componente B similar usa p-3 e p-6 sem razao aparente
|
|
388
|
+
grep -rn "class.*p-[0-9]\|class.*m-[0-9]\|class.*gap-[0-9]" src/ --include="*.tsx" --include="*.jsx"
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Problema em codigo:**
|
|
392
|
+
```css
|
|
393
|
+
/* Spacing arbitrario sem escala -- 13px, 17px, 21px nao seguem logica */
|
|
394
|
+
.card { padding: 13px; }
|
|
395
|
+
.sidebar { padding: 17px; }
|
|
396
|
+
.modal { padding: 21px; }
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Solucao:**
|
|
400
|
+
```css
|
|
401
|
+
/* Escala de spacing consistente baseada em multiplos de 4 */
|
|
402
|
+
:root {
|
|
403
|
+
--space-sm: 8px;
|
|
404
|
+
--space-md: 16px;
|
|
405
|
+
--space-lg: 24px;
|
|
406
|
+
}
|
|
407
|
+
.card { padding: var(--space-md); }
|
|
408
|
+
.sidebar { padding: var(--space-md); }
|
|
409
|
+
.modal { padding: var(--space-lg); }
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**Limitacao:** Valores de spacing que nao seguem multiplos de 4/8 nao sao necessariamente errados -- podem ser intencionais para ajuste fino. Reportar apenas quando a variacao e claramente arbitraria (muitos valores unicos sem padrao).
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
### TIPOGRAFIA-SEM-ESCALA
|
|
417
|
+
|
|
418
|
+
**Heuristica de Nielsen:** #4 - Consistencia e padroes
|
|
419
|
+
**Frameworks:** All
|
|
420
|
+
**Impacto tipico:** M
|
|
421
|
+
**Sinal de deteccao:**
|
|
422
|
+
```bash
|
|
423
|
+
# Procurar font-size com muitos valores unicos sem custom properties
|
|
424
|
+
grep -rn "font-size:" src/ --include="*.css" --include="*.scss" | sort -t: -k3
|
|
425
|
+
# Tailwind: verificar se usa classes padrao (text-sm, text-base, text-lg) vs valores arbitrarios
|
|
426
|
+
grep -rn "text-\[" src/ --include="*.tsx" --include="*.jsx"
|
|
427
|
+
# Se mais de 5 valores de font-size unicos sem --font-* custom properties -> problema
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**Problema em codigo:**
|
|
431
|
+
```css
|
|
432
|
+
/* 8 tamanhos de fonte diferentes sem escala definida -- hierarquia confusa */
|
|
433
|
+
.title { font-size: 28px; }
|
|
434
|
+
.subtitle { font-size: 19px; }
|
|
435
|
+
.body { font-size: 15px; }
|
|
436
|
+
.caption { font-size: 11px; }
|
|
437
|
+
.small { font-size: 13px; }
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**Solucao:**
|
|
441
|
+
```css
|
|
442
|
+
/* Escala tipografica definida -- hierarquia visual clara e previsivel */
|
|
443
|
+
:root {
|
|
444
|
+
--text-xs: 0.75rem;
|
|
445
|
+
--text-sm: 0.875rem;
|
|
446
|
+
--text-base: 1rem;
|
|
447
|
+
--text-lg: 1.125rem;
|
|
448
|
+
--text-xl: 1.25rem;
|
|
449
|
+
--text-2xl: 1.5rem;
|
|
450
|
+
}
|
|
451
|
+
.title { font-size: var(--text-2xl); }
|
|
452
|
+
.subtitle { font-size: var(--text-lg); }
|
|
453
|
+
.body { font-size: var(--text-base); }
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Limitacao:** Projetos com Tailwind ja tem escala tipografica integrada. Verificar se o projeto usa as classes padrao antes de reportar.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
### BORDAS-SOMBRAS-INCONSISTENTES
|
|
461
|
+
|
|
462
|
+
**Heuristica de Nielsen:** #4 - Consistencia e padroes
|
|
463
|
+
**Frameworks:** All
|
|
464
|
+
**Impacto tipico:** P
|
|
465
|
+
**Sinal de deteccao:**
|
|
466
|
+
```bash
|
|
467
|
+
# Verificar variedade de border-radius sem padrao
|
|
468
|
+
grep -rn "border-radius:" src/ --include="*.css" --include="*.scss" | sort -t: -k3
|
|
469
|
+
# Verificar variedade de box-shadow
|
|
470
|
+
grep -rn "box-shadow:" src/ --include="*.css" --include="*.scss" | sort -t: -k3
|
|
471
|
+
# Tailwind: verificar mistura de classes de rounded/shadow
|
|
472
|
+
grep -rn "rounded-\|shadow-" src/ --include="*.tsx" --include="*.jsx"
|
|
473
|
+
# Se mais de 4 valores unicos de border-radius sem variavel CSS -> problema
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
**Problema em codigo:**
|
|
477
|
+
```css
|
|
478
|
+
/* Cada componente com border-radius diferente -- visual incoerente */
|
|
479
|
+
.card { border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
480
|
+
.button { border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
|
|
481
|
+
.modal { border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); }
|
|
482
|
+
.input { border-radius: 6px; }
|
|
483
|
+
.badge { border-radius: 16px; }
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Solucao:**
|
|
487
|
+
```css
|
|
488
|
+
/* Tokens de borda e sombra padronizados */
|
|
489
|
+
:root {
|
|
490
|
+
--radius-sm: 4px;
|
|
491
|
+
--radius-md: 8px;
|
|
492
|
+
--radius-lg: 12px;
|
|
493
|
+
--radius-full: 9999px;
|
|
494
|
+
--shadow-sm: 0 1px 3px rgba(0,0,0,0.1);
|
|
495
|
+
--shadow-md: 0 4px 12px rgba(0,0,0,0.15);
|
|
496
|
+
}
|
|
497
|
+
.card { border-radius: var(--radius-md); box-shadow: var(--shadow-sm); }
|
|
498
|
+
.button { border-radius: var(--radius-sm); }
|
|
499
|
+
.modal { border-radius: var(--radius-lg); box-shadow: var(--shadow-md); }
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**Limitacao:** Variacao intencional (ex: botoes com radius diferente de cards) e valida. Reportar apenas quando componentes do mesmo tipo tem valores visuais inconsistentes entre si.
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
### BOTOES-SEM-ESTILO-PADRAO
|
|
507
|
+
|
|
508
|
+
**Heuristica de Nielsen:** #4 - Consistencia e padroes
|
|
509
|
+
**Frameworks:** All
|
|
510
|
+
**Impacto tipico:** M
|
|
511
|
+
**Sinal de deteccao:**
|
|
512
|
+
```bash
|
|
513
|
+
# Procurar botoes sem classe de estilo
|
|
514
|
+
grep -rn "<button\|<Button\|type=\"submit\"\|type=\"button\"" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
515
|
+
# Verificar se botoes tem className/class ou se usam componente Button padrao
|
|
516
|
+
# Se <button> sem className ou com estilos inline variados -> problema
|
|
517
|
+
grep -rn "<button[^>]*style=" src/ --include="*.tsx" --include="*.jsx"
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Problema em codigo:**
|
|
521
|
+
```tsx
|
|
522
|
+
// Cada botao com estilo inline diferente -- usuario nao reconhece padrao de acao
|
|
523
|
+
<button style={{ background: "blue", color: "white", padding: "8px" }}>Salvar</button>
|
|
524
|
+
<button style={{ background: "red", color: "white", padding: "10px 16px" }}>Excluir</button>
|
|
525
|
+
<button style={{ background: "gray", padding: "6px 12px" }}>Cancelar</button>
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**Solucao:**
|
|
529
|
+
```tsx
|
|
530
|
+
// Componente Button padrao com variantes -- acoes sao visualmente previsiveis
|
|
531
|
+
<Button variant="primary">Salvar</Button>
|
|
532
|
+
<Button variant="destructive">Excluir</Button>
|
|
533
|
+
<Button variant="secondary">Cancelar</Button>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Limitacao:** Projetos com UI library (shadcn, MUI, etc.) ja tem componente Button padronizado. Verificar imports antes de reportar. Botoes com estilo inline podem ser intencionais em prototipos.
|
|
537
|
+
|
|
538
|
+
</category>
|
|
539
|
+
|
|
540
|
+
<category name="formularios">
|
|
541
|
+
## Prevencao de Erros e Formularios
|
|
542
|
+
|
|
543
|
+
Heuristicas relacionadas a Heuristica #5 de Nielsen: "Prevencao de erros". Melhor que boas mensagens de erro e um design cuidadoso que previne a ocorrencia do problema. Eliminar condicoes propensas a erro ou apresentar confirmacao antes de acoes.
|
|
544
|
+
|
|
545
|
+
Em analise estatica, detectamos ausencia de prevencao verificando se formularios tem validacao, labels, e mecanismos que guiam o usuario a evitar erros.
|
|
546
|
+
|
|
547
|
+
### INPUT-SEM-LABEL
|
|
548
|
+
|
|
549
|
+
**Heuristica de Nielsen:** #5 - Prevencao de erros / #6 - Reconhecimento em vez de memorizacao
|
|
550
|
+
**Frameworks:** All
|
|
551
|
+
**Impacto tipico:** G
|
|
552
|
+
**Sinal de deteccao:**
|
|
553
|
+
```bash
|
|
554
|
+
# Procurar inputs sem label associada
|
|
555
|
+
grep -rn "<input\|<Input\|<textarea\|<Textarea\|<select\|<Select" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
556
|
+
# Verificar se proximo ao input existe <label htmlFor> ou <Label> ou aria-label ou aria-labelledby
|
|
557
|
+
# Se input existe sem nenhuma forma de label -> problema de acessibilidade e usabilidade
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Problema em codigo:**
|
|
561
|
+
```tsx
|
|
562
|
+
// Usuario nao sabe o que digitar -- campo sem identificacao
|
|
563
|
+
<input type="text" placeholder="Digite aqui..." />
|
|
564
|
+
<input type="email" />
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**Solucao:**
|
|
568
|
+
```tsx
|
|
569
|
+
// Label associada informa o proposito do campo; acessivel para screen readers
|
|
570
|
+
<div>
|
|
571
|
+
<label htmlFor="email">Email</label>
|
|
572
|
+
<input id="email" type="email" placeholder="seu@email.com" />
|
|
573
|
+
</div>
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
**Limitacao:** Inputs com `aria-label` ou dentro de componentes de UI library que adicionam label automaticamente (ex: FormField do shadcn) sao validos mesmo sem `<label>` visivel. Verificar atributos ARIA.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
### FORMULARIO-SEM-VALIDACAO
|
|
581
|
+
|
|
582
|
+
**Heuristica de Nielsen:** #5 - Prevencao de erros
|
|
583
|
+
**Frameworks:** All
|
|
584
|
+
**Impacto tipico:** G
|
|
585
|
+
**Sinal de deteccao:**
|
|
586
|
+
```bash
|
|
587
|
+
# Procurar forms com onSubmit mas sem validacao
|
|
588
|
+
grep -rn "onSubmit\|@submit\|handleSubmit" src/ --include="*.tsx" --include="*.jsx" --include="*.vue"
|
|
589
|
+
# No mesmo componente, verificar se existe: schema validation (zod/yup), field validation, required checks
|
|
590
|
+
# Se form com submit handler mas sem nenhuma validacao client-side -> problema
|
|
591
|
+
grep -rn "z\.object\|yup\.object\|validate\|required\|\.errors" src/ --include="*.tsx" --include="*.jsx"
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
**Problema em codigo:**
|
|
595
|
+
```tsx
|
|
596
|
+
// Formulario envia dados sem validar -- usuario descobre erro so na resposta do servidor
|
|
597
|
+
function handleSubmit(e: FormEvent) {
|
|
598
|
+
e.preventDefault();
|
|
599
|
+
const data = new FormData(e.target);
|
|
600
|
+
api.createUser(Object.fromEntries(data)); // Sem validacao
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return (
|
|
604
|
+
<form onSubmit={handleSubmit}>
|
|
605
|
+
<input name="email" />
|
|
606
|
+
<input name="age" />
|
|
607
|
+
<button type="submit">Criar</button>
|
|
608
|
+
</form>
|
|
609
|
+
);
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**Solucao:**
|
|
613
|
+
```tsx
|
|
614
|
+
// Validacao client-side previne envio de dados invalidos
|
|
615
|
+
const schema = z.object({
|
|
616
|
+
email: z.string().email("Email invalido"),
|
|
617
|
+
age: z.number().min(18, "Idade minima: 18 anos"),
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
function handleSubmit(e: FormEvent) {
|
|
621
|
+
e.preventDefault();
|
|
622
|
+
const data = Object.fromEntries(new FormData(e.target));
|
|
623
|
+
const result = schema.safeParse(data);
|
|
624
|
+
if (!result.success) {
|
|
625
|
+
setErrors(result.error.flatten().fieldErrors);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
api.createUser(result.data);
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
**Limitacao:** Formularios simples (ex: campo de busca) nao precisam de validacao complexa. Focar em formularios que coletam dados do usuario para envio ao servidor.
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
### ERRO-GENERICO
|
|
637
|
+
|
|
638
|
+
**Heuristica de Nielsen:** #9 - Ajudar usuarios a reconhecer, diagnosticar e recuperar de erros
|
|
639
|
+
**Frameworks:** All
|
|
640
|
+
**Impacto tipico:** M
|
|
641
|
+
**Sinal de deteccao:**
|
|
642
|
+
```bash
|
|
643
|
+
# Procurar mensagens de erro genericas hardcoded
|
|
644
|
+
grep -rn '"Erro"\|"Error"\|"Invalido"\|"Invalid"\|"Algo deu errado"\|"Something went wrong"' src/ --include="*.tsx" --include="*.jsx" --include="*.vue"
|
|
645
|
+
# Verificar se mensagens de erro sao contextuais (especificas para o campo/acao)
|
|
646
|
+
# Se mensagem de erro e texto generico sem contexto -> problema
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
**Problema em codigo:**
|
|
650
|
+
```tsx
|
|
651
|
+
// Mensagem nao ajuda o usuario a corrigir o problema
|
|
652
|
+
{error && <p className="text-red-500">Erro ao processar</p>}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
**Solucao:**
|
|
656
|
+
```tsx
|
|
657
|
+
// Mensagem especifica para cada tipo de erro
|
|
658
|
+
{error?.type === "email" && <p className="text-red-500">Email ja cadastrado. Tente fazer login.</p>}
|
|
659
|
+
{error?.type === "network" && <p className="text-red-500">Sem conexao. Verifique sua internet e tente novamente.</p>}
|
|
660
|
+
{error?.type === "validation" && <p className="text-red-500">{error.message}</p>}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**Limitacao:** Erros genericos em catch-all handlers podem ser aceitaveis como fallback, desde que erros especificos sejam tratados antes. Verificar se existe tratamento granular alem do generico.
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
### REQUIRED-SEM-INDICACAO
|
|
668
|
+
|
|
669
|
+
**Heuristica de Nielsen:** #5 - Prevencao de erros / #6 - Reconhecimento
|
|
670
|
+
**Frameworks:** All
|
|
671
|
+
**Impacto tipico:** P
|
|
672
|
+
**Sinal de deteccao:**
|
|
673
|
+
```bash
|
|
674
|
+
# Procurar inputs com required mas sem indicacao visual
|
|
675
|
+
grep -rn "required" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
676
|
+
# No mesmo contexto, verificar se existe asterisco (*), texto "obrigatorio", ou classe visual de required
|
|
677
|
+
# Se input tem required attribute mas sem indicacao visual -> problema
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
**Problema em codigo:**
|
|
681
|
+
```tsx
|
|
682
|
+
// Campo obrigatorio sem indicacao -- usuario descobre so ao tentar enviar
|
|
683
|
+
<label htmlFor="name">Nome</label>
|
|
684
|
+
<input id="name" required />
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
**Solucao:**
|
|
688
|
+
```tsx
|
|
689
|
+
// Asterisco ou texto indica que o campo e obrigatorio
|
|
690
|
+
<label htmlFor="name">Nome <span className="text-red-500">*</span></label>
|
|
691
|
+
<input id="name" required aria-required="true" />
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**Limitacao:** Se todos os campos do formulario sao obrigatorios, indicar "todos obrigatorios" no topo e mais limpo que asterisco em cada campo. Verificar proporacao de campos required no form.
|
|
695
|
+
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
### AUTOCOMPLETE-AUSENTE
|
|
699
|
+
|
|
700
|
+
**Heuristica de Nielsen:** #6 - Reconhecimento em vez de memorizacao / #7 - Flexibilidade e eficiencia
|
|
701
|
+
**Frameworks:** All
|
|
702
|
+
**Impacto tipico:** P
|
|
703
|
+
**Sinal de deteccao:**
|
|
704
|
+
```bash
|
|
705
|
+
# Procurar inputs de campos comuns sem atributo autocomplete
|
|
706
|
+
grep -rn 'type="email"\|type="tel"\|type="password"\|name="address"\|name="city"\|name="zip"\|name="name"' src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
707
|
+
# Verificar se estes inputs tem atributo autocomplete
|
|
708
|
+
# Se input de email/telefone/endereco sem autocomplete -> problema
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**Problema em codigo:**
|
|
712
|
+
```tsx
|
|
713
|
+
// Usuario precisa digitar email manualmente mesmo que o browser saiba
|
|
714
|
+
<input type="email" name="email" />
|
|
715
|
+
<input type="tel" name="phone" />
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
**Solucao:**
|
|
719
|
+
```tsx
|
|
720
|
+
// Browser oferece preenchimento automatico -- menos digitacao para o usuario
|
|
721
|
+
<input type="email" name="email" autoComplete="email" />
|
|
722
|
+
<input type="tel" name="phone" autoComplete="tel" />
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
**Limitacao:** Campos de busca e filtros nao devem ter autocomplete de endereco/email. Aplicar apenas a formularios de cadastro, perfil, checkout, e contato.
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
### SUBMIT-SEM-DISABLED
|
|
730
|
+
|
|
731
|
+
**Heuristica de Nielsen:** #5 - Prevencao de erros
|
|
732
|
+
**Frameworks:** All
|
|
733
|
+
**Impacto tipico:** M
|
|
734
|
+
**Sinal de deteccao:**
|
|
735
|
+
```bash
|
|
736
|
+
# Procurar botoes de submit sem disabled durante loading
|
|
737
|
+
grep -rn 'type="submit"' src/ --include="*.tsx" --include="*.jsx" --include="*.vue"
|
|
738
|
+
# Verificar se o botao tem disabled={isLoading} ou disabled={isPending} ou equivalente
|
|
739
|
+
# Se botao submit existe sem condicao de disabled e componente tem estado de loading -> problema
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
**Problema em codigo:**
|
|
743
|
+
```tsx
|
|
744
|
+
// Duplo clique envia formulario duas vezes
|
|
745
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
746
|
+
// ... handler que seta isLoading
|
|
747
|
+
<button type="submit">Enviar</button>
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
**Solucao:**
|
|
751
|
+
```tsx
|
|
752
|
+
// Botao desabilitado previne envio duplicado
|
|
753
|
+
<button type="submit" disabled={isLoading}>
|
|
754
|
+
{isLoading ? "Enviando..." : "Enviar"}
|
|
755
|
+
</button>
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
**Limitacao:** Formularios que usam React Hook Form com `formState.isSubmitting` ja controlam isso automaticamente. Verificar se form library gerencia o estado antes de reportar.
|
|
759
|
+
|
|
760
|
+
</category>
|
|
761
|
+
|
|
762
|
+
<category name="navegacao">
|
|
763
|
+
## Reconhecimento e Navegacao
|
|
764
|
+
|
|
765
|
+
Heuristicas relacionadas a Heuristica #6 de Nielsen: "Reconhecimento em vez de memorizacao". Minimizar a carga de memoria do usuario tornando objetos, acoes e opcoes visiveis. O usuario nao deve ter que memorizar informacoes de uma parte da interface para outra.
|
|
766
|
+
|
|
767
|
+
Em analise estatica, detectamos problemas de navegacao verificando a estrutura de links, rotas, titulos e elementos de orientacao.
|
|
768
|
+
|
|
769
|
+
### LINK-SEM-TEXTO-DESCRITIVO
|
|
770
|
+
|
|
771
|
+
**Heuristica de Nielsen:** #6 - Reconhecimento em vez de memorizacao
|
|
772
|
+
**Frameworks:** All
|
|
773
|
+
**Impacto tipico:** M
|
|
774
|
+
**Sinal de deteccao:**
|
|
775
|
+
```bash
|
|
776
|
+
# Procurar links com texto generico
|
|
777
|
+
grep -rn ">clique aqui<\|>click here<\|>saiba mais<\|>learn more<\|>here<\|>aqui<" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
778
|
+
# Procurar links que sao apenas icones sem texto acessivel
|
|
779
|
+
grep -rn "<a [^>]*>\s*<[A-Z][a-zA-Z]*Icon\|<Link [^>]*>\s*<[A-Z][a-zA-Z]*Icon" src/ --include="*.tsx" --include="*.jsx"
|
|
780
|
+
# Se link tem apenas icone sem aria-label ou sr-only text -> problema
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
**Problema em codigo:**
|
|
784
|
+
```tsx
|
|
785
|
+
// "Clique aqui" nao informa o destino -- usuario precisa ler contexto
|
|
786
|
+
<p>Para ver seus pedidos, <a href="/orders">clique aqui</a>.</p>
|
|
787
|
+
// Icone sem texto acessivel -- usuario de screen reader nao sabe o destino
|
|
788
|
+
<Link href="/settings"><GearIcon /></Link>
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**Solucao:**
|
|
792
|
+
```tsx
|
|
793
|
+
// Texto do link descreve o destino
|
|
794
|
+
<p>Veja seus <a href="/orders">pedidos recentes</a>.</p>
|
|
795
|
+
// Icone com texto acessivel
|
|
796
|
+
<Link href="/settings" aria-label="Configuracoes"><GearIcon /></Link>
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
**Limitacao:** Links em menus de navegacao com contexto visual claro (ex: icone + label adjacente) podem nao precisar de aria-label adicional. Avaliar contexto visual do componente pai.
|
|
800
|
+
|
|
801
|
+
---
|
|
802
|
+
|
|
803
|
+
### BREADCRUMB-AUSENTE
|
|
804
|
+
|
|
805
|
+
**Heuristica de Nielsen:** #6 - Reconhecimento em vez de memorizacao
|
|
806
|
+
**Frameworks:** All
|
|
807
|
+
**Impacto tipico:** P
|
|
808
|
+
**Sinal de deteccao:**
|
|
809
|
+
```bash
|
|
810
|
+
# Verificar se existem rotas aninhadas (3+ niveis)
|
|
811
|
+
# Next.js: pastas aninhadas em app/ ou pages/
|
|
812
|
+
ls -R src/app/ 2>/dev/null | grep -c "/" # Contar niveis de aninhamento
|
|
813
|
+
# Verificar se existe componente Breadcrumb
|
|
814
|
+
grep -rn "Breadcrumb\|breadcrumb" src/ --include="*.tsx" --include="*.jsx" --include="*.vue"
|
|
815
|
+
# Se rotas com 3+ niveis existem sem componente Breadcrumb -> problema
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
**Problema em codigo:**
|
|
819
|
+
```tsx
|
|
820
|
+
// Rota /admin/users/123/edit sem breadcrumb -- usuario nao sabe como voltar
|
|
821
|
+
export default function EditUserPage({ params }) {
|
|
822
|
+
return (
|
|
823
|
+
<div>
|
|
824
|
+
<h1>Editar Usuario</h1>
|
|
825
|
+
{/* Sem breadcrumb -- usuario perde contexto de navegacao */}
|
|
826
|
+
<UserForm userId={params.id} />
|
|
827
|
+
</div>
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
**Solucao:**
|
|
833
|
+
```tsx
|
|
834
|
+
// Breadcrumb mostra caminho hierarquico com links para cada nivel
|
|
835
|
+
export default function EditUserPage({ params }) {
|
|
836
|
+
return (
|
|
837
|
+
<div>
|
|
838
|
+
<Breadcrumb>
|
|
839
|
+
<BreadcrumbItem href="/admin">Admin</BreadcrumbItem>
|
|
840
|
+
<BreadcrumbItem href="/admin/users">Usuarios</BreadcrumbItem>
|
|
841
|
+
<BreadcrumbItem>Editar</BreadcrumbItem>
|
|
842
|
+
</Breadcrumb>
|
|
843
|
+
<h1>Editar Usuario</h1>
|
|
844
|
+
<UserForm userId={params.id} />
|
|
845
|
+
</div>
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
**Limitacao:** Apps simples com poucos niveis de navegacao (1-2 niveis) nao precisam de breadcrumbs. Aplicar apenas quando existem 3+ niveis de profundidade. SPAs com tabs laterais podem usar outra forma de orientacao.
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
### PAGINA-SEM-TITULO
|
|
855
|
+
|
|
856
|
+
**Heuristica de Nielsen:** #6 - Reconhecimento em vez de memorizacao
|
|
857
|
+
**Frameworks:** All
|
|
858
|
+
**Impacto tipico:** M
|
|
859
|
+
**Sinal de deteccao:**
|
|
860
|
+
```bash
|
|
861
|
+
# Next.js App Router: verificar se paginas exportam metadata
|
|
862
|
+
grep -rn "export.*metadata\|export.*generateMetadata" src/app/ --include="*.tsx" --include="*.ts"
|
|
863
|
+
# Next.js Pages Router: verificar uso de Head
|
|
864
|
+
grep -rn "<Head>\|import Head" src/pages/ --include="*.tsx" --include="*.jsx"
|
|
865
|
+
# HTML: verificar <title>
|
|
866
|
+
grep -rn "<title>" src/ --include="*.html"
|
|
867
|
+
# Contar paginas sem titulo vs total de paginas
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
**Problema em codigo:**
|
|
871
|
+
```tsx
|
|
872
|
+
// Pagina sem titulo -- aba do browser mostra URL ou titulo padrao
|
|
873
|
+
// app/dashboard/page.tsx
|
|
874
|
+
export default function DashboardPage() {
|
|
875
|
+
return <div>Dashboard content</div>;
|
|
876
|
+
}
|
|
877
|
+
// Sem export de metadata -> aba mostra "localhost:3000" ou titulo generico
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
**Solucao:**
|
|
881
|
+
```tsx
|
|
882
|
+
// Titulo descritivo para cada pagina
|
|
883
|
+
// app/dashboard/page.tsx
|
|
884
|
+
export const metadata = {
|
|
885
|
+
title: "Dashboard | MeuApp",
|
|
886
|
+
description: "Visao geral dos seus projetos e metricas",
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
export default function DashboardPage() {
|
|
890
|
+
return <div>Dashboard content</div>;
|
|
891
|
+
}
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**Limitacao:** Layouts pai em Next.js podem definir titulo base com template (`title: { template: "%s | App" }`). Verificar se layout.tsx ja define titulo antes de reportar paginas individuais.
|
|
895
|
+
|
|
896
|
+
---
|
|
897
|
+
|
|
898
|
+
### PAGINA-404-AUSENTE
|
|
899
|
+
|
|
900
|
+
**Heuristica de Nielsen:** #9 - Ajudar usuarios a recuperar de erros
|
|
901
|
+
**Frameworks:** All
|
|
902
|
+
**Impacto tipico:** M
|
|
903
|
+
**Sinal de deteccao:**
|
|
904
|
+
```bash
|
|
905
|
+
# Next.js App Router: verificar not-found.tsx
|
|
906
|
+
find src/app -name "not-found.tsx" -o -name "not-found.jsx" 2>/dev/null
|
|
907
|
+
# Next.js Pages Router: verificar 404.tsx
|
|
908
|
+
find src/pages -name "404.tsx" -o -name "404.jsx" 2>/dev/null
|
|
909
|
+
# Rota catch-all generica
|
|
910
|
+
grep -rn "\[\.\.\.slug\]\|\[\[\.\.\.slug\]\]" src/ --include="*.tsx"
|
|
911
|
+
# Se nenhuma pagina 404 customizada encontrada -> problema
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
**Problema em codigo:**
|
|
915
|
+
```
|
|
916
|
+
// Nenhum arquivo not-found.tsx ou 404.tsx no projeto
|
|
917
|
+
// Usuario que acessa URL invalida ve pagina de erro padrao do framework
|
|
918
|
+
// sem orientacao sobre como encontrar o que procurava
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
**Solucao:**
|
|
922
|
+
```tsx
|
|
923
|
+
// app/not-found.tsx -- pagina 404 customizada com navegacao
|
|
924
|
+
export default function NotFound() {
|
|
925
|
+
return (
|
|
926
|
+
<div className="flex flex-col items-center justify-center min-h-screen">
|
|
927
|
+
<h1 className="text-4xl font-bold">Pagina nao encontrada</h1>
|
|
928
|
+
<p className="mt-4 text-gray-600">
|
|
929
|
+
A pagina que voce procura nao existe ou foi movida.
|
|
930
|
+
</p>
|
|
931
|
+
<Link href="/" className="mt-6 text-blue-600 hover:underline">
|
|
932
|
+
Voltar para a pagina inicial
|
|
933
|
+
</Link>
|
|
934
|
+
</div>
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
**Limitacao:** Projetos com apenas API routes (sem frontend) nao precisam de pagina 404 visual. Aplicar apenas a projetos com interface de usuario.
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
### TAB-ORDER-QUEBRADO
|
|
944
|
+
|
|
945
|
+
**Heuristica de Nielsen:** #7 - Flexibilidade e eficiencia de uso
|
|
946
|
+
**Frameworks:** All
|
|
947
|
+
**Impacto tipico:** M
|
|
948
|
+
**Sinal de deteccao:**
|
|
949
|
+
```bash
|
|
950
|
+
# Procurar tabIndex com valores positivos (quebra ordem natural)
|
|
951
|
+
grep -rn "tabIndex=[\"']\?[1-9]" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
952
|
+
# Valores positivos de tabIndex criam ordem de tab customizada que confunde usuarios
|
|
953
|
+
# tabIndex="0" e tabIndex="-1" sao validos e comuns
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
**Problema em codigo:**
|
|
957
|
+
```tsx
|
|
958
|
+
// tabIndex positivo cria ordem de navegacao confusa
|
|
959
|
+
<input tabIndex={3} placeholder="Nome" />
|
|
960
|
+
<input tabIndex={1} placeholder="Email" /> {/* Tab vai aqui primeiro */}
|
|
961
|
+
<input tabIndex={2} placeholder="Telefone" />
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
**Solucao:**
|
|
965
|
+
```tsx
|
|
966
|
+
// Sem tabIndex positivo -- ordem segue o DOM (previsivel para o usuario)
|
|
967
|
+
<input placeholder="Email" />
|
|
968
|
+
<input placeholder="Nome" />
|
|
969
|
+
<input placeholder="Telefone" />
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
**Limitacao:** `tabIndex={0}` e `tabIndex={-1}` sao usos validos e comuns (tornar elemento focavel ou remover do tab order). Reportar apenas valores positivos (1, 2, 3...).
|
|
973
|
+
|
|
974
|
+
</category>
|
|
975
|
+
|
|
976
|
+
<category name="responsividade">
|
|
977
|
+
## Flexibilidade e Responsividade
|
|
978
|
+
|
|
979
|
+
Heuristicas relacionadas a Heuristica #7 de Nielsen: "Flexibilidade e eficiencia de uso". A interface deve acomodar tanto usuarios novatos quanto experientes, e deve funcionar em diferentes dispositivos e contextos.
|
|
980
|
+
|
|
981
|
+
Em analise estatica, detectamos problemas de responsividade verificando meta tags, media queries, unidades de medida e suporte a temas.
|
|
982
|
+
|
|
983
|
+
### META-VIEWPORT-AUSENTE
|
|
984
|
+
|
|
985
|
+
**Heuristica de Nielsen:** #7 - Flexibilidade e eficiencia de uso
|
|
986
|
+
**Frameworks:** Vanilla HTML
|
|
987
|
+
**Impacto tipico:** G
|
|
988
|
+
**Sinal de deteccao:**
|
|
989
|
+
```bash
|
|
990
|
+
# Procurar meta viewport em HTML
|
|
991
|
+
grep -rn 'name="viewport"' src/ --include="*.html" public/ --include="*.html"
|
|
992
|
+
# Em Next.js: verificar layout.tsx (App Router gera viewport automaticamente)
|
|
993
|
+
# Se projeto tem HTML customizado sem meta viewport -> problema critico em mobile
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
**Problema em codigo:**
|
|
997
|
+
```html
|
|
998
|
+
<!-- Pagina nao tem meta viewport -- mobile mostra versao desktop minuscula -->
|
|
999
|
+
<html>
|
|
1000
|
+
<head>
|
|
1001
|
+
<title>Meu App</title>
|
|
1002
|
+
<!-- Sem meta viewport -->
|
|
1003
|
+
</head>
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
**Solucao:**
|
|
1007
|
+
```html
|
|
1008
|
+
<!-- Meta viewport garante que mobile renderiza na escala correta -->
|
|
1009
|
+
<html>
|
|
1010
|
+
<head>
|
|
1011
|
+
<title>Meu App</title>
|
|
1012
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1013
|
+
</head>
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
**Limitacao:** Frameworks modernos (Next.js, Nuxt, SvelteKit) adicionam viewport automaticamente. Verificar apenas em projetos com HTML customizado ou templates manuais. Nao reportar se framework gera o HTML.
|
|
1017
|
+
|
|
1018
|
+
---
|
|
1019
|
+
|
|
1020
|
+
### LARGURA-FIXA-PX
|
|
1021
|
+
|
|
1022
|
+
**Heuristica de Nielsen:** #7 - Flexibilidade e eficiencia de uso
|
|
1023
|
+
**Frameworks:** All
|
|
1024
|
+
**Impacto tipico:** M
|
|
1025
|
+
**Sinal de deteccao:**
|
|
1026
|
+
```bash
|
|
1027
|
+
# Procurar larguras fixas em pixels em containers principais
|
|
1028
|
+
grep -rn "width:\s*[0-9]\{3,\}px" src/ --include="*.css" --include="*.scss"
|
|
1029
|
+
# Tailwind: procurar w-[NNNpx] sem max-w
|
|
1030
|
+
grep -rn "w-\[[0-9]\{3,\}px\]" src/ --include="*.tsx" --include="*.jsx"
|
|
1031
|
+
# Se container/wrapper/main/section tem width fixa em px sem max-width -> problema
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
**Problema em codigo:**
|
|
1035
|
+
```css
|
|
1036
|
+
/* Container com largura fixa -- cortado em telas menores */
|
|
1037
|
+
.main-content {
|
|
1038
|
+
width: 960px; /* Nao se adapta a telas menores que 960px */
|
|
1039
|
+
}
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
**Solucao:**
|
|
1043
|
+
```css
|
|
1044
|
+
/* max-width com width relativa -- se adapta a qualquer tela */
|
|
1045
|
+
.main-content {
|
|
1046
|
+
width: 100%;
|
|
1047
|
+
max-width: 960px;
|
|
1048
|
+
margin: 0 auto;
|
|
1049
|
+
}
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
**Limitacao:** Larguras fixas em componentes internos pequenos (icones, avatares, badges) sao aceitaveis. Focar em containers de layout (main, section, wrapper, content area). Modais com largura fixa e max-width sao validos.
|
|
1053
|
+
|
|
1054
|
+
---
|
|
1055
|
+
|
|
1056
|
+
### BREAKPOINTS-AUSENTES
|
|
1057
|
+
|
|
1058
|
+
**Heuristica de Nielsen:** #7 - Flexibilidade e eficiencia de uso
|
|
1059
|
+
**Frameworks:** All
|
|
1060
|
+
**Impacto tipico:** G
|
|
1061
|
+
**Sinal de deteccao:**
|
|
1062
|
+
```bash
|
|
1063
|
+
# CSS puro: verificar se existem media queries
|
|
1064
|
+
grep -rn "@media" src/ --include="*.css" --include="*.scss" | wc -l
|
|
1065
|
+
# Tailwind: verificar se existem classes responsivas
|
|
1066
|
+
grep -rn "sm:\|md:\|lg:\|xl:" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" | wc -l
|
|
1067
|
+
# Se projeto tem componentes de UI mas zero ou muito poucas media queries/classes responsivas -> problema
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
**Problema em codigo:**
|
|
1071
|
+
```css
|
|
1072
|
+
/* Nenhuma media query no projeto -- layout identico em mobile e desktop */
|
|
1073
|
+
.sidebar { width: 250px; float: left; }
|
|
1074
|
+
.content { margin-left: 260px; }
|
|
1075
|
+
/* Em mobile, sidebar ocupa 70% da tela e conteudo fica espremido */
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
**Solucao:**
|
|
1079
|
+
```css
|
|
1080
|
+
/* Media queries adaptam layout para diferentes tamanhos de tela */
|
|
1081
|
+
.sidebar { width: 250px; }
|
|
1082
|
+
.content { margin-left: 260px; }
|
|
1083
|
+
|
|
1084
|
+
@media (max-width: 768px) {
|
|
1085
|
+
.sidebar { display: none; } /* Ou transforma em drawer/menu */
|
|
1086
|
+
.content { margin-left: 0; }
|
|
1087
|
+
}
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
**Limitacao:** APIs e projetos sem interface visual nao precisam de responsividade. Admin panels internos com audiencia conhecida (desktop-only) podem ser menos responsivos. Verificar se o projeto tem usuario final mobile.
|
|
1091
|
+
|
|
1092
|
+
---
|
|
1093
|
+
|
|
1094
|
+
### IMAGEM-SEM-MAX-WIDTH
|
|
1095
|
+
|
|
1096
|
+
**Heuristica de Nielsen:** #7 - Flexibilidade e eficiencia de uso
|
|
1097
|
+
**Frameworks:** All
|
|
1098
|
+
**Impacto tipico:** M
|
|
1099
|
+
**Sinal de deteccao:**
|
|
1100
|
+
```bash
|
|
1101
|
+
# Procurar imagens sem restricao de largura
|
|
1102
|
+
grep -rn "<img " src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
1103
|
+
# Verificar se img tem max-width ou classe responsiva
|
|
1104
|
+
# CSS global: verificar se existe reset de img
|
|
1105
|
+
grep -rn "img.*max-width\|img.*width.*100%" src/ --include="*.css" --include="*.scss"
|
|
1106
|
+
# Tailwind: verificar se img tem w-full, max-w-*, ou classe de tamanho
|
|
1107
|
+
# Se <img> sem restricao de largura e sem reset CSS global -> problema
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
**Problema em codigo:**
|
|
1111
|
+
```tsx
|
|
1112
|
+
// Imagem maior que o container estoura o layout em mobile
|
|
1113
|
+
<img src="/hero-banner.jpg" alt="Banner" />
|
|
1114
|
+
/* Imagem de 1920px de largura estoura container de 375px em mobile */
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
**Solucao:**
|
|
1118
|
+
```tsx
|
|
1119
|
+
// Imagem se adapta ao container
|
|
1120
|
+
<img src="/hero-banner.jpg" alt="Banner" className="w-full max-w-full h-auto" />
|
|
1121
|
+
// Ou no CSS global:
|
|
1122
|
+
// img { max-width: 100%; height: auto; }
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
**Limitacao:** Projetos com CSS reset moderno (Tailwind preflight, normalize.css, modern-normalize) ja incluem `img { max-width: 100% }`. Verificar se existe reset antes de reportar. Next.js `Image` component ja gerencia isso.
|
|
1126
|
+
|
|
1127
|
+
---
|
|
1128
|
+
|
|
1129
|
+
### DARK-MODE-INCOMPLETO
|
|
1130
|
+
|
|
1131
|
+
**Heuristica de Nielsen:** #7 - Flexibilidade e eficiencia de uso
|
|
1132
|
+
**Frameworks:** Tailwind, CSS custom properties
|
|
1133
|
+
**Impacto tipico:** P
|
|
1134
|
+
**Sinal de deteccao:**
|
|
1135
|
+
```bash
|
|
1136
|
+
# Verificar se Tailwind tem dark mode configurado
|
|
1137
|
+
grep -rn "darkMode" tailwind.config.* 2>/dev/null
|
|
1138
|
+
# Verificar se dark: classes sao usadas
|
|
1139
|
+
grep -rn "dark:" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" | wc -l
|
|
1140
|
+
# CSS: verificar prefers-color-scheme
|
|
1141
|
+
grep -rn "prefers-color-scheme" src/ --include="*.css" --include="*.scss" | wc -l
|
|
1142
|
+
# Se Tailwind configurado com dark mode mas poucas classes dark: usadas -> incompleto
|
|
1143
|
+
# Comparar: total de classes bg-* vs total de classes dark:bg-*
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
**Problema em codigo:**
|
|
1147
|
+
```tsx
|
|
1148
|
+
// Tailwind configurado com dark mode, mas componentes nao usam
|
|
1149
|
+
// tailwind.config.js: darkMode: "class"
|
|
1150
|
+
// Componentes:
|
|
1151
|
+
<div className="bg-white text-gray-900">
|
|
1152
|
+
<h1 className="text-black">Titulo</h1>
|
|
1153
|
+
{/* Nenhuma classe dark: -- dark mode fica com fundo branco e texto escuro */}
|
|
1154
|
+
</div>
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
**Solucao:**
|
|
1158
|
+
```tsx
|
|
1159
|
+
// Classes dark: aplicadas para adaptar ao tema escuro
|
|
1160
|
+
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
|
1161
|
+
<h1 className="text-black dark:text-white">Titulo</h1>
|
|
1162
|
+
</div>
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
**Limitacao:** Nem todo projeto precisa de dark mode. Reportar apenas quando o framework esta configurado para suportar dark mode (darkMode habilitado no Tailwind, ou prefers-color-scheme presente) mas componentes nao implementam. Nao reportar se dark mode nao esta configurado.
|
|
1166
|
+
|
|
1167
|
+
</category>
|
|
1168
|
+
|
|
1169
|
+
<category name="hierarquia-visual">
|
|
1170
|
+
## Estetica e Hierarquia Visual
|
|
1171
|
+
|
|
1172
|
+
Heuristicas relacionadas a Heuristica #8 de Nielsen: "Design estetico e minimalista". Dialogos nao devem conter informacoes irrelevantes ou raramente necessarias. Cada unidade extra de informacao compete com as unidades relevantes e diminui sua visibilidade relativa.
|
|
1173
|
+
|
|
1174
|
+
Em analise estatica, detectamos problemas de hierarquia verificando uso de headings, espacamento, e estrutura visual dos componentes.
|
|
1175
|
+
|
|
1176
|
+
### HEADING-LEVELS-PULADOS
|
|
1177
|
+
|
|
1178
|
+
**Heuristica de Nielsen:** #8 - Design estetico e minimalista / #6 - Reconhecimento
|
|
1179
|
+
**Frameworks:** All
|
|
1180
|
+
**Impacto tipico:** M
|
|
1181
|
+
**Sinal de deteccao:**
|
|
1182
|
+
```bash
|
|
1183
|
+
# Procurar headings e verificar se a sequencia e logica
|
|
1184
|
+
grep -rn "<h[1-6]\|<H[1-6]" src/ --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.html"
|
|
1185
|
+
# Ordenar e verificar se h1 existe e se niveis nao sao pulados (h1 -> h3 sem h2)
|
|
1186
|
+
# Verificar se pagina tem h1
|
|
1187
|
+
grep -rn "<h1\|<H1" src/ --include="*.tsx" --include="*.jsx" | wc -l
|
|
1188
|
+
# Se h1 ausente ou heading levels pulados -> problema
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
**Problema em codigo:**
|
|
1192
|
+
```tsx
|
|
1193
|
+
// h1 seguido de h3 -- h2 ausente. Hierarquia confusa para screen readers e SEO
|
|
1194
|
+
<div>
|
|
1195
|
+
<h1>Dashboard</h1>
|
|
1196
|
+
<h3>Projetos Recentes</h3> {/* Pulou h2 */}
|
|
1197
|
+
<h5>Ver todos</h5> {/* Pulou h4 */}
|
|
1198
|
+
</div>
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
**Solucao:**
|
|
1202
|
+
```tsx
|
|
1203
|
+
// Headings em sequencia logica -- hierarquia clara
|
|
1204
|
+
<div>
|
|
1205
|
+
<h1>Dashboard</h1>
|
|
1206
|
+
<h2>Projetos Recentes</h2>
|
|
1207
|
+
<h3>Ver todos</h3>
|
|
1208
|
+
</div>
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
**Limitacao:** Componentes reutilizaveis podem usar headings que fazem sentido no contexto onde sao inseridos. Avaliar a pagina completa, nao componentes isolados. Bibliotecas de UI podem definir headings internamente.
|
|
1212
|
+
|
|
1213
|
+
---
|
|
1214
|
+
|
|
1215
|
+
### CONTEUDO-SEM-QUEBRA-VISUAL
|
|
1216
|
+
|
|
1217
|
+
**Heuristica de Nielsen:** #8 - Design estetico e minimalista
|
|
1218
|
+
**Frameworks:** All
|
|
1219
|
+
**Impacto tipico:** P
|
|
1220
|
+
**Sinal de deteccao:**
|
|
1221
|
+
```bash
|
|
1222
|
+
# Procurar componentes com muito texto corrido sem headings ou listas
|
|
1223
|
+
# Heuristica: contar linhas de texto JSX entre headings em componentes de conteudo
|
|
1224
|
+
grep -rn "<p>" src/ --include="*.tsx" --include="*.jsx" --include="*.vue"
|
|
1225
|
+
# Se componente tem mais de 5 paragrafos consecutivos sem heading, lista, ou separador visual -> problema
|
|
1226
|
+
# Verificar presenca de <ul>, <ol>, <hr>, headings entre paragrafos
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1229
|
+
**Problema em codigo:**
|
|
1230
|
+
```tsx
|
|
1231
|
+
// Bloco de texto longo sem quebra visual -- usuario desiste de ler
|
|
1232
|
+
<div>
|
|
1233
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
|
|
1234
|
+
<p>Sed do eiusmod tempor incididunt ut labore et dolore...</p>
|
|
1235
|
+
<p>Ut enim ad minim veniam, quis nostrud exercitation...</p>
|
|
1236
|
+
<p>Duis aute irure dolor in reprehenderit in voluptate...</p>
|
|
1237
|
+
<p>Excepteur sint occaecat cupidatat non proident...</p>
|
|
1238
|
+
<p>Sed ut perspiciatis unde omnis iste natus error...</p>
|
|
1239
|
+
</div>
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
**Solucao:**
|
|
1243
|
+
```tsx
|
|
1244
|
+
// Conteudo organizado com headings, listas e espacamento
|
|
1245
|
+
<div>
|
|
1246
|
+
<h2>Como funciona</h2>
|
|
1247
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
|
|
1248
|
+
|
|
1249
|
+
<h3>Beneficios</h3>
|
|
1250
|
+
<ul>
|
|
1251
|
+
<li>Primeiro beneficio com descricao</li>
|
|
1252
|
+
<li>Segundo beneficio com descricao</li>
|
|
1253
|
+
</ul>
|
|
1254
|
+
|
|
1255
|
+
<h3>Proximos passos</h3>
|
|
1256
|
+
<p>Duis aute irure dolor in reprehenderit...</p>
|
|
1257
|
+
</div>
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
**Limitacao:** Paginas de artigos/blog com paragrafos longos podem ser intencionais no design editorial. Aplicar principalmente a paginas de produto, landing pages, e documentacao tecnica.
|
|
1261
|
+
|
|
1262
|
+
---
|
|
1263
|
+
|
|
1264
|
+
### WHITESPACE-INSUFICIENTE
|
|
1265
|
+
|
|
1266
|
+
**Heuristica de Nielsen:** #8 - Design estetico e minimalista
|
|
1267
|
+
**Frameworks:** All
|
|
1268
|
+
**Impacto tipico:** P
|
|
1269
|
+
**Sinal de deteccao:**
|
|
1270
|
+
```bash
|
|
1271
|
+
# CSS: procurar containers sem padding ou com padding minimo
|
|
1272
|
+
grep -rn "padding:\s*0\b\|padding: 0;" src/ --include="*.css" --include="*.scss"
|
|
1273
|
+
# Tailwind: procurar containers sem gap ou space-between
|
|
1274
|
+
# Heuristica: componentes com filhos diretos sem gap, space-y, space-x, ou py/px
|
|
1275
|
+
grep -rn "flex\|grid" src/ --include="*.tsx" --include="*.jsx" | head -20
|
|
1276
|
+
# Verificar se containers flex/grid tem gap- classes
|
|
1277
|
+
# Se flex/grid containers sem gap e sem space- classes -> problema
|
|
1278
|
+
```
|
|
1279
|
+
|
|
1280
|
+
**Problema em codigo:**
|
|
1281
|
+
```tsx
|
|
1282
|
+
// Elementos colados sem espaco -- interface parece apertada e confusa
|
|
1283
|
+
<div className="flex">
|
|
1284
|
+
<Card>Projeto A</Card>
|
|
1285
|
+
<Card>Projeto B</Card>
|
|
1286
|
+
<Card>Projeto C</Card>
|
|
1287
|
+
</div>
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
**Solucao:**
|
|
1291
|
+
```tsx
|
|
1292
|
+
// Gap entre elementos cria respiracao visual
|
|
1293
|
+
<div className="flex gap-4">
|
|
1294
|
+
<Card>Projeto A</Card>
|
|
1295
|
+
<Card>Projeto B</Card>
|
|
1296
|
+
<Card>Projeto C</Card>
|
|
1297
|
+
</div>
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
**Limitacao:** Elementos intencionalmente adjacentes (ex: grupo de botoes conectados, tabs) nao precisam de gap. Avaliar contexto do layout. Componentes de UI library podem gerenciar spacing internamente.
|
|
1301
|
+
|
|
1302
|
+
---
|
|
1303
|
+
|
|
1304
|
+
### CONTRASTE-INSUFICIENTE
|
|
1305
|
+
|
|
1306
|
+
**Heuristica de Nielsen:** #8 - Design estetico e minimalista / Acessibilidade WCAG
|
|
1307
|
+
**Frameworks:** All
|
|
1308
|
+
**Impacto tipico:** M
|
|
1309
|
+
**Sinal de deteccao:**
|
|
1310
|
+
```bash
|
|
1311
|
+
# Heuristica aproximada: procurar texto com cores claras em contexto de fundo claro
|
|
1312
|
+
# Cores de texto problematicas tipicas: cinzas muito claros
|
|
1313
|
+
grep -rn "color:\s*#[a-fA-F0-9]\{3,6\}" src/ --include="*.css" --include="*.scss"
|
|
1314
|
+
# Tailwind: procurar classes de texto claro que podem ter contraste baixo
|
|
1315
|
+
grep -rn "text-gray-300\|text-gray-200\|text-gray-100\|text-slate-300\|text-slate-200" src/ --include="*.tsx" --include="*.jsx"
|
|
1316
|
+
# NOTA: esta heuristica e MUITO aproximada -- contraste depende da combinacao fundo + texto
|
|
1317
|
+
# Focar em: texto cinza claro (#ccc, #ddd, gray-300) em componentes SEM fundo escuro
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
**Problema em codigo:**
|
|
1321
|
+
```tsx
|
|
1322
|
+
// Texto cinza claro em fundo branco -- dificil de ler
|
|
1323
|
+
<p className="text-gray-300">Informacao importante sobre o produto</p>
|
|
1324
|
+
// gray-300 = #d1d5db em fundo branco = contraste ~1.8:1 (WCAG requer 4.5:1)
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
**Solucao:**
|
|
1328
|
+
```tsx
|
|
1329
|
+
// Cor de texto com contraste adequado
|
|
1330
|
+
<p className="text-gray-600">Informacao importante sobre o produto</p>
|
|
1331
|
+
// gray-600 = #4b5563 em fundo branco = contraste ~7:1 (passa WCAG AA)
|
|
1332
|
+
```
|
|
1333
|
+
|
|
1334
|
+
**Limitacao:** Analise estatica NAO pode calcular contraste real porque depende do fundo efetivo (que pode vir de componente pai, tema, ou CSS em cascata). Esta heuristica so detecta cores conhecidamente problematicas (gray-100/200/300 em contexto sem fundo escuro). False positives sao esperados em dark mode.
|
|
1335
|
+
|
|
1336
|
+
</category>
|
|
1337
|
+
|
|
1338
|
+
<category name="erros-recuperacao">
|
|
1339
|
+
## Recuperacao de Erros
|
|
1340
|
+
|
|
1341
|
+
Heuristicas relacionadas a Heuristica #9 de Nielsen: "Ajudar usuarios a reconhecer, diagnosticar e recuperar de erros". Mensagens de erro devem ser expressas em linguagem simples, indicar precisamente o problema e sugerir construtivamente uma solucao.
|
|
1342
|
+
|
|
1343
|
+
Em analise estatica, detectamos ausencia de recuperacao verificando se blocos de erro exibem informacao ao usuario e oferecem acoes de recuperacao.
|
|
1344
|
+
|
|
1345
|
+
### CATCH-SEM-UI-ERRO
|
|
1346
|
+
|
|
1347
|
+
**Heuristica de Nielsen:** #9 - Ajudar usuarios a reconhecer, diagnosticar e recuperar de erros
|
|
1348
|
+
**Frameworks:** All
|
|
1349
|
+
**Impacto tipico:** G
|
|
1350
|
+
**Sinal de deteccao:**
|
|
1351
|
+
```bash
|
|
1352
|
+
# Procurar catch blocks que nao setam estado de erro para UI
|
|
1353
|
+
grep -rn "catch\s*(" src/ --include="*.tsx" --include="*.jsx" --include="*.vue"
|
|
1354
|
+
# No mesmo handler/funcao, verificar se existe setError, setErrorMessage, ou equivalente
|
|
1355
|
+
# Se catch existe mas so faz console.log/console.error sem setar estado de UI -> problema
|
|
1356
|
+
grep -rn "catch.*console\.\(log\|error\)" src/ --include="*.tsx" --include="*.jsx"
|
|
1357
|
+
```
|
|
1358
|
+
|
|
1359
|
+
**Problema em codigo:**
|
|
1360
|
+
```tsx
|
|
1361
|
+
// Erro engolido silenciosamente -- usuario nao sabe que a acao falhou
|
|
1362
|
+
async function handleSave(data: FormData) {
|
|
1363
|
+
try {
|
|
1364
|
+
await api.saveProfile(data);
|
|
1365
|
+
} catch (error) {
|
|
1366
|
+
console.error("Failed to save:", error);
|
|
1367
|
+
// Nenhum feedback visual -- usuario acha que salvou
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
**Solucao:**
|
|
1373
|
+
```tsx
|
|
1374
|
+
// Erro comunicado ao usuario com acao de recuperacao
|
|
1375
|
+
const [error, setError] = useState<string | null>(null);
|
|
1376
|
+
|
|
1377
|
+
async function handleSave(data: FormData) {
|
|
1378
|
+
try {
|
|
1379
|
+
setError(null);
|
|
1380
|
+
await api.saveProfile(data);
|
|
1381
|
+
toast.success("Perfil salvo com sucesso");
|
|
1382
|
+
} catch (err) {
|
|
1383
|
+
console.error("Failed to save:", err);
|
|
1384
|
+
setError("Nao foi possivel salvar o perfil. Verifique sua conexao e tente novamente.");
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
return (
|
|
1389
|
+
<>
|
|
1390
|
+
{error && <Alert variant="destructive">{error}</Alert>}
|
|
1391
|
+
<form onSubmit={handleSave}>...</form>
|
|
1392
|
+
</>
|
|
1393
|
+
);
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
**Limitacao:** Catch blocks em funcoes utilitarias/services (nao componentes) nao precisam de UI -- o erro deve ser propagado para o componente tratar. Focar em catch blocks dentro de componentes ou handlers de evento.
|
|
1397
|
+
|
|
1398
|
+
---
|
|
1399
|
+
|
|
1400
|
+
### ERROR-BOUNDARY-AUSENTE
|
|
1401
|
+
|
|
1402
|
+
**Heuristica de Nielsen:** #9 - Ajudar usuarios a reconhecer, diagnosticar e recuperar de erros
|
|
1403
|
+
**Frameworks:** React
|
|
1404
|
+
**Impacto tipico:** G
|
|
1405
|
+
**Sinal de deteccao:**
|
|
1406
|
+
```bash
|
|
1407
|
+
# Verificar se projeto React tem ErrorBoundary
|
|
1408
|
+
grep -rn "ErrorBoundary\|componentDidCatch\|getDerivedStateFromError" src/ --include="*.tsx" --include="*.jsx"
|
|
1409
|
+
# Next.js App Router: verificar error.tsx
|
|
1410
|
+
find src/app -name "error.tsx" -o -name "error.jsx" 2>/dev/null
|
|
1411
|
+
# Se projeto React sem nenhum ErrorBoundary ou error.tsx -> problema
|
|
1412
|
+
# Um erro nao tratado em qualquer componente quebra a app inteira
|
|
1413
|
+
```
|
|
1414
|
+
|
|
1415
|
+
**Problema em codigo:**
|
|
1416
|
+
```
|
|
1417
|
+
// Nenhum ErrorBoundary no projeto React
|
|
1418
|
+
// Se qualquer componente lanca erro durante render, toda a app fica em branco
|
|
1419
|
+
// Usuario ve tela branca sem explicacao e sem forma de recuperar
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
**Solucao:**
|
|
1423
|
+
```tsx
|
|
1424
|
+
// ErrorBoundary envolve areas criticas da app
|
|
1425
|
+
// app/error.tsx (Next.js App Router)
|
|
1426
|
+
"use client";
|
|
1427
|
+
|
|
1428
|
+
export default function Error({
|
|
1429
|
+
error,
|
|
1430
|
+
reset,
|
|
1431
|
+
}: {
|
|
1432
|
+
error: Error;
|
|
1433
|
+
reset: () => void;
|
|
1434
|
+
}) {
|
|
1435
|
+
return (
|
|
1436
|
+
<div className="flex flex-col items-center justify-center min-h-screen">
|
|
1437
|
+
<h2 className="text-2xl font-bold">Algo deu errado</h2>
|
|
1438
|
+
<p className="mt-2 text-gray-600">{error.message}</p>
|
|
1439
|
+
<button onClick={reset} className="mt-4 px-4 py-2 bg-blue-500 text-white rounded">
|
|
1440
|
+
Tentar novamente
|
|
1441
|
+
</button>
|
|
1442
|
+
</div>
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
```
|
|
1446
|
+
|
|
1447
|
+
**Limitacao:** Aplicar apenas a projetos React. Vue e Svelte tem mecanismos diferentes de tratamento de erros (`errorCaptured` no Vue, `onError` no Svelte). Next.js App Router com `error.tsx` substitui ErrorBoundary customizado.
|
|
1448
|
+
|
|
1449
|
+
---
|
|
1450
|
+
|
|
1451
|
+
### RETRY-AUSENTE
|
|
1452
|
+
|
|
1453
|
+
**Heuristica de Nielsen:** #9 - Ajudar usuarios a reconhecer, diagnosticar e recuperar de erros
|
|
1454
|
+
**Frameworks:** All
|
|
1455
|
+
**Impacto tipico:** M
|
|
1456
|
+
**Sinal de deteccao:**
|
|
1457
|
+
```bash
|
|
1458
|
+
# Procurar tratamento de erros de rede sem opcao de retry
|
|
1459
|
+
grep -rn "fetch\|axios\|useMutation\|useQuery" src/ --include="*.tsx" --include="*.jsx"
|
|
1460
|
+
# Verificar se componentes com erro de rede oferecem botao de retry/tentar novamente
|
|
1461
|
+
grep -rn "retry\|tentar novamente\|try again\|refetch\|reset" src/ --include="*.tsx" --include="*.jsx"
|
|
1462
|
+
# Se operacoes de rede existem com UI de erro mas sem acao de retry -> problema
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
**Problema em codigo:**
|
|
1466
|
+
```tsx
|
|
1467
|
+
// Erro sem opcao de retry -- usuario precisa recarregar a pagina inteira
|
|
1468
|
+
if (error) {
|
|
1469
|
+
return <p className="text-red-500">Erro ao carregar dados</p>;
|
|
1470
|
+
}
|
|
1471
|
+
```
|
|
1472
|
+
|
|
1473
|
+
**Solucao:**
|
|
1474
|
+
```tsx
|
|
1475
|
+
// Botao de retry permite recuperacao sem recarregar pagina
|
|
1476
|
+
if (error) {
|
|
1477
|
+
return (
|
|
1478
|
+
<div className="text-center">
|
|
1479
|
+
<p className="text-red-500">Erro ao carregar dados</p>
|
|
1480
|
+
<button
|
|
1481
|
+
onClick={() => refetch()}
|
|
1482
|
+
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
|
|
1483
|
+
>
|
|
1484
|
+
Tentar novamente
|
|
1485
|
+
</button>
|
|
1486
|
+
</div>
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
**Limitacao:** Erros de validacao (400) nao devem ter retry -- o usuario precisa corrigir o input. Retry e para erros de rede (timeout, 500, offline). Verificar o tipo de erro antes de sugerir retry. React Query/SWR tem retry automatico configuravel.
|
|
1492
|
+
|
|
1493
|
+
---
|
|
1494
|
+
|
|
1495
|
+
### ERRO-PAGE-GENERICA
|
|
1496
|
+
|
|
1497
|
+
**Heuristica de Nielsen:** #9 - Ajudar usuarios a reconhecer, diagnosticar e recuperar de erros
|
|
1498
|
+
**Frameworks:** All
|
|
1499
|
+
**Impacto tipico:** M
|
|
1500
|
+
**Sinal de deteccao:**
|
|
1501
|
+
```bash
|
|
1502
|
+
# Verificar paginas de erro existentes
|
|
1503
|
+
find src/ -name "error.*" -o -name "Error.*" -o -name "_error.*" 2>/dev/null
|
|
1504
|
+
# Verificar se paginas de erro tem informacao util e acoes de recuperacao
|
|
1505
|
+
grep -rn "error\|Error" src/app/error.tsx src/pages/_error.tsx 2>/dev/null
|
|
1506
|
+
# Se pagina de erro existe mas so mostra "Algo deu errado" sem acao -> problema
|
|
1507
|
+
# Verificar se tem: botao de retry, link para home, informacao do erro
|
|
1508
|
+
```
|
|
1509
|
+
|
|
1510
|
+
**Problema em codigo:**
|
|
1511
|
+
```tsx
|
|
1512
|
+
// Pagina de erro sem informacao util ou acao de recuperacao
|
|
1513
|
+
export default function Error() {
|
|
1514
|
+
return <h1>Erro</h1>;
|
|
1515
|
+
}
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1518
|
+
**Solucao:**
|
|
1519
|
+
```tsx
|
|
1520
|
+
// Pagina de erro com contexto e acoes de recuperacao
|
|
1521
|
+
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
|
1522
|
+
return (
|
|
1523
|
+
<div className="flex flex-col items-center justify-center min-h-screen gap-4">
|
|
1524
|
+
<h1 className="text-3xl font-bold">Algo deu errado</h1>
|
|
1525
|
+
<p className="text-gray-600 max-w-md text-center">
|
|
1526
|
+
Houve um problema ao carregar esta pagina.
|
|
1527
|
+
Se o erro persistir, entre em contato com o suporte.
|
|
1528
|
+
</p>
|
|
1529
|
+
<div className="flex gap-2">
|
|
1530
|
+
<button onClick={reset} className="px-4 py-2 bg-blue-500 text-white rounded">
|
|
1531
|
+
Tentar novamente
|
|
1532
|
+
</button>
|
|
1533
|
+
<a href="/" className="px-4 py-2 border rounded">
|
|
1534
|
+
Voltar ao inicio
|
|
1535
|
+
</a>
|
|
1536
|
+
</div>
|
|
1537
|
+
</div>
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
```
|
|
1541
|
+
|
|
1542
|
+
**Limitacao:** Em ambiente de desenvolvimento, paginas de erro detalhadas com stack trace sao uteis. Esta heuristica aplica-se a paginas de erro em producao. Verificar se existe diferenciacao dev/prod.
|
|
1543
|
+
|
|
1544
|
+
</category>
|