@wcag-checkr/ci 1.0.0-rc.326 → 1.0.0-rc.327

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import"./modulepreload-polyfill-B5Qt9EMX.js";import{_ as y}from"./preload-helper-D7HrI6pR.js";const P=new Set(["Document","Part","Art","Sect","Div","BlockQuote","Caption","TOC","TOCI","Index","NonStruct","Private","H","H1","H2","H3","H4","H5","H6","P","L","LI","Lbl","LBody","Table","TR","TH","TD","THead","TBody","TFoot","Span","Quote","Note","Reference","BibEntry","Code","Link","Annot","Ruby","RB","RT","RP","Warichu","WT","WP","Figure","Formula","Form"]);function T(){const t=new Map;let r=0;return{ruleAggregates:t,get lastHeading(){return r},set lastHeading(n){r=n},bumpRule(n,e){let a=t.get(n);a||(a={count:0,pages:new Set},t.set(n,a)),a.count++,a.pages.add(e)},setLastHeadingLevel(n){r=n},getLastHeading(){return r}}}function k(t,r,n){if(!t||typeof t!="object")return;const e=t.role;if(e&&!P.has(e)&&n.bumpRule("pdf-non-standard-role",r),e==="Figure"&&((t.alt??"").trim()||n.bumpRule("pdf-figure-missing-alt",r)),e&&/^H[1-6]$/.test(e)){const a=parseInt(e.slice(1),10),o=n.getLastHeading();o>0&&a>o+1&&n.bumpRule("pdf-heading-skip",r),n.setLastHeadingLevel(a)}if(e==="Table"&&!w(t,"TH")&&n.bumpRule("pdf-table-no-header",r),e==="L"){const a=t.children??[],o=a.some(i=>i&&i.role&&i.role!=="LI"&&i.role!=="Caption");(a.length===0||o)&&n.bumpRule("pdf-list-malformed",r)}for(const a of t.children??[])k(a,r,n)}function w(t,r){if(!t)return!1;if(t.role===r)return!0;for(const n of t.children??[])if(w(n,r))return!0;return!1}function A(t){if(!t)return 0;const r=t.filter(e=>e.subtype==="Widget");let n=0;for(const e of r){const a=(e.alternativeText??"").trim(),o=(e.fieldName??"").trim(),i=/^(field|text\s*box|check\s*box|radio|button)\s*\d*$/i.test(o);!a&&(!o||i)&&n++}return n}function S(t){const r=[],n={"pdf-figure-missing-alt":{ruleId:"pdf-figure-missing-alt",impact:"serious",wcagCriterion:"1.1.1",describe:e=>({description:`${e.count} Figure tag${e.count===1?"":"s"} (across ${e.pages.size} page${e.pages.size===1?"":"s"}) have no alt text. Screen-reader users hear nothing for these images. PDF/UA-1 §7.4.`,failureSummary:"Open the PDF in Acrobat → Accessibility → Reading Order → click each Figure → Edit Alternate Text. Or, in source: add alt text to each image in Word/InDesign/PowerPoint before exporting."})},"pdf-heading-skip":{ruleId:"pdf-heading-skip",impact:"moderate",wcagCriterion:"1.3.1",describe:e=>({description:`Heading levels skip (e.g. H1 → H3 without H2). Detected ${e.count} skip${e.count===1?"":"s"} across ${e.pages.size} page${e.pages.size===1?"":"s"}. Screen-reader users hear the section hierarchy as broken.`,failureSummary:"Re-tag the headings sequentially. In Acrobat → Accessibility → Reading Order, change the offending tag to the correct heading level. In source documents use Heading 1 → Heading 2 → Heading 3 styles consistently."})},"pdf-table-no-header":{ruleId:"pdf-table-no-header",impact:"serious",wcagCriterion:"1.3.1",describe:e=>({description:`${e.count} Table tag${e.count===1?"":"s"} (across ${e.pages.size} page${e.pages.size===1?"":"s"}) have no TH (header) cells. Screen-reader table-navigation mode cannot announce headers when reading cells.`,failureSummary:"Open the PDF in Acrobat → Accessibility → Reading Order → select the table → Table Editor. Mark the first row (or first column) as headers. In source documents (Word: Table Tools → Layout → Repeat Header Rows; PowerPoint: tables need to be tagged as data tables)."})},"pdf-list-malformed":{ruleId:"pdf-list-malformed",impact:"moderate",wcagCriterion:"1.3.1",describe:e=>({description:`${e.count} List tag${e.count===1?"":"s"} (across ${e.pages.size} page${e.pages.size===1?"":"s"}) are malformed — L tags should contain LI children with optional Lbl + LBody. PDF/UA-1 §7.8.`,failureSummary:'Re-tag the lists in Acrobat → Accessibility → Reading Order, or recreate the PDF from a source document that produces proper list tagging (bullet/numbered lists in Word/Pages/Google Docs export correctly when "tagged PDF" is enabled).'})},"pdf-non-standard-role":{ruleId:"pdf-non-standard-role",impact:"minor",wcagCriterion:"1.3.1",describe:e=>{const a=Array.from(e.pages).slice(0,3).join(", ");return{description:`${e.count} structure-tree node${e.count===1?"":"s"} use non-standard tag roles (sample pages: ${a}). PDF/UA-1 §7.3 requires a RoleMap entry in StructTreeRoot mapping each custom role to a standard one.`,failureSummary:"Either replace the non-standard tags with PDF/UA standard ones (Document/Part/Art/Sect/Div/H1-H6/P/L/LI/Table/TR/TH/TD/Figure/Form/etc.), or add RoleMap entries in StructTreeRoot mapping each custom name to a standard equivalent."}}},"pdf-untagged-content":{ruleId:"pdf-untagged-content",impact:"critical",wcagCriterion:"1.3.1",describe:e=>({description:`${e.pages.size} page${e.pages.size===1?"":"s"} have no structure tree (untagged content). Screen-reader users get no reading order on these pages — content may be read in random visual-position order or skipped entirely.`,failureSummary:"Re-tag the document end-to-end. In Acrobat → Accessibility → Autotag Document is a starting point but rarely produces good results — re-export from the source document with tagging enabled, then refine in Acrobat's Reading Order tool."})}};for(const[e,a]of t.ruleAggregates){const o=n[e];if(!o)continue;const{description:i,failureSummary:c}=o.describe(a);r.push({ruleId:o.ruleId,impact:o.impact,wcagCriterion:o.wcagCriterion,description:i,failureSummary:c})}return r}function D(t,r){return t===0?null:{ruleId:"pdf-field-no-name",impact:"serious",wcagCriterion:"4.1.2",description:`${t} form field${t===1?"":"s"} across ${r} page${r===1?"":"s"} have no accessible name (no /TU alternate text + no descriptive /T field name). Screen-reader users hear nothing or hear the field type generically ("text field", "check box").`,failureSummary:'In Acrobat: open the Tools → Prepare Form panel → right-click each field → Properties → General tab → fill in the "Tooltip" (this becomes the PDF /TU). Or rename the /T field name to something descriptive ("First name", "Total amount due"). PDF/UA-1 §7.15.'}}function R(t,r){return t<=5||r?null:{ruleId:"pdf-no-bookmarks",impact:"moderate",wcagCriterion:"2.4.5",description:`Multi-page PDF (${t} pages) has no bookmarks / outline. Keyboard and AT users cannot navigate the document structure without scrolling page-by-page.`,failureSummary:'Add bookmarks to the PDF in Acrobat (View → Show/Hide → Navigation Panes → Bookmarks, then add entries for each section heading). Or re-export from the source document with "Generate bookmarks" enabled (Word: Save As PDF → Options → Create bookmarks using: Headings).'}}let g=null;function $(){return g||(g=(async()=>{try{const t=(await y(async()=>{const{default:n}=await import("./pdf.worker-B4IDxXWI.js");return{default:n}},[])).default,r=await y(()=>import("./pdf-DfaD4CCm.js"),[]);return r.GlobalWorkerOptions&&(r.GlobalWorkerOptions.workerSrc=t),r}catch(t){return{error:t instanceof Error?t.message:String(t)}}})()),g}async function H(t,r){let n;try{n=await fetch(t)}catch(o){return{ok:!1,source:t,findings:[],pageCount:null,fatalError:`Network error fetching PDF: ${o instanceof Error?o.message:String(o)}`}}if(!n.ok)return{ok:!1,source:t,findings:[],pageCount:null,fatalError:`Fetch failed with HTTP ${n.status}`};const e=n.headers.get("content-length");if(e&&parseInt(e,10)>r)return{ok:!1,source:t,findings:[],pageCount:null,fatalError:`PDF exceeds size cap (${e} bytes > ${r}). Skipped — audit it manually via the CLI: \`wcagcheckr-ci pdf ${t}\`.`};const a=await n.arrayBuffer();return a.byteLength>r?{ok:!1,source:t,findings:[],pageCount:null,fatalError:`PDF exceeds size cap (${a.byteLength} bytes > ${r}). Skipped.`}:E(new Uint8Array(a),t)}async function E(t,r){const n=await $();if("error"in n)return{ok:!1,source:r,findings:[],pageCount:null,fatalError:`pdfjs failed to load in offscreen document: ${n.error}`};const e=n;let a;try{a=await e.getDocument({data:t,verbosity:0,disableFontFace:!0,useSystemFonts:!1}).promise}catch(s){return{ok:!0,source:r,findings:[{ruleId:"pdf-structure-tree-unparseable",impact:"serious",wcagCriterion:"1.3.1",description:`pdfjs could not open the PDF: ${s instanceof Error?s.message:String(s)}. Usually means encrypted, malformed, or non-standard structure.`,failureSummary:"Try opening the PDF in a recent Acrobat Reader — if it opens there but fails the extension audit, the PDF uses uncommon encoding pdfjs cannot handle. If it fails everywhere the source document needs to be regenerated."}],pageCount:null}}const o=a.numPages;let i=!1;try{const s=await a.getOutline();i=Array.isArray(s)&&s.length>0}catch{}const c=T();let d=0,p=0;for(let s=1;s<=o;s++){let u;try{u=await a.getPage(s)}catch{continue}let f=null;try{f=await u.getStructTree()}catch{}f?k(f,s,c):c.bumpRule("pdf-untagged-content",s);try{const b=await u.getAnnotations();(b??[]).filter(F=>F.subtype==="Widget").length>0&&(p++,d+=A(b))}catch{}}const l=S(c),m=D(d,p);m&&l.push(m);const h=R(o,i);return h&&l.push(h),{ok:!0,source:r,findings:l,pageCount:o}}chrome.runtime.onMessage.addListener((t,r,n)=>{const e=t;if(!e||e.type!=="OFFSCREEN_PDF_AUDIT_REQUEST")return!1;const a=typeof e.url=="string"?e.url:"",o=typeof e.maxBytes=="number"?e.maxBytes:16*1024*1024;return(async()=>{let i;try{i=await H(a,o)}catch(d){i={ok:!1,source:a,findings:[],pageCount:null,fatalError:`Offscreen audit threw: ${d instanceof Error?d.message:String(d)}`}}const c={type:"OFFSCREEN_PDF_AUDIT_RESPONSE",ok:i.ok,source:i.source,findings:i.findings,pageCount:i.pageCount,...i.fatalError?{fatalError:i.fatalError}:{}};n(c)})(),!0});
1
+ import"./modulepreload-polyfill-B5Qt9EMX.js";import{_ as A}from"./preload-helper-D7HrI6pR.js";const D=new Set(["Document","Part","Art","Sect","Div","BlockQuote","Caption","TOC","TOCI","Index","NonStruct","Private","H","H1","H2","H3","H4","H5","H6","P","L","LI","Lbl","LBody","Table","TR","TH","TD","THead","TBody","TFoot","Span","Quote","Note","Reference","BibEntry","Code","Link","Annot","Ruby","RB","RT","RP","Warichu","WT","WP","Figure","Formula","Form"]);function $(){const t=new Map;let r=0;return{ruleAggregates:t,get lastHeading(){return r},set lastHeading(n){r=n},bumpRule(n,e){let a=t.get(n);a||(a={count:0,pages:new Set},t.set(n,a)),a.count++,a.pages.add(e)},setLastHeadingLevel(n){r=n},getLastHeading(){return r}}}function R(t,r,n){if(!t||typeof t!="object")return;const e=t.role;if(e&&!D.has(e)&&n.bumpRule("pdf-non-standard-role",r),e==="Figure"&&((t.alt??"").trim()||n.bumpRule("pdf-figure-missing-alt",r)),e&&/^H[1-6]$/.test(e)){const a=parseInt(e.slice(1),10),o=n.getLastHeading();o>0&&a>o+1&&n.bumpRule("pdf-heading-skip",r),n.setLastHeadingLevel(a)}if(e==="Table"&&!S(t,"TH")&&n.bumpRule("pdf-table-no-header",r),e==="L"){const a=t.children??[],o=a.some(i=>i&&i.role&&i.role!=="LI"&&i.role!=="Caption");(a.length===0||o)&&n.bumpRule("pdf-list-malformed",r)}for(const a of t.children??[])R(a,r,n)}function S(t,r){if(!t)return!1;if(t.role===r)return!0;for(const n of t.children??[])if(S(n,r))return!0;return!1}function C(t){if(!t)return 0;const r=t.filter(e=>e.subtype==="Widget");let n=0;for(const e of r){const a=(e.alternativeText??"").trim(),o=(e.fieldName??"").trim(),i=/^(field|text\s*box|check\s*box|radio|button)\s*\d*$/i.test(o);!a&&(!o||i)&&n++}return n}function H(t){const r=[],n={"pdf-figure-missing-alt":{ruleId:"pdf-figure-missing-alt",impact:"serious",wcagCriterion:"1.1.1",describe:e=>({description:`${e.count} Figure tag${e.count===1?"":"s"} (across ${e.pages.size} page${e.pages.size===1?"":"s"}) have no alt text. Screen-reader users hear nothing for these images. PDF/UA-1 §7.4.`,failureSummary:"Open the PDF in Acrobat → Accessibility → Reading Order → click each Figure → Edit Alternate Text. Or, in source: add alt text to each image in Word/InDesign/PowerPoint before exporting."})},"pdf-heading-skip":{ruleId:"pdf-heading-skip",impact:"moderate",wcagCriterion:"1.3.1",describe:e=>({description:`Heading levels skip (e.g. H1 → H3 without H2). Detected ${e.count} skip${e.count===1?"":"s"} across ${e.pages.size} page${e.pages.size===1?"":"s"}. Screen-reader users hear the section hierarchy as broken.`,failureSummary:"Re-tag the headings sequentially. In Acrobat → Accessibility → Reading Order, change the offending tag to the correct heading level. In source documents use Heading 1 → Heading 2 → Heading 3 styles consistently."})},"pdf-table-no-header":{ruleId:"pdf-table-no-header",impact:"serious",wcagCriterion:"1.3.1",describe:e=>({description:`${e.count} Table tag${e.count===1?"":"s"} (across ${e.pages.size} page${e.pages.size===1?"":"s"}) have no TH (header) cells. Screen-reader table-navigation mode cannot announce headers when reading cells.`,failureSummary:"Open the PDF in Acrobat → Accessibility → Reading Order → select the table → Table Editor. Mark the first row (or first column) as headers. In source documents (Word: Table Tools → Layout → Repeat Header Rows; PowerPoint: tables need to be tagged as data tables)."})},"pdf-list-malformed":{ruleId:"pdf-list-malformed",impact:"moderate",wcagCriterion:"1.3.1",describe:e=>({description:`${e.count} List tag${e.count===1?"":"s"} (across ${e.pages.size} page${e.pages.size===1?"":"s"}) are malformed — L tags should contain LI children with optional Lbl + LBody. PDF/UA-1 §7.8.`,failureSummary:'Re-tag the lists in Acrobat → Accessibility → Reading Order, or recreate the PDF from a source document that produces proper list tagging (bullet/numbered lists in Word/Pages/Google Docs export correctly when "tagged PDF" is enabled).'})},"pdf-non-standard-role":{ruleId:"pdf-non-standard-role",impact:"minor",wcagCriterion:"1.3.1",describe:e=>{const a=Array.from(e.pages).slice(0,3).join(", ");return{description:`${e.count} structure-tree node${e.count===1?"":"s"} use non-standard tag roles (sample pages: ${a}). PDF/UA-1 §7.3 requires a RoleMap entry in StructTreeRoot mapping each custom role to a standard one.`,failureSummary:"Either replace the non-standard tags with PDF/UA standard ones (Document/Part/Art/Sect/Div/H1-H6/P/L/LI/Table/TR/TH/TD/Figure/Form/etc.), or add RoleMap entries in StructTreeRoot mapping each custom name to a standard equivalent."}}},"pdf-untagged-content":{ruleId:"pdf-untagged-content",impact:"critical",wcagCriterion:"1.3.1",describe:e=>({description:`${e.pages.size} page${e.pages.size===1?"":"s"} have no structure tree (untagged content). Screen-reader users get no reading order on these pages — content may be read in random visual-position order or skipped entirely.`,failureSummary:"Re-tag the document end-to-end. In Acrobat → Accessibility → Autotag Document is a starting point but rarely produces good results — re-export from the source document with tagging enabled, then refine in Acrobat's Reading Order tool."})}};for(const[e,a]of t.ruleAggregates){const o=n[e];if(!o)continue;const{description:i,failureSummary:c}=o.describe(a);r.push({ruleId:o.ruleId,impact:o.impact,wcagCriterion:o.wcagCriterion,description:i,failureSummary:c})}return r}function E(t,r){return t===0?null:{ruleId:"pdf-field-no-name",impact:"serious",wcagCriterion:"4.1.2",description:`${t} form field${t===1?"":"s"} across ${r} page${r===1?"":"s"} have no accessible name (no /TU alternate text + no descriptive /T field name). Screen-reader users hear nothing or hear the field type generically ("text field", "check box").`,failureSummary:'In Acrobat: open the Tools → Prepare Form panel → right-click each field → Properties → General tab → fill in the "Tooltip" (this becomes the PDF /TU). Or rename the /T field name to something descriptive ("First name", "Total amount due"). PDF/UA-1 §7.15.'}}function I(t,r,n){return r===0||n||t>0?null:{ruleId:"pdf-image-only",impact:"critical",wcagCriterion:"1.1.1",description:`PDF has no extractable text on any of its ${r} page${r===1?"":"s"} — appears to be a scanned document with no OCR layer. Screen-reader users hear silence; keyboard search returns no hits; copy/paste yields nothing.`,failureSummary:'Run OCR on the PDF before distributing it. Acrobat Pro → Tools → Scan & OCR → "Recognize Text". Free options: ocrmypdf (CLI), Google Drive (upload → open as Google Doc), or Tesseract. After OCR, re-tag the document (Acrobat → Accessibility → Autotag Document, then refine in Reading Order) so the recognized text gets a proper structure tree.'}}function L(t,r){return t<=5||r?null:{ruleId:"pdf-no-bookmarks",impact:"moderate",wcagCriterion:"2.4.5",description:`Multi-page PDF (${t} pages) has no bookmarks / outline. Keyboard and AT users cannot navigate the document structure without scrolling page-by-page.`,failureSummary:'Add bookmarks to the PDF in Acrobat (View → Show/Hide → Navigation Panes → Bookmarks, then add entries for each section heading). Or re-export from the source document with "Generate bookmarks" enabled (Word: Save As PDF → Options → Create bookmarks using: Headings).'}}let h=null;function O(){return h||(h=(async()=>{try{const t=(await A(async()=>{const{default:n}=await import("./pdf.worker-B4IDxXWI.js");return{default:n}},[])).default,r=await A(()=>import("./pdf-DfaD4CCm.js"),[]);return r.GlobalWorkerOptions&&(r.GlobalWorkerOptions.workerSrc=t),r}catch(t){return{error:t instanceof Error?t.message:String(t)}}})()),h}async function v(t,r){let n;try{n=await fetch(t)}catch(o){return{ok:!1,source:t,findings:[],pageCount:null,fatalError:`Network error fetching PDF: ${o instanceof Error?o.message:String(o)}`}}if(!n.ok)return{ok:!1,source:t,findings:[],pageCount:null,fatalError:`Fetch failed with HTTP ${n.status}`};const e=n.headers.get("content-length");if(e&&parseInt(e,10)>r)return{ok:!1,source:t,findings:[],pageCount:null,fatalError:`PDF exceeds size cap (${e} bytes > ${r}). Skipped — audit it manually via the CLI: \`wcagcheckr-ci pdf ${t}\`.`};const a=await n.arrayBuffer();return a.byteLength>r?{ok:!1,source:t,findings:[],pageCount:null,fatalError:`PDF exceeds size cap (${a.byteLength} bytes > ${r}). Skipped.`}:_(new Uint8Array(a),t)}async function _(t,r){const n=await O();if("error"in n)return{ok:!1,source:r,findings:[],pageCount:null,fatalError:`pdfjs failed to load in offscreen document: ${n.error}`};const e=n;let a;try{a=await e.getDocument({data:t,verbosity:0,disableFontFace:!0,useSystemFonts:!1}).promise}catch(s){return{ok:!0,source:r,findings:[{ruleId:"pdf-structure-tree-unparseable",impact:"serious",wcagCriterion:"1.3.1",description:`pdfjs could not open the PDF: ${s instanceof Error?s.message:String(s)}. Usually means encrypted, malformed, or non-standard structure.`,failureSummary:"Try opening the PDF in a recent Acrobat Reader — if it opens there but fails the extension audit, the PDF uses uncommon encoding pdfjs cannot handle. If it fails everywhere the source document needs to be regenerated."}],pageCount:null}}const o=a.numPages;let i=!1;try{const s=await a.getOutline();i=Array.isArray(s)&&s.length>0}catch{}const c=$();let l=0,b=0,y=0,F=0;for(let s=1;s<=o;s++){let f;try{f=await a.getPage(s)}catch{continue}let p=null;try{p=await f.getStructTree()}catch{}p?R(p,s,c):c.bumpRule("pdf-untagged-content",s);try{const d=await f.getAnnotations();(d??[]).filter(g=>g.subtype==="Widget").length>0&&(b++,l+=C(d))}catch{}try{const d=await f.getTextContent(),T=(d==null?void 0:d.items)??[];let g=0;for(const m of T)g+=((m==null?void 0:m.str)??"").length;y+=g}catch{F++}}const u=H(c),w=E(l,b);w&&u.push(w);const k=L(o,i);k&&u.push(k);const P=I(y,o,F===o);return P&&u.push(P),{ok:!0,source:r,findings:u,pageCount:o}}chrome.runtime.onMessage.addListener((t,r,n)=>{const e=t;if(!e||e.type!=="OFFSCREEN_PDF_AUDIT_REQUEST")return!1;const a=typeof e.url=="string"?e.url:"",o=typeof e.maxBytes=="number"?e.maxBytes:16*1024*1024;return(async()=>{let i;try{i=await v(a,o)}catch(l){i={ok:!1,source:a,findings:[],pageCount:null,fatalError:`Offscreen audit threw: ${l instanceof Error?l.message:String(l)}`}}const c={type:"OFFSCREEN_PDF_AUDIT_RESPONSE",ok:i.ok,source:i.source,findings:i.findings,pageCount:i.pageCount,...i.fatalError?{fatalError:i.fatalError}:{}};n(c)})(),!0});
@@ -2,8 +2,8 @@
2
2
  "manifest_version": 3,
3
3
  "name": "wcagcheckr",
4
4
  "description": "Audit components across hover, focus, dark mode, forced colors, RTL — every state your users actually encounter. Per-component baselines surface only NEW violations.",
5
- "version": "1.0.0.326",
6
- "version_name": "1.0.0-rc.326",
5
+ "version": "1.0.0.327",
6
+ "version_name": "1.0.0-rc.327",
7
7
  "author": "Locustware",
8
8
  "homepage_url": "https://wcagcheckr.com",
9
9
  "icons": {
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <title>wcagcheckr offscreen worker</title>
6
- <script type="module" crossorigin src="/assets/offscreen-Ck5SL__O.js"></script>
6
+ <script type="module" crossorigin src="/assets/offscreen-DOLG6pRi.js"></script>
7
7
  <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
8
8
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-D7HrI6pR.js">
9
9
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcag-checkr/ci",
3
- "version": "1.0.0-rc.326",
3
+ "version": "1.0.0-rc.327",
4
4
  "private": false,
5
5
  "description": "Headless wcagcheckr accessibility audit runner for CI/CD pipelines. Drives the wcagcheckr Chrome extension via Playwright, runs full-page audits across the state matrix (108 combinations: hover, focus, dark mode, RTL, breakpoints), outputs JSON / SARIF / JUnit, exits with severity-aware codes.",
6
6
  "license": "UNLICENSED",