Repository rules and conventions for contributors and automation tools.
Front-End Checklist is an open-source monorepo providing 385 quality-gated frontend best practice rules with an MCP server for structured review workflows.
- MCP Server:
https://mcp.frontendchecklist.io(11 tools) - Rules:
packages/content/rules/en/<category>/<slug>.mdx - Web App:
apps/web/(Next.js App Router) - MCP Package:
packages/mcp/(TypeScript) - Package Manager: pnpm (workspaces + Turborepo)
When working on frontend implementation, review, debugging, or audit tasks, use the Front-End Checklist MCP server when available so recommendations stay grounded in the project rule corpus.
- Use
review_codefirst for pasted or inspected HTML, CSS, JavaScript, TypeScript, React, or Next.js code. - Use
search_rulesbefore making accessibility, performance, SEO, security, image, privacy, i18n, or testing recommendations. - Use
get_rule,check_rule,fix_rule, orexplain_rulefor rule-specific guidance and remediation. - Use
get_workfloworget_checklist_rulesfor broad launch, accessibility, SEO, security, performance, and testing audits. - Use
audit_urlfor publichttps://pages.
Some clients lazy-load installed MCP tools. If the tools are not initially visible, search for or explicitly request the Front-End Checklist MCP before proceeding with frontend work.
apps/
web/ # Next.js app (App Router)
packages/
mcp/ # MCP server implementation
content/ # Rules MDX content + content-collections config
auth/ # Better-Auth + Prisma + Supabase
design-system/ # Shared UI components
types/ # Shared TypeScript types
data-layer/ # Data access layer
schemas/ # Zod v4 schemas
Rules live at packages/content/rules/en/<category>/<slug>.mdx.
---
title: "Human-readable rule title (string, use quotes)"
description: "One-sentence description of what to do"
categories: ['html'] # Array; first item = primaryCategory
priority: 'high' # 'critical' | 'high' | 'medium' | 'low'
difficulty: 'beginner' # 'beginner' | 'intermediate' | 'advanced' | 'expert'
estimatedTime: 15 # Minutes as integer
prompts:
check: "Prompt for checking whether the implementation follows this rule"
fix: "Prompt for fixing the issue"
explain: "Prompt for explaining why this rule matters"
---These fields are not required by the parser, but they materially improve rule quality, MCP usefulness, and generated skills.
subcategory: 'keyboard' # Sub-grouping within category
tldr: # Array of bullet points (used in llms.txt)
- "Key point one"
- "Key point two"
whyItMatters: "Paragraph explaining the business/user impact"
sources:
- title: "MDN or spec page title"
url: "https://..."
type: "mdn|spec|guide|wcag|google"
id: "stable-source-id"
role: "standard|reference|implementation|compatibility|regulation|search|research"
authority: "primary|secondary"
resources:
- name: "Supplementary tool or article"
url: "https://..."
type: "tool|article|reference"
relatedRules:
- slug: other-rule-slug
reason: "Why these rules relate"
tools:
- name: "Tool Name"
url: "https://..." # or null
tags: ['wcag', 'performance']
prompts:
codeReview: "Recommended prompt for systematic code review context"
aiContext: "Additional context for automated review about when this rule applies"Authoring placement rules:
- Put authoritative citations in
sources, not in body prose likeReference: ... sourcesare proof metadata, not generic further reading. Every rule needs at least 2 sources, 2 source roles, and 1authority: primary.resourcesare for further reading or tools only. Do not map evidence claims toresources.- The system may infer or normalize
id,role, andauthorityfor sources at build time; authors should focus on strong, relevant sources rather than maintaining claim-by-claim proof objects. - Put cross-rule navigation in
relatedRules, not in body prose likeSee also ... - Keep intro paragraphs focused on the rule itself; metadata should render from frontmatter-driven UI
Inline link contract:
- Natural inline links inside body prose are allowed when they support a specific claim, comparison, or implementation recommendation.
- Prefer light-touch density: usually
2-5inline links per rule, with0-1in the intro and1-3across the main guidance sections. - Prefer external links over internal ones; internal rule links should usually stay at
0-1, with2only for unusually interconnected rules. - Keep standalone metadata lines out of the body. Do not add paragraphs that start with
See also ...orReference: .... - Keep evergreen proof in
sources, and use newer inline external articles only when they add practical or recent context that the primary sources do not cover alone. - When a named tool is mentioned in prose for the first meaningful time (for example
Screaming FrogorGoogle Search Console), link that first mention if the tool materially helps the reader and already exists inresourcesortools. - Approved secondary inline links should stay curated. Prefer strong platforms such as
CSS-Tricks,Smashing Magazine,Nielsen Norman Group,WebAIM,Screaming Frog,Sitebulb,Moz, orYoastwhen they add practical implementation context. - Use descriptive anchor text tied to the sentence claim, not generic labels like “read more”.
impact, standards, and automation are intentionally not part of the active rule-writing contract right now. The current corpus and tooling do not consume them consistently enough to justify treating them as standard authoring fields.
New and refreshed rules should follow this structure unless there is a strong reason not to:
- Short intro paragraph explaining the rule in plain language
## Code Exampleor## Code Examples## Why It Matters- Optional implementation or guidance sections
## Verification
Canonical ordering rules:
## Code Exampleor## Code Examplesmust appear before## Why It Matters## Why It Mattersmust appear before## Verification## Verificationmust be the final H2 in the rule body- Optional H2 sections belong between
## Why It Mattersand## Verification
Preferred optional H2 taxonomy:
## Best Practices## Common Mistakes## Framework Examples## Tools & Validation## Thresholds## Exceptions## Browser Support## Support Notes## Standards## Implementation Notes
The validator reports headings outside this preferred taxonomy so section sprawl stays visible over time.
Rule Contract V2 adds three conditional concepts:
## Exceptionsfor nuanced rules, false positives, or valid caveats### Automated Checksand### Manual Checksinside## Verificationwhen both matter- a short visible standards/support note for compatibility-sensitive or compliance-sensitive rules
Apply these conditionally, not universally.
Browser support guidance should be derived from the repo browser policy in .browserslistrc plus package-backed compatibility data, not from memory.
Use explicit thresholds whenever the rule is measurable:
- Web Vitals and performance metrics (
LCP <= 2.5s,CLS <= 0.1, bundle or image size budgets) - Accessibility measurements (contrast ratios, target sizes, zoom or reflow thresholds)
- SEO and crawling states (indexability, status codes, canonical-url behavior)
- Security headers and transport requirements
Prefer “how to verify” guidance over generic advice. A strong rule should tell an engineer exactly how to confirm the rule passes.
- Unquoted colons in YAML: Always quote strings containing
:— e.g.title: "Use rel: new-tab"nottitle: Use rel: noopener - Stub prompts: Prompts must be actionable. Bad:
"Check this rule". Good:"Find all <img> elements missing an alt attribute in this HTML" - Missing sources: Add at least one authoritative reference (
MDN,W3C/WCAG, specs,web.dev, or product docs) before considering a rule complete - No verification path: Include a final
## Verificationsection so the fix is testable - Verification not last: Do not place additional H2 sections after
## Verification - Missing caveats: Add
## Exceptionswhen the rule has important false positives or valid exceptions - Missing support note: Add
## Browser Support,## Support Notes, or a visible standards note when compatibility or compliance changes implementation decisions - Wrong priority:
critical= site-breaking / security / legal.high= significantly degrades UX.medium= best practice.low= nice-to-have - Missing required fields: All 7 required fields must be present; builds fail otherwise
- Emoji in titles: Titles should be plain text (no emoji)
Valid values for the categories array:
| Slug | Description |
|---|---|
html |
Document structure, semantics, forms |
css |
Layout, typography, animation, responsive |
javascript |
Client-side scripting, async, security |
performance |
Loading speed, rendering, Core Web Vitals |
accessibility |
WCAG, ARIA, keyboard, screen readers |
seo |
Meta tags, structured data, crawlability |
security |
HTTPS, CSP, XSS prevention, headers |
images |
Optimization, formats, responsive images |
testing |
Unit, integration, e2e, a11y testing |
privacy |
Consent, data minimisation, data rights |
i18n |
Localization, pluralization, bidi, locale formatting |
general |
Cross-cutting best practices |
Rules are scored automatically via pnpm score:rules (100-point base score plus up to 12 conditional points, minimum 50 to pass the current quality gate).
| Dimension | Points | What to Check |
|---|---|---|
Prompts (check/fix/explain) |
24 | All 3 prompts are specific and non-generic |
| TLDR bullets | 4 | 3+ actionable bullets |
| Why it matters | 4 | Explains user, business, or operational impact |
| AI context | 6 | Captures where and when the rule applies in automated reviews |
| Related rules | 6 | Links adjacent rules with clear reasons |
| Sources | 10 | Includes authoritative references |
| Resources / tools | 6 | Includes useful supporting docs or tools |
| Code review prompt | 6 | Provides a systematic review strategy |
| Code examples | 10 | Includes realistic examples, not just prose |
| Body depth | 10 | Provides substantive implementation detail |
| Verification section | 8 | Includes Verification, Testing, Audit Checklist, or equivalent |
| Thresholds / pass-fail criteria | 6 | Uses explicit numbers or conditions where the rule is measurable |
| Exceptions (conditional) | 4 | Adds an Exceptions section when the rule is nuanced or false-positive-prone |
| Verification split (conditional) | 4 | Splits verification into automated and manual checks when both apply |
| Standards/support visibility (conditional) | 4 | Adds a visible threshold, standards note, or support note when required by the rule type |
Rules scoring < 50 are excluded from MCP tool responses and the web UI.
- Create
packages/content/rules/en/<category>/<slug>.mdx - Fill all required frontmatter fields
- Add recommended metadata:
sources,resources,aiContext,relatedRules, andprompts.codeReviewwhere applicable - Write the rule using the canonical-url structure (intro, examples, why it matters, verification, category-specific guidance)
- Run
pnpm score:rules— must score ≥ 50 for now; target 60+ for new work - Run
pnpm validate:rule-structure— validates rule section order and finalVerificationheading - Run
pnpm report:rule-links— reviews inline-link density and suggests safe internal/external candidates - Run
pnpm validate:sources— validates external URLs - Run
pnpm generate:skills— regenerates skills index
pnpm score:rules # Score all rules; flags < 50 quality gate failures
pnpm validate:rule-structure # Validate rule heading structure and report drift
pnpm report:v2-gaps # Report conditional V2 gaps by category and migration batch
pnpm report:rule-links # Review inline-link density and candidate internal/external links
pnpm generate:exceptions-hints # Dry-run suggestions for missing Exceptions sections
pnpm generate:support-notes # Dry-run support-note suggestions from browser data
pnpm generate:verification-split # Dry-run Verification split suggestions
pnpm validate:sources # Validate external URLs in rule frontmatter
pnpm validate:packages # Check package dependency consistency
pnpm generate:skills # Regenerate skills/ directory from rules
pnpm build # Full monorepo build (via Turborepo)
pnpm typecheck # TypeScript check across all packages
pnpm lint # Biome lint
pnpm lint:fix # Biome lint with auto-fixNew MCP tools go in packages/mcp/src/tools/<tool-name>.ts and must be:
- Exported from
packages/mcp/src/tools/index.ts - Added to
BASE_TOOL_DEFINITIONSinpackages/mcp/src/server.ts - Added as a
casein thehandleToolsCallswitch inserver.ts
Tool definition shape:
export const myToolDefinition = {
name: 'my_tool',
description: '...', // Critical for tool discoverability — be specific
inputSchema: {
type: 'object' as const,
properties: { ... },
required: ['fieldName'],
},
}- Auth: Better-Auth + Prisma + Supabase Postgres in
packages/auth/ - Validation: Zod v4 — use
.issuesnot.errorsonZodError - Forms:
@tanstack/react-formwith manual Zod validation on submit - Email: Resend SDK v6, sending domain
mail.frontendchecklist.io - Rate limiting:
@upstash/ratelimit+@upstash/redisinapps/web/lib/rate-limit.ts - Query: TanStack Query v5 with
getQueryClient()factory (not singleton) - Dev server:
portless run next dev
- Put shared concerns (e.g. fonts, typography) in the design system and import from there; apps wire implementation (e.g. next/font) using config from the design system.
- Do not use
suppressHydrationWarning; fix server/client mismatch (e.g. defer client-only state until after mount). - Reuse existing components instead of creating page-specific variants (e.g. shared
FAQAccordionfor any FAQ section,.code-block/.code-inlinefor all code display, ChecklistActionBar instead of new progress bars). When adding a feature to one page type, add it to all relevant page types (e.g. ShareButton on category, rule detail, and all-rules pages). - Rule row: click on rule title toggles completion; expand/collapse only via the plus/minus icon. Expanded content shows description first, then tags and action buttons on one row aligned with the description.
- Prefer Tailwind utility classes in components over custom CSS classes in globals.css.
- Use a scalable overrides map for special-case labels (e.g. subcategory aria → ARIA) instead of hardcoded conditionals.
- Do not use
git commit --no-verify; fix ALL pre-commit violations (JSDoc, as-casts, barrel files, complexity, relative imports) rather than deferring or working around them. Write JSDoc at the same time as the code, not as a batch fix later. - Card-style links: Use the stretched-link pattern: the
<a>/<Link>wraps only the title text inside a heading and usesafter:absolute after:inset-0 after:content-[""]so its::afterpseudo-element expands to fill the card container (position: relative). Secondary interactive elements needrelative z-10. Seedocs/card-links.md. - Centralize all icons in
@repo/design-system: brand icons inbrand-icons.tsx, Lucide re-exported fromicons.ts. Use official brand icons (@icons-pack/react-simple-icons) for branded services, not Lucide approximations. Consumers never import directly fromlucide-react. - Never use Tailwind opacity modifiers (
/50,/60,/70) on semantic theme text colors liketext-foreground-muted— it crushes contrast in light mode. Use the appropriate token (text-foreground-muted,text-foreground-subtle) at full opacity instead. - Always refer to the social platform as "X", never "Twitter". Schema fields use
xUrl(nottwitterUrl), UI labels say "X", and URLs point tox.com. - About page link belongs in the footer only, not in the main navbar.
- Fluid typography tokens use
clamp()in@theme inlinefortext-baseand above;text-xsandtext-smstay fixed for compact UI controls (buttons, badges, inputs, tooltips). - next/font: apply each font’s
.classNameto the document element (not.variable); Next.js requires literal option objects, so the app must inline font options while the design system can document them. - Do not apply client-side bot protection (e.g. Vercel BotID) to programmatic API endpoints like MCP; use it only for browser-originated form and mutation endpoints.
- Next.js reads next.config.js at startup; restart the dev server after changing it (e.g. images.remotePatterns).
withContentCollectionsreturns a Promise (async config) so it must be the outermost wrapper —export default withContentCollections(withBotId(nextConfig)).withBotIddoes not handle async configs and will silently drop all properties includingimages. - Tailwind preflight resets
list-styleonul/ol;.prosestyles must explicitly setlist-style-type: disc(ul),decimal(ol), and nested variants (circle,lower-alpha). - CSS custom properties use OKLCH color format. Light mode tokens need visible contrast on all screens:
--borderat least oklch(0.885),--foreground-subtleat oklch(0.64), shadows at 0.06+ opacity. Usecolor-mix(in oklch, var(--token) N%, transparent)for theme-aware transparent colors instead of hardcodedrgba(). - Pre-commit hooks (lefthook) enforce: biome check, JSDoc, no-as-casts, no-barrel-files, file-complexity (300 lines React / 500 lines other), no-deep-relative-imports, no-console-logs, test coverage.
- Button default variant uses
bg-accent text-accent-foreground hover:bg-accent-hover(project's accent tokens), not shadcn'sbg-primary; keep project-specific semantic tokens in component variants even when shadcn aliases exist. - Scripts are organized in subfolders:
scripts/{validation,generate,rule-structure,validate,audit}/;scripts/README.mddocuments each script and subfolder. - Biome's
useSortedClassesonly sorts class order; it cannot rename deprecated classes. Use@tailwindcss/upgrade(installed inapps/web) to canonicalize class names project-wide. - Monorepo uses pnpm catalogs (
catalog:inpnpm-workspace.yaml) for centralized dependency versions; shared deps appearing in 2+ packages usecatalog:instead of hardcoded version strings. - Brand icons (ChatGPT, Claude, X, LinkedIn, Reddit, Cursor, VSCode) live in
packages/design-system/src/brand-icons.tsxand are re-exported from the design-system barrel.