ganbatte-os 0.2.36 → 0.2.38

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.
Files changed (39) hide show
  1. package/.gos/agents/profiles/ganbatte-os-master.md +113 -0
  2. package/.gos/libraries/caveman-rules.md +58 -0
  3. package/.gos/libraries/cloudflare-stack-kb.md +161 -0
  4. package/.gos/libraries/default-stack-kb.md +98 -0
  5. package/.gos/libraries/engineering-best-practices.md +208 -0
  6. package/.gos/libraries/gos-compress-setup.md +62 -0
  7. package/.gos/libraries/intake-questions-mom-test.md +91 -0
  8. package/.gos/libraries/lucide-icons-policy.md +174 -0
  9. package/.gos/libraries/security-best-practices.md +138 -0
  10. package/.gos/libraries/supabase-stack-kb.md +124 -0
  11. package/.gos/libraries/timer-pattern-spec.md +252 -0
  12. package/.gos/libraries/typeform-pattern-spec.md +204 -0
  13. package/.gos/libraries/ui-guardrails-checklist.md +144 -0
  14. package/.gos/libraries/visual-diff-lenses.md +114 -0
  15. package/.gos/skills/adr-tech-decisions/SKILL.md +166 -0
  16. package/.gos/skills/audit-screenshots/SKILL.md +219 -0
  17. package/.gos/skills/cloudflare-pages-setup/SKILL.md +180 -0
  18. package/.gos/skills/execute-plan/SKILL.md +5 -0
  19. package/.gos/skills/figma-print-diff/SKILL.md +165 -0
  20. package/.gos/skills/gos-caveman/SKILL.md +110 -0
  21. package/.gos/skills/gos-compress/SKILL.md +134 -0
  22. package/.gos/skills/gos-compress/scripts/compress.py +346 -0
  23. package/.gos/skills/gos-compress/scripts/setup.py +91 -0
  24. package/.gos/skills/idea-intake/SKILL.md +147 -0
  25. package/.gos/skills/plan-blueprint/SKILL.md +48 -3
  26. package/.gos/skills/plan-to-tasks/SKILL.md +28 -0
  27. package/.gos/skills/prd-from-intake/SKILL.md +94 -0
  28. package/.gos/skills/prototype-orchestrator/SKILL.md +120 -0
  29. package/.gos/skills/registry.json +13 -1
  30. package/.gos/skills/timer-component-pattern/SKILL.md +245 -0
  31. package/.gos/skills/typeform-form-pattern/SKILL.md +210 -0
  32. package/.gos/skills/ui-guardrails/SKILL.md +111 -0
  33. package/.gos/skills/validate-plan/SKILL.md +2 -0
  34. package/.gos/templates/intakeTemplate.md +41 -0
  35. package/.gos/templates/planTemplate.md +21 -0
  36. package/.gos/templates/prdLeanTemplate.md +40 -0
  37. package/.gos/templates/taskTemplate.md +2 -0
  38. package/CLAUDE.md +9 -1
  39. package/package.json +1 -1
@@ -0,0 +1,252 @@
1
+ # Timer Pattern — Spec abstrata
2
+
3
+ > Abstracao do timer-de-estudo passo-a-passo, baseada em `code-kata-timer/script.js`
4
+ > + `code-kata-timer/style.css`. Adaptado para React + TypeScript + Tailwind.
5
+ >
6
+ > Use quando o usuario pedir contador regressivo, timer de pomodoro, code kata,
7
+ > stopwatch com countdown, ou qualquer interface de tempo com play/pause/edit.
8
+
9
+ ## State machine
10
+
11
+ ```ts
12
+ export const TimerStatus = {
13
+ STOPPED: 'STOPPED',
14
+ COUNTDOWN: 'COUNTDOWN', // animacao 3-2-1-GO antes de iniciar
15
+ RUNNING: 'RUNNING',
16
+ PAUSED: 'PAUSED',
17
+ EDITING: 'EDITING', // usuario editando valor
18
+ } as const;
19
+ export type TimerStatusType = typeof TimerStatus[keyof typeof TimerStatus];
20
+
21
+ export const isRunning = (s: TimerStatusType) =>
22
+ s === TimerStatus.RUNNING || s === TimerStatus.COUNTDOWN;
23
+ ```
24
+
25
+ Transicoes validas:
26
+ ```
27
+ STOPPED -> COUNTDOWN -> RUNNING -> PAUSED -> RUNNING
28
+ -> STOPPED
29
+ EDITING <-> STOPPED (so edita parado)
30
+ ```
31
+
32
+ ## Persistencia
33
+
34
+ LocalStorage com chave `timerSettings`:
35
+ ```ts
36
+ interface TimerSettings {
37
+ currentSeconds: number;
38
+ timerStatus: TimerStatusType; // sempre STOPPED ao recarregar
39
+ theme: 'dark' | 'light';
40
+ showContributors?: boolean; // app-specific, opcional
41
+ }
42
+ ```
43
+
44
+ **Regra critica** (do code-kata-timer original): ao carregar settings, **sempre forcar status para STOPPED** — nao restaurar RUNNING. Caso contrario timer "fantasma" continua contando.
45
+
46
+ ## Hook reutilizavel
47
+
48
+ ```ts
49
+ export function useTimer(defaultSeconds = 30) {
50
+ const [status, setStatus] = useState<TimerStatusType>(TimerStatus.STOPPED);
51
+ const [seconds, setSeconds] = useState(defaultSeconds);
52
+ const intervalRef = useRef<number | null>(null);
53
+
54
+ const start = useCallback(() => {
55
+ if (status === TimerStatus.RUNNING) return;
56
+ setStatus(TimerStatus.RUNNING);
57
+ intervalRef.current = window.setInterval(() => {
58
+ setSeconds(s => {
59
+ if (s <= 1) {
60
+ if (intervalRef.current) clearInterval(intervalRef.current);
61
+ setStatus(TimerStatus.STOPPED);
62
+ // dispatch event 'timer:done' para componentes ouvirem
63
+ window.dispatchEvent(new CustomEvent('timer:done'));
64
+ return 0;
65
+ }
66
+ return s - 1;
67
+ });
68
+ }, 1000);
69
+ }, [status]);
70
+
71
+ const pause = useCallback(() => {
72
+ if (intervalRef.current) clearInterval(intervalRef.current);
73
+ setStatus(TimerStatus.PAUSED);
74
+ }, []);
75
+
76
+ const reset = useCallback((to = defaultSeconds) => {
77
+ if (intervalRef.current) clearInterval(intervalRef.current);
78
+ setSeconds(to);
79
+ setStatus(TimerStatus.STOPPED);
80
+ }, [defaultSeconds]);
81
+
82
+ useEffect(() => {
83
+ return () => {
84
+ if (intervalRef.current) clearInterval(intervalRef.current);
85
+ };
86
+ }, []);
87
+
88
+ return { status, seconds, start, pause, reset, setSeconds };
89
+ }
90
+ ```
91
+
92
+ ## Display
93
+
94
+ Formato `MM:SS` com font monospace (Orbitron, JetBrains Mono ou Tailwind `font-mono`):
95
+
96
+ ```tsx
97
+ function formatTime(s: number) {
98
+ const m = Math.floor(s / 60);
99
+ const ss = s % 60;
100
+ return `${String(m).padStart(2, '0')}:${String(ss).padStart(2, '0')}`;
101
+ }
102
+
103
+ <div className="text-7xl md:text-9xl font-mono font-bold tabular-nums">
104
+ {formatTime(seconds)}
105
+ </div>
106
+ ```
107
+
108
+ `tabular-nums` garante que digitos nao "pulem" durante a contagem.
109
+
110
+ ## Controles (Lucide React)
111
+
112
+ ```tsx
113
+ import { Play, Pause, RotateCcw, Pencil } from "lucide-react";
114
+
115
+ <div className="flex gap-3 justify-center">
116
+ {status === TimerStatus.STOPPED || status === TimerStatus.PAUSED ? (
117
+ <Button onClick={start} size="lg" aria-label="Iniciar">
118
+ <Play className="h-5 w-5" />
119
+ </Button>
120
+ ) : (
121
+ <Button onClick={pause} size="lg" aria-label="Pausar">
122
+ <Pause className="h-5 w-5" />
123
+ </Button>
124
+ )}
125
+ <Button onClick={() => reset()} size="lg" variant="outline" aria-label="Resetar">
126
+ <RotateCcw className="h-5 w-5" />
127
+ </Button>
128
+ <Button
129
+ onClick={() => setStatus(TimerStatus.EDITING)}
130
+ size="lg"
131
+ variant="outline"
132
+ aria-label="Editar tempo"
133
+ disabled={isRunning(status)}
134
+ >
135
+ <Pencil className="h-5 w-5" />
136
+ </Button>
137
+ </div>
138
+ ```
139
+
140
+ ## Atalhos de teclado
141
+
142
+ ```ts
143
+ useEffect(() => {
144
+ function onKey(e: KeyboardEvent) {
145
+ if (e.target instanceof HTMLInputElement) return; // nao interferir em editing
146
+ if (e.code === 'Space') { e.preventDefault(); status === TimerStatus.RUNNING ? pause() : start(); }
147
+ if (e.code === 'KeyR') reset();
148
+ if (e.code === 'KeyE') setStatus(TimerStatus.EDITING);
149
+ }
150
+ window.addEventListener('keydown', onKey);
151
+ return () => window.removeEventListener('keydown', onKey);
152
+ }, [status, start, pause, reset]);
153
+ ```
154
+
155
+ ## Tema
156
+
157
+ Variaveis CSS (root data-theme):
158
+ - `--timer-bg`: bg principal
159
+ - `--timer-fg`: cor do texto/digitos
160
+ - `--timer-accent`: cor do CTA primario
161
+
162
+ Toggle via `document.documentElement.dataset.theme = 'dark' | 'light'`. Persistir em `localStorage`.
163
+
164
+ ## Edicao inline
165
+
166
+ Ao entrar em `EDITING`:
167
+ - Display vira `<input type="number">` com mesmo styling do display.
168
+ - Enter -> salva e volta para `STOPPED`.
169
+ - Esc -> cancela e volta para `STOPPED`.
170
+
171
+ ```tsx
172
+ {status === TimerStatus.EDITING ? (
173
+ <input
174
+ type="number"
175
+ autoFocus
176
+ defaultValue={seconds}
177
+ className="text-7xl font-mono font-bold tabular-nums bg-transparent w-64 text-center"
178
+ onBlur={(e) => {
179
+ const v = Number(e.currentTarget.value);
180
+ if (v > 0) setSeconds(v);
181
+ setStatus(TimerStatus.STOPPED);
182
+ }}
183
+ onKeyDown={(e) => {
184
+ if (e.key === 'Enter') e.currentTarget.blur();
185
+ if (e.key === 'Escape') setStatus(TimerStatus.STOPPED);
186
+ }}
187
+ />
188
+ ) : (
189
+ <div>{formatTime(seconds)}</div>
190
+ )}
191
+ ```
192
+
193
+ ## Eventos customizados
194
+
195
+ Dispatch quando timer termina:
196
+ ```ts
197
+ window.dispatchEvent(new CustomEvent('timer:done', { detail: { duration } }));
198
+ ```
199
+
200
+ Componentes ouvem:
201
+ ```ts
202
+ useEffect(() => {
203
+ const handler = (e: Event) => {
204
+ const evt = e as CustomEvent<{ duration: number }>;
205
+ // mostrar toast, tocar som, etc
206
+ };
207
+ window.addEventListener('timer:done', handler);
208
+ return () => window.removeEventListener('timer:done', handler);
209
+ }, []);
210
+ ```
211
+
212
+ ## Som ao terminar (opcional)
213
+
214
+ Web Audio API simples — sem precisar arquivo:
215
+ ```ts
216
+ function beep(frequency = 800, duration = 200) {
217
+ const ctx = new AudioContext();
218
+ const osc = ctx.createOscillator();
219
+ const gain = ctx.createGain();
220
+ osc.connect(gain);
221
+ gain.connect(ctx.destination);
222
+ osc.frequency.value = frequency;
223
+ osc.start();
224
+ setTimeout(() => { osc.stop(); ctx.close(); }, duration);
225
+ }
226
+ ```
227
+
228
+ ## Estados visuais (UI guardrails)
229
+
230
+ - **Loading**: nao aplicavel (timer e estado puro).
231
+ - **Empty**: tempo zerado mostra `00:00` mas botao Iniciar disabled.
232
+ - **Error**: nao deve haver erro em runtime — se houver, fallback para reset.
233
+ - **Running indicator**: Lucide `Activity` pulsando ou borda do display animada.
234
+
235
+ ## Anti-patterns
236
+
237
+ - `setInterval` sem cleanup -> memory leak.
238
+ - Restaurar status RUNNING do localStorage -> timer fantasma.
239
+ - Sem `tabular-nums` -> digitos pulam visualmente.
240
+ - Sem atalho de teclado -> mau UX em uso intensivo.
241
+ - Emoji nos botoes -> usar Lucide.
242
+ - Sem dispatch de evento ao terminar -> componentes externos nao reagem.
243
+
244
+ ## Reuso no G-OS
245
+
246
+ `gos-master` reconhece pedidos:
247
+ - "timer", "contador regressivo", "countdown"
248
+ - "pomodoro", "pomodoro timer"
249
+ - "code kata timer", "kata"
250
+ - "stopwatch" (variacao — countup vs countdown)
251
+
252
+ Aciona skill `timer-component-pattern` que aplica spec acima.
@@ -0,0 +1,204 @@
1
+ # Typeform Pattern — Spec abstrata
2
+
3
+ > Abstracao do quiz/form passo-a-passo estilo Typeform, baseada em
4
+ > `body-metrics-edge/.examples/tsx/old/body-metrics-quiz-typeform.tsx`
5
+ > e `template-lp-wanderson/.a8z/skills/designkit/templates/form.tsx.template`.
6
+ >
7
+ > Use sempre que o usuario pedir form de coleta de dados, quiz, onboarding multi-step,
8
+ > survey, lead form, ou qualquer entrada estruturada que beneficia de **uma pergunta por vez**.
9
+
10
+ ## Por que esse pattern
11
+
12
+ - **Conversao 2-3x maior** que forms longos (uma tela por pergunta vs paredao de inputs).
13
+ - **Cognitive load baixo** — usuario ve so uma decisao por vez.
14
+ - **Mobile-first natural** — funciona perfeitamente em telas pequenas.
15
+ - **Estado intermediario salvavel** — refresh nao perde progresso.
16
+
17
+ ## Anatomia do pattern
18
+
19
+ ### 1. Question schema
20
+
21
+ ```ts
22
+ export type QuestionType = 'welcome' | 'text' | 'number' | 'choice' | 'multiChoice' | 'date' | 'review';
23
+
24
+ export interface QuestionOption {
25
+ value: string;
26
+ label: string;
27
+ description?: string;
28
+ iconName?: keyof typeof import('lucide-react'); // Lucide React (zero emoji)
29
+ }
30
+
31
+ export interface Question<F = Record<string, unknown>> {
32
+ id: string;
33
+ type: QuestionType;
34
+ field: keyof F | null;
35
+ question: string | ((data: F) => string); // pode ser dinamica
36
+ description?: string;
37
+ placeholder?: string;
38
+ suffix?: string;
39
+ decimal?: boolean;
40
+ buttonText?: string;
41
+ options?: QuestionOption[];
42
+ validate?: (value: unknown, data: F) => string | null; // null = ok, string = erro
43
+ showWhen?: (data: F) => boolean; // condicional
44
+ }
45
+ ```
46
+
47
+ ### 2. State
48
+
49
+ ```ts
50
+ const [currentIndex, setCurrentIndex] = useState(0);
51
+ const [data, setData] = useState<FormData>({});
52
+ const [isAnimating, setIsAnimating] = useState(false);
53
+ ```
54
+
55
+ Persistir em `sessionStorage` ou `Supabase row` se descartavel = false.
56
+
57
+ ### 3. Filtro de ativas (condicional)
58
+
59
+ ```ts
60
+ const activeQuestions = questions.filter(q => !q.showWhen || q.showWhen(data));
61
+ const currentQ = activeQuestions[currentIndex];
62
+ ```
63
+
64
+ ### 4. Navegacao
65
+
66
+ - `Enter` -> next (com validacao).
67
+ - `Shift+Enter` (em textarea) -> nova linha.
68
+ - `Esc` -> previous (opcional).
69
+ - Botao "Voltar" sempre visivel exceto na welcome.
70
+ - Animacao opacity+translate-y com 200-300ms entre transicoes.
71
+
72
+ ### 5. Componentes-tipo
73
+
74
+ **Welcome screen**
75
+ ```
76
+ - Titulo grande (text-4xl/5xl)
77
+ - Subtitulo
78
+ - CTA primario "Comecar" (Button size lg)
79
+ - Lucide icon ilustrativo (h-16 w-16)
80
+ ```
81
+
82
+ **Text input**
83
+ ```
84
+ - Question (text-2xl semibold)
85
+ - Description opcional (text-base muted-foreground)
86
+ - Input grande (h-12 ou h-14, text-lg)
87
+ - Hint "Pressione Enter para continuar" + CornerDownLeft icon
88
+ ```
89
+
90
+ **Number input**
91
+ ```
92
+ - Igual text input mas type="number"
93
+ - Suffix visual ao lado (ex: "anos", "kg", "cm")
94
+ - Decimal opt-in via prop
95
+ ```
96
+
97
+ **Choice (radio)**
98
+ ```
99
+ - Stack vertical de cards clicaveis
100
+ - Cada card: Lucide icon + label + description opcional
101
+ - Hover ring + active filled
102
+ - Selecao avanca automaticamente apos 300ms
103
+ ```
104
+
105
+ **MultiChoice (checkbox)**
106
+ ```
107
+ - Igual choice mas selecao acumula
108
+ - Botao "Continuar" obrigatorio (nao auto-avanca)
109
+ - Mostra contagem ("3 de 5 selecionados")
110
+ ```
111
+
112
+ **Review (final)**
113
+ ```
114
+ - Resumo dos dados coletados em cards
115
+ - Botao "Editar" por secao (volta para a question correspondente)
116
+ - CTA primario "Confirmar" / "Calcular" / "Enviar"
117
+ ```
118
+
119
+ ### 6. Animacao
120
+
121
+ ```tsx
122
+ <div className={`transition-all duration-300 ${
123
+ isAnimating ? 'opacity-0 translate-y-4' : 'opacity-100 translate-y-0'
124
+ }`}>
125
+ {/* question */}
126
+ </div>
127
+ ```
128
+
129
+ ### 7. Progress bar
130
+
131
+ Top of screen, fixed:
132
+ ```tsx
133
+ <div className="fixed top-0 left-0 right-0 h-1 bg-muted">
134
+ <div
135
+ className="h-full bg-primary transition-all duration-300"
136
+ style={{ width: `${((currentIndex + 1) / activeQuestions.length) * 100}%` }}
137
+ />
138
+ </div>
139
+ ```
140
+
141
+ ### 8. Validacao
142
+
143
+ Por question:
144
+ ```ts
145
+ {
146
+ id: 'email',
147
+ type: 'text',
148
+ field: 'email',
149
+ question: 'Qual seu email?',
150
+ validate: (value) => {
151
+ if (!value) return 'Campo obrigatorio';
152
+ if (!/^[^@]+@[^@]+\.[^@]+$/.test(String(value))) return 'Email invalido';
153
+ return null;
154
+ }
155
+ }
156
+ ```
157
+
158
+ Erro inline abaixo do input. Nunca toast/alert.
159
+
160
+ ### 9. Estados visuais (UI guardrails)
161
+
162
+ - **Loading** (submitting): botao disabled + Lucide `Loader2` com `animate-spin`.
163
+ - **Empty** (review sem dados): mostrar "Voce ainda nao respondeu — comece pelo inicio."
164
+ - **Error** (submit falhou): inline acima do CTA + botao retry.
165
+ - **Success**: tela final com Lucide `CheckCircle2` h-16 w-16 + redirect ou prox-step.
166
+
167
+ ### 10. Acessibilidade
168
+
169
+ - `aria-live="polite"` no container de question (anuncia mudanca pra screen reader).
170
+ - Focus auto no input ao mudar de question (`useEffect` + `inputRef.current?.focus()`).
171
+ - Labels via `<Label htmlFor>` ou `aria-label`.
172
+ - Botao voltar com `aria-label="Pergunta anterior"`.
173
+
174
+ ## Stack obrigatorio
175
+
176
+ - React 18+
177
+ - TypeScript strict
178
+ - Tailwind v4 + shadcn/ui (Button, Input, Card, Label)
179
+ - Lucide React (UNICA lib de icones — zero emoji)
180
+ - React Hook Form + Zod (validacao schema-first quando form complexo)
181
+
182
+ ## Variantes
183
+
184
+ - **Quiz com calculo** (body-metrics-quiz original): apos ultima question, calcula metricas e mostra resultado.
185
+ - **Lead form** (capturar email/whats): submita para Supabase.
186
+ - **Onboarding** (apos signup): salva preferencias na profiles table.
187
+ - **Survey publico** (NPS, feedback): tabela com RLS public-insert apenas.
188
+
189
+ ## Anti-patterns
190
+
191
+ - Mostrar todas perguntas de uma vez como form tradicional — defeats the purpose.
192
+ - Sem progress bar — usuario nao sabe quanto falta.
193
+ - Pular validacao no front — mau UX (back so pra forma).
194
+ - Auto-avanco SEM debounce em choice — usuario clica errado e ja avancou.
195
+ - Texto unicode/emoji nas opcoes — usar `iconName` Lucide.
196
+ - Sem estado de submitting no final — usuario clica enviar 3x.
197
+
198
+ ## Reuso no G-OS
199
+
200
+ Quando usuario pede "form de cadastro", "quiz", "onboarding", "captura de leads":
201
+ 1. `gos-master` sugere pattern typeform.
202
+ 2. Usuario aprova -> skill `typeform-form-pattern` gera estrutura base.
203
+ 3. Customiza questions[] do dominio.
204
+ 4. Estilizacao via tokens shadcn (sem hardcoded colors).
@@ -0,0 +1,144 @@
1
+ # UI Guardrails Checklist (detalhe)
2
+
3
+ > Detalhe expandido de cada item do `ui-guardrails`. Consultar quando uma violacao precisar ser explicada ao usuario.
4
+
5
+ ## A — Estados visuais
6
+
7
+ ### A1 — Loading
8
+ - Tela inteira: skeleton com a estrutura visual (header skeleton, lista de cards skeleton).
9
+ - Componente isolado (ex: botao salvando): spinner inline ou disabled state.
10
+ - Anti-padrao: tela em branco enquanto carrega.
11
+
12
+ Codegen exige no plan:
13
+ ```markdown
14
+ ### Estado: loading
15
+ - Skeleton: 3 cards 240x80px com bg-muted-foreground/10 + shimmer.
16
+ - Trigger: query.isPending
17
+ - Duracao max: 3s antes de error.
18
+ ```
19
+
20
+ ### A2 — Empty
21
+ - Copy explicando POR QUE esta vazio.
22
+ - Ilustracao OU icone grande.
23
+ - CTA primario para sair do empty (criar item, importar, etc.).
24
+ - Anti-padrao: "Nenhum dado" sem nada mais.
25
+
26
+ Codegen exige:
27
+ ```markdown
28
+ ### Estado: empty
29
+ - Copy: "Voce ainda nao tem nenhum projeto. Comece criando um."
30
+ - Icone: FolderPlus 48px text-muted-foreground.
31
+ - CTA: "Criar primeiro projeto" (Button variant=primary).
32
+ ```
33
+
34
+ ### A3 — Error
35
+ - Mensagem clara (nao "erro 500").
36
+ - Acao de recovery (tentar de novo, voltar, contato).
37
+ - Distincao: erro recuperavel vs nao-recuperavel.
38
+
39
+ ```markdown
40
+ ### Estado: error
41
+ - Tipo recuperavel: toast variant=error + retry inline.
42
+ - Tipo nao-recuperavel: tela full com copy + CTA voltar.
43
+ ```
44
+
45
+ ### A4 — Success (quando aplicavel)
46
+ - Toast inline ou success state pos-form.
47
+ - Auto-dismiss em 3-5s OU acao explicita do usuario.
48
+
49
+ ### A5 — Default
50
+ - Estado normal, dados presentes.
51
+
52
+ ## B — Responsividade
53
+
54
+ ### Breakpoints obrigatorios
55
+ - Mobile: <768px (preferencia: comeca daqui — mobile-first).
56
+ - Tablet: 768-1024px.
57
+ - Desktop: 1024-1920px.
58
+
59
+ ### Comportamentos esperados
60
+ - Sidebar -> drawer no mobile.
61
+ - Tabela -> cards no mobile.
62
+ - Drawer right -> bottom sheet no mobile.
63
+ - Tipografia escala (text-base mobile, text-lg desktop ou similar).
64
+
65
+ ### Codegen exige no plan
66
+ ```markdown
67
+ ### Responsividade
68
+ - Mobile: lista vira cards verticais empilhados, sem hover.
69
+ - Tablet: 2 colunas de cards.
70
+ - Desktop: tabela tradicional com hover.
71
+ ```
72
+
73
+ ## C — Acessibilidade
74
+
75
+ ### Roles ARIA
76
+ - `<button>` para acao -> nao usar `<div onClick>`.
77
+ - `<dialog>` ou `role="dialog"` + aria-labelledby para modals.
78
+ - `role="listbox"` + `role="option"` em selects custom.
79
+
80
+ ### Labels
81
+ - Input sem `<label>` visivel: usar `aria-label`.
82
+ - Icon-only button: `aria-label="Fechar"` obrigatorio.
83
+
84
+ ### Focus
85
+ - Modal aberto: focus vai para primeiro elemento focavel.
86
+ - Modal fechado: focus volta para trigger.
87
+ - Tab order segue ordem visual.
88
+ - Focus indicator distinto de hover (outline 2px ring-primary).
89
+
90
+ ### Contraste
91
+ - AA: 4.5:1 texto, 3:1 UI.
92
+ - Erro inline NAO so vermelho — adicionar icone/texto "Erro:".
93
+
94
+ ## D — Tokens do DS
95
+
96
+ ### Cores
97
+ - OK: `bg-primary`, `text-foreground`, `border-border`.
98
+ - Anti: `bg-[#3b82f6]`, `text-blue-500` quando primary existe.
99
+
100
+ ### Spacing
101
+ - OK: `gap-2`, `p-4`, `mt-6`.
102
+ - Anti: `gap-[7px]`, `p-[13px]`.
103
+
104
+ ### Typography
105
+ - OK: `text-sm`, `font-semibold`, `leading-tight`.
106
+ - Anti: `text-[13px]`, `tracking-[0.02em]` quando token existe.
107
+
108
+ ### Radius
109
+ - OK: `rounded`, `rounded-md`, `rounded-lg`.
110
+ - Anti: `rounded-[7px]`.
111
+
112
+ ### Excecoes permitidas (com justificativa)
113
+ - Animacao especifica nao-mapeada: `[animation-delay:120ms]` OK.
114
+ - Posicionamento dinamico: `top-[calc(100%+8px)]` OK.
115
+
116
+ ## E — Interacao
117
+
118
+ ### Trigger declaration
119
+ Cada elemento interativo precisa:
120
+ - Trigger: o que dispara (click, hover, focus, keypress, mount, query)
121
+ - Acao: que callback/mutation roda
122
+ - Resultado: qual estado muda + UI feedback
123
+
124
+ ### Edge cases obrigatorios
125
+ - Double-click rapido em CTA -> debounce 300ms ou disabled durante mutation.
126
+ - Mutation lenta (>3s) -> mostrar loading inline ou progress.
127
+ - Mutation falha -> rollback ou error state com retry.
128
+ - Concorrencia (2 usuarios editando) -> conflict resolution declarado.
129
+
130
+ ## Self-check rapido
131
+
132
+ Antes de submeter tela para codegen:
133
+
134
+ ```
135
+ [ ] A1 loading state declarado e visualmente coerente?
136
+ [ ] A2 empty state existe + CTA primaria?
137
+ [ ] A3 error state com recovery?
138
+ [ ] B responsividade nos 3 breakpoints?
139
+ [ ] C aria-label em icon buttons + focus visivel?
140
+ [ ] D 0 valores hardcoded fora do DS?
141
+ [ ] E todos os triggers tem acao + resultado + edge cases?
142
+ ```
143
+
144
+ Falhou em algum -> NAO codar ainda. Volte ao plano.
@@ -0,0 +1,114 @@
1
+ # Visual Diff Lenses
2
+
3
+ > Referencia das 6 lenses usadas por `figma-print-diff` e `audit-screenshots`. Inspirado em Nielsen Heuristics + design-critique (uxdudu) + WCAG.
4
+
5
+ ## Lens 1 — Layout & hierarquia
6
+
7
+ Pergunta-mestra: "A estrutura espacial bate?"
8
+
9
+ Sub-checks:
10
+ - Containers principais (sidebar, header, footer, main) presentes em ambos?
11
+ - Ordem de filhos dentro de cada container coincide?
12
+ - Proporcoes (sidebar 240px, main 1fr) coerentes?
13
+ - Densidade visual (cards/lista) similar?
14
+
15
+ Severity guide:
16
+ - high: container ausente/extra
17
+ - medium: ordem trocada
18
+ - low: proporcao 10-20% diferente
19
+
20
+ ## Lens 2 — Tokens visuais
21
+
22
+ Pergunta-mestra: "Cor, tipo, espacamento batem com o DS?"
23
+
24
+ Sub-checks:
25
+ - Cor de background dos blocos
26
+ - Cor de texto (primary, secondary, muted)
27
+ - Borders e radius
28
+ - Tipografia (family, size, weight, line-height)
29
+ - Spacing (padding/margin/gap)
30
+ - Shadows e elevations
31
+
32
+ Severity guide:
33
+ - high: cor de acao primaria errada
34
+ - medium: spacing 2x diferente
35
+ - low: radius cosmetico
36
+
37
+ ## Lens 3 — Estados
38
+
39
+ Pergunta-mestra: "Todos os estados estao implementados?"
40
+
41
+ Sub-checks:
42
+ - Loading (skeleton, spinner)
43
+ - Empty (placeholder, CTA)
44
+ - Error (mensagem, recovery)
45
+ - Success (toast, inline)
46
+ - Hover/Focus/Active
47
+ - Disabled
48
+
49
+ Severity guide:
50
+ - high: empty/error nao implementado
51
+ - medium: hover sem feedback
52
+ - low: focus ring cosmetico
53
+
54
+ ## Lens 4 — Conteudo
55
+
56
+ Pergunta-mestra: "Textos e visuais coincidem?"
57
+
58
+ Sub-checks:
59
+ - Textos literais (sem inventar traducao)
60
+ - Icones (mesma lib, mesma variante)
61
+ - Imagens (placeholder vs real)
62
+ - Dados mockados (formato, plausibilidade)
63
+
64
+ Severity guide:
65
+ - high: copy errada que altera significado
66
+ - medium: icone trocado
67
+ - low: lorem ipsum esquecido
68
+
69
+ ## Lens 5 — Interacao (inferida do estatico)
70
+
71
+ Pergunta-mestra: "Affordances batem?"
72
+
73
+ Sub-checks:
74
+ - Cursor pointer onde Figma mostra elemento "clicavel"
75
+ - Botoes com aparencia de botao (nao texto plano)
76
+ - Inputs com aparencia de input (border, placeholder)
77
+ - Tooltips/popovers posicionados corretamente quando visiveis
78
+
79
+ Severity guide:
80
+ - high: botao primario sem aparencia de botao
81
+ - medium: link disfarcado de texto
82
+ - low: cursor em element nao-clicavel
83
+
84
+ ## Lens 6 — Acessibilidade visual
85
+
86
+ Pergunta-mestra: "Acessivel ao olhar?"
87
+
88
+ Sub-checks:
89
+ - Contraste AA (4.5:1 texto, 3:1 UI components)
90
+ - Toque minimo 44x44px em mobile
91
+ - Focus indicator distinto de hover
92
+ - Texto sem so cor (icone + cor para indicar erro)
93
+
94
+ Severity guide:
95
+ - high: contraste reprovado em texto principal
96
+ - medium: toque <44px em mobile
97
+ - low: focus mesmo cor que hover
98
+
99
+ ## Composicao das lenses
100
+
101
+ Ordem fixa: 1 -> 2 -> 3 -> 4 -> 5 -> 6.
102
+
103
+ Por que essa ordem?
104
+ - Layout antes de token (sem layout, token nao importa).
105
+ - Token antes de estado (estado depende de tokens corretos).
106
+ - Estado antes de conteudo (conteudo so faz sentido com estado completo).
107
+ - Interacao depois de conteudo (precisa ver afford).
108
+ - A11y por ultimo (gate final).
109
+
110
+ ## Anti-patterns
111
+
112
+ - Pular lens 3: time esquece estados, codegen sai sem loading/empty/error.
113
+ - Inverter 1 e 2: comeca medindo cor e nao percebe que falta sidebar inteira.
114
+ - Severity hyperinflation: tudo high -> nada e high.