
Web Quality Audit
Run a read-only HTML quality pass on a file or directory and get structured JSON findings for doctype, viewport, charset, and related baseline checks before ship.
Overview
web-quality-audit is an agent skill most often used in Ship (also Launch SEO pages, Build frontend polish) that performs a read-only HTML quality analysis and returns capped JSON findings.
Install
npx skills add https://github.com/addyosmani/web-quality-skills --skill web-quality-auditWhat is this skill?
- Read-only bash analyzer v2: stderr for human logs, stdout for JSON only
- Requires `jq` for safe structured output; fails fast with install suggestion
- Caps at 100 total findings and 20 per category per file to avoid noisy floods
- Baseline checks include HTML5 doctype, UTF-8 charset, viewport meta, and html `lang`
- Accepts a single file or directory target path
- 100 maximum findings per run
- 20 maximum findings per category per file
Adoption & trust: 8k installs on skills.sh; 2.2k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are about to ship static HTML or templated pages but lack a quick, scriptable baseline audit that will not rewrite files behind your back.
Who is it for?
Indie builders batch-auditing HTML templates or exported static sites in CI or local pre-push checks.
Skip if: Teams needing full performance, accessibility lab scores, or runtime-only behavior without static HTML sources.
When should I use this skill?
You have an existing HTML file or directory path and need structured quality findings without modifying sources.
What do I get? / Deliverables
You receive structured JSON issues and warnings you can triage in PR review or feed back to the agent for targeted HTML fixes.
- JSON success payload with issues and warnings
- Human-readable analysis log on stderr
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Ship → review is the canonical shelf because the analyzer targets pre-release HTML hygiene, though you can rerun the same checker after Grow-driven landing updates. review subphase fits automated quality gates that supplement human code review without mutating the tree.
Where it fits
Scan new component HTML exports right after a design pass to catch missing viewport tags.
Gate a PR that touches multiple templates with a 100-finding-capped JSON report for the reviewer.
Re-audit landing pages after copy changes to ensure `lang` and charset remain valid for crawlers.
How it compares
Static HTML checker with JSON output—not a replacement for Lighthouse performance audits or visual regression suites.
Common Questions / FAQ
Who is web-quality-audit for?
Solo frontend builders and agents fixing HTML templates who want read-only, automatable quality signals before release.
When should I use web-quality-audit?
In Ship review before merging UI changes; in Build frontend when scaffolding new pages; in Launch seo when publishing landing HTML; after Grow content updates to static marketing files.
Is web-quality-audit safe to install?
Review the Security Audits panel on this Prism page; the v2 script explicitly avoids filesystem mutations and only reads targets you pass—still verify the script hash and run in a trusted repo.
SKILL.md
READMESKILL.md - Web Quality Audit
#!/bin/bash # Read-only HTML quality analyzer (v2). No filesystem mutations. # stderr = human logs, stdout = structured JSON. set -euo pipefail MAX_FINDINGS=100 MAX_PER_CATEGORY_PER_FILE=20 # cap per high-volume check per file so one category can't fill MAX_FINDINGS fail() { local type="$1" msg="$2" suggestion="$3" if command -v jq >/dev/null 2>&1; then jq -n \ --arg type "$type" \ --arg msg "$msg" \ --arg suggestion "$suggestion" \ '{success: false, error: {type: $type, message: $msg, retryable: false, suggestion: $suggestion}}' else printf '{"success":false,"error":{"type":"%s","message":"%s","suggestion":"%s","retryable":false}}\n' \ "$type" "$msg" "$suggestion" fi exit 1 } command -v jq >/dev/null 2>&1 || \ fail "missing_dependency" "jq is required for safe JSON output" "Install: brew install jq" [ $# -ge 1 ] || fail "invalid_input" "No target provided" "Usage: $0 <file_or_directory>" TARGET="$1" [ -e "$TARGET" ] || fail "invalid_input" "Target not found: $TARGET" "Pass an existing file or directory path" ISSUES=() WARNINGS=() analyze_html() { local file="$1" echo "Analyzing: $file" >&2 grep -qi "<!doctype html>" "$file" || ISSUES+=("$file:0: Missing HTML5 doctype") grep -qi 'charset.*utf-8' "$file" || WARNINGS+=("$file:0: Missing or non-UTF-8 charset") grep -qi 'name="viewport"' "$file" || ISSUES+=("$file:0: Missing viewport meta tag") grep -qi '<html[^>]*lang=' "$file" || ISSUES+=("$file:0: Missing lang attribute on <html>") grep -qi '<title>' "$file" || ISSUES+=("$file:0: Missing <title> tag") # <img> without alt — two-pass replaces broken PCRE lookahead local alt_count=0 while IFS=: read -r ln tag; do if grep -qE 'alt=' <<<"$tag"; then continue; fi if [ "$alt_count" -ge "$MAX_PER_CATEGORY_PER_FILE" ]; then WARNINGS+=("$file:0: <img>-without-alt findings truncated (>${MAX_PER_CATEGORY_PER_FILE} in this file)") break fi WARNINGS+=("$file:$ln: <img> without alt attribute") alt_count=$((alt_count + 1)) done < <(grep -noE '<img[^>]*>' "$file" || true) # Non-HTTPS URLs with line numbers local http_count=0 while IFS=: read -r ln _; do if [ "$http_count" -ge "$MAX_PER_CATEGORY_PER_FILE" ]; then WARNINGS+=("$file:0: Non-HTTPS URL findings truncated (>${MAX_PER_CATEGORY_PER_FILE} in this file)") break fi WARNINGS+=("$file:$ln: Non-HTTPS URL") http_count=$((http_count + 1)) done < <(grep -noE 'http://[^"'\''[:space:]>]*' "$file" || true) } # Process substitution keeps arrays in main shell (fixes v1 subshell bug) if [ -d "$TARGET" ]; then while IFS= read -r -d '' file; do analyze_html "$file" done < <(find "$TARGET" \( -name "*.html" -o -name "*.htm" \) -print0) elif [ -f "$TARGET" ]; then analyze_html "$TARGET" else fail "invalid_input" "Target is not a regular file or directory: $TARGET" "Pass a path to an .html/.htm file or a directory" fi issue_total=${#ISSUES[@]} warning_total=${#WARNINGS[@]} to_json_array() { printf '%s\n' "$@" | jq -Rs 'split("\n") | map(select(length > 0))' } if [ "$issue_total" -gt 0 ]; then issues_json=$(to_json_array "${ISSUES[@]:0:$MAX_FINDINGS}") else issues_json='[]' fi if [ "$warning_total" -gt 0 ]; then warnings_json=$(to_json_array "${WARNINGS[@]:0:$MAX_FINDINGS}") else warnings_json='[]' fi echo "Scanned. $issue_total issues, $warning_total warnings." >&2 jq -n \ --argjson issues "$issues_json" \ --argjson warnings "$warnings_json" \ --argjson issue_total "$issue_total" \ --argjson warning_total "$warning_total" \ --argjson max "$MAX_FINDINGS" \ '{ success: true, issues: $issues, warnings: $warnings, issueCount: $issue_total, warningCount: $warning_total, truncated: (($issue_total > $max) or ($warning_total > $max)) }' --- name: web-quality-audit description: Comprehensive web quality audit covering performance, accessibility, SEO, and best practices. U