Senior Frontend
Padrões de desenvolvimento frontend, otimização de desempenho e ferramentas de automação para aplicações React/Next.js.
Sumário
- Scaffolding de Projeto
- Geração de Componentes
- Análise de Bundle
- Padrões React
- Otimização Next.js
- Acessibilidade e Testes
Scaffolding de Projeto
Gere um novo projeto Next.js ou React com TypeScript, Tailwind CSS e configurações de melhores práticas.
Fluxo de Trabalho: Criar Novo Projeto Frontend
-
Execute o scaffolder com o nome do seu projeto e template:
python scripts/frontend_scaffolder.py my-app --template nextjs -
Adicione funcionalidades opcionais (auth, api, forms, testing, storybook):
python scripts/frontend_scaffolder.py dashboard --template nextjs --features auth,api -
Navegue até o projeto e instale as dependências:
cd my-app && npm install -
Inicie o servidor de desenvolvimento:
npm run dev
Opções do Scaffolder
| Opção | Descrição |
|---|---|
--template nextjs | Next.js 14+ com App Router e Server Components |
--template react | React + Vite com TypeScript |
--features auth | Adiciona autenticação NextAuth.js |
--features api | Adiciona React Query + cliente API |
--features forms | Adiciona React Hook Form + validação Zod |
--features testing | Adiciona Vitest + Testing Library |
--dry-run | Visualiza arquivos sem criá-los |
Estrutura Gerada (Next.js)
my-app/
├── app/
│ ├── layout.tsx # Layout raiz com fontes
│ ├── page.tsx # Página inicial
│ ├── globals.css # Tailwind + variáveis CSS
│ └── api/health/route.ts
├── components/
│ ├── ui/ # Button, Input, Card
│ └── layout/ # Header, Footer, Sidebar
├── hooks/ # useDebounce, useLocalStorage
├── lib/ # utils (cn), constants
├── types/ # Interfaces TypeScript
├── tailwind.config.ts
├── next.config.js
└── package.json
Geração de Componentes
Gere componentes React com TypeScript, testes e histórias Storybook.
Fluxo de Trabalho: Criar um Novo Componente
-
Gere um componente cliente:
python scripts/component_generator.py Button --dir src/components/ui -
Gere um componente servidor:
python scripts/component_generator.py ProductCard --type server -
Gere com arquivos de teste e story:
python scripts/component_generator.py UserProfile --with-test --with-story -
Gere um hook customizado:
python scripts/component_generator.py FormValidation --type hook
Opções do Gerador
| Opção | Descrição |
|---|---|
--type client | Componente cliente com 'use client' (padrão) |
--type server | Componente servidor assíncrono |
--type hook | Hook React customizado |
--with-test | Inclui arquivo de teste |
--with-story | Inclui story Storybook |
--flat | Cria no diretório de saída sem subdiretório |
--dry-run | Visualiza sem criar arquivos |
Exemplo de Componente Gerado
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface ButtonProps {
className?: string;
children?: React.ReactNode;
}
export function Button({ className, children }: ButtonProps) {
return (
<div className={cn('', className)}>
{children}
</div>
);
}
Análise de Bundle
Analise o package.json e a estrutura do projeto em busca de oportunidades de otimização de bundle.
Fluxo de Trabalho: Otimizar Tamanho do Bundle
-
Execute o analisador no seu projeto:
python scripts/bundle_analyzer.py /path/to/project -
Revise a pontuação de saúde e os problemas:
Pontuação de Saúde do Bundle: 75/100 (C) DEPENDÊNCIAS PESADAS: moment (290KB) Alternativa: date-fns (12KB) ou dayjs (2KB) lodash (71KB) Alternativa: lodash-es com tree-shaking -
Aplique as correções recomendadas substituindo dependências pesadas.
-
Execute novamente com modo verbose para verificar padrões de importação:
python scripts/bundle_analyzer.py . --verbose
Interpretação da Pontuação do Bundle
| Pontuação | Nota | Ação |
|---|---|---|
| 90-100 | A | Bundle bem otimizado |
| 80-89 | B | Pequenas otimizações disponíveis |
| 70-79 | C | Substituir dependências pesadas |
| 60-69 | D | Múltiplos problemas precisam de atenção |
| 0-59 | F | Problemas críticos de tamanho do bundle |
Dependências Pesadas Detectadas
O analisador identifica estes pacotes pesados comuns:
| Pacote | Tamanho | Alternativa |
|---|---|---|
| moment | 290KB | date-fns (12KB) ou dayjs (2KB) |
| lodash | 71KB | lodash-es com tree-shaking |
| axios | 14KB | Native fetch ou ky (3KB) |
| jquery | 87KB | APIs DOM nativas |
| @mui/material | Grande | shadcn/ui ou Radix UI |
Padrões React
Referência: references/react_patterns.md
Componentes Compostos
Compartilhe estado entre componentes relacionados:
const Tabs = ({ children }) => {
const [active, setActive] = useState(0);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
};
Tabs.List = TabList;
Tabs.Panel = TabPanel;
// Uso
<Tabs>
<Tabs.List>
<Tabs.Tab>Um</Tabs.Tab>
<Tabs.Tab>Dois</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Conteúdo 1</Tabs.Panel>
<Tabs.Panel>Conteúdo 2</Tabs.Panel>
</Tabs>
Hooks Personalizados
Extraia lógica reutilizável:
function useDebounce<T>(value: T, delay = 500): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Uso
const debouncedSearch = useDebounce(searchTerm, 300);
Render Props
Compartilhe lógica de renderização:
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(setData).finally(() => setLoading(false));
}, [url]);
return render({ data, loading });
}
// Uso
<DataFetcher
url="/api/users"
render={({ data, loading }) =>
loading ? <Spinner /> : <UserList users={data} />
}
/>
Otimização Next.js
Referência: references/nextjs_optimization_guide.md
Componentes Servidor vs Cliente
Use Server Components por padrão. Adicione 'use client' apenas quando precisar de:
- Manipuladores de evento (onClick, onChange)
- Estado (useState, useReducer)
- Efeitos (useEffect)
- APIs do navegador
// Server Component (padrão) - sem 'use client'
async function ProductPage({ params }) {
const product = await getProduct(params.id); // Fetch no servidor
return (
<div>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} /> {/* Componente cliente */}
</div>
);
}
// Client Component
'use client';
function AddToCartButton({ productId }) {
const [adding, setAdding] = useState(false);
return <button onClick={() => addToCart(productId)}>Adicionar</button>;
}
Otimização de Imagens
import Image from 'next/image';
// Acima da dobra - carregar imediatamente
<Image
src="/hero.jpg"
alt="Herói"
width={1200}
height={600}
priority
/>
// Imagem responsiva com fill
<div className="relative aspect-video">
<Image
src="/product.jpg"
alt="Produto"
fill
sizes="(max-width: 768px) 100vw, 50vw"
className="object-cover"
/>
</div>
Padrões de Fetch de Dados
// Fetch paralelo
async function Dashboard() {
const [user, stats] = await Promise.all([
getUser(),
getStats()
]);
return <div>...</div>;
}
// Streaming com Suspense
async function ProductPage({ params }) {
return (
<div>
<ProductDetails id={params.id} />
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews productId={params.id} />
</Suspense>
</div>
);
}
Acessibilidade e Testes
Referência: references/frontend_best_practices.md
Checklist de Acessibilidade
- HTML Semântico: Use elementos apropriados (
<button>,<nav>,<main>) - Navegação por Teclado: Todos os elementos interativos focalizáveis
- Rótulos ARIA: Forneça rótulos para ícones e widgets complexos
- Contraste de Cor: Mínimo 4.5:1 para texto normal
- Indicadores de Foco: Estados de foco visíveis
// Botão acessível
<button
type="button"
aria-label="Fechar diálogo"
onClick={onClose}
className="focus-visible:ring-2 focus-visible:ring-blue-500"
>
<XIcon aria-hidden="true" />
</button>
// Link de pular para usuários de teclado
<a href="#main-content" className="sr-only focus:not-sr-only">
Pular para o conteúdo principal
</a>
Estratégia de Testes
// Teste de componente com React Testing Library
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('botão aciona ação ao clicar', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Clique em mim</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
// Teste de acessibilidade
test('diálogo é acessível', async () => {
render(<Dialog open={true} title="Confirmar" />);
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby');
});
Referência Rápida
Configuração Comum do Next.js
// next.config.js
const nextConfig = {
images: {
remotePatterns: [{ protocol: 'https', hostname: 'cdn.example.com' }],
formats: ['image/avif', 'image/webp'],
},
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react'],
},
};
Utilitários Tailwind CSS
// Classes condicionais com cn()
import { cn } from '@/lib/utils';
<button className={cn(
'px-4 py-2 rounded',
variant === 'primary' && 'bg-blue-500 text-white',
disabled && 'opacity-50 cursor-not-allowed'
)} />
Padrões TypeScript
// Props com children
interface CardProps {
className?: string;
children: React.ReactNode;
}
// Componente genérico
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
Recursos
- Padrões React:
references/react_patterns.md - Otimização Next.js:
references/nextjs_optimization_guide.md - Melhores Práticas:
references/frontend_best_practices.md - Biblioteca de perguntas forçadas (Matt Pocock grill):
references/forcing_questions.md - Mapa de composição (para qual especialista bifurcar):
references/composition_map.md
Suposições e Critérios de Sucesso Verificáveis (disciplina Karpathy)
Antes de esta skill gerar um componente, recomendar um framework ou auditar um bundle, as quatro suposições a seguir DEVEM ser explicitadas.
- Dispositivo do usuário primário + rede — mobile-4G, desktop-fibra, Android-baixo-custo ou rede-corporativa. Determina cada decisão de desempenho.
- Alvo LCP em milissegundos — um único número, não "rápido". Determina o orçamento do bundle e a escolha de renderização.
- Dependente de SEO vs. protegido por auth — determina a renderização (SSR/SSG/RSC vs. SPA).
- Alvo WCAG + responsável nomeado por acessibilidade — AA, AAA ou melhor esforço. Determina o investimento em acessibilidade e portões de CI.
Critérios de sucesso verificáveis (Karpathy #4) — toda recomendação deve incluir:
- Metas de Core Web Vitals (LCP, INP, CLS) no p75 no dispositivo primário
- Um orçamento de bundle JS por rota em KB-gzip
- Um piso de acessibilidade Lighthouse + piso de desempenho
Se qualquer um desses três não for declarado, a recomendação está incompleta — retorne à Q2 da biblioteca de perguntas forçadas.
A ferramenta scripts/frontend_decision_engine.py codifica essas verificações: ela se recusa a recomendar um perfil sem as quatro entradas de suposição e imprime os limites verificáveis para o perfil correspondente.
Perfis de Personalização
Quatro perfis integrados em profiles/ calibram toda recomendação:
| Perfil | Quando escolher | Alvo LCP (mobile-4G p75) | Orçamento de bundle |
|---|---|---|---|
next-app-router | SaaS voltado ao cliente, SEO + dinâmico, RSC-first | 2000ms | 150 KB-gzip / rota |
remix-or-sveltekit | Mobile-4G primário, low-JS-first, aprimoramento progressivo | 1500ms | 80 KB-gzip / rota |
vite-spa | App protegido por auth, desktop/corporativo primário | 2500ms | 200 KB init + 80 KB / rota |
astro-or-static | Marketing / docs / blog, quase zero escrita, SEO crítico | 1200ms | 30 KB JS / página |
Escolha um perfil via:
python scripts/frontend_decision_engine.py \
--primary-device mobile-4g --lcp-target-ms 2000 \
--seo-dependent true --auth-walled false --team-size 5
A ferramenta retorna o perfil mais adequado, a troca do vice-campeão (se dentro de 15%), as escolhas de stack, os anti-padrões a evitar nesse perfil e os portões de CI necessários.
Para adicionar um perfil personalizado (por exemplo, os padrões da ferramenta interna da sua organização): copie profiles/vite-spa.json para profiles/<sua-org>.json e ajuste constraints + success_thresholds.
Mapa de Composição
Esta skill NÃO reimplementa escopo pertencente a especialistas de nível POWERFUL. Ela bifurca neles. Consulte references/composition_map.md para a tabela de roteamento completa. Bifurcações principais:
| Preocupação | Bifurcar em |
|---|---|
| Auditoria WCAG, contraste, leitor de tela | engineering-team/skills/a11y-audit/ |
| Perfilamento de bundle + desempenho em tempo de execução | engineering/skills/performance-profiler/ |
| Landing de scroll / storytelling cinematográfico | engineering-team/skills/epic-design/ |
| Apple HIG (iOS / macOS / visionOS) | product-team/skills/apple-hig-expert/ |
| Revisão Karpathy pré-commit | engineering/karpathy-coder/ |
| Grilla de arquitetura pré-voo | engineering/grill-me/ |
O agente cs-frontend-engineer orquestra essas bifurcações via context: fork. Invoque-o de outro agente com Agent({subagent_type: "cs-frontend-engineer", prompt: "..."}) ou ...
[truncado para tradução]