New /validate skill runs a narrow-scope web standards audit covering W3C HTML validity (validator.nu API in FULL, html-validate / vnu.jar in LOCAL), W3C CSS validity (jigsaw.w3.org/css-validator in FULL, stylelint / css-tree in LOCAL), and WCAG 2.1 accessibility (pa11y, @axe-core/cli, WAVE API, or static checklist fallback). Dedicated validator-analyzer agent with a strict IN/OUT scope filter so the report stays focused on conformance — no meta/OG/JSON-LD/ sitemap/CSP/cookie/CWV noise. Those remain owned by /seo, /geo, and /harden respectively. LOCAL mode degrades gracefully: tries local npm tools first, falls back to static analysis if none present (same 12-point a11y checklist as /onboard a11y dispatch). Never fails hard. Framework awareness: validates built output (dist/, _site/, build/, out/) for SPA/JS frameworks, not JSX/TSX source. Warns if no build dir found. Fix mode (--fix) produces a conservative auto-fix bundle: missing lang attr, alt="" on decorative images, unclosed void tags, duplicate IDs, unambiguous heading level skips. Content decisions (form labels, color contrast, landmark restructure, alt text on content images) always go to User actions, never auto-applied. Flags: --local, --full, --fix, --no-external. Routing updated in CLAUDE.md. /harden and /seo cross-refs narrowed to redirect W3C / WCAG concerns to /validate (was previously routed to /onboard a11y dispatch, which only runs at setup). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
/validate — web standards audit (W3C + WCAG)
This skill orchestrates a narrow-scope standards audit :
- W3C HTML validity — validator.nu API (FULL) or
html-validate/vnu.jar(LOCAL) or static fallback. - W3C CSS validity — jigsaw.w3.org/css-validator API (FULL) or
stylelint/css-tree(LOCAL) or static fallback. - WCAG 2.1 accessibility —
pa11y/@axe-core/cli/ WAVE API (FULL) or axe against built HTML (LOCAL) or static checklist.
Scope boundary :
- In : HTML syntactic validity (DOCTYPE, required attrs, tag nesting, ID uniqueness, heading hierarchy), CSS syntactic validity (parsing errors, unknown properties, at-rule misuse), WCAG 2.1 A / AA / AAA violations (ARIA, landmarks, contrast, keyboard, SR affordances, alt text).
- Out : meta tags (title/description/OG), JSON-LD / Schema.org, sitemap.xml, robots.txt, llms.txt, security headers (HSTS/CSP), cookie flags, Core Web Vitals, image compression, hreflang, i18n routing, generic code linting (ESLint, Prettier), TypeScript type errors.
If a finding appears in an out-of-scope area (e.g. missing meta
description), the agent drops it silently — /validate stays focused.
Relation to other skills
/onboardruns an initial a11y audit at project setup (axe or static checklist →.onboard-audit/a11y.md)./validateis the on-demand equivalent, re-runnable anytime against the current codebase, and also covers HTML/CSS validity (which/onboarddoes not)./hardenaudits security posture (headers, TLS, redirects)./validateaudits conformance. They share no findings./seoand/geoaudit indexability. They may flag the same HTML features (alt attrs, heading structure) but from a ranking perspective./validateflags from a standards perspective (WCAG SC number, W3C rule id). Findings may overlap — both reports are still valid.
STEP 0 — Collect context
Parse arguments
- If
$ARGUMENTScontainshttps?://<url>→TARGET_URL. - Extract
DOMAINfromTARGET_URL:DOMAIN=${TARGET_URL#http*://}; DOMAIN=${DOMAIN%%/*}. - If
$ARGUMENTScontains--fix→MODE=fix. ElseMODE=audit. - If
--local→DEPTH=LOCAL. If--full→DEPTH=FULL. - If URL present and no depth flag → default
DEPTH=FULL. - If no URL and no depth flag → default
DEPTH=LOCAL. - If
--no-external→EXTERNAL=off. Elseon. EXTERNALauto-off in LOCAL mode (no URL to scan remotely).
Detect HTML / CSS files
HTML_COUNT=$(find . -name "*.html" \
-not -path "*/node_modules/*" \
-not -path "*/dist/*" \
-not -path "*/.next/*" \
-not -path "*/.validate-cache/*" 2>/dev/null | wc -l)
CSS_COUNT=$(find . -name "*.css" \
-not -path "*/node_modules/*" \
-not -path "*/dist/*" \
-not -path "*/.next/*" 2>/dev/null | wc -l)
If both counts are 0 and no URL provided → abort with :
⚠️ No HTML or CSS files found and no URL provided. /validate needs
either local files or a live URL. Re-run with --full <url>.
Detect framework
FRAMEWORK="static"
[ -f astro.config.mjs ] || [ -f astro.config.ts ] && FRAMEWORK="astro"
[ -f next.config.js ] || [ -f next.config.mjs ] || [ -f next.config.ts ] && FRAMEWORK="next"
[ -f vite.config.js ] || [ -f vite.config.ts ] && FRAMEWORK="vite"
[ -f svelte.config.js ] && FRAMEWORK="svelte"
[ -f nuxt.config.js ] || [ -f nuxt.config.ts ] && FRAMEWORK="nuxt"
[ -f vue.config.js ] && FRAMEWORK="vue"
For JS frameworks, HTML validity must target built output. Check for build dir :
BUILD_DIR=""
for d in dist _site build out public; do
[ -d "$d" ] && BUILD_DIR="$d" && break
done
If framework is JS-based and BUILD_DIR is empty, warn :
⚠️ Framework detected : <name>. No build output found.
HTML validity on JSX/TSX source is not meaningful.
Options :
1. Run `npm run build` then re-run /validate
2. Use --full <url> to audit production
3. Continue with partial LOCAL audit (CSS + static WCAG only)
Detect LOCAL tooling
HAS_HTML_VALIDATE=$(npx --no-install html-validate --version >/dev/null 2>&1 && echo yes || echo no)
HAS_STYLELINT=$(npx --no-install stylelint --version >/dev/null 2>&1 && echo yes || echo no)
HAS_AXE=$(npx --no-install @axe-core/cli --version >/dev/null 2>&1 && echo yes || echo no)
HAS_PA11Y=$(npx --no-install pa11y --version >/dev/null 2>&1 && echo yes || echo no)
HAS_VNU=$([ -f /usr/share/vnu/vnu.jar ] || [ -f /opt/vnu/vnu.jar ] && echo yes || echo no)
Missing tools are NOT blockers — agent falls back to remote APIs (FULL) or static checks.
Display collected context
VALIDATE — context
URL : <url or — (local mode)>
Domain : <domain or —>
Depth : LOCAL | FULL
Mode : audit | fix
External : on | off (auto-off in LOCAL)
HTML files : <N>
CSS files : <N>
Framework : <astro | next | vite | svelte | nuxt | vue | static>
Build dir : <dist/ | _site/ | ... | — none found>
Local tools : html-validate=<y/n>, stylelint=<y/n>, axe=<y/n>, pa11y=<y/n>, vnu=<y/n>
If MODE=fix, warn :
⚠️ Fixes proposés comme diffs. Appliqués seulement après confirmation.
STEP 1 — Dispatch validator-analyzer
Spawn a single validator-analyzer subagent with explicit scope and
collected context :
Agent(
subagent_type="validator-analyzer",
description="validate — W3C HTML + CSS + WCAG audit",
prompt="""
Dispatched from /validate. STRICT SCOPE — W3C HTML validity + W3C
CSS validity + WCAG 2.1 accessibility ONLY.
CONTEXT:
TARGET_URL : <url or "none — LOCAL mode">
DOMAIN : <domain or —>
DEPTH : <LOCAL | FULL>
MODE : <audit | fix>
EXTERNAL : <on | off>
HTML_FILES : <count>
CSS_FILES : <count>
FRAMEWORK : <name>
BUILD_DIR : <path or "none">
LOCAL_TOOLS : html-validate=<y/n>, stylelint=<y/n>, axe=<y/n>, pa11y=<y/n>, vnu=<y/n>
Execute your spec at $HOME/.claude/agents/validator-analyzer.md
starting at STEP 1 (skip STEP 0 — context is above).
OUT OF SCOPE — DROP silently if encountered :
- meta tags (title/description/OG/Twitter/canonical)
- JSON-LD / Schema.org / microdata
- sitemap.xml, robots.txt, llms.txt
- AI crawler directives
- security headers (HSTS/CSP/X-Frame-Options/cookie flags)
- Core Web Vitals, perf budgets
- hreflang, i18n routing
- image compression, video formats
- generic code linting (ESLint, Prettier, TS errors)
Mode behavior :
- MODE=audit : NO file modifications. Report-only. Propose fixes
as diffs in the report (```diff blocks), do NOT apply.
- MODE=fix : Report issues, then produce Fix bundle (§5) with
concrete diffs for auto-fixable items. STOP and emit
"READY TO APPLY — awaiting dispatcher confirmation" at the end
of §5. Do NOT apply any Edit/Write — the dispatcher handles STEP 3.
Output: write <PROJECT_ROOT>/VALIDATE.md per the structure in your
spec (sections 0-8, score XX/100).
"""
)
STEP 2 — Verify output
test -s VALIDATE.md && wc -l VALIDATE.md || echo "MISSING VALIDATE.md"
If missing or empty :
⚠️ validator-analyzer did not produce VALIDATE.md. Options :
A) Retry with same scope
B) Downgrade to LOCAL and retry (if FULL failed on network)
C) Abort
Extract the score and critical-alert count from VALIDATE.md for the console summary :
grep -oE '\*\*Score\*\*\s+:\s+[0-9]+ / 100' VALIDATE.md | head -1
grep -c '^### \[Critique\]' VALIDATE.md
STEP 3 — Apply fixes (MODE=fix only)
Skip this step if MODE=audit.
If VALIDATE.md ends with READY TO APPLY — awaiting dispatcher confirmation :
- Parse the
## 5. Fix bundlesection. - Group by file. For each group, show the combined diff to the user.
- Ask :
VALIDATE — fix bundle ready
Files to modify (N) :
- src/Layout.astro (3 fixes : lang attr, alt="", heading renumber)
- src/styles/main.css (1 fix : invalid property removed)
- src/pages/contact.html (2 fixes : unclosed tag, duplicate id)
Critical : X | Haute : Y | Moyenne : Z | Basse : W
Options :
A) Apply all
B) Review each diff before applying
C) Apply only Critique + Haute
D) Abort — keep VALIDATE.md as audit report
- On
A: apply each bundle viaEdit(targetedold_string/new_string). Never useWriteon shared templates (risk of overwriting /seo or /geo content — meta tags, JSON-LD). - On
B: for each diff, show and ask yes/no/skip. - On
C: filter to Critique + Haute, then behave asA. - On
D: stop, leave VALIDATE.md untouched.
After applying, append a ## 8. Changes applied section with
commit-ready summary lines :
## 8. Changes applied
Date : <ISO-8601>
Files modified : <N>
Fixes applied : <N>
### src/Layout.astro
- [Haute][HTML] Added `lang="en"` to `<html>` (WCAG 3.1.1, W3C required attr)
- [Haute][WCAG AA] Added `alt=""` to decorative icon at line 42
- [Moyenne][HTML] Renumbered h3 → h2 (heading hierarchy, line 67)
### src/styles/main.css
- [Moyenne][CSS] Removed invalid property `bakground` → `background` at line 23
Verification :
- Re-run /validate → expected score bump <before> → <after>
- Tests to run : a11y regression (pa11y-ci), visual snapshot
Never apply fixes without explicit confirmation.
Never use --no-verify on git hooks.
STEP 4 — Console summary
VALIDATE AUDIT COMPLETE
URL : <url or static>
Depth : LOCAL | FULL
Mode : audit | fix
Score : XX / 100 (<before> → <after> if fix applied)
Report : VALIDATE.md
BREAKDOWN :
W3C HTML : <N errors / M warnings>
W3C CSS : <N errors / M warnings>
WCAG 2.1 : <N A> / <M AA> / <K AAA> violations, <L incomplete>
TOP 3 ACTIONS (by severity × user impact) :
1. [Critique] <title> — <file:line>
2. [Haute] <title>
3. [Haute] <title>
NEXT STEPS :
• /validate <url> --fix → apply recommended fixes
• /validate <url> --full → re-run with live URL + remote APIs
• /validate --no-external → skip third-party APIs (faster, LOCAL-like)
• /harden / /seo / /geo → complementary audits (other scopes)
Install for better LOCAL coverage :
npm i -D html-validate stylelint @axe-core/cli pa11y
Rules
- Scope is non-negotiable. If you find yourself reporting meta
tags, sitemap, CSP, or JSON-LD, you drifted. Drop it.
/harden,/seo,/geoown those respectively. - Single agent dispatch. Only
validator-analyzer. No parallel fan-out. - Never apply fixes without user confirmation, even in
--fix. The fix mode prepares the bundle; the dispatcher confirms (A/B/C/D). - LOCAL vs FULL is about data sources, not scope. Both cover the same 3 axes. LOCAL may be degraded if local tools missing (agent falls back to static checks — flagged "STATIC MODE" in report).
- Framework awareness. For SPA/JS frameworks, validate built
output (
dist/,_site/,build/,out/), not JSX/TSX source. Warn if no build dir present. - Respect CLAUDE.md architecture rules. Public websites must ship WCAG 2.1 AA per France RGAA 4.1 when in scope. Flag AA violations as Haute, A violations as Critique.
- External validators are authoritative on live URLs. validator.nu and jigsaw are the W3C backends. If a local tool disagrees with them, trust the W3C backend; flag the divergence as a finding.
- One report file.
VALIDATE.mdat project root (ordocs/VALIDATE.mdif that convention exists). On re-run, move previous content to a## Historiquesection, do not overwrite silently. - Cache dir.
.validate-cache/(gitignored) stores raw tool outputs for debugging. Do not commit. - Conservative auto-fix. Only structural/syntactic fixes with no ambiguity. Content decisions (alt text, labels, contrast choices) always go to §6 User actions — never auto-applied.