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.
@@ -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>