Passo-a-passo detalhado do skill, referenciando as fases cognitivas:
1SENSE — Identificar requisitos do componente
Verificar design existente (Figma, Storybook) ou descrever o comportamento esperado
Identificar design system e suas primitivas disponíveis
2CONTEXTUALIZE — Analisar padrões do projeto
```bash
# Verificar versão do React e padrões de componente
cat package.json | jq '.dependencies.react'
ls src/components/ | head -10 # ver naming conventions
```
3RECOMMEND — Gerar componente TypeScript com acessibilidade
```typescript
// components/Modal/Modal.tsx
import { useEffect, useRef, type ReactNode } from 'react';
interface ModalProps {
/** Controla visibilidade do modal */
open: boolean;
/** Callback chamado ao fechar (ESC key ou backdrop click) */
onClose: () => void;
/** Título para aria-labelledby */
title: string;
children: ReactNode;
/** Tamanho do modal (default: 'md') */
size?: 'sm' | 'md' | 'lg' | 'xl';
}
export function Modal({ open, onClose, title, children, size = 'md' }: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open) return;
// Fechar com ESC
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleKeyDown);
// Focus no modal ao abrir
modalRef.current?.focus();
return () => document.removeEventListener('keydown', handleKeyDown);
}, [open, onClose]);
if (!open) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
ref={modalRef}
tabIndex={-1}
className="fixed inset-0 z-50 flex items-center justify-center"
onClick={(e) => e.target === e.currentTarget && onClose()}
>
<div className={`bg-white rounded-xl shadow-xl p-6 w-full max-w-${size}`}>
<h2 id="modal-title" className="text-xl font-semibold">{title}</h2>
{children}
</div>
</div>
);
}
```
4EVALUATE — Gerar testes com RTL
```typescript
// Modal.test.tsx
describe('Modal', () => {
it('closes on ESC key', async () => {
const onClose = vi.fn();
render(<Modal open title="Test" onClose={onClose}><p>Content</p></Modal>);
await userEvent.keyboard('{Escape}');
expect(onClose).toHaveBeenCalledOnce();
});
it('has correct ARIA attributes', () => {
render(<Modal open title="Test" onClose={() => {}}><p>Content</p></Modal>);
expect(screen.getByRole('dialog')).toHaveAttribute('aria-modal', 'true');
});
});
```
5RECOMMEND — Criar Storybook story
```typescript
// Modal.stories.tsx
export default { component: Modal, title: 'UI/Modal' };
export const Default = { args: { open: true, title: 'Exemplo', onClose: fn() } };
export const Large = { args: { ...Default.args, size: 'lg' } };
```
6REFLECT — Validar e documentar
Rodar `axe` ou `eslint-plugin-jsx-a11y` para verificar acessibilidade
Confirmar que o componente funciona com navegação apenas por teclado
Reportar telemetria via mcp-skillschain