frontend — telas que funcionam para todos os usuários, em todos os estados e que permanecem fáceis de alterar
Siga primeiro o CLAUDE.md, docs/conventions.md e docs/design-system.md do repositório; este skill
preenche lacunas e nunca os substitui. O trabalho visual (tokens, layout, cor, CSS) pertence ao
css-design; executar o aplicativo como o usuário fará e o veredito final pertencem ao verify-app.
Escolha a stack. Vue 3 e React são ambos cidadãos de primeira classe e seguem as mesmas regras. Use o
framework que o repositório já possui; para um novo aplicativo, use o padrão frontend do perfil de entrega
(veja o README do repositório), caso contrário, pergunte uma vez na triagem. Vue: TypeScript + Vite (npm create vue@latest -- --ts --router --pinia --vitest --playwright --eslint --prettier) com references/vue-patterns.md.
React: TypeScript + Vite (ou o meta-framework do repositório) com references/react-patterns.md. Nunca
misture frameworks em um único aplicativo. Precisa de SSR ou SSG para SEO? Registre Nuxt ou Next.js em um ADR (architect);
este skill cobre as partes do lado do cliente de qualquer forma.
Fluxo de trabalho
- Carregue as convenções e o inventário do repositório. Leia
CLAUDE.md,docs/conventions.md,docs/design-system.md; liste as partes reutilizáveis (ls src/components src/features src/composables src/hooks src/stores). Detecte a camada de dados, roteador, bibliotecas de formulário e i18n (Vue:grep -E '"(@tanstack/vue-query|pinia|vue-router|vee-validate|vue-i18n)"' package.json; React:grep -E '"(@tanstack/react-query|@tanstack/react-router|react-router|react-hook-form|i18next|react-intl)"' package.json). Nunca adicione uma segunda camada de dados, roteador, store, formulário ou biblioteca i18n. Evidência: as notas da história listam os componentes e composables/hooks reutilizados e qualquer novo, com o motivo. - Escreva a tabela de estados antes do código. Para cada visão: cada dado e onde ele vive —
servidor (query), URL (parâmetros de rota / search), cliente compartilhado (store Pinia, ou React context ou a
store do repositório), local (
ref/useState), derivado (computed/ uma const em render), externo (um composable ouuseSyncExternalStoreenvolvendo a API do navegador). Em seguida, liste os quatro estados (carregando, vazio, erro, sucesso) e as chaves de cópia para cada um. Evidência: a tabela na história. - Escreva os testes que falham primeiro. Vitest +
@testing-library/vueou@testing-library/react,userEvent.setup(), consultas por papel e nome acessível, um comportamento por teste, rede simulada no limite do serviço (MSW ou o cliente falso do repositório emsrc/test-utils). Cubra cada estado, o caminho do teclado e um caso negativo. Evidência: a execução mostra vermelho pelo motivo certo. - Construa dentro da pasta de feature.
src/features/<nome>/services(chamadas de API tipadas, sem framework) →composables/hooks(useXenvolvendouseQuery/useMutatione uma fábrica de chaves) →components(Nome.vuecom<script setup lang="ts">, ouNome.tsx) → a visão da rota que os compõe. Props e eventos tipados, semany, sem non-null!, imports com@/. Evidência:git diff --statpermanece dentro da feature mais alterações deliberadas compartilhadas. - Cópia e formatação. Cada string visível vem dos arquivos de localidade (vue-i18n
useI18n(), ou a biblioteca i18n do repositório React ou módulo de cópia) com as mesmas chaves em cada localidade; um teste garante paridade. Números e datas através do formatador i18n ouIntl. Evidência: teste de paridade verde; sem cópia hard-coded, garantido por lint (@intlify/vue-i18n/no-raw-text; React:react/jsx-no-literalsou a regra do plugin i18n) — cite o código de saída do lint. - Estilize com
css-design. Apenas tokens, BEM block = nome do componente, todos os estados estilizados; estilos.vuecom lint através depostcss-html. Evidência: Stylelint verde. - Histórias (bibliotecas de componentes). Uma história por estado, ambos os temas; Storybook axe passa. Evidência: os testes de história e a execução do axe.
- Gates, encadeados por código de saída.
npm run lint && npm run type-check && npm run test:unit -- --run(Vue:vue-tsc --build; React:tsc -b), oumake ci; nunca julgue a partir de| tail. Evidência: código de saída 0 e cobertura igual ou acima do limite do repositório. - Jornada em um navegador real contra o build de produção.
npm run builde sirva a saída (vite preview, ou o servidor estático que o repositório usa). Adicione ou estenda uma especificação Playwright para o caminho principal com@axe-core/playwright(tagswcag2a wcag2aa wcag21aa wcag22aa, serious e critical = 0). Capturedocs/delivery/<slug>/evidence/<view>-390.pnge-1280.png, mais tema escuro se o aplicativo tiver um. Abra cada captura de tela antes de citá-la. Evidência: spec verde, PNGs. - Verificação de teclado e leitor de tela. Navegue pela nova visão com Tab: ordem, foco visível, Escape
fecha sobreposições e o foco retorna ao gatilho; após navegação, o foco pousa no novo título e o título mudou.
Verifique nomes e anúncios com um leitor de tela (VoiceOver
Cmd+F5, ou NVDA). Evidência: uma nota de três linhas na seção de QA da história. - Passe o bastão. Execute
verify-apppara a afirmação de ponta a ponta; dentro dodeliver-loopa revisão independente avalia o item. Não repita os passos deles aqui.
Checklist principal
- Nada armazenado que possa ser derivado: use
computed(React: uma const em render); nenhum watcher ou effect cuja única função seja definir outro estado. - Watchers e effects síncronizam apenas com o externo (APIs do DOM, subscriptions, timers, widgets de terceiros)
e fazem a limpeza (
onWatcherCleanup/onUnmounted; React: o retorno do effect). Trabalho causado por uma ação do usuário executa no event handler. - Dados do servidor nunca são copiados para estado local ou store; as chaves de query contêm toda entrada que a busca usa (em Vue como refs ou getters, de modo que uma alteração refetch).
- Seleção, filtros, abas e paginação que um usuário compartilharia ou recarregaria vivem na query da URL.
- Toda visão de dados renderiza carregando, vazio, erro (com retry) e sucesso; testes cobrem todos os quatro.
- Toda entrada tem um
<label for>visível (ids deuseId()em ambos os frameworks); erros são texto vinculados comaria-describedby, a entrada é mantida, o foco move-se para o primeiro campo inválido ao enviar. - Elementos nativos primeiro (
button,a href/link do roteador,dialog,details,input type); nenhumdiv @click/div onClick. - Widgets personalizados seguem o padrão WAI-ARIA APG correspondente, incluindo teclado.
- Resultados assíncronos que não movem o foco são anunciados através de uma região com
role="status"/role="alert"renderizada uma vez e vazia (Vue: teleportada parabody; React: um portal, se precisar sobreviver à visão). - Mudança de rota:
document.titleatualizado e foco movido para oh1oumainda página (Vue: umrouter.afterEach+ template ref; React: o equivalente do roteador). - Erros são capturados por rota e por widget arriscado (Vue: um componente de boundary
onErrorCaptured; React: um error boundary) e mostram um retry, nunca uma página em branco. - Sem strings de exibição hard-coded; toda localidade tem as mesmas chaves.
- Testes consultam por papel/label/texto, interagem com
user-event, e nunca afirmam nomes de classe, internos de componente (wrapper.vm, valores de estado), ou chamadas de composable/hook. - Sem
v-html/dangerouslySetInnerHTMLa menos que sanitizado com DOMPurify imediatamente antes do uso; URLs de dados são permitidos apenas parahttps:/mailto:/relativos. shallowRef/markRaw/v-memo/KeepAlive(React:memo/useMemo/useCallback) apenas com uma medição de profiler ou uma necessidade documentada.- Gráficos e visualizações de dados têm um nome e resumo acessíveis, uma alternativa de tabela de dados (ou texto), uma paleta segura para daltonismo com séries também distinguidas por rótulo, forma ou padrão, e acesso por teclado a qualquer interação — independentemente da biblioteca de gráficos que o repositório usa.
Anti-padrões
Vue:
watch(items, v => { filtered.value = v.filter(...) })— usecomputed.const { count } = reactive(state)ouconst { name } = store— reatividade perdida; usetoRefs/storeToRefs, ou prefiraref.watch(props.id, ...)ou passar uma prop desestruturada para um composable — passe um getter() => id.- Mutar uma prop ou dados de
useQuery; ligar dados de query diretamente aov-model— copie para um formulário. v-ifev-forno mesmo elemento;:key="index"em uma lista reordenável.awaitantes deonWatcherCleanupou antes de registrar lifecycle hooks em um composable.defineAsyncComponentpara um componente de rota (rotas aceitam() => import(...)diretamente).next()em um navigation guard — retornefalse, uma localização, ou nada.- Testes que acessam
wrapper.vmou chamamsetData.
React:
useEffect(() => setFiltered(items.filter(...)), [items])— compute em render.- Resetar um formulário em um effect quando um id muda — passe
key={id}. - Hooks chamados dentro de condições ou loops; estado mutado no lugar em vez de substituído.
key={index}em uma lista reordenável; um novo objeto ou função prop em cada render passado para um filho memoizado que foi memoizado por um motivo medido.
Ambos:
- Buscar no mount sem cancelamento, cache, retry ou branch de erro — use a camada de dados.
- Spinner sem branch vazio ou de erro; erro engolido em
console.log. staleTimedeixado em 0 para dados que mudam diariamente, então cada focus refetch.- Atualização otimista sem snapshot, rollback e invalidate no settle.
- Placeholder usado como o label; cor sozinha marcando um erro;
role="button"sem teclas. getByTestIdem toda parte; snapshots de página inteira; testar detalhes de implementação.- Um barrel file de feature puxando tudo para o primeiro chunk de rota.
:href="user.url"/href={user.url}sem verificar o esquema.
Evidência antes de "pronto"
- Testes unitários que falham e depois passam para cada estado e o caminho do teclado.
- Lint,
vue-tscoutsc, testes encadeados, exit 0; cobertura igual ou acima do limite. - Jornada Playwright contra o build de produção, axe serious/critical = 0.
- Capturas de tela em 390 e 1280 px (e tema escuro) em
docs/delivery/<slug>/evidence/, cada uma aberta. - Nota de teclado e leitor de tela na seção de QA da história.
- Então
verify-app.
Referências
references/vue-patterns.md— a checklist Vue: SFC e API de componente, armadilhas de reatividade, computed vs watch, composables, Pinia, TanStack Query para Vue, Vue Router (estado de URL, guards, foco), formulários, i18n, especificidades de a11y, desempenho, segurança, ferramentas; cada item com fonte.references/react-patterns.md— a checklist React: os mesmos tópicos, mais uma variante de exportação estática (Next.js / Nuxt generate).references/a11y-and-testing.md— itens WCAG 2.2 AA para código de UI, ARIA/APG, teclado e regiões vivas, gráficos, Testing Library para Vue e React, MSW, Playwright + axe, o que não testar.