
React Code Review
Run a structured React accessibility and markup review before merge or release.
Overview
React Code Review is an agent skill most often used in Ship (also Build) that guides structured React accessibility and semantic HTML review before you ship UI changes.
Install
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill react-code-reviewWhat is this skill?
- Semantic HTML-first mapping table (button, link, nav, headings, labels, tables)
- Explicit button-vs-link rules with correct and incorrect TSX examples
- Keyboard navigation requirement for all interactive elements
- Custom control pattern using role="switch" and aria-checked
- Discourages div-onClick and navigation implemented as buttons
- Semantic HTML mapping table covering eight common UI needs (button, link, list, heading, nav, main, label, table)
Adoption & trust: 973 installs on skills.sh; 271 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You have React components that look fine visually but may use the wrong elements, break keyboard use, or lean on ARIA when native HTML would suffice.
Who is it for?
Solo builders reviewing React or Next.js UI PRs who want repeatable accessibility and markup rules the agent can cite while editing components.
Skip if: Non-React stacks, backend-only changes, or teams that only want automated Lighthouse scores without human-readable markup guidance.
When should I use this skill?
Reviewing React or TSX UI for accessibility, semantic HTML correctness, and button versus link usage.
What do I get? / Deliverables
After the review, you get concrete TSX-level fixes aligned to semantic HTML, button/link boundaries, and keyboard-accessible interaction patterns.
- Accessibility and semantic HTML findings tied to specific components
- Suggested TSX replacements for anti-patterns (div buttons, wrong roles, missing keyboard handlers)
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Ship because the skill is framed as code review—checking components against accessibility and semantic HTML rules before they ship. Review subphase matches checklist-driven critique of existing TSX, not greenfield scaffolding or backend work.
Where it fits
While implementing a settings panel, verify toggles use button or switch roles with aria-checked instead of clickable divs.
On a PR touching navigation, flag router.push inside buttons and recommend anchor links for true navigation.
Before store screenshots go live, review marketing pages built in React for heading hierarchy and labeled form inputs.
Audit blog or docs React templates for tables used as div grids and restore semantic table markup for screen readers.
How it compares
Use for checklist-driven React a11y markup review instead of ad-hoc “scan with Lighthouse once” without semantic HTML rules.
Common Questions / FAQ
Who is react-code-review for?
Solo and indie builders shipping React or Next.js frontends who want their coding agent to enforce accessibility and semantic HTML during review.
When should I use react-code-review?
During Ship review before merging UI PRs, while refactoring interactive components in Build frontend work, or when auditing legacy div-onClick patterns before launch.
Is react-code-review safe to install?
It is procedural review guidance; check the Security Audits panel on this Prism page before installing any skill from the catalog.
SKILL.md
READMESKILL.md - React Code Review
# React Accessibility Checklist and ARIA Patterns ## Semantic HTML First Always use the correct semantic element before reaching for ARIA attributes. | Need | Use | Not | |------|-----|-----| | Button | `<button>` | `<div onClick>` | | Link/navigation | `<a href>` | `<span onClick>` | | List | `<ul>` / `<ol>` | `<div>` with items | | Heading | `<h1>`–`<h6>` | `<div className="title">` | | Navigation | `<nav>` | `<div className="nav">` | | Main content | `<main>` | `<div className="content">` | | Form input label | `<label htmlFor>` | Placeholder text only | | Table data | `<table>` | `<div>` grid layout | ## Interactive Elements ### Buttons vs Links - **Button**: Triggers an action (submit, toggle, open modal) - **Link**: Navigates to a URL or page section ```tsx // ✅ Correct: Button for action <button onClick={handleSave} type="button">Save</button> // ✅ Correct: Link for navigation <a href="/settings">Go to Settings</a> // ❌ Wrong: Div as button <div onClick={handleSave} className="btn">Save</div> // ❌ Wrong: Button for navigation <button onClick={() => router.push('/settings')}>Go to Settings</button> ``` ### Keyboard Navigation All interactive elements must be keyboard accessible. ```tsx // ✅ Custom interactive element with keyboard support function ToggleSwitch({ checked, onChange }: ToggleSwitchProps) { return ( <button role="switch" aria-checked={checked} onClick={() => onChange(!checked)} onKeyDown={(e) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); onChange(!checked); } }} > {checked ? 'On' : 'Off'} </button> ); } ``` ## Focus Management ### Modal Focus Trap ```tsx 'use client'; import { useEffect, useRef } from 'react'; function Modal({ isOpen, onClose, children }: ModalProps) { const modalRef = useRef<HTMLDivElement>(null); const previousFocus = useRef<HTMLElement | null>(null); useEffect(() => { if (isOpen) { previousFocus.current = document.activeElement as HTMLElement; modalRef.current?.focus(); } else { previousFocus.current?.focus(); } }, [isOpen]); if (!isOpen) return null; return ( <div role="dialog" aria-modal="true" aria-labelledby="modal-title" ref={modalRef} tabIndex={-1} onKeyDown={(e) => { if (e.key === 'Escape') onClose(); }} > <h2 id="modal-title">Modal Title</h2> {children} <button onClick={onClose}>Close</button> </div> ); } ``` ### Skip Navigation Link Allow keyboard users to skip repetitive navigation. ```tsx function SkipToMain() { return ( <a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:p-4 focus:bg-white" > Skip to main content </a> ); } function Layout({ children }: { children: ReactNode }) { return ( <> <SkipToMain /> <Header /> <main id="main-content" tabIndex={-1}> {children} </main> </> ); } ``` ## ARIA Patterns ### Live Regions Announce dynamic content changes to screen readers. ```tsx // ✅ Announce form submission status function ContactForm() { const [status, setStatus] = useState(''); return ( <form onSubmit={handleSubmit}> {/* Form fields */} <button type="submit">Send</button> <div role="status" aria-live="polite" aria-atomic="true"> {status} </div> </form> ); } ``` ### Expandable Sections ```tsx function Accordion({ title, children }: AccordionProps) { const [expanded, setExpanded] = useState(false); const contentId = useId(); return ( <div> <button aria-expanded={expanded} aria-controls={contentId} onClick={() => setExpanded(!expanded)} > {title} </button> <div id={contentId} role="region" hidden={!expanded} > {children} </div>