Accessibility (a11y) ensures the application is usable by everyone, including people with disabilities. SEO ensures the application is discoverable. Both are review requirements, not nice-to-haves.
Accessibility Checklist
Semantic HTML
Use semantic elements: <header>, <nav>, <main>, <section>, <article>, <aside>, <footer>
Dynamic routes have generateStaticParams where appropriate
404 page returns proper 404 status code
Redirects use proper status codes (301 permanent, 307 temporary)
No duplicate content (canonical tags, proper pagination)
Page titles unique and descriptive (< 60 characters)
Meta descriptions present and compelling (< 160 characters)
Open Graph and Twitter Card tags on shareable pages
Performance (SEO Impact)
Core Web Vitals within targets:
LCP (Largest Contentful Paint) < 2.5s
FID (First Input Delay) < 100ms
CLS (Cumulative Layout Shift) < 0.1
Server-side rendering for content pages
Images optimized with next/image
Common Issues
Issue
Impact
Fix
Clickable div without button role
Screen readers can't identify as interactive
Use <button> or add role="button" + keyboard handling
Missing alt text
Screen readers skip the image
Add descriptive alt or alt="" for decorative
No focus indicator
Keyboard users can't see where they are
Use :focus-visible styles
Placeholder-only labels
Disappear when typing, not announced properly
Add visible <label>
Missing page title
Poor SEO, confusing tab names
Add <title> via metadata
No heading structure
Navigation impossible for screen readers
Use proper heading hierarchy
Color-only error indication
Color blind users miss errors
Add icon, text, or pattern
outline: none without alternative
Focus invisible
Use focus-visible or custom focus styles
Review Severity
Issue
Severity
No keyboard access to critical functionality
P1 — High
Missing alt text on informational images
P1 — High
Focus trap missing on modal
P1 — High
Missing page metadata / title
P1 — High
Color contrast below AA threshold
P2 — Medium
Missing ARIA on custom components
P2 — Medium
No structured data on content pages
P3 — Low
Minor heading hierarchy issue
P3 — Low
1---2name: accessibility-seo3description: Accessibility & SEO — Forge Skill4---5# Accessibility & SEO — Forge Skill67## Overview89Accessibility (a11y) ensures the application is usable by everyone, including people with disabilities. SEO ensures the application is discoverable. Both are review requirements, not nice-to-haves.1011## Accessibility Checklist1213### Semantic HTML1415- [ ] Use semantic elements: `<header>`, `<nav>`, `<main>`, `<section>`, `<article>`, `<aside>`, `<footer>`16- [ ] Headings follow hierarchy (`h1` → `h2` → `h3`, no skipping levels)17- [ ] One `<h1>` per page18- [ ] Lists use `<ul>`, `<ol>`, `<dl>` — not styled divs19- [ ] Tables use `<table>`, `<thead>`, `<tbody>`, `<th>` with scope20- [ ] Buttons are `<button>`, not clickable `<div>` or `<span>`21- [ ] Links are `<a>` with `href`, not clickable divs22- [ ] Form inputs have associated `<label>` elements2324### ARIA Attributes2526- [ ] ARIA only used when semantic HTML is insufficient27- [ ] `aria-label` or `aria-labelledby` on elements without visible text (icon buttons, etc.)28- [ ] `aria-describedby` for supplementary descriptions29- [ ] `aria-live` regions for dynamic content updates (toasts, notifications)30- [ ] `aria-expanded` on toggleable elements (accordions, dropdowns)31- [ ] `aria-hidden="true"` on decorative elements32- [ ] `role` attributes only when semantic HTML doesn't convey the role33- [ ] No redundant ARIA (e.g., `role="button"` on a `<button>`)3435### Keyboard Navigation3637- [ ] All interactive elements reachable via Tab38- [ ] Tab order follows visual/logical order39- [ ] Focus visible on all interactive elements (no `outline: none` without alternative)40- [ ] Escape closes modals, dropdowns, and popovers41- [ ] Enter/Space activates buttons and links42- [ ] Arrow keys navigate within composite widgets (tabs, menus, listboxes)43- [ ] Focus trapped in modals (no tabbing out to background content)44- [ ] Focus restored to trigger element when modal/popover closes45- [ ] Skip-to-content link present4647### Visual4849- [ ] Color contrast ratio meets WCAG AA (4.5:1 for normal text, 3:1 for large text)50- [ ] Information not conveyed by color alone (icons, patterns, text as alternatives)51- [ ] Text resizable to 200% without loss of content or functionality52- [ ] No content that flashes more than 3 times per second53- [ ] Motion respects `prefers-reduced-motion` media query5455### Images & Media5657- [ ] All `<img>` elements have `alt` attributes58- [ ] Decorative images have `alt=""` (empty alt)59- [ ] Complex images have detailed descriptions60- [ ] Videos have captions or transcripts61- [ ] Audio content has transcripts6263### Forms6465- [ ] Every input has a visible label (not just placeholder)66- [ ] Required fields are indicated (not just with color)67- [ ] Error messages are specific and associated with the field (`aria-describedby`)68- [ ] Form validation errors announced to screen readers69- [ ] Autocomplete attributes used where appropriate70- [ ] Fieldsets and legends used for related form groups7172## SEO — Next.js Specific7374### Metadata7576```typescript77// app/layout.tsx — global metadata78import type { Metadata } from 'next';7980export const metadata: Metadata = {81 title: {82 template: '%s | OpenClaw',83 default: 'OpenClaw',84 },85 description: 'Application description',86 metadataBase: new URL('https://openclaw.io'),87 openGraph: {88 type: 'website',89 locale: 'en_US',90 siteName: 'OpenClaw',91 },92 twitter: {93 card: 'summary_large_image',94 },95};96```9798```typescript99// app/posts/[id]/page.tsx — dynamic metadata100export async function generateMetadata({ params }: Props): Promise<Metadata> {101 const post = await getPost(params.id);102103 return {104 title: post.title,105 description: post.excerpt,106 openGraph: {107 title: post.title,108 description: post.excerpt,109 images: [{ url: post.coverImage }],110 },111 };112}113```114115### Structured Data116117```typescript118// JSON-LD for rich search results119export default function PostPage({ post }: Props) {120 const jsonLd = {121 '@context': 'https://schema.org',122 '@type': 'Article',123 headline: post.title,124 description: post.excerpt,125 author: {126 '@type': 'Person',127 name: post.author.name,128 },129 datePublished: post.createdAt,130 dateModified: post.updatedAt,131 };132133 return (134 <>135 <script136 type="application/ld+json"137 dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}138 />139 <article>{/* ... */}</article>140 </>141 );142}143```144145### Technical SEO146147- [ ] `robots.txt` configured properly148- [ ] `sitemap.xml` generated (use Next.js `sitemap.ts`)149- [ ] Canonical URLs set on all pages150- [ ] Dynamic routes have `generateStaticParams` where appropriate151- [ ] 404 page returns proper 404 status code152- [ ] Redirects use proper status codes (301 permanent, 307 temporary)153- [ ] No duplicate content (canonical tags, proper pagination)154- [ ] Page titles unique and descriptive (< 60 characters)155- [ ] Meta descriptions present and compelling (< 160 characters)156- [ ] Open Graph and Twitter Card tags on shareable pages157158### Performance (SEO Impact)159160- [ ] Core Web Vitals within targets:161 - LCP (Largest Contentful Paint) < 2.5s162 - FID (First Input Delay) < 100ms163 - CLS (Cumulative Layout Shift) < 0.1164- [ ] Server-side rendering for content pages165- [ ] Images optimized with `next/image`166167## Common Issues168169| Issue | Impact | Fix |170|-------|--------|-----|171| Clickable div without button role | Screen readers can't identify as interactive | Use `<button>` or add `role="button"` + keyboard handling |172| Missing alt text | Screen readers skip the image | Add descriptive `alt` or `alt=""` for decorative |173| No focus indicator | Keyboard users can't see where they are | Use `:focus-visible` styles |174| Placeholder-only labels | Disappear when typing, not announced properly | Add visible `<label>` |175| Missing page title | Poor SEO, confusing tab names | Add `<title>` via metadata |176| No heading structure | Navigation impossible for screen readers | Use proper heading hierarchy |177| Color-only error indication | Color blind users miss errors | Add icon, text, or pattern |178| `outline: none` without alternative | Focus invisible | Use `focus-visible` or custom focus styles |179180## Review Severity181182| Issue | Severity |183|-------|----------|184| No keyboard access to critical functionality | P1 — High |185| Missing alt text on informational images | P1 — High |186| Focus trap missing on modal | P1 — High |187| Missing page metadata / title | P1 — High |188| Color contrast below AA threshold | P2 — Medium |189| Missing ARIA on custom components | P2 — Medium |190| No structured data on content pages | P3 — Low |191| Minor heading hierarchy issue | P3 — Low |
Run npx skillmds@latest add nickgallick/accessibility-seo in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Accessibility & SEO — Forge Skill It is listed under Marketing & Growth on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
nickgallick (@nickgallick) published this skill. Their other Agent Skills are listed on their SkillMD profile.