Search for "frontend code review checklist" and most of what comes back is a JS checklist wearing an HTML/CSS label — state management, component architecture, bundle splitting, a dozen linter configs. Those are useful lists, but they're not what you need when the thing on the screen is markup and stylesheets. This one stays in that lane on purpose.
TL;DR: a markup-focused code review checks semantic HTML (right elements, heading order, landmarks), CSS specificity and naming (BEM-style, no ID selectors for styling), accessibility (ARIA only where needed, focus order, contrast), cross-browser behavior, and render-weight — nothing about state management or build tooling.
It matters most at the exact moment a team scales: once you start augmenting your team with contract developers, verbal "house style" stops transferring. A new contributor's first ten pull requests will match whatever is written down, not whatever used to be said out loud in standups. A short, specific checklist is what keeps their markup consistent with the rest of the codebase from the first commit.
The two most-referenced frontend code review checklists online spend most of their length on JS: hooks and re-renders, module boundaries, PR tooling that auto-assigns reviewers. Legitimate — for a JavaScript review. But if a pull request only touches a template and a stylesheet, running it through a 20-item JS-architecture list wastes time on irrelevant questions or trains reviewers to skip the list because half of it never applies. A checklist people actually use has to match the diff in front of them.
The most common markup regression in review is quiet: a <div> doing a button's job, or a heading skipped because the smaller font "looked right" at that spot. Neither breaks visually. Both break for anyone using a screen reader or keyboard, and both erode whatever SEO value a clean document outline was giving the page.
What to look for on every diff:
<button> for actions, <a> for navigation, <nav>, <main>, <article>, <aside> used as landmarks rather than decoration.<ul>/<ol> when they're a list, not a stack of styled <div>s.<!-- flagged in review -->
<div class="btn" onclick="submit()">Save</div>
<!-- approved -->
<button type="submit" class="btn">Save</button>The first version looks identical once styled. It's also unreachable by keyboard, invisible to assistive tech, and excluded from form submission semantics. Catching this at review time costs one comment; catching it in a support ticket costs a lot more.
Specificity fights are the CSS equivalent of merge conflicts: avoidable with a convention, miserable without one. A specificity war usually starts with one well-meaning !important and ends with three more undoing it.
Review checks for CSS:
block__element--modifier) is still the most widely understood default, and consistency matters more than which convention you pick./* flagged in review — unscoped, high specificity */
#sidebar .card div.title { color: #222 !important; }
/* approved — BEM, scoped, no override needed */
.card__title { color: var(--ink); }
.card__title--muted { color: var(--smoke); }Treating accessibility as something QA catches later means it's always a retrofit. Code review is the cheaper, earlier gate — most of what matters here needs a reviewer who knows what to look for, not specialized tooling.
flex-direction, grid placement).<label> — a placeholder is not a label.A diff eyeballed in one browser at one viewport width is only half-reviewed. Two checks cover most real-world breakage:
HTML and CSS have their own performance story, separate from JS bundle size. It's easy to overlook in review because nothing crashes when it's wrong — pages just get slower.
A checklist that lives only in one reviewer's head doesn't scale past that reviewer. The moment a team brings in contract or augmented engineers to move faster, that becomes the bottleneck — new contributors can't match a standard they were never shown. Writing it down, linking it from the pull request template, and pointing new hires at it on day one turns "the reviewer caught it" into "the PR already matched house style."
Most published checklists scatter their items across ten or more sections and never consolidate into something you can paste into a PR description. Here's everything above, in one block:
<ul>/<ol>, not styled <div>sCode review should catch what's visible in the diff — missing labels, wrong ARIA, broken focus order — since that's the cheapest point to fix it. QA still matters for issues that only surface with a real screen reader in hand, but it shouldn't be the first line of defense.
A linter enforces rules a machine can check mechanically — syntax, naming patterns, known anti-patterns. A checklist covers judgment calls a linter can't make: is this the right element for the job, does this focus order make sense. They're complementary, not interchangeable.
The syntax matters less than having one consistent convention the whole team follows. BEM remains widely understood, which makes it a reasonable default — but a team already standardized on something else consistent doesn't need to migrate just to match an external list.
Even "modern browsers only" spans meaningfully different CSS Grid and container query support across engines. Strict here doesn't mean testing every browser ever shipped — it means testing the list your team committed to supporting, not whichever browser the reviewer happens to have open.
Lightweight checks belong in code review — render-blocking CSS, redundant selectors — since they're cheap to catch per pull request. A full performance audit is a separate, periodic exercise, not something every PR needs.
Keep it short enough that reviewers actually read each item, and revisit it when it stops catching real issues. A checklist that hasn't changed in two years despite the codebase changing underneath it usually means nobody's actually checking against it anymore.