{"_id":"@didrod2539/a11ylint","name":"@didrod2539/a11ylint","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@didrod2539/a11ylint","version":"0.1.0","publishConfig":{"access":"public"},"description":"Static accessibility (a11y) linter for HTML — checks WCAG-mapped rules (alt text, form labels, heading order, ARIA validity, landmarks, color contrast, tables) with no headless browser. Deterministic CLI, JSON/Markdown reports, runs in CI.","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"bin":{"a11ylint":"dist/cli.js"},"engines":{"node":">=18"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","lint":"tsc --noEmit","example":"node dist/cli.js scan examples/bad.html examples/good.html","prepublishOnly":"npm run build"},"keywords":["accessibility","a11y","wcag","accessibility-linter","a11y-linter","html-accessibility","axe-alternative","wcag-checker","aria","a11y-ci","accessibility-audit","section508","cli","typescript"],"author":{"name":"didrod205","url":"https://github.com/didrod205"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/didrod205/a11ylint.git"},"bugs":{"url":"https://github.com/didrod205/a11ylint/issues"},"homepage":"https://github.com/didrod205/a11ylint#readme","dependencies":{"cac":"^6.7.14","node-html-parser":"^6.1.13","picocolors":"^1.1.1"},"devDependencies":{"@types/node":"^22.10.0","tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^2.1.8"},"gitHead":"4fa6233dc974f1b10bc1e5a0ce85122a44ed5d6d","_id":"@didrod2539/a11ylint@0.1.0","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-IzJWyT3OdqvGQUkFp75RNw1UR+OeRFp5P6RPEki15YTfYl+MJMEgDH+F5FV1y8i/3+hQW+pl9THKVM1Htepjkg==","shasum":"65ff800fa0fc08d8900b06e9ce02a87c78c14a50","tarball":"https://registry.npmjs.org/@didrod2539/a11ylint/-/a11ylint-0.1.0.tgz","fileCount":15,"unpackedSize":451981,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEuFhUvzh/FU8ctengmPzQvXWpO7jKIMfzmnSZVTvv3+AiEAhsKMb1Af2KiM6zNPfl1SzJDUStFhAwef7o6l4DQ/xvk="}]},"_npmUser":{"name":"didrod2539","email":"ykc205@naver.com"},"directories":{},"maintainers":[{"name":"didrod2539","email":"ykc205@naver.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/a11ylint_0.1.0_1780285202744_0.6054738508574571"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-01T03:40:02.537Z","0.1.0":"2026-06-01T03:40:02.919Z","modified":"2026-06-01T03:40:03.105Z"},"maintainers":[{"name":"didrod2539","email":"ykc205@naver.com"}],"description":"Static accessibility (a11y) linter for HTML — checks WCAG-mapped rules (alt text, form labels, heading order, ARIA validity, landmarks, color contrast, tables) with no headless browser. Deterministic CLI, JSON/Markdown reports, runs in CI.","homepage":"https://github.com/didrod205/a11ylint#readme","keywords":["accessibility","a11y","wcag","accessibility-linter","a11y-linter","html-accessibility","axe-alternative","wcag-checker","aria","a11y-ci","accessibility-audit","section508","cli","typescript"],"repository":{"type":"git","url":"git+https://github.com/didrod205/a11ylint.git"},"author":{"name":"didrod205","url":"https://github.com/didrod205"},"bugs":{"url":"https://github.com/didrod205/a11ylint/issues"},"license":"MIT","readme":"<div align=\"center\">\n\n# ♿ a11ylint\n\n### Catch accessibility (a11y) bugs in your HTML — no browser, right in CI.\n\n[![npm version](https://img.shields.io/npm/v/@didrod2539/a11ylint.svg?color=success)](https://www.npmjs.com/package/@didrod2539/a11ylint)\n[![CI](https://github.com/didrod205/a11ylint/actions/workflows/ci.yml/badge.svg)](https://github.com/didrod205/a11ylint/actions/workflows/ci.yml)\n[![node](https://img.shields.io/node/v/@didrod2539/a11ylint.svg)](https://www.npmjs.com/package/@didrod2539/a11ylint)\n[![license](https://img.shields.io/npm/l/@didrod2539/a11ylint.svg)](./LICENSE)\n\nA deterministic, **static** accessibility linter for HTML. It checks WCAG-mapped\nrules — alt text, form labels, heading order, ARIA validity, landmarks, color\ncontrast, data tables and more — **without a headless browser**, so it runs on\nyour build output in milliseconds. Score, A–F grade, and JSON/Markdown reports.\n\n</div>\n\n---\n\n## One-line summary\n\n`a11ylint` statically analyzes HTML files and reports WCAG accessibility issues\nwith line numbers, fixes, and a score you can gate in CI — no Puppeteer, no API\nkey, no server.\n\n## Why this project exists\n\nAccessibility isn't optional anymore: the **ADA** (US), the **European\nAccessibility Act** (in force June 2025), **Section 508**, and similar laws make\ninaccessible sites a legal and financial risk — and thousands of lawsuits are\nfiled every year. Yet most a11y tools (axe-core, pa11y, Lighthouse) need to spin\nup a **headless browser**, which is slow, heavy, and awkward to wire into a build\nthat just emitted static HTML.\n\n`a11ylint` focuses on the large set of issues you **can** catch from the markup\nalone — missing alt text, unlabeled inputs, empty buttons/links, broken heading\norder, invalid ARIA, missing landmarks, low-contrast inline styles, header-less\ntables — and runs them as a fast, deterministic lint. Perfect for a pre-commit\nhook or CI gate. It complements (doesn't replace) runtime tools.\n\n## Key features\n\n- 🖼️ **Images** — missing `alt`, redundant \"image of…\" alt text.\n- 📝 **Forms** — inputs/selects/textareas with no associated label, empty labels.\n- 🔤 **Structure** — skipped heading levels, empty headings, missing `<h1>`,\n  missing `<main>` landmark, invalid list nesting.\n- 🔘 **Controls** — empty buttons/links, `<a>` without `href`, positive `tabindex`.\n- 🧩 **ARIA** — invalid roles, misspelled `aria-*` attributes, focusable content\n  inside `aria-hidden`.\n- 🌐 **Language & meta** — missing `lang`/`<title>`, zoom-disabling viewport,\n  duplicate `id`s.\n- 📊 **Tables** — data tables without `<th>`/scope or `<caption>`.\n- 🎨 **Color** — WCAG contrast math on inline `style` colors.\n- Every issue maps to a **WCAG 2.1 success criterion** and level (A/AA/AAA).\n- Score + **A–F grade**, **JSON/Markdown** export, **CI gate** exit codes.\n\n## Install\n\n```bash\n# run without installing\nnpx @didrod2539/a11ylint scan index.html\n\n# or install\nnpm install -g @didrod2539/a11ylint    # global CLI (provides `a11ylint`)\nnpm install -D @didrod2539/a11ylint    # project dev-dependency (for CI)\n```\n\nNode ≥ 18. ESM + CJS + TypeScript types.\n\n## Quick start\n\n```bash\na11ylint scan ./dist\n```\n\n```\npage.html  80/100 (B)\n  Images & media          79\n  Forms & labels          76\n  Interactive controls    66\n  ARIA usage              70\n  ...\n\n  ✗ Image is missing an alt attribute:8 [WCAG 1.1.1 A]\n      → Add alt text, or alt=\"\" with role=\"presentation\" if decorative.\n  ✗ Form control <input type=\"text\"> has no associated label:19 [WCAG 3.3.2 A]\n      → Add <label for=\"…\">, wrap it in a <label>, or use aria-label.\n  ✗ Invalid ARIA role \"buton\":27 [WCAG 4.1.2 A]\n  ⚠ Low contrast 1.92:1 (needs 4.5:1):25 [WCAG 1.4.3 AA]\n\nOverall  80/100 (B)  1 page(s), 12 error(s), 8 warning(s), 2 info\n```\n\n## CLI usage\n\n```bash\na11ylint scan [...targets]    # lint HTML files or directories\na11ylint report <input.json>  # re-render a saved JSON report as Markdown\na11ylint init                 # scaffold a11ylint.config.json\na11ylint --help\na11ylint --version\n```\n\n`scan` options:\n\n| Option | Description |\n| --- | --- |\n| `--config <file>` | Path to a config file (otherwise auto-detected) |\n| `--level <A\\|AA\\|AAA>` | Target WCAG conformance level (default AA) |\n| `--json <file>` | Write a JSON report |\n| `--md <file>` | Write a Markdown report |\n| `--min-score <n>` | Exit non-zero if the overall score < n (CI gate) |\n| `--quiet` | Hide info-level issues in the console |\n\nPointed at a directory, `scan` finds every `.html`/`.htm` recursively.\n\n## Example result\n\nFull reports for the bundled samples are in\n[`examples/sample-report.md`](./examples/sample-report.md) and\n[`examples/sample-report.json`](./examples/sample-report.json).\n\n> 📸 _Screenshot / demo GIF placeholder:_ `./docs/screenshot.png` — record the\n> terminal running `npx @didrod2539/a11ylint scan examples/bad.html`.\n\n## Configuration\n\nCreate `a11ylint.config.json` (or run `a11ylint init`):\n\n```json\n{\n  \"minLevel\": \"AA\",\n  \"minScore\": 90,\n  \"disableCategories\": [],\n  \"disableRules\": [\"img-redundant-alt\"],\n  \"ruleSeverity\": { \"table-caption\": \"warning\" },\n  \"categoryWeights\": { \"images\": 1.2, \"forms\": 1.2, \"controls\": 1.2 }\n}\n```\n\n| Field | Meaning |\n| --- | --- |\n| `minLevel` | Target WCAG level: `A` (A only), `AA` (A+AA), `AAA` (all) |\n| `minScore` | CI gate threshold (overridable with `--min-score`) |\n| `disableCategories` | Skip whole categories (e.g. `[\"color\"]`) |\n| `disableRules` | Skip individual rules by id |\n| `ruleSeverity` | Override severity per rule id |\n| `categoryWeights` | Re-weight categories in the overall score |\n\nCategories: `images`, `forms`, `structure`, `controls`, `aria`, `language`,\n`tables`, `color`. Run with `--help` or read `src/types.ts` for the full rule id\nlist.\n\n## Real-world use cases\n\n1. **Gate accessibility in CI.** Add `a11ylint scan ./dist --min-score 90` to\n   your pipeline. A PR that ships an unlabeled form field or an image without alt\n   text fails the build before it reaches users (or auditors).\n2. **Audit a static export or template.** Run `a11ylint scan ./public\n   --md a11y-audit.md` to get a per-page, WCAG-referenced Markdown report you can\n   hand to a designer or compliance reviewer.\n3. **Pre-commit safety net.** Wire `a11ylint scan <changed>.html` into a\n   pre-commit hook so regressions are caught at authoring time, no browser needed.\n\n## Programmatic API\n\n```ts\nimport { analyze, buildReport, toMarkdown } from \"@didrod2539/a11ylint\";\n\nconst page = analyze({ source: \"index.html\", html });\nconsole.log(page.score, page.grade, page.issues);\n\nconst report = buildReport([page], { version: \"0.1.0\" });\nawait fs.writeFile(\"a11y.md\", toMarkdown(report));\n```\n\n## Roadmap\n\n- More rules: autocomplete tokens, `lang` on inline language changes, iframe\n  titles, label/placeholder-only inputs, redundant `role`.\n- Contrast for `<style>` blocks and class-based colors (lightweight CSS cascade).\n- A GitHub Action + annotations on PR diffs.\n- SARIF output for code-scanning integration.\n- `--fix` for safe auto-fixes (add `scope`, quote ids, etc.).\n- Config presets (`strict`, `recommended`).\n\n## FAQ\n\n**Is this a replacement for axe-core / Lighthouse?**\nNo — it's a **complement**. Those run in a real browser and catch dynamic and\ncomputed-style issues a11ylint can't. a11ylint catches the large class of\n**static, markup-level** problems with zero browser overhead, which makes it\nideal for CI and pre-commit. Use both.\n\n**Does it need a browser or network?**\nNo. It parses HTML with a fast static parser and runs entirely locally — no\nPuppeteer, no API key, no uploads.\n\n**Why did my page score 80 with 12 errors?**\nThe score is per-category (each capped at 0–100) then weighted, so one terrible\ncategory doesn't zero out a page that's otherwise fine. Tune `categoryWeights`\nand `minScore` for your bar, and use `--min-score` to gate.\n\n**Can it check color contrast?**\nFor colors declared in **inline `style`** attributes, yes (full WCAG math).\nContrast from external/embedded CSS needs the cascade and is on the roadmap.\n\n**Does it understand ARIA?**\nIt validates role and `aria-*` attribute names against WAI-ARIA and flags\nfocusable content hidden with `aria-hidden`. Deep role-semantics checks are\nplanned.\n\n## Contributing\n\nContributions welcome! Each check is a small, self-contained rule in\n`src/rules/`, and WCAG mappings live in `src/wcag.ts`. See\n[CONTRIBUTING.md](./CONTRIBUTING.md) and the\n[Code of Conduct](./CODE_OF_CONDUCT.md).\n\n```bash\ngit clone https://github.com/didrod205/a11ylint.git\ncd a11ylint\nnpm install\nnpm test\nnpm run build\nnode dist/cli.js scan examples/bad.html\n```\n\n## License\n\n[MIT](./LICENSE) © a11ylint contributors\n\n## 💖 Sponsor\n\na11ylint is free, MIT-licensed, and built in spare time. If it helped you ship a\nmore accessible (and more compliant) site, please consider supporting it:\n\n- ⭐ **Star this repo** — free, and it helps others find it.\n- 🍋 **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** — one-time or recurring.\n\n**Where your support goes:** more WCAG rules, a GitHub Action with PR\nannotations, SARIF output, CSS-aware contrast checking, a `--fix` mode, and fast\nissue responses.\n","readmeFilename":"README.md","_rev":"1-5918c781b41f475aae000784680a5937"}