@wcag-checkr/ci 1.0.0-rc.272 → 1.0.0-rc.273
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.
- package/dist/assets/{content-script.ts-CDnX48KZ.js → content-script.ts-CX6ON5Ua.js} +1 -1
- package/dist/assets/{content-script.ts-loader-BUWxUTh3.js → content-script.ts-loader-DOSB_ugu.js} +1 -1
- package/dist/assets/{dom-criterion-analyzers-CieyvSY5.js → dom-criterion-analyzers-DoUaJV5C.js} +8 -1
- package/dist/manifest.json +5 -5
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@ See: https://github.com/dequelabs/axe-core/blob/master/doc/context.md`))}functio
|
|
|
36
36
|
transition-delay: 0ms !important;
|
|
37
37
|
caret-color: transparent !important;
|
|
38
38
|
}
|
|
39
|
-
`,kO=.85,Bh=80,OO=300,IO=4e3;async function NO(){var d;if(!(typeof document>"u"||typeof window>"u"))try{const m=window.scrollY,p=window.scrollX,w=Math.max(document.documentElement.scrollHeight,((d=document.body)==null?void 0:d.scrollHeight)??0),b=window.innerHeight||0;if(w<=b)return;const g=Math.max(100,b*kO),R=performance.now();for(let S=0;S<w&&!(performance.now()-R>IO);S+=g)window.scrollTo({top:S,left:0,behavior:"instant"}),await new Promise($=>setTimeout($,Bh));window.scrollTo({top:w,left:0,behavior:"instant"}),await new Promise(S=>setTimeout(S,Bh)),window.scrollTo({top:m,left:p,behavior:"instant"}),await new Promise(S=>setTimeout(S,OO))}catch(m){ar.warn("scroll-reveal warmup failed (continuing with audit)",m)}}function MO(){if(typeof document>"u")return()=>{};if(document.getElementById($h))return()=>{};const d=document.createElement("style");return d.id=$h,d.textContent=SO,document.head.appendChild(d),()=>d.remove()}function PO(d){return d==="minor"||d==="moderate"||d==="serious"||d==="critical"?d:"moderate"}function LO(d){return d.some(m=>m.endsWith("aaa"))?"AAA":d.some(m=>m.endsWith("aa"))?"AA":"A"}function Fo(d){for(const m of d){const p=/^wcag(\d)(\d)(\d{1,2})$/.exec(m);if(p)return`${p[1]}.${p[2]}.${parseInt(p[3],10)}`}return""}function $O(d,m){const w=[];for(let b=0;b<m.length&&w.length<25;b++){const g=m[b],R=Array.isArray(g.target)?g.target.join(" "):g.target;if(!R)continue;const S=(()=>{try{return document.querySelector(R)}catch{return null}})(),$={selector:R,failureSummary:g.failureSummary};if(S instanceof HTMLElement){const q=S.outerHTML;if($.outerHTMLSnippet=q.length>1200?q.slice(0,1200)+"…":q,d==="color-contrast"||d==="color-contrast-enhanced"){const B=window.getComputedStyle(S);$.styles={foreground:B.color||void 0,background:B.backgroundColor||void 0,fontSize:parseFloat(B.fontSize)||void 0,fontWeight:parseInt(B.fontWeight,10)||void 0,textSample:(S.textContent??"").trim().slice(0,120)||void 0,effectiveBackground:BO(S)}}}w.push($)}return w}function BO(d){const m=new Set(["rgba(0, 0, 0, 0)","transparent",""]);let p=d,w,b=!1;for(;p;){const g=window.getComputedStyle(p),R=g.backgroundImage;R&&R!=="none"&&!b&&(b=!0,w=qh(p));const S=g.backgroundColor;if(S&&!m.has(S))return{color:S,fromAncestor:p===d?void 0:qh(p),hasBackgroundImageInChain:b,imageAncestor:w};p=p.parentElement}return{color:"rgb(255, 255, 255)",fromAncestor:"document default",hasBackgroundImageInChain:b,imageAncestor:w}}function qh(d){const m=d.tagName.toLowerCase(),p=d.id?`#${d.id}`:"",w=d.classList.length>0?`.${d.classList[0]}`:"";return`${m}${p}${w}`}function qO(d,m,p){var R,S;const w=((R=d.textContent)==null?void 0:R.trim().slice(0,50))??null,b=(S=d.getBoundingClientRect)==null?void 0:S.call(d),g=b?{x:Math.round(b.left),y:Math.round(b.top),w:Math.round(b.width),h:Math.round(b.height)}:void 0;return{selector:Array.isArray(m.target)?m.target.join(" "):String(m.target),outerHTML:(d.outerHTML??"").slice(0,500),failureSummary:m.failureSummary??"",tagName:d.tagName,role:d.getAttribute("role"),accessibleName:d.getAttribute("aria-label")??w,textSnippet:w,attrId:d.id||null,attrTestid:d.getAttribute("data-testid"),boundingRect:g,opacityContext:HO(d),cascadeChain:VO(d,p)}}function HO(d){if(typeof getComputedStyle!="function")return;let m=1,p=1,w;try{const b=getComputedStyle(d);m=parseFloat(b.opacity||"1");let g=d.parentElement;for(;g&&g!==document.documentElement;){const R=getComputedStyle(g),S=parseFloat(R.opacity||"1");S<p&&(p=S,w=jO(g)),g=g.parentElement}}catch{return}if(!(m>=.99&&p>=.99))return{self:Math.round(m*100)/100,ancestor:Math.round(p*100)/100,ancestorSelector:w}}function jO(d){const m=d.tagName.toLowerCase();if(d.id)return`${m}#${d.id}`;const p=d.classList.length>0?"."+Array.from(d.classList).slice(0,2).join("."):"";return`${m}${p}`}const zO=/var\(\s*(--[A-Za-z0-9_-]+)/g;function VO(d,m){if(!(m!=="color-contrast"&&m!=="color-contrast-enhanced")&&typeof getComputedStyle=="function")try{const p=getComputedStyle(d);return{color:Hh(d,"color",p.color),backgroundColor:Hh(d,"background-color",p.backgroundColor)}}catch{return}}function Hh(d,m,p){let w,b;for(const R of Array.from(document.styleSheets)){let S=null;try{S=R.cssRules}catch{continue}S&&Dv(S,d,m,($,q)=>{w=$,b=q})}if(!w)return;const g=[...w.matchAll(zO)].map(R=>R[1]).filter(R=>!!R);return{authored:w,rendered:p,vars:g,ruleSelector:b}}function Dv(d,m,p,w){for(const b of Array.from(d)){if(b instanceof CSSStyleRule){try{if(!m.matches(b.selectorText))continue}catch{continue}const g=b.style.getPropertyValue(p);g&&w(g,b.selectorText);continue}"cssRules"in b&&b.cssRules&&Dv(b.cssRules,m,p,w)}}async function GO(d,m,p,w){var qa,gr,Cr,Vn,Gn,Un,la,Wn;const b=MO();await new Promise(le=>setTimeout(le,RO)),await FO();const g=new Date().toISOString(),R=performance.now(),S=document.querySelector(d);if(!S)throw b(),new Error(`scope not found: ${d}`);const $=hv({element:S,url:window.location.href}),q=await _O(),B=await AO();let K=!1;try{const le=await Ik();le.length>0&&(EO(q,le),K=!0)}catch(le){ar.warn("custom-rule registration failed; continuing with built-ins only",le)}const pe=p??xO,ue=K&&pe.runOnly&&typeof pe.runOnly=="object"&&Array.isArray(pe.runOnly.values)&&!pe.runOnly.values.includes(Lh)?{...pe,runOnly:{...pe.runOnly,values:[...pe.runOnly.values,Lh]}}:pe,ae=Object.keys(B).length>0?{...ue,rules:{...ue.rules??{},...B}}:ue,fe=w&&w.length>0?{...ae,runOnly:{type:"rule",values:w}}:ae;let ke;try{ke=await q.run(S,fe)}catch(le){throw b(),le}const Je=q.version??"unknown",Gt=[];for(const le of ke.violations??[])if(!(le.tags??[]).some(et=>Ro.test(et)))for(const et of le.nodes??[]){const Fe=Array.isArray(et.target)?et.target.join(" "):et.target,at=document.querySelector(Fe);if(!at)continue;const Nt=qO(at,et,le.id),je=await Nl({ruleId:le.id,componentId:$.id,currentState:m,target:Nt});Gt.push({ruleId:le.id,wcagCriterion:Fo(le.tags??[]),wcagLevel:LO(le.tags??[]),impact:PO(le.impact),description:le.description??"",helpUrl:le.helpUrl??"",target:Nt,componentId:$.id,currentState:m,axeVersion:Je,detectedAt:new Date().toISOString(),matchKey:je})}const ir=[];if(m.breakpoint.width<=320)try{const{analyzeReflow:le}=await Rh(async()=>{const{analyzeReflow:Fe}=await import("./reflow-analyzer-DNgBX8N_.js");return{analyzeReflow:Fe}},[]),et=le(m.breakpoint.width,S);et.findings.length===0&&ir.push({ruleId:"wcc-reflow-clean",wcagCriterion:"1.4.10"});for(const Fe of et.findings){const at=document.querySelector(Fe.selector)??S,Nt={selector:Fe.selector,outerHTML:Fe.outerHtml||(at.outerHTML??"").slice(0,500),failureSummary:Fe.kind==="document-overflow"?`Page scrolls horizontally at ${m.breakpoint.width}px (content width ${et.documentScrollWidthPx}px, overflow ${Fe.overflowPx}px).`:Fe.kind==="element-overflow"?`Element extends ${Fe.overflowPx}px past the ${m.breakpoint.width}px viewport edge.`:`Element scrolls horizontally (${Fe.overflowPx}px past its container).`,tagName:at.tagName,role:((qa=at.getAttribute)==null?void 0:qa.call(at,"role"))??null,accessibleName:((gr=at.getAttribute)==null?void 0:gr.call(at,"aria-label"))??null,textSnippet:((Cr=at.textContent)==null?void 0:Cr.trim().slice(0,50))??null,attrId:at.id||null,attrTestid:((Vn=at.getAttribute)==null?void 0:Vn.call(at,"data-testid"))??null},je=await Nl({ruleId:"wcagcheckr-reflow",componentId:$.id,currentState:m,target:Nt});Gt.push({ruleId:"wcagcheckr-reflow",wcagCriterion:"1.4.10",wcagLevel:"AA",impact:"serious",description:Fe.kind==="document-overflow"?"Content does not reflow at 320 CSS pixels — page requires horizontal scrolling.":"Element does not reflow at 320 CSS pixels — content is cut off or requires horizontal scrolling.",helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/reflow.html",target:Nt,componentId:$.id,currentState:m,axeVersion:Je,detectedAt:new Date().toISOString(),matchKey:je})}}catch(le){console.warn("[audit-engine] reflow analyzer failed:",le)}if(m.pseudoState==="default"&&m.theme==="light"&&m.direction==="ltr"&&m.breakpoint.id==="desktop")try{const{runAllDomCriterionAnalyzers:le,DOM_ANALYZER_COVERED_CRITERIA:et}=await Rh(async()=>{const{runAllDomCriterionAnalyzers:je,DOM_ANALYZER_COVERED_CRITERIA:Ye}=await import("./dom-criterion-analyzers-CieyvSY5.js");return{runAllDomCriterionAnalyzers:je,DOM_ANALYZER_COVERED_CRITERIA:Ye}},[]),Fe=le(),at=new Set(Fe.map(je=>je.wcagCriterion));for(const je of et)at.has(je)||ir.push({ruleId:`wcc-clean::${je}`,wcagCriterion:je});const Nt=Fe.filter(je=>!/-needs-site-crawl$/.test(je.ruleId));for(const je of Nt){const Ye=je.element,Tr={selector:Ye.id?`#${Ye.id}`:Ye===document.documentElement?"html":(Ye.tagName||"unknown").toLowerCase()+(Ye.classList[0]?"."+Ye.classList[0]:""),outerHTML:(Ye.outerHTML??"").slice(0,500),failureSummary:je.failureSummary,tagName:Ye.tagName,role:((Gn=Ye.getAttribute)==null?void 0:Gn.call(Ye,"role"))??null,accessibleName:((Un=Ye.getAttribute)==null?void 0:Un.call(Ye,"aria-label"))??null,textSnippet:((la=Ye.textContent)==null?void 0:la.trim().slice(0,50))??null,attrId:Ye.id||null,attrTestid:((Wn=Ye.getAttribute)==null?void 0:Wn.call(Ye,"data-testid"))??null},ca=await Nl({ruleId:je.ruleId,componentId:$.id,currentState:m,target:Tr});Gt.push({ruleId:je.ruleId,wcagCriterion:je.wcagCriterion,wcagLevel:je.wcagLevel,impact:je.impact,description:je.description,helpUrl:je.helpUrl,target:Tr,componentId:$.id,currentState:m,axeVersion:Je,detectedAt:new Date().toISOString(),matchKey:ca})}}catch(le){console.warn("[audit-engine] dom-criterion-analyzers failed:",le)}return b(),{componentId:$.id,scope:d,pageUrl:typeof window<"u"&&window.location?window.location.href:void 0,state:m,violations:Gt,passes:(ke.passes??[]).length,incomplete:(ke.incomplete??[]).length,inapplicable:(ke.inapplicable??[]).length,axeVersion:Je,startedAt:g,durationMs:performance.now()-R,axeRulesEvaluated:{passed:[...(ke.passes??[]).filter(le=>!(le.tags??[]).some(et=>Ro.test(et))).map(le=>({ruleId:le.id,wcagCriterion:Fo(le.tags??[])})).filter(le=>le.wcagCriterion),...ir],inapplicable:(ke.inapplicable??[]).filter(le=>!(le.tags??[]).some(et=>Ro.test(et))).map(le=>({ruleId:le.id,wcagCriterion:Fo(le.tags??[])})).filter(le=>le.wcagCriterion),incomplete:(ke.incomplete??[]).filter(le=>!(le.tags??[]).some(et=>Ro.test(et))).map(le=>({ruleId:le.id,wcagCriterion:Fo(le.tags??[]),elements:$O(le.id,le.nodes??[])})).filter(le=>le.wcagCriterion)}}}function UO(){const d=De("SCOPE_FINGERPRINT_REQUEST",async q=>{try{const B=q.selector==="html"?document.documentElement:document.querySelector(q.selector);if(!B)return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null};const K=B.outerHTML,pe=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(K));return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:Array.from(new Uint8Array(pe)).map(ae=>ae.toString(16).padStart(2,"0")).join("")}}catch(B){return ar.warn("scope fingerprint failed",B),{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null}}}),m=De("AUDIT_REQUEST",async q=>{try{return{type:"AUDIT_RESPONSE",success:!0,result:await GO(q.selector,q.currentState,q.axeConfig,q.rulesToRun)}}catch(B){ar.error("audit failed",B);const K=B instanceof Error?B.message:String(B),pe=K.toLowerCase().includes("paint")?"PAINT_TIMEOUT":"AXE_FAILED";return{type:"AUDIT_RESPONSE",success:!1,error:Fk(pe,K,!1)}}}),p=De("CUSTOM_PROPERTY_REQUEST",async()=>{try{return{type:"CUSTOM_PROPERTY_RESPONSE",undefined:mO()}}catch(q){return ar.warn("custom-property analysis failed",q),{type:"CUSTOM_PROPERTY_RESPONSE",undefined:[]}}}),w=De("THEME_AWARENESS_REQUEST",async()=>{try{const q=yO();return{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:q.hasLightModeCss,hasDarkModeCss:q.hasDarkModeCss,hasUnreadableSheets:q.hasUnreadableSheets}}catch(q){return ar.warn("theme-awareness probe failed",q),{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:!0,hasDarkModeCss:!0,hasUnreadableSheets:!0}}}),b=De("READING_ORDER_REQUEST",async q=>{try{const B=document.querySelector(q.selector)??document;return{type:"READING_ORDER_RESPONSE",issues:bv(B)}}catch(B){return ar.warn("reading-order analysis failed",B),{type:"READING_ORDER_RESPONSE",issues:[]}}}),g=De("TYPOGRAPHY_ANALYZE_REQUEST",async q=>{try{const B=document.querySelector(q.selector)??document;return{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:dO(B)}}catch(B){return ar.warn("typography analysis failed",B),{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:[]}}}),R=De("TAB_ORDER_ANALYZE_REQUEST",async()=>{try{return{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:iO()}}catch(q){return ar.warn("tab-order analysis failed",q),{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:[]}}}),S=De("WARMUP_REQUEST",async()=>(await NO(),{type:"WARMUP_RESPONSE"})),$=De("AI_CANDIDATES_REQUEST",async q=>{try{return{type:"AI_CANDIDATES_RESPONSE",images:WO(q.selector),headings:KO(q.selector),instructions:ZO(q.selector),ariaElements:QO(q.selector),links:JO(q.selector),longTextBlocks:eI(q.selector),languageInfo:tI(),colorOnlyRegions:rI(q.selector)}}catch(B){return ar.warn("ai-candidates enumeration failed",B),{type:"AI_CANDIDATES_RESPONSE",images:[],headings:[],instructions:[],ariaElements:[],links:[],longTextBlocks:[],languageInfo:null,colorOnlyRegions:[]}}});return()=>{m(),d(),b(),R(),g(),S(),p(),w(),$()}}function WO(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('img, [role="img"]');for(const b of Array.from(w)){const g=b.currentSrc||b.src||b.getAttribute("src")||"";if(!g||g.startsWith("data:"))continue;const R=b.getBoundingClientRect();if(R.width<16||R.height<16)continue;const S=b.getAttribute("alt")??"";YO(b,S)||p.push({imageUrl:g,alt:S,selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,500),surroundingContext:XO(b),pixelArea:Math.round(R.width*R.height)})}return p}function YO(d,m){if(sa(d))return!0;const p=d.getAttribute("role");if(p==="presentation"||p==="none")return!0;if(m===""){let w=d.parentElement,b=0;for(;w&&b<3;){const g=w.tagName.toLowerCase(),R=w.getAttribute("role");if(g==="a"||g==="button"||R==="link"||R==="button"){if((w.textContent??"").trim().length>0)return!0;break}w=w.parentElement,b++}}if(m===""){const w=(d.getAttribute("class")??"").toLowerCase();if(/\b(logo|brand|icon|emblem|crest|wordmark)\b/.test(w)&&d.parentElement&&(d.parentElement.textContent??"").trim().length>0)return!0}return!1}function sa(d){let m=d;for(;m&&m!==document.body;){if(m.getAttribute("aria-hidden")==="true")return!0;m=m.parentElement}return!1}function ua(d){if(d.id)return`#${CSS.escape(d.id)}`;const m=d.tagName.toLowerCase(),p=d.parentElement;if(!p)return m;const w=Array.from(p.children).filter(g=>g.tagName===d.tagName),b=w.indexOf(d);return w.length===1?`${p.tagName.toLowerCase()} > ${m}`:`${p.tagName.toLowerCase()} > ${m}:nth-of-type(${b+1})`}function XO(d){let m=d.parentElement;for(;m&&m!==document.body;){const p=(m.textContent??"").trim();if(p.length>20&&p.length<800)return p;m=m.parentElement}}function KO(d){var b,g;const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');for(const R of Array.from(w)){if(sa(R))continue;const S=(R.textContent??"").trim();if(!S||S.length>200)continue;const $=parseInt(((b=R.tagName.match(/^H(\d)$/))==null?void 0:b[1])??"6",10);let q="",B=R.nextElementSibling;for(;B&&q.length<800;){const K=parseInt(((g=B.tagName.match(/^H(\d)$/))==null?void 0:g[1])??"0",10);if(K>0&&K<=$)break;const pe=(B.textContent??"").trim();pe&&(q+=(q?" ":"")+pe),B=B.nextElementSibling}q.length<30||p.push({level:$,text:S,sectionContent:q.slice(0,800),selector:ua(R),outerHTML:(R.outerHTML??"").slice(0,300)})}return p}function ZO(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('p, li, div[role="note"], aside');for(const b of Array.from(w)){if(sa(b))continue;const g=(b.textContent??"").trim();if(!(g.length<20||g.length>600)&&/\b(click|tap|press|select|choose|find|locate|use|see|look|hit|push|drag)\b/i.test(g)&&(p.push({text:g.slice(0,600),selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20))break}return p}function QO(d){var b;const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll("[role]");for(const g of Array.from(w)){if(sa(g))continue;const R=g.getAttribute("role")??"";if(R&&(p.push({role:R,selector:ua(g),outerHTML:(g.outerHTML??"").slice(0,500),surroundingHtml:(((b=g.parentElement)==null?void 0:b.outerHTML)??"").slice(0,600)}),p.length>=30))break}return p}function JO(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll("a[href]");for(const b of Array.from(w)){if(sa(b))continue;const g=(b.textContent??"").trim();if(!g||g.length>60)continue;let R="",S=b.parentElement;for(;S&&S!==document.body;){const $=S.tagName;if($==="P"||$==="LI"||$==="DD"||$==="FIGCAPTION"||/^H[1-6]$/.test($)){const q=(S.textContent??"").trim();if(q.length>=g.length){R=q;break}}S=S.parentElement}if(p.push({linkText:g,surroundingText:R.slice(0,800),selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=25)break}return p}function eI(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=new Set,b=m.querySelectorAll("article, section, main, div, aside");for(const g of Array.from(b)){if(sa(g))continue;let R=g.parentElement,S=!1;for(;R;){if(w.has(R)){S=!0;break}R=R.parentElement}if(S)continue;const $=(g.textContent??"").trim();if(!$||$.split(/\s+/).filter(Boolean).length<300)continue;const B=g.querySelectorAll("h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, figure, table, button, input").length;if(!(B>4)&&(p.push({blockText:$.slice(0,1500),structuralChildrenCount:B,selector:ua(g),outerHTML:(g.outerHTML??"").slice(0,400)}),w.add(g),p.length>=5))break}return p}function tI(){const d=document.documentElement.getAttribute("lang")??"",m=document.body;if(!m)return null;const p=(m.textContent??"").trim().replace(/\s+/g," ").slice(0,1500);return p.length<60?null:{declaredLang:d,bodyTextSample:p,selector:"html",outerHTML:`<html lang="${d}">`}}function rI(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('[class*="status"], [class*="badge"], [class*="dot"], [class*="indicator"], [aria-invalid="true"], [class*="pill"]');for(const b of Array.from(w)){if(sa(b))continue;const g=(b.textContent??"").trim(),R=window.getComputedStyle(b),S=R.backgroundColor,$=R.borderColor;if(!(S&&S!=="rgba(0, 0, 0, 0)"&&S!=="transparent"||$&&$!=="rgba(0, 0, 0, 0)"&&$!=="transparent"))continue;const B=b.querySelector("svg, [aria-label], [aria-labelledby], [title]")!==null;if(g.length>0&&B)continue;const K=b.getAttribute("aria-label")??b.getAttribute("title")??"",pe=[g?`text: "${g.slice(0,60)}"`:"no text content",K?`label: "${K}"`:"no aria-label",`bg: ${S}`,B?"has icon child":"no icon child"].join("; ");if(p.push({elementHtml:(b.outerHTML??"").slice(0,400),contextDescription:pe.slice(0,400),selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20)break}return p}const aI=Dt("storybook-integration"),nI=3e3;function iI(){const d=window.__STORYBOOK_PREVIEW__;return d&&typeof d=="object"?{detected:!0,version:d.version??"unknown"}:{detected:!1}}async function oI(){var b,g;const d=window.__STORYBOOK_PREVIEW__;if(!d)return[];const m=d.storyStoreValue??d.storyStore;if(!m)return[];typeof m.cacheAllCSFFiles=="function"&&await m.cacheAllCSFFiles();const p=((b=m.extract)==null?void 0:b.call(m))??((g=m.cachedCSFFiles)==null?void 0:g.call(m));if(!p)return[];const w=[];if(p instanceof Map)for(const[R,S]of p)w.push({id:R,name:S.name??R,kind:S.title??S.kind??""});else if(typeof p=="object")for(const[R,S]of Object.entries(p))w.push({id:R,name:S.name??R,kind:S.title??S.kind??""});return w}async function sI(d){const m=new URL(window.location.href);m.searchParams.set("id",d),m.searchParams.set("viewMode","story"),window.location.href!==m.href&&(window.history.pushState({},"",m.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(p=>{const w=setTimeout(()=>p(),nI),b=()=>{clearTimeout(w),p()};window.addEventListener("storyrendered",b,{once:!0})}),ns({type:"STORYBOOK_NAVIGATED_EVENT",storyId:d,ready:!0})}function uI(){return document.getElementById("storybook-root")?"#storybook-root":document.getElementById("root")?"#root":"body"}function lI(){const d=[];return d.push(De("STORYBOOK_DETECT_REQUEST",async()=>{const m=iI();return await chrome.storage.local.set({"storybook:lastDetected":{detected:m.detected,version:m.version,detectedAt:new Date().toISOString()}}),{type:"STORYBOOK_DETECT_RESPONSE",detected:m.detected,version:m.version}})),d.push(De("STORYBOOK_LIST_STORIES_REQUEST",async()=>({type:"STORYBOOK_LIST_STORIES_RESPONSE",stories:await oI()}))),d.push(De("STORYBOOK_NAVIGATE_REQUEST",async m=>{await sI(m.storyId)})),d.push(De("STORYBOOK_GET_STORY_TARGET_REQUEST",()=>({type:"STORYBOOK_GET_STORY_TARGET_RESPONSE",selector:uI()}))),aI.info("handlers registered"),()=>d.forEach(m=>m())}function cI(d){var S,$,q,B,K,pe,ue;const m=d.getAttribute("aria-labelledby");if(m){const fe=m.split(/\s+/).filter(Boolean).map(ke=>{var Je;return(Je=d.ownerDocument)==null?void 0:Je.getElementById(ke)}).filter(ke=>ke!=null);if(fe.length>0){const ke=fe.map(Je=>(Je.textContent??"").trim()).filter(Boolean).join(" ");if(ke)return ke}}const p=(S=d.getAttribute("aria-label"))==null?void 0:S.trim();if(p)return p;const w=d.id;if(w){const ae=($=d.ownerDocument)==null?void 0:$.querySelector(`label[for="${fI(w)}"]`);if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}const b=(q=d.closest)==null?void 0:q.call(d,"label");if(b){const ae=b.cloneNode(!0);for(const ke of ae.querySelectorAll("input, select, textarea, button"))(B=ke.parentNode)==null||B.removeChild(ke);const fe=(ae.textContent??"").trim();if(fe)return fe}const g=(K=d.closest)==null?void 0:K.call(d,"fieldset");if(g&&d.tagName!=="FIELDSET"){const ae=Array.from(g.children).find(fe=>fe.tagName==="LEGEND");if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}if(d.tagName==="TABLE"){const ae=d.caption;if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}if(d.tagName==="FIGURE"){const ae=Array.from(d.children).find(fe=>fe.tagName==="FIGCAPTION");if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}if(d.tagName==="IMG"||d.tagName==="INPUT"&&d.type==="image"||d.tagName==="AREA"){const ae=d.getAttribute("alt");if(ae!==null)return ae}if(dI(d)){const ae=(d.textContent??"").replace(/\s+/g," ").trim();if(ae)return ae}const R=(pe=d.getAttribute("title"))==null?void 0:pe.trim();if(R)return R;if(d.tagName==="INPUT"||d.tagName==="TEXTAREA"){const ae=(ue=d.getAttribute("placeholder"))==null?void 0:ue.trim();if(ae)return ae}return""}function dI(d){const m=Na(d);return new Set(["button","link","heading","menuitem","menuitemcheckbox","menuitemradio","option","radio","tab","switch","treeitem","gridcell","cell","columnheader","rowheader","checkbox"]).has(m)}function fI(d){var p;const m=(p=globalThis.CSS)==null?void 0:p.escape;return m?m(d):d.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g,"\\$1")}function Na(d){const m=d.getAttribute("role");if(m)return m.trim().toLowerCase();switch(d.tagName){case"A":return d.href?"link":"generic";case"BUTTON":return"button";case"INPUT":{const w=d.type;return w==="button"||w==="submit"||w==="reset"?"button":w==="checkbox"?"checkbox":w==="radio"?"radio":w==="range"?"slider":w==="search"?"searchbox":w==="image"?"button":"textbox"}case"TEXTAREA":return"textbox";case"SELECT":return d.multiple?"listbox":"combobox";case"OPTION":return"option";case"IMG":return d.getAttribute("alt")===""?"presentation":"img";case"NAV":return"navigation";case"MAIN":return"main";case"HEADER":return"banner";case"FOOTER":return"contentinfo";case"ASIDE":return"complementary";case"SECTION":return d.getAttribute("aria-label")||d.getAttribute("aria-labelledby")?"region":"generic";case"ARTICLE":return"article";case"H1":case"H2":case"H3":case"H4":case"H5":case"H6":return"heading";case"UL":case"OL":return"list";case"LI":return"listitem";case"TABLE":return"table";case"TR":return"row";case"TD":return"cell";case"TH":return"columnheader";case"DIALOG":return"dialog";case"DETAILS":return"group";case"SUMMARY":return"button";case"FIELDSET":return"group";case"LEGEND":return"caption";case"FIGURE":return"figure";case"FIGCAPTION":return"caption";case"PROGRESS":return"progressbar";case"METER":return"meter";case"OUTPUT":return"status";case"HR":return"separator";default:return"generic"}}function pI(d){var B;const m=[];if(Na(d)==="heading"){const K=d.getAttribute("aria-level")??((B=d.tagName.match(/^H(\d)$/))==null?void 0:B[1]);K&&m.push(`level ${K}`)}const w=d.getAttribute("aria-expanded");w==="true"?m.push("expanded"):w==="false"&&m.push("collapsed");const b=d.getAttribute("aria-pressed");b==="true"?m.push("pressed"):b==="false"&&m.push("not pressed");const g=d.getAttribute("aria-checked");g==="true"?m.push("checked"):g==="false"?m.push("not checked"):g==="mixed"?m.push("mixed"):d.checked===!0&&d.tagName==="INPUT"&&(d.type==="checkbox"||d.type==="radio")&&m.push("checked"),d.getAttribute("aria-selected")==="true"&&m.push("selected"),(d.getAttribute("aria-disabled")==="true"||d.disabled)&&m.push("disabled"),(d.getAttribute("aria-required")==="true"||d.required)&&m.push("required");const S=d.getAttribute("aria-invalid");(S==="true"||S==="spelling"||S==="grammar")&&m.push(`invalid${S!=="true"?" "+S:""}`),(d.getAttribute("aria-readonly")==="true"||d.readOnly)&&m.push("read only");const $=d.getAttribute("aria-haspopup");$&&$!=="false"&&m.push(`has ${$==="true"?"menu":$} popup`),d.getAttribute("aria-busy")==="true"&&m.push("busy");const q=d.getAttribute("aria-current");return q&&q!=="false"&&m.push(`current ${q==="true"?"item":q}`),m}function mI(d){const m=Na(d);if(!new Set(["option","menuitem","menuitemcheckbox","menuitemradio","tab","treeitem","radio","listitem"]).has(m))return"";const b={option:"listbox",menuitem:"menu menubar",menuitemcheckbox:"menu menubar",menuitemradio:"menu menubar",tab:"tablist",treeitem:"tree",radio:"radiogroup",listitem:"list"}[m].split(" ");let g=d.parentElement;for(;g&&!b.includes(Na(g));)g=g.parentElement;if(!g)return"";const R=Array.from(g.children).filter($=>Na($)===m),S=R.indexOf(d);return S<0?"":`${S+1} of ${R.length}`}function hI(d){const m=cI(d),p=Na(d),w=pI(d),b=mI(d);return{name:m,role:p,states:w,group:b}}const Ev=Dt("element-picker");let nr=null,$a=!1;const vI=`
|
|
39
|
+
`,kO=.85,Bh=80,OO=300,IO=4e3;async function NO(){var d;if(!(typeof document>"u"||typeof window>"u"))try{const m=window.scrollY,p=window.scrollX,w=Math.max(document.documentElement.scrollHeight,((d=document.body)==null?void 0:d.scrollHeight)??0),b=window.innerHeight||0;if(w<=b)return;const g=Math.max(100,b*kO),R=performance.now();for(let S=0;S<w&&!(performance.now()-R>IO);S+=g)window.scrollTo({top:S,left:0,behavior:"instant"}),await new Promise($=>setTimeout($,Bh));window.scrollTo({top:w,left:0,behavior:"instant"}),await new Promise(S=>setTimeout(S,Bh)),window.scrollTo({top:m,left:p,behavior:"instant"}),await new Promise(S=>setTimeout(S,OO))}catch(m){ar.warn("scroll-reveal warmup failed (continuing with audit)",m)}}function MO(){if(typeof document>"u")return()=>{};if(document.getElementById($h))return()=>{};const d=document.createElement("style");return d.id=$h,d.textContent=SO,document.head.appendChild(d),()=>d.remove()}function PO(d){return d==="minor"||d==="moderate"||d==="serious"||d==="critical"?d:"moderate"}function LO(d){return d.some(m=>m.endsWith("aaa"))?"AAA":d.some(m=>m.endsWith("aa"))?"AA":"A"}function Fo(d){for(const m of d){const p=/^wcag(\d)(\d)(\d{1,2})$/.exec(m);if(p)return`${p[1]}.${p[2]}.${parseInt(p[3],10)}`}return""}function $O(d,m){const w=[];for(let b=0;b<m.length&&w.length<25;b++){const g=m[b],R=Array.isArray(g.target)?g.target.join(" "):g.target;if(!R)continue;const S=(()=>{try{return document.querySelector(R)}catch{return null}})(),$={selector:R,failureSummary:g.failureSummary};if(S instanceof HTMLElement){const q=S.outerHTML;if($.outerHTMLSnippet=q.length>1200?q.slice(0,1200)+"…":q,d==="color-contrast"||d==="color-contrast-enhanced"){const B=window.getComputedStyle(S);$.styles={foreground:B.color||void 0,background:B.backgroundColor||void 0,fontSize:parseFloat(B.fontSize)||void 0,fontWeight:parseInt(B.fontWeight,10)||void 0,textSample:(S.textContent??"").trim().slice(0,120)||void 0,effectiveBackground:BO(S)}}}w.push($)}return w}function BO(d){const m=new Set(["rgba(0, 0, 0, 0)","transparent",""]);let p=d,w,b=!1;for(;p;){const g=window.getComputedStyle(p),R=g.backgroundImage;R&&R!=="none"&&!b&&(b=!0,w=qh(p));const S=g.backgroundColor;if(S&&!m.has(S))return{color:S,fromAncestor:p===d?void 0:qh(p),hasBackgroundImageInChain:b,imageAncestor:w};p=p.parentElement}return{color:"rgb(255, 255, 255)",fromAncestor:"document default",hasBackgroundImageInChain:b,imageAncestor:w}}function qh(d){const m=d.tagName.toLowerCase(),p=d.id?`#${d.id}`:"",w=d.classList.length>0?`.${d.classList[0]}`:"";return`${m}${p}${w}`}function qO(d,m,p){var R,S;const w=((R=d.textContent)==null?void 0:R.trim().slice(0,50))??null,b=(S=d.getBoundingClientRect)==null?void 0:S.call(d),g=b?{x:Math.round(b.left),y:Math.round(b.top),w:Math.round(b.width),h:Math.round(b.height)}:void 0;return{selector:Array.isArray(m.target)?m.target.join(" "):String(m.target),outerHTML:(d.outerHTML??"").slice(0,500),failureSummary:m.failureSummary??"",tagName:d.tagName,role:d.getAttribute("role"),accessibleName:d.getAttribute("aria-label")??w,textSnippet:w,attrId:d.id||null,attrTestid:d.getAttribute("data-testid"),boundingRect:g,opacityContext:HO(d),cascadeChain:VO(d,p)}}function HO(d){if(typeof getComputedStyle!="function")return;let m=1,p=1,w;try{const b=getComputedStyle(d);m=parseFloat(b.opacity||"1");let g=d.parentElement;for(;g&&g!==document.documentElement;){const R=getComputedStyle(g),S=parseFloat(R.opacity||"1");S<p&&(p=S,w=jO(g)),g=g.parentElement}}catch{return}if(!(m>=.99&&p>=.99))return{self:Math.round(m*100)/100,ancestor:Math.round(p*100)/100,ancestorSelector:w}}function jO(d){const m=d.tagName.toLowerCase();if(d.id)return`${m}#${d.id}`;const p=d.classList.length>0?"."+Array.from(d.classList).slice(0,2).join("."):"";return`${m}${p}`}const zO=/var\(\s*(--[A-Za-z0-9_-]+)/g;function VO(d,m){if(!(m!=="color-contrast"&&m!=="color-contrast-enhanced")&&typeof getComputedStyle=="function")try{const p=getComputedStyle(d);return{color:Hh(d,"color",p.color),backgroundColor:Hh(d,"background-color",p.backgroundColor)}}catch{return}}function Hh(d,m,p){let w,b;for(const R of Array.from(document.styleSheets)){let S=null;try{S=R.cssRules}catch{continue}S&&Dv(S,d,m,($,q)=>{w=$,b=q})}if(!w)return;const g=[...w.matchAll(zO)].map(R=>R[1]).filter(R=>!!R);return{authored:w,rendered:p,vars:g,ruleSelector:b}}function Dv(d,m,p,w){for(const b of Array.from(d)){if(b instanceof CSSStyleRule){try{if(!m.matches(b.selectorText))continue}catch{continue}const g=b.style.getPropertyValue(p);g&&w(g,b.selectorText);continue}"cssRules"in b&&b.cssRules&&Dv(b.cssRules,m,p,w)}}async function GO(d,m,p,w){var qa,gr,Cr,Vn,Gn,Un,la,Wn;const b=MO();await new Promise(le=>setTimeout(le,RO)),await FO();const g=new Date().toISOString(),R=performance.now(),S=document.querySelector(d);if(!S)throw b(),new Error(`scope not found: ${d}`);const $=hv({element:S,url:window.location.href}),q=await _O(),B=await AO();let K=!1;try{const le=await Ik();le.length>0&&(EO(q,le),K=!0)}catch(le){ar.warn("custom-rule registration failed; continuing with built-ins only",le)}const pe=p??xO,ue=K&&pe.runOnly&&typeof pe.runOnly=="object"&&Array.isArray(pe.runOnly.values)&&!pe.runOnly.values.includes(Lh)?{...pe,runOnly:{...pe.runOnly,values:[...pe.runOnly.values,Lh]}}:pe,ae=Object.keys(B).length>0?{...ue,rules:{...ue.rules??{},...B}}:ue,fe=w&&w.length>0?{...ae,runOnly:{type:"rule",values:w}}:ae;let ke;try{ke=await q.run(S,fe)}catch(le){throw b(),le}const Je=q.version??"unknown",Gt=[];for(const le of ke.violations??[])if(!(le.tags??[]).some(et=>Ro.test(et)))for(const et of le.nodes??[]){const Fe=Array.isArray(et.target)?et.target.join(" "):et.target,at=document.querySelector(Fe);if(!at)continue;const Nt=qO(at,et,le.id),je=await Nl({ruleId:le.id,componentId:$.id,currentState:m,target:Nt});Gt.push({ruleId:le.id,wcagCriterion:Fo(le.tags??[]),wcagLevel:LO(le.tags??[]),impact:PO(le.impact),description:le.description??"",helpUrl:le.helpUrl??"",target:Nt,componentId:$.id,currentState:m,axeVersion:Je,detectedAt:new Date().toISOString(),matchKey:je})}const ir=[];if(m.breakpoint.width<=320)try{const{analyzeReflow:le}=await Rh(async()=>{const{analyzeReflow:Fe}=await import("./reflow-analyzer-DNgBX8N_.js");return{analyzeReflow:Fe}},[]),et=le(m.breakpoint.width,S);et.findings.length===0&&ir.push({ruleId:"wcc-reflow-clean",wcagCriterion:"1.4.10"});for(const Fe of et.findings){const at=document.querySelector(Fe.selector)??S,Nt={selector:Fe.selector,outerHTML:Fe.outerHtml||(at.outerHTML??"").slice(0,500),failureSummary:Fe.kind==="document-overflow"?`Page scrolls horizontally at ${m.breakpoint.width}px (content width ${et.documentScrollWidthPx}px, overflow ${Fe.overflowPx}px).`:Fe.kind==="element-overflow"?`Element extends ${Fe.overflowPx}px past the ${m.breakpoint.width}px viewport edge.`:`Element scrolls horizontally (${Fe.overflowPx}px past its container).`,tagName:at.tagName,role:((qa=at.getAttribute)==null?void 0:qa.call(at,"role"))??null,accessibleName:((gr=at.getAttribute)==null?void 0:gr.call(at,"aria-label"))??null,textSnippet:((Cr=at.textContent)==null?void 0:Cr.trim().slice(0,50))??null,attrId:at.id||null,attrTestid:((Vn=at.getAttribute)==null?void 0:Vn.call(at,"data-testid"))??null},je=await Nl({ruleId:"wcagcheckr-reflow",componentId:$.id,currentState:m,target:Nt});Gt.push({ruleId:"wcagcheckr-reflow",wcagCriterion:"1.4.10",wcagLevel:"AA",impact:"serious",description:Fe.kind==="document-overflow"?"Content does not reflow at 320 CSS pixels — page requires horizontal scrolling.":"Element does not reflow at 320 CSS pixels — content is cut off or requires horizontal scrolling.",helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/reflow.html",target:Nt,componentId:$.id,currentState:m,axeVersion:Je,detectedAt:new Date().toISOString(),matchKey:je})}}catch(le){console.warn("[audit-engine] reflow analyzer failed:",le)}if(m.pseudoState==="default"&&m.theme==="light"&&m.direction==="ltr"&&m.breakpoint.id==="desktop")try{const{runAllDomCriterionAnalyzers:le,DOM_ANALYZER_COVERED_CRITERIA:et}=await Rh(async()=>{const{runAllDomCriterionAnalyzers:je,DOM_ANALYZER_COVERED_CRITERIA:Ye}=await import("./dom-criterion-analyzers-DoUaJV5C.js");return{runAllDomCriterionAnalyzers:je,DOM_ANALYZER_COVERED_CRITERIA:Ye}},[]),Fe=le(),at=new Set(Fe.map(je=>je.wcagCriterion));for(const je of et)at.has(je)||ir.push({ruleId:`wcc-clean::${je}`,wcagCriterion:je});const Nt=Fe.filter(je=>!/-needs-site-crawl$/.test(je.ruleId));for(const je of Nt){const Ye=je.element,Tr={selector:Ye.id?`#${Ye.id}`:Ye===document.documentElement?"html":(Ye.tagName||"unknown").toLowerCase()+(Ye.classList[0]?"."+Ye.classList[0]:""),outerHTML:(Ye.outerHTML??"").slice(0,500),failureSummary:je.failureSummary,tagName:Ye.tagName,role:((Gn=Ye.getAttribute)==null?void 0:Gn.call(Ye,"role"))??null,accessibleName:((Un=Ye.getAttribute)==null?void 0:Un.call(Ye,"aria-label"))??null,textSnippet:((la=Ye.textContent)==null?void 0:la.trim().slice(0,50))??null,attrId:Ye.id||null,attrTestid:((Wn=Ye.getAttribute)==null?void 0:Wn.call(Ye,"data-testid"))??null},ca=await Nl({ruleId:je.ruleId,componentId:$.id,currentState:m,target:Tr});Gt.push({ruleId:je.ruleId,wcagCriterion:je.wcagCriterion,wcagLevel:je.wcagLevel,impact:je.impact,description:je.description,helpUrl:je.helpUrl,target:Tr,componentId:$.id,currentState:m,axeVersion:Je,detectedAt:new Date().toISOString(),matchKey:ca})}}catch(le){console.warn("[audit-engine] dom-criterion-analyzers failed:",le)}return b(),{componentId:$.id,scope:d,pageUrl:typeof window<"u"&&window.location?window.location.href:void 0,state:m,violations:Gt,passes:(ke.passes??[]).length,incomplete:(ke.incomplete??[]).length,inapplicable:(ke.inapplicable??[]).length,axeVersion:Je,startedAt:g,durationMs:performance.now()-R,axeRulesEvaluated:{passed:[...(ke.passes??[]).filter(le=>!(le.tags??[]).some(et=>Ro.test(et))).map(le=>({ruleId:le.id,wcagCriterion:Fo(le.tags??[])})).filter(le=>le.wcagCriterion),...ir],inapplicable:(ke.inapplicable??[]).filter(le=>!(le.tags??[]).some(et=>Ro.test(et))).map(le=>({ruleId:le.id,wcagCriterion:Fo(le.tags??[])})).filter(le=>le.wcagCriterion),incomplete:(ke.incomplete??[]).filter(le=>!(le.tags??[]).some(et=>Ro.test(et))).map(le=>({ruleId:le.id,wcagCriterion:Fo(le.tags??[]),elements:$O(le.id,le.nodes??[])})).filter(le=>le.wcagCriterion)}}}function UO(){const d=De("SCOPE_FINGERPRINT_REQUEST",async q=>{try{const B=q.selector==="html"?document.documentElement:document.querySelector(q.selector);if(!B)return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null};const K=B.outerHTML,pe=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(K));return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:Array.from(new Uint8Array(pe)).map(ae=>ae.toString(16).padStart(2,"0")).join("")}}catch(B){return ar.warn("scope fingerprint failed",B),{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null}}}),m=De("AUDIT_REQUEST",async q=>{try{return{type:"AUDIT_RESPONSE",success:!0,result:await GO(q.selector,q.currentState,q.axeConfig,q.rulesToRun)}}catch(B){ar.error("audit failed",B);const K=B instanceof Error?B.message:String(B),pe=K.toLowerCase().includes("paint")?"PAINT_TIMEOUT":"AXE_FAILED";return{type:"AUDIT_RESPONSE",success:!1,error:Fk(pe,K,!1)}}}),p=De("CUSTOM_PROPERTY_REQUEST",async()=>{try{return{type:"CUSTOM_PROPERTY_RESPONSE",undefined:mO()}}catch(q){return ar.warn("custom-property analysis failed",q),{type:"CUSTOM_PROPERTY_RESPONSE",undefined:[]}}}),w=De("THEME_AWARENESS_REQUEST",async()=>{try{const q=yO();return{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:q.hasLightModeCss,hasDarkModeCss:q.hasDarkModeCss,hasUnreadableSheets:q.hasUnreadableSheets}}catch(q){return ar.warn("theme-awareness probe failed",q),{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:!0,hasDarkModeCss:!0,hasUnreadableSheets:!0}}}),b=De("READING_ORDER_REQUEST",async q=>{try{const B=document.querySelector(q.selector)??document;return{type:"READING_ORDER_RESPONSE",issues:bv(B)}}catch(B){return ar.warn("reading-order analysis failed",B),{type:"READING_ORDER_RESPONSE",issues:[]}}}),g=De("TYPOGRAPHY_ANALYZE_REQUEST",async q=>{try{const B=document.querySelector(q.selector)??document;return{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:dO(B)}}catch(B){return ar.warn("typography analysis failed",B),{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:[]}}}),R=De("TAB_ORDER_ANALYZE_REQUEST",async()=>{try{return{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:iO()}}catch(q){return ar.warn("tab-order analysis failed",q),{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:[]}}}),S=De("WARMUP_REQUEST",async()=>(await NO(),{type:"WARMUP_RESPONSE"})),$=De("AI_CANDIDATES_REQUEST",async q=>{try{return{type:"AI_CANDIDATES_RESPONSE",images:WO(q.selector),headings:KO(q.selector),instructions:ZO(q.selector),ariaElements:QO(q.selector),links:JO(q.selector),longTextBlocks:eI(q.selector),languageInfo:tI(),colorOnlyRegions:rI(q.selector)}}catch(B){return ar.warn("ai-candidates enumeration failed",B),{type:"AI_CANDIDATES_RESPONSE",images:[],headings:[],instructions:[],ariaElements:[],links:[],longTextBlocks:[],languageInfo:null,colorOnlyRegions:[]}}});return()=>{m(),d(),b(),R(),g(),S(),p(),w(),$()}}function WO(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('img, [role="img"]');for(const b of Array.from(w)){const g=b.currentSrc||b.src||b.getAttribute("src")||"";if(!g||g.startsWith("data:"))continue;const R=b.getBoundingClientRect();if(R.width<16||R.height<16)continue;const S=b.getAttribute("alt")??"";YO(b,S)||p.push({imageUrl:g,alt:S,selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,500),surroundingContext:XO(b),pixelArea:Math.round(R.width*R.height)})}return p}function YO(d,m){if(sa(d))return!0;const p=d.getAttribute("role");if(p==="presentation"||p==="none")return!0;if(m===""){let w=d.parentElement,b=0;for(;w&&b<3;){const g=w.tagName.toLowerCase(),R=w.getAttribute("role");if(g==="a"||g==="button"||R==="link"||R==="button"){if((w.textContent??"").trim().length>0)return!0;break}w=w.parentElement,b++}}if(m===""){const w=(d.getAttribute("class")??"").toLowerCase();if(/\b(logo|brand|icon|emblem|crest|wordmark)\b/.test(w)&&d.parentElement&&(d.parentElement.textContent??"").trim().length>0)return!0}return!1}function sa(d){let m=d;for(;m&&m!==document.body;){if(m.getAttribute("aria-hidden")==="true")return!0;m=m.parentElement}return!1}function ua(d){if(d.id)return`#${CSS.escape(d.id)}`;const m=d.tagName.toLowerCase(),p=d.parentElement;if(!p)return m;const w=Array.from(p.children).filter(g=>g.tagName===d.tagName),b=w.indexOf(d);return w.length===1?`${p.tagName.toLowerCase()} > ${m}`:`${p.tagName.toLowerCase()} > ${m}:nth-of-type(${b+1})`}function XO(d){let m=d.parentElement;for(;m&&m!==document.body;){const p=(m.textContent??"").trim();if(p.length>20&&p.length<800)return p;m=m.parentElement}}function KO(d){var b,g;const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');for(const R of Array.from(w)){if(sa(R))continue;const S=(R.textContent??"").trim();if(!S||S.length>200)continue;const $=parseInt(((b=R.tagName.match(/^H(\d)$/))==null?void 0:b[1])??"6",10);let q="",B=R.nextElementSibling;for(;B&&q.length<800;){const K=parseInt(((g=B.tagName.match(/^H(\d)$/))==null?void 0:g[1])??"0",10);if(K>0&&K<=$)break;const pe=(B.textContent??"").trim();pe&&(q+=(q?" ":"")+pe),B=B.nextElementSibling}q.length<30||p.push({level:$,text:S,sectionContent:q.slice(0,800),selector:ua(R),outerHTML:(R.outerHTML??"").slice(0,300)})}return p}function ZO(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('p, li, div[role="note"], aside');for(const b of Array.from(w)){if(sa(b))continue;const g=(b.textContent??"").trim();if(!(g.length<20||g.length>600)&&/\b(click|tap|press|select|choose|find|locate|use|see|look|hit|push|drag)\b/i.test(g)&&(p.push({text:g.slice(0,600),selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20))break}return p}function QO(d){var b;const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll("[role]");for(const g of Array.from(w)){if(sa(g))continue;const R=g.getAttribute("role")??"";if(R&&(p.push({role:R,selector:ua(g),outerHTML:(g.outerHTML??"").slice(0,500),surroundingHtml:(((b=g.parentElement)==null?void 0:b.outerHTML)??"").slice(0,600)}),p.length>=30))break}return p}function JO(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll("a[href]");for(const b of Array.from(w)){if(sa(b))continue;const g=(b.textContent??"").trim();if(!g||g.length>60)continue;let R="",S=b.parentElement;for(;S&&S!==document.body;){const $=S.tagName;if($==="P"||$==="LI"||$==="DD"||$==="FIGCAPTION"||/^H[1-6]$/.test($)){const q=(S.textContent??"").trim();if(q.length>=g.length){R=q;break}}S=S.parentElement}if(p.push({linkText:g,surroundingText:R.slice(0,800),selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=25)break}return p}function eI(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=new Set,b=m.querySelectorAll("article, section, main, div, aside");for(const g of Array.from(b)){if(sa(g))continue;let R=g.parentElement,S=!1;for(;R;){if(w.has(R)){S=!0;break}R=R.parentElement}if(S)continue;const $=(g.textContent??"").trim();if(!$||$.split(/\s+/).filter(Boolean).length<300)continue;const B=g.querySelectorAll("h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, figure, table, button, input").length;if(!(B>4)&&(p.push({blockText:$.slice(0,1500),structuralChildrenCount:B,selector:ua(g),outerHTML:(g.outerHTML??"").slice(0,400)}),w.add(g),p.length>=5))break}return p}function tI(){const d=document.documentElement.getAttribute("lang")??"",m=document.body;if(!m)return null;const p=(m.textContent??"").trim().replace(/\s+/g," ").slice(0,1500);return p.length<60?null:{declaredLang:d,bodyTextSample:p,selector:"html",outerHTML:`<html lang="${d}">`}}function rI(d){const m=document.querySelector(d)??document.body;if(!m)return[];const p=[],w=m.querySelectorAll('[class*="status"], [class*="badge"], [class*="dot"], [class*="indicator"], [aria-invalid="true"], [class*="pill"]');for(const b of Array.from(w)){if(sa(b))continue;const g=(b.textContent??"").trim(),R=window.getComputedStyle(b),S=R.backgroundColor,$=R.borderColor;if(!(S&&S!=="rgba(0, 0, 0, 0)"&&S!=="transparent"||$&&$!=="rgba(0, 0, 0, 0)"&&$!=="transparent"))continue;const B=b.querySelector("svg, [aria-label], [aria-labelledby], [title]")!==null;if(g.length>0&&B)continue;const K=b.getAttribute("aria-label")??b.getAttribute("title")??"",pe=[g?`text: "${g.slice(0,60)}"`:"no text content",K?`label: "${K}"`:"no aria-label",`bg: ${S}`,B?"has icon child":"no icon child"].join("; ");if(p.push({elementHtml:(b.outerHTML??"").slice(0,400),contextDescription:pe.slice(0,400),selector:ua(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20)break}return p}const aI=Dt("storybook-integration"),nI=3e3;function iI(){const d=window.__STORYBOOK_PREVIEW__;return d&&typeof d=="object"?{detected:!0,version:d.version??"unknown"}:{detected:!1}}async function oI(){var b,g;const d=window.__STORYBOOK_PREVIEW__;if(!d)return[];const m=d.storyStoreValue??d.storyStore;if(!m)return[];typeof m.cacheAllCSFFiles=="function"&&await m.cacheAllCSFFiles();const p=((b=m.extract)==null?void 0:b.call(m))??((g=m.cachedCSFFiles)==null?void 0:g.call(m));if(!p)return[];const w=[];if(p instanceof Map)for(const[R,S]of p)w.push({id:R,name:S.name??R,kind:S.title??S.kind??""});else if(typeof p=="object")for(const[R,S]of Object.entries(p))w.push({id:R,name:S.name??R,kind:S.title??S.kind??""});return w}async function sI(d){const m=new URL(window.location.href);m.searchParams.set("id",d),m.searchParams.set("viewMode","story"),window.location.href!==m.href&&(window.history.pushState({},"",m.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(p=>{const w=setTimeout(()=>p(),nI),b=()=>{clearTimeout(w),p()};window.addEventListener("storyrendered",b,{once:!0})}),ns({type:"STORYBOOK_NAVIGATED_EVENT",storyId:d,ready:!0})}function uI(){return document.getElementById("storybook-root")?"#storybook-root":document.getElementById("root")?"#root":"body"}function lI(){const d=[];return d.push(De("STORYBOOK_DETECT_REQUEST",async()=>{const m=iI();return await chrome.storage.local.set({"storybook:lastDetected":{detected:m.detected,version:m.version,detectedAt:new Date().toISOString()}}),{type:"STORYBOOK_DETECT_RESPONSE",detected:m.detected,version:m.version}})),d.push(De("STORYBOOK_LIST_STORIES_REQUEST",async()=>({type:"STORYBOOK_LIST_STORIES_RESPONSE",stories:await oI()}))),d.push(De("STORYBOOK_NAVIGATE_REQUEST",async m=>{await sI(m.storyId)})),d.push(De("STORYBOOK_GET_STORY_TARGET_REQUEST",()=>({type:"STORYBOOK_GET_STORY_TARGET_RESPONSE",selector:uI()}))),aI.info("handlers registered"),()=>d.forEach(m=>m())}function cI(d){var S,$,q,B,K,pe,ue;const m=d.getAttribute("aria-labelledby");if(m){const fe=m.split(/\s+/).filter(Boolean).map(ke=>{var Je;return(Je=d.ownerDocument)==null?void 0:Je.getElementById(ke)}).filter(ke=>ke!=null);if(fe.length>0){const ke=fe.map(Je=>(Je.textContent??"").trim()).filter(Boolean).join(" ");if(ke)return ke}}const p=(S=d.getAttribute("aria-label"))==null?void 0:S.trim();if(p)return p;const w=d.id;if(w){const ae=($=d.ownerDocument)==null?void 0:$.querySelector(`label[for="${fI(w)}"]`);if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}const b=(q=d.closest)==null?void 0:q.call(d,"label");if(b){const ae=b.cloneNode(!0);for(const ke of ae.querySelectorAll("input, select, textarea, button"))(B=ke.parentNode)==null||B.removeChild(ke);const fe=(ae.textContent??"").trim();if(fe)return fe}const g=(K=d.closest)==null?void 0:K.call(d,"fieldset");if(g&&d.tagName!=="FIELDSET"){const ae=Array.from(g.children).find(fe=>fe.tagName==="LEGEND");if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}if(d.tagName==="TABLE"){const ae=d.caption;if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}if(d.tagName==="FIGURE"){const ae=Array.from(d.children).find(fe=>fe.tagName==="FIGCAPTION");if(ae){const fe=(ae.textContent??"").trim();if(fe)return fe}}if(d.tagName==="IMG"||d.tagName==="INPUT"&&d.type==="image"||d.tagName==="AREA"){const ae=d.getAttribute("alt");if(ae!==null)return ae}if(dI(d)){const ae=(d.textContent??"").replace(/\s+/g," ").trim();if(ae)return ae}const R=(pe=d.getAttribute("title"))==null?void 0:pe.trim();if(R)return R;if(d.tagName==="INPUT"||d.tagName==="TEXTAREA"){const ae=(ue=d.getAttribute("placeholder"))==null?void 0:ue.trim();if(ae)return ae}return""}function dI(d){const m=Na(d);return new Set(["button","link","heading","menuitem","menuitemcheckbox","menuitemradio","option","radio","tab","switch","treeitem","gridcell","cell","columnheader","rowheader","checkbox"]).has(m)}function fI(d){var p;const m=(p=globalThis.CSS)==null?void 0:p.escape;return m?m(d):d.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g,"\\$1")}function Na(d){const m=d.getAttribute("role");if(m)return m.trim().toLowerCase();switch(d.tagName){case"A":return d.href?"link":"generic";case"BUTTON":return"button";case"INPUT":{const w=d.type;return w==="button"||w==="submit"||w==="reset"?"button":w==="checkbox"?"checkbox":w==="radio"?"radio":w==="range"?"slider":w==="search"?"searchbox":w==="image"?"button":"textbox"}case"TEXTAREA":return"textbox";case"SELECT":return d.multiple?"listbox":"combobox";case"OPTION":return"option";case"IMG":return d.getAttribute("alt")===""?"presentation":"img";case"NAV":return"navigation";case"MAIN":return"main";case"HEADER":return"banner";case"FOOTER":return"contentinfo";case"ASIDE":return"complementary";case"SECTION":return d.getAttribute("aria-label")||d.getAttribute("aria-labelledby")?"region":"generic";case"ARTICLE":return"article";case"H1":case"H2":case"H3":case"H4":case"H5":case"H6":return"heading";case"UL":case"OL":return"list";case"LI":return"listitem";case"TABLE":return"table";case"TR":return"row";case"TD":return"cell";case"TH":return"columnheader";case"DIALOG":return"dialog";case"DETAILS":return"group";case"SUMMARY":return"button";case"FIELDSET":return"group";case"LEGEND":return"caption";case"FIGURE":return"figure";case"FIGCAPTION":return"caption";case"PROGRESS":return"progressbar";case"METER":return"meter";case"OUTPUT":return"status";case"HR":return"separator";default:return"generic"}}function pI(d){var B;const m=[];if(Na(d)==="heading"){const K=d.getAttribute("aria-level")??((B=d.tagName.match(/^H(\d)$/))==null?void 0:B[1]);K&&m.push(`level ${K}`)}const w=d.getAttribute("aria-expanded");w==="true"?m.push("expanded"):w==="false"&&m.push("collapsed");const b=d.getAttribute("aria-pressed");b==="true"?m.push("pressed"):b==="false"&&m.push("not pressed");const g=d.getAttribute("aria-checked");g==="true"?m.push("checked"):g==="false"?m.push("not checked"):g==="mixed"?m.push("mixed"):d.checked===!0&&d.tagName==="INPUT"&&(d.type==="checkbox"||d.type==="radio")&&m.push("checked"),d.getAttribute("aria-selected")==="true"&&m.push("selected"),(d.getAttribute("aria-disabled")==="true"||d.disabled)&&m.push("disabled"),(d.getAttribute("aria-required")==="true"||d.required)&&m.push("required");const S=d.getAttribute("aria-invalid");(S==="true"||S==="spelling"||S==="grammar")&&m.push(`invalid${S!=="true"?" "+S:""}`),(d.getAttribute("aria-readonly")==="true"||d.readOnly)&&m.push("read only");const $=d.getAttribute("aria-haspopup");$&&$!=="false"&&m.push(`has ${$==="true"?"menu":$} popup`),d.getAttribute("aria-busy")==="true"&&m.push("busy");const q=d.getAttribute("aria-current");return q&&q!=="false"&&m.push(`current ${q==="true"?"item":q}`),m}function mI(d){const m=Na(d);if(!new Set(["option","menuitem","menuitemcheckbox","menuitemradio","tab","treeitem","radio","listitem"]).has(m))return"";const b={option:"listbox",menuitem:"menu menubar",menuitemcheckbox:"menu menubar",menuitemradio:"menu menubar",tab:"tablist",treeitem:"tree",radio:"radiogroup",listitem:"list"}[m].split(" ");let g=d.parentElement;for(;g&&!b.includes(Na(g));)g=g.parentElement;if(!g)return"";const R=Array.from(g.children).filter($=>Na($)===m),S=R.indexOf(d);return S<0?"":`${S+1} of ${R.length}`}function hI(d){const m=cI(d),p=Na(d),w=pI(d),b=mI(d);return{name:m,role:p,states:w,group:b}}const Ev=Dt("element-picker");let nr=null,$a=!1;const vI=`
|
|
40
40
|
position: fixed;
|
|
41
41
|
pointer-events: none;
|
|
42
42
|
background: rgba(99, 102, 241, 0.20);
|
package/dist/assets/{content-script.ts-loader-BUWxUTh3.js → content-script.ts-loader-DOSB_ugu.js}
RENAMED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
(async () => {
|
|
6
6
|
const { onExecute } = await import(
|
|
7
7
|
/* @vite-ignore */
|
|
8
|
-
chrome.runtime.getURL("assets/content-script.ts-
|
|
8
|
+
chrome.runtime.getURL("assets/content-script.ts-CX6ON5Ua.js")
|
|
9
9
|
);
|
|
10
10
|
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
|
|
11
11
|
})().catch(console.error);
|
package/dist/assets/{dom-criterion-analyzers-CieyvSY5.js → dom-criterion-analyzers-DoUaJV5C.js}
RENAMED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
const I=/\b(cookie|cookies|consent|gdpr|privacy|tracker|analytics|preference)s?\b/i,$=/\b(accept|agree|allow|got it|ok|okay|continue|confirm|accept all|allow all)\b/i,q=/\b(reject|decline|deny|no thanks|cancel|disagree|reject all|deny all)\b/i,y="https://www.w3.org/WAI/WCAG21/Understanding/";function x(t){let e=0;const a=window.getComputedStyle(t),i=a.position;if(i!=="fixed"&&i!=="sticky")return 0;const n=parseInt(a.zIndex,10);Number.isFinite(n)&&n>=100&&(e+=2),Number.isFinite(n)&&n>=1e3&&(e+=1);const o=(t.textContent??"").slice(0,2e3);I.test(o)&&(e+=3),$.test(o)&&(e+=1),q.test(o)&&(e+=1);const r=t.getBoundingClientRect();return r.width>=100&&r.height>=40&&(e+=1),a.display==="none"||a.visibility==="hidden"||t.getAttribute("aria-hidden")==="true"?0:e}function E(t=document){const e=[],a=t.querySelectorAll('[class*="cookie" i], [class*="consent" i], [id*="cookie" i], [id*="consent" i], dialog[open], div[role="dialog"], div[role="alertdialog"]');for(const o of a){const r=x(o);r>0&&e.push({el:o,score:r})}const i=t.querySelectorAll("body *");for(const o of i){if(e.some(d=>d.el===o))continue;const r=window.getComputedStyle(o);if(r.position!=="fixed"&&r.position!=="sticky")continue;const s=(o.textContent??"").slice(0,2e3);if(!I.test(s))continue;const l=x(o);l>0&&e.push({el:o,score:l})}if(e.length===0)return null;e.sort((o,r)=>r.score-o.score);const n=e[0];return n.score>=5?n.el:null}function v(t){var n,o,r,s;const e=(n=t.getAttribute("aria-label"))==null?void 0:n.trim();if(e)return e;const a=t.getAttribute("aria-labelledby");if(a){const l=(o=t.ownerDocument)==null?void 0:o.getElementById(a),d=(r=l==null?void 0:l.textContent)==null?void 0:r.trim();if(d)return d}const i=(s=t.getAttribute("title"))==null?void 0:s.trim();return i||(t.textContent??"").trim()}function R(t){const e=t.tagName;if(e==="BUTTON"||e==="A"||e==="INPUT"||e==="SELECT"||e==="TEXTAREA")return!0;const a=t.getAttribute("role");if(a==="button"||a==="link"||a==="checkbox"||a==="radio"||a==="switch")return!0;const i=t.getAttribute("tabindex");return i!==null&&parseInt(i,10)>=0}function T(t){const e=[],a=Array.from(t.querySelectorAll('button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [tabindex]'));for(const s of a){if(!R(s)||s.type==="hidden")continue;const l=v(s);(!l||l.length<2)&&e.push({ruleId:"cookie-banner-button-name",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:"Cookie banner: interactive element has no accessible name. Screen-reader users cannot tell what this button does (accept, reject, manage preferences, etc.).",helpUrl:`${y}name-role-value`,element:s,failureSummary:'Add an aria-label, visible text content, or aria-labelledby to label this control. "Accept all cookies" / "Reject all cookies" / "Manage preferences" are the conventional labels.'})}a.some(s=>{const l=v(s);return $.test(l)})||e.push({ruleId:"cookie-banner-no-accept",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"moderate",description:'Cookie banner: no clearly-labeled "accept" control found. Users (especially with cognitive disabilities or screen-reader users skimming labels) need a consistent, recognizable label.',helpUrl:`${y}consistent-identification`,element:t,failureSummary:'Add a button with text or aria-label containing "Accept" / "Allow" / "Agree" so the consent action is identifiable from the label alone.'}),a.some(s=>{const l=v(s);return q.test(l)})||e.push({ruleId:"cookie-banner-no-reject",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"serious",description:'Cookie banner: no clearly-labeled "reject all" control found. GDPR Art. 7 requires consent withdrawal to be as easy as consent giving; for accessibility this also means the reject control must be discoverable from its label, not buried in a "manage preferences" sub-flow.',helpUrl:`${y}consistent-identification`,element:t,failureSummary:'Add a button with text or aria-label containing "Reject" / "Decline" / "Reject all" alongside the accept control at the same hierarchy level.'});const o=t.getAttribute("role");t.tagName==="DIALOG"||o==="dialog"||o==="alertdialog"||e.push({ruleId:"cookie-banner-no-dialog-role",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:"Cookie banner: no dialog semantics. Screen-reader users will not be alerted that an interrupting modal is on-screen; keyboard users do not get the standard Escape-to-dismiss affordance.",helpUrl:`${y}info-and-relationships`,element:t,failureSummary:'Use <dialog open> or role="dialog" / role="alertdialog" plus aria-labelledby pointing to the banner heading. With the dialog role, screen readers announce "dialog: <name>" when the banner mounts.'});for(const s of a){const l=s.getAttribute("tabindex");l&&parseInt(l,10)<0&&e.push({ruleId:"cookie-banner-unreachable-control",wcagCriterion:"2.1.1",wcagLevel:"A",impact:"critical",description:"Cookie banner: interactive control has tabindex < 0 so keyboard users cannot reach it. If this is the accept/reject button, consent cannot be given without a mouse.",helpUrl:`${y}keyboard`,element:s,failureSummary:'Remove the negative tabindex, or replace with tabindex="0" so the control participates in the keyboard tab order.'})}return e}function U(t=document){const e=E(t);return e?T(e):[]}const h="https://www.w3.org/WAI/ARIA/apg/patterns/";function P(t){var n,o,r,s;const e=(n=t.getAttribute("aria-label"))==null?void 0:n.trim();if(e)return e;const a=t.getAttribute("aria-labelledby");if(a){const l=(o=t.ownerDocument)==null?void 0:o.getElementById(a),d=(r=l==null?void 0:l.textContent)==null?void 0:r.trim();if(d)return d}const i=(s=t.getAttribute("title"))==null?void 0:s.trim();return i||""}function z(t=document){const e=[],a=Array.from(t.querySelectorAll('[role="dialog"], [role="alertdialog"], dialog[open]'));for(const i of a){const n=i.getAttribute("role")??(i.tagName==="DIALOG"?"dialog":"");P(i)||e.push({ruleId:"aria-pattern-dialog-no-name",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:`${n} has no accessible name. Screen readers will announce "dialog" with no label, leaving the user unable to identify what's open.`,helpUrl:`${h}dialog-modal/`,element:i,failureSummary:'Add aria-labelledby pointing to the dialog heading, or aria-label with a short title (e.g. aria-labelledby="dlg-title" + <h2 id="dlg-title">Confirm deletion</h2>).'});const o=i.getAttribute("aria-modal");!(i.tagName==="DIALOG")&&o!=="true"&&o!=="false"&&e.push({ruleId:"aria-pattern-dialog-no-modal-attr",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"moderate",description:`${n} has no aria-modal attribute. If this dialog blocks the rest of the page, set aria-modal="true" so assistive technology can mute the background. If it's non-modal, set aria-modal="false" explicitly.`,helpUrl:`${h}dialog-modal/`,element:i,failureSummary:'Set aria-modal="true" on modal dialogs (most common). Native <dialog open> handles this automatically; custom div-based dialogs need the attribute.'})}return e}function W(t=document){const e=[],a=Array.from(t.querySelectorAll('[role="tablist"]'));for(const i of a){const n=Array.from(i.querySelectorAll('[role="tab"]'));if(n.length===0){e.push({ruleId:"aria-pattern-tablist-no-tabs",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:'role="tablist" contains no role="tab" descendants. Screen readers announce a tab interface but provide nothing to navigate.',helpUrl:`${h}tabs/`,element:i,failureSummary:'Every tablist must directly contain role="tab" elements as children. Restructure the markup so the tabs are descendants of the tablist.'});continue}const o=n.filter(r=>r.getAttribute("aria-selected")==="true").length;o===0?e.push({ruleId:"aria-pattern-tabs-no-selected",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:'No tab in this tablist has aria-selected="true". Screen-reader users hear all tabs as inactive, with no indication which panel is currently shown.',helpUrl:`${h}tabs/`,element:i,failureSummary:'Exactly one tab must have aria-selected="true" at any time. Update the active tab when the user picks a new one.'}):o>1&&e.push({ruleId:"aria-pattern-tabs-multiple-selected",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:`${o} tabs have aria-selected="true" at once. Only one tab in a tablist may be selected at a time.`,helpUrl:`${h}tabs/`,element:i,failureSummary:"Clear aria-selected on all other tabs when one is activated. Use the keyboard-handler that updates aria-selected to also clear the previously-active tab."});for(const r of n){const s=r.getAttribute("aria-controls");if(!s){e.push({ruleId:"aria-pattern-tab-no-controls",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:'role="tab" has no aria-controls pointing to its tabpanel. Programmatic relationship between tab and panel is missing.',helpUrl:`${h}tabs/`,element:r,failureSummary:'Set aria-controls to the id of the corresponding tabpanel: <button role="tab" aria-controls="panel-1">Tab 1</button> + <div role="tabpanel" id="panel-1">…</div>'});continue}const l=t.getElementById(s);l?l.getAttribute("role")!=="tabpanel"&&e.push({ruleId:"aria-pattern-tab-wrong-target-role",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:`role="tab" aria-controls target has role="${l.getAttribute("role")??"(none)"}" instead of role="tabpanel".`,helpUrl:`${h}tabs/`,element:r,failureSummary:'The element a tab controls must have role="tabpanel".'}):e.push({ruleId:"aria-pattern-tab-broken-controls",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:`role="tab" aria-controls="${s}" points to an id that doesn't exist on the page.`,helpUrl:`${h}tabs/`,element:r,failureSummary:`Add an element with id="${s}" + role="tabpanel" containing the tab's content, or fix the aria-controls value to match an existing panel.`})}}return e}function N(t=document){const e=[],a=Array.from(t.querySelectorAll('[role="combobox"]'));for(const i of a){const n=i.getAttribute("aria-expanded");n===null?e.push({ruleId:"aria-pattern-combobox-no-expanded",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:`role="combobox" has no aria-expanded attribute. Screen-reader users can't tell whether the popup is open.`,helpUrl:`${h}combobox/`,element:i,failureSummary:'Set aria-expanded="false" when the popup is closed (default) and aria-expanded="true" when open. Toggle on user interaction.'}):n!=="true"&&n!=="false"&&e.push({ruleId:"aria-pattern-combobox-invalid-expanded",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"moderate",description:`role="combobox" aria-expanded="${n}" is not a valid value. Must be "true" or "false".`,helpUrl:`${h}combobox/`,element:i,failureSummary:'Use aria-expanded="true" or aria-expanded="false" (lowercase, exact match).'});const o=i.getAttribute("aria-controls")||i.getAttribute("aria-owns");if(!o)e.push({ruleId:"aria-pattern-combobox-no-popup",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:'role="combobox" has no aria-controls (or aria-owns) pointing to the popup container.',helpUrl:`${h}combobox/`,element:i,failureSummary:"Set aria-controls to the id of the listbox / tree / grid / dialog that opens. The popup container needs the corresponding role."});else{const r=t.getElementById(o);if(!r)e.push({ruleId:"aria-pattern-combobox-broken-popup",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:`role="combobox" aria-controls="${o}" points to an id that doesn't exist.`,helpUrl:`${h}combobox/`,element:i,failureSummary:`Add the popup element with id="${o}" and the appropriate role (listbox, tree, grid, or dialog).`});else{const s=r.getAttribute("role");(!s||!new Set(["listbox","tree","grid","dialog"]).has(s))&&e.push({ruleId:"aria-pattern-combobox-wrong-popup-role",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:`role="combobox" popup target has role="${s??"(none)"}". Must be listbox, tree, grid, or dialog per APG.`,helpUrl:`${h}combobox/`,element:i,failureSummary:'Set role="listbox" on the popup container (most common). Use tree / grid / dialog only when the popup is structured that way.'})}}if(n==="true"){const r=i.getAttribute("aria-activedescendant");r&&!t.getElementById(r)&&e.push({ruleId:"aria-pattern-combobox-broken-activedescendant",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"moderate",description:`role="combobox" aria-activedescendant="${r}" references an id that doesn't exist.`,helpUrl:`${h}combobox/`,element:i,failureSummary:"Update aria-activedescendant to the id of the currently-highlighted option, or remove it and rely on DOM focus."})}}return e}function O(t=document){return[...z(t),...W(t),...N(t)]}const S="https://www.w3.org/WAI/WCAG21/Understanding/non-text-content";function F(t){const e=t.getAttribute("href")??"";return!!(/\.pdf(\?|#|$)/i.test(e)||t.getAttribute("type")==="application/pdf")}function G(t){const e=(t.textContent??"").toLowerCase();return!!(/\bpdf\b/.test(e)||/\(pdf/i.test(t.getAttribute("aria-label")??"")||t.querySelector('[aria-label*="PDF" i], [aria-label*="pdf" i], [title*="PDF" i]'))}function D(t=document){const e=[],a=Array.from(t.querySelectorAll("a[href]"));for(const i of a){if(!F(i))continue;const n=i.getAttribute("href")??"";e.push({ruleId:"pdf-needs-cli-audit",wcagCriterion:"1.1.1",wcagLevel:"A",impact:"moderate",description:`Linked PDF at ${n}. PDF accessibility cannot be verified from a browser audit — run \`wcagcheckr-ci pdf ${n}\` (or pass the local file path) to check tagging, language, title, and structure tree.`,helpUrl:S,element:i,failureSummary:"PDFs are a separate accessibility document type with their own structural requirements (PDF/UA-1, WCAG via EN 301 549 §10). The wcagcheckr CLI runs a metadata audit on the PDF binary; see PDF column in the deposition packet for the result."}),G(i)||e.push({ruleId:"pdf-link-no-format-hint",wcagCriterion:"2.4.4",wcagLevel:"A",impact:"serious",description:`Link points to a PDF but the visible label gives no "(PDF)" hint. Screen-reader users hear the link text and won't know the activation will download a document.`,helpUrl:S,element:i,failureSummary:'Add "(PDF)" to the link text, or use an aria-label that includes "PDF" / "PDF document". Bonus: include the file size — "Annual report (PDF, 2.4 MB)".'})}return e}const c="https://www.w3.org/WAI/WCAG21/Understanding/";function M(t=document){const e=[],a=t.querySelectorAll('meta[http-equiv="refresh" i]');for(const n of Array.from(a)){const o=n.getAttribute("content")??"",r=parseInt(o.split(";")[0]??"",10);Number.isFinite(r)&&r>0&&e.push({ruleId:"wcc-meta-refresh",wcagCriterion:"2.2.1",wcagLevel:"A",impact:"serious",description:`Page uses <meta http-equiv="refresh"> to auto-refresh after ${r}s. WCAG 2.2.1 requires that users can turn off, adjust, or extend any time limit. Auto-refresh without user control fails this criterion.`,helpUrl:`${c}timing-adjustable.html`,element:n,failureSummary:`Auto-refresh interval: ${r}s. No user control mechanism on the page.`})}const i=t.querySelectorAll("script:not([src])");for(const n of Array.from(i)){const o=n.textContent??"";o.length===0||!/\b(setTimeout|setInterval)\s*\(/.test(o)||!/\b(innerHTML|textContent|replaceChildren|window\.location|document\.location|location\.href|location\.replace)\b/.test(o)||e.push({ruleId:"wcc-timer-mutates-content",wcagCriterion:"2.2.1",wcagLevel:"A",impact:"moderate",description:"Inline script uses setTimeout/setInterval to mutate page content. WCAG 2.2.1 requires that users can turn off, adjust, or extend any time-limited content change. Verify a pause / extend / disable mechanism exists in the UI.",helpUrl:`${c}timing-adjustable.html`,element:n,failureSummary:"Script combines timer + DOM mutation. Without a user control to pause/extend/disable, fails 2.2.1."})}return e}function H(t=document){var e,a;try{const i=t.styleSheets;for(let n=0;n<i.length;n++){const o=i[n];if(!o)continue;let r=null;try{r=o.cssRules}catch{continue}if(r)for(let s=0;s<r.length;s++){const l=r[s];if(l&&l.constructor&&l.constructor.name==="CSSMediaRule"){const d=l,p=d.conditionText??((e=d.media)==null?void 0:e.mediaText)??"";if(!/prefers-reduced-motion\s*:\s*reduce/i.test(p))continue;for(let u=0;u<d.cssRules.length;u++){const m=d.cssRules[u];if(m&&m.constructor&&m.constructor.name==="CSSStyleRule"){const f=((a=m.style)==null?void 0:a.cssText)??"";if(/animation|transition/i.test(f))return!0}}}}}}catch{}return!1}function _(t=document){const e=[],a=H(t),i=t.querySelectorAll("marquee, blink");for(const s of Array.from(i))e.push({ruleId:"wcc-marquee-blink",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"serious",description:`<${s.tagName.toLowerCase()}> creates continuous motion with no built-in pause control. WCAG 2.2.2 requires a way to pause, stop, or hide auto-moving content.`,helpUrl:`${c}pause-stop-hide.html`,element:s,failureSummary:`Deprecated <${s.tagName.toLowerCase()}> element auto-animates without pause control.`});const n=t.querySelectorAll("video[autoplay]");for(const s of Array.from(n))!s.hasAttribute("controls")&&!s.muted&&e.push({ruleId:"wcc-autoplay-no-controls",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"serious",description:"<video autoplay> without controls attribute (or muted) creates moving content the user cannot pause. WCAG 2.2.2 requires a pause / stop / hide mechanism.",helpUrl:`${c}pause-stop-hide.html`,element:s,failureSummary:"Autoplay video lacks user-accessible pause control."});const o=t.querySelectorAll("*");let r=0;for(const s of Array.from(o)){if(r>1500)break;r++;const l=window.getComputedStyle(s);if(l.animationName==="none"||!l.animationName)continue;const d=l.animationDuration.split(",").map(u=>parseFloat(u)),p=l.animationIterationCount.split(",").map(u=>u.trim());for(let u=0;u<d.length;u++){const m=d[u]??0,b=(p[u]??"1")==="infinite";b&&m>0&&m<.34&&e.push({ruleId:"wcc-flash-risk",wcagCriterion:"2.3.1",wcagLevel:"A",impact:"critical",description:`Element animates with duration ${m.toFixed(2)}s on infinite repeat — that's > 3 cycles/sec, which can trigger photosensitive seizures. WCAG 2.3.1 prohibits content that flashes more than 3 times per second unless the flashing area is below threshold.`,helpUrl:`${c}three-flashes-or-below-threshold.html`,element:s,failureSummary:`Infinite CSS animation @ ${m.toFixed(2)}s/cycle = ${(1/m).toFixed(1)} flashes/sec.`}),b&&m>=5&&!a&&e.push({ruleId:"wcc-long-infinite-animation",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"moderate",description:`Element runs an infinite CSS animation (${m.toFixed(1)}s/cycle). WCAG 2.2.2 requires a way to pause, stop, or hide animation longer than 5 seconds. The canonical mitigation is a \`@media (prefers-reduced-motion: reduce)\` block that disables or shortens the animation — the wcagcheckr rule detects this automatically and stops firing once present. Alternative: a visible pause button.`,helpUrl:`${c}pause-stop-hide.html`,element:s,failureSummary:`Infinite animation @ ${m.toFixed(1)}s/cycle. Confirm user can pause.`})}}return e}function B(t=document){const e=[],a=new Set,i=t.querySelectorAll("[ontouchstart], [ontouchmove], [ongesturestart], [ongesturechange]");for(const o of Array.from(i)){const r=k(o);a.has(r)||(a.add(r),e.push({ruleId:"wcc-gesture-no-alternative",wcagCriterion:"2.5.1",wcagLevel:"A",impact:"serious",description:"Element handles multi-point or path-based gestures (touch/gesture events). WCAG 2.5.1 requires that all functionality operable with a path-based or multi-point gesture is ALSO operable with a single-pointer activation. Verify a button / link / single-tap alternative exists.",helpUrl:`${c}pointer-gestures.html`,element:o,failureSummary:"Touch/gesture handler on this element. Confirm single-pointer alternative."}))}const n=t.querySelectorAll('[draggable="true"], [ondragstart]');for(const o of Array.from(n)){const r=k(o);a.has(r)||(a.add(r),e.push({ruleId:"wcc-drag-no-alternative",wcagCriterion:"2.5.7",wcagLevel:"AA",impact:"serious",description:'Element uses HTML5 drag-and-drop (`draggable="true"` or `ondragstart`). WCAG 2.5.7 requires that any function achieved via a dragging movement is ALSO operable by a single-pointer action (click, button, arrow keys). Confirm an alternative exists.',helpUrl:`${c}dragging-movements.html`,element:o,failureSummary:"Drag-and-drop interaction. Confirm non-drag alternative for keyboard / touch users."}))}return e}function j(t=document){const e=[],a=t.querySelectorAll("video");for(const n of Array.from(a)){const o=Array.from(n.querySelectorAll("track")),r=o.some(l=>(l.kind??"").toLowerCase()==="captions"||(l.kind??"").toLowerCase()==="subtitles"),s=o.some(l=>(l.kind??"").toLowerCase()==="descriptions");r||e.push({ruleId:"wcc-video-no-captions",wcagCriterion:"1.2.2",wcagLevel:"A",impact:"serious",description:'<video> element without `<track kind="captions">` (or `subtitles`). WCAG 1.2.2 requires captions for all prerecorded audio content in synchronized media. If this video has no audio, mark this finding as not-applicable.',helpUrl:`${c}captions-prerecorded.html`,element:n,failureSummary:'No <track kind="captions"> child. Captions required unless video is silent.'}),s||(e.push({ruleId:"wcc-video-no-descriptions",wcagCriterion:"1.2.5",wcagLevel:"AA",impact:"moderate",description:'<video> element without `<track kind="descriptions">`. WCAG 1.2.5 (AA) requires audio description of important visual content in prerecorded video. If all video content is conveyed by the audio track alone, mark this finding as not-applicable.',helpUrl:`${c}audio-description-prerecorded.html`,element:n,failureSummary:'No <track kind="descriptions"> child. Audio description required when visuals carry info not in audio.'}),e.push({ruleId:"wcc-video-no-audio-desc-or-alt",wcagCriterion:"1.2.3",wcagLevel:"A",impact:"serious",description:'<video> element with no audio description track AND no obvious text-alternative link nearby. WCAG 1.2.3 (A) requires EITHER audio description OR a full text alternative for prerecorded video content. Add a <track kind="descriptions"> OR link a transcript/synopsis adjacent to the video.',helpUrl:`${c}audio-description-or-media-alternative-prerecorded.html`,element:n,failureSummary:'No <track kind="descriptions"> and no detectable text alternative. Need at least one.'}))}const i=t.querySelectorAll("audio");for(const n of Array.from(i))e.push({ruleId:"wcc-audio-needs-transcript",wcagCriterion:"1.2.1",wcagLevel:"A",impact:"moderate",description:"<audio> element present. WCAG 1.2.1 requires a text alternative (transcript) for prerecorded audio-only content. Confirm a transcript is linked nearby.",helpUrl:`${c}audio-only-and-video-only-prerecorded.html`,element:n,failureSummary:"Audio element detected. Verify nearby transcript link."});return e}function V(t=document){const e=[],a=/\b(window\.location|document\.location|location\.href|location\.replace|window\.open|\.submit\s*\()/,i=t.querySelectorAll("[onfocus]");for(const o of Array.from(i)){const r=o.getAttribute("onfocus")??"";a.test(r)&&e.push({ruleId:"wcc-onfocus-context-change",wcagCriterion:"3.2.1",wcagLevel:"A",impact:"serious",description:"Element's `onfocus` handler causes a context change (navigation, submit, popup). WCAG 3.2.1 forbids context changes triggered solely by focus — they disorient keyboard and screen-reader users who land on the element while scanning.",helpUrl:`${c}on-focus.html`,element:o,failureSummary:"Focus handler navigates / submits / opens window. Move trigger to explicit click/keypress."})}const n=t.querySelectorAll("select[onchange], input[onchange], textarea[onchange]");for(const o of Array.from(n)){const r=o.getAttribute("onchange")??"";a.test(r)&&e.push({ruleId:"wcc-oninput-context-change",wcagCriterion:"3.2.2",wcagLevel:"A",impact:"serious",description:"Form control's `onchange` handler causes a context change without explicit user request. WCAG 3.2.2 requires that changing a setting in a form control does NOT automatically navigate or submit — the user must explicitly confirm via a button.",helpUrl:`${c}on-input.html`,element:o,failureSummary:"Change handler navigates / submits. Replace with a separate submit button."})}return e}function K(t=document){const e=[],a=[".g-recaptcha","[data-sitekey]",".h-captcha",".cf-turnstile",".frc-captcha",'iframe[src*="recaptcha"]','iframe[src*="hcaptcha"]','iframe[src*="turnstile"]'].join(", "),i=t.querySelectorAll(a),n=new Set;for(const o of Array.from(i)){if(n.has(o))continue;n.add(o);const r=o.closest("form"),s=((r==null?void 0:r.textContent)??"").toLowerCase(),l=/audio captcha|alternative|sign in with|continue with|use email|magic link/.test(s);e.push({ruleId:"wcc-cognitive-auth-challenge",wcagCriterion:"3.3.8",wcagLevel:"AA",impact:l?"moderate":"serious",description:l?"CAPTCHA / cognitive-function challenge detected. An alternative authentication mechanism appears available — verify it actually bypasses the CAPTCHA. WCAG 3.3.8 requires that the alternative not also require a cognitive function test.":'CAPTCHA / cognitive-function challenge detected with no apparent alternative. WCAG 3.3.8 requires an alternative authentication mechanism that does NOT depend on a cognitive function test (recognizing distorted text, solving puzzles, identifying objects in images, etc.). Add a "Sign in with [SSO]" or magic-link option.',helpUrl:`${c}accessible-authentication-minimum.html`,element:o,failureSummary:l?"CAPTCHA detected; alternative may exist — verify it skips the cognitive challenge.":"CAPTCHA detected without an alternative authentication path."})}return e}function Y(t=document){const e=[],a=t.querySelectorAll('meta[name="viewport" i]');for(const n of Array.from(a)){const o=n.getAttribute("content")??"";(/user-scalable\s*=\s*(no|0)\b/i.test(o)||/maximum-scale\s*=\s*1(\.0+)?\b/i.test(o))&&e.push({ruleId:"wcc-viewport-locks-zoom",wcagCriterion:"1.4.4",wcagLevel:"AA",impact:"serious",description:'<meta name="viewport"> disables user zoom (`user-scalable=no` or `maximum-scale=1`). WCAG 1.4.4 requires that users can scale text up to 200% without loss of content or functionality. Remove the zoom-disabling tokens.',helpUrl:`${c}resize-text.html`,element:n,failureSummary:"Viewport meta disables zoom. Remove user-scalable=no and maximum-scale<2."})}let i=0;for(const n of Array.from(t.styleSheets)){if(i>40)break;i++;try{const o=n.cssRules;if(!o)continue;for(const r of Array.from(o)){if(!(r instanceof CSSMediaRule))continue;const s=r.media.mediaText.toLowerCase(),l=/\(\s*orientation\s*:\s*portrait\s*\)/.test(s),d=/\(\s*orientation\s*:\s*landscape\s*\)/.test(s);if(!(!l&&!d)){for(const p of Array.from(r.cssRules))if(p instanceof CSSStyleRule&&/display\s*:\s*none/i.test(p.style.cssText)){e.push({ruleId:"wcc-orientation-locked-content",wcagCriterion:"1.3.4",wcagLevel:"AA",impact:"serious",description:`Stylesheet hides content via "display: none" inside an @media (orientation: ${l?"portrait":"landscape"}) block. WCAG 1.3.4 requires content not be restricted to a single display orientation unless that orientation is essential. Provide content in both orientations or document the essential-orientation exception.`,helpUrl:`${c}orientation.html`,element:t.documentElement,failureSummary:`Orientation-locked display:none on selector "${p.selectorText}".`});break}}}}catch{}}return e}function X(t=document){const e=[];let a=0;const i=t.querySelectorAll("[title]");for(const n of Array.from(i)){if(a>200)break;a++;const o=n.getAttribute("title");!o||o.trim().length<8||n.hasAttribute("aria-describedby")||n.tagName==="A"&&(n.textContent??"").includes(o)||e.push({ruleId:"wcc-title-tooltip-no-persistence",wcagCriterion:"1.4.13",wcagLevel:"AA",impact:"moderate",description:'Element uses the `title` attribute for a tooltip-style description. Browser title tooltips auto-dismiss when the pointer moves, violating WCAG 1.4.13\'s "hoverable" and "persistent" requirements (content triggered on hover must remain visible until the user dismisses it). Use a proper aria-describedby pattern with a hoverable, dismissible tooltip element.',helpUrl:`${c}content-on-hover-or-focus.html`,element:n,failureSummary:"title-attribute tooltip is not dismissible / hoverable."})}return e}function Z(t=document){const e=[],a=typeof window<"u"?window.location.pathname:"/";if(a==="/"||a===""||/\/(search|results)\b/.test(a))return e;const i=t.querySelector('nav, [role="navigation"]')!==null,n=t.querySelector('input[type="search"], [role="search"]')!==null||Array.from(t.querySelectorAll('input[type="text"]')).some(l=>/search/i.test(l.name)||/search/i.test(l.placeholder??"")),o=t.querySelector('a[href*="sitemap" i]')!==null,r=Array.from(t.querySelectorAll('a[href^="#"]')).length>=3,s=[i,n,o,r].filter(Boolean).length;return s<2&&e.push({ruleId:"wcc-only-one-way-to-find",wcagCriterion:"2.4.5",wcagLevel:"AA",impact:"moderate",description:`Page provides only ${s} navigation aid${s===1?"":"s"}. WCAG 2.4.5 requires at least TWO ways to locate a page within a set: typically navigation menu + site search, OR navigation + sitemap link. Home pages and search results are exempt.`,helpUrl:`${c}multiple-ways.html`,element:t.documentElement,failureSummary:`Found ${s} of 4 affordances (nav=${i}, search=${n}, sitemap=${o}, toc=${r}).`}),e}function J(t=document){const e=[],a=/\b(window\.location|location\.href|\.submit\s*\(|window\.open|fetch\s*\(|XMLHttpRequest)\b/,i=t.querySelectorAll("[onmousedown], [onpointerdown]");for(const n of Array.from(i)){const o=(n.getAttribute("onmousedown")??"")+";"+(n.getAttribute("onpointerdown")??"");a.test(o)&&e.push({ruleId:"wcc-down-event-fires-action",wcagCriterion:"2.5.2",wcagLevel:"A",impact:"serious",description:"Element's mousedown/pointerdown handler fires a navigation, submit, or network action. WCAG 2.5.2 requires that single-pointer actions execute on UP-EVENT (mouseup, click), not down-event, so users can abort by dragging away. Move the action to the click handler or onmouseup.",helpUrl:`${c}pointer-cancellation.html`,element:n,failureSummary:"Action fires on pointer-down; user cannot abort by dragging away before release."})}return e}function Q(t=document){const e=[],a=t.querySelectorAll("script:not([src])");for(const i of Array.from(a)){const n=i.textContent??"";if(/\b(devicemotion|deviceorientation)\b/.test(n)&&/\baddEventListener\s*\(\s*['"`](devicemotion|deviceorientation)/.test(n)){e.push({ruleId:"wcc-motion-actuation-no-alt",wcagCriterion:"2.5.4",wcagLevel:"A",impact:"serious",description:"Page registers a `devicemotion` or `deviceorientation` listener (shake / tilt to act). WCAG 2.5.4 requires that any function triggered by device motion is ALSO operable via standard UI controls AND can be disabled to prevent accidental actuation. Add an on-screen button equivalent.",helpUrl:`${c}motion-actuation.html`,element:i,failureSummary:"Device-motion listener present. Confirm UI-control alternative + disable mechanism."});break}}return e}function ee(t=document){const e=[],a=t.querySelectorAll("form");for(const i of Array.from(a)){const n=i.querySelectorAll("input[required], select[required], textarea[required]");if(n.length===0||Array.from(n).some(s=>s.hasAttribute("aria-describedby")||s.hasAttribute("aria-invalid")||s.hasAttribute("aria-errormessage")))continue;const r=/\b(required|please|must|\*)\b/i.test(i.textContent??"");e.push({ruleId:"wcc-form-no-aria-error-pattern",wcagCriterion:"3.3.1",wcagLevel:"A",impact:r?"moderate":"serious",description:`Form has ${n.length} required field${n.length===1?"":"s"} but no fields use ARIA error patterns (\`aria-invalid\`, \`aria-describedby\`, \`aria-errormessage\`). WCAG 3.3.1 requires that input errors be identified in text the user can perceive. Browser-default HTML5 validation bubbles aren't reliably announced by all screen readers; wire each required field to a visible error region via aria-describedby and toggle aria-invalid when validation fails.`,helpUrl:`${c}error-identification.html`,element:i,failureSummary:`${n.length} required input(s) without ARIA error pattern.`})}return e}function te(t=document){const e=[],a=t.querySelectorAll("[onkeydown], [onkeypress], [onkeyup]");if(a.length===0)return e;const i=Array.from(a).slice(0,5);for(const n of i)e.push({ruleId:"wcc-inline-key-handler",wcagCriterion:"2.1.4",wcagLevel:"A",impact:"moderate",description:"Element has an inline keyboard event handler (onkeydown / onkeypress / onkeyup). WCAG 2.1.4 requires single-character key shortcuts to be togglable, remappable, or only-on-focus. Verify this handler complies, OR — preferred — replace inline handlers with addEventListener-attached handlers gated by a focused-element check.",helpUrl:`${c}character-key-shortcuts.html`,element:n,failureSummary:"Inline keyboard handler detected — verify shortcuts are togglable / scoped."});return e}function ne(t=document){const e=[];if(t.querySelectorAll('[role="status"], [role="alert"], [role="log"], [aria-live]').length>0)return e;const i=t.querySelector("form input[required], form input[pattern]")!=null,n=t.getElementById("root")!=null||t.getElementById("app")!=null||t.querySelector("[data-reactroot], [data-vue-app]")!=null,o=t.querySelector('[class*="toast" i], [class*="snackbar" i], [class*="notif" i]')!=null;return(i||n||o)&&e.push({ruleId:"wcc-no-live-regions",wcagCriterion:"4.1.3",wcagLevel:"AA",impact:"moderate",description:'Page appears to contain dynamic content (form validation, SPA mount, toast/snackbar container) but has no `role="status"` / `role="alert"` / `aria-live` regions. WCAG 4.1.3 requires status updates that don\'t cause a focus change to be announced via a live region. Add `role="status"` (polite) or `role="alert"` (assertive) to your status / error / toast containers. If your dynamic updates already steal focus, this finding may be N/A.',helpUrl:`${c}status-messages.html`,element:t.documentElement,failureSummary:"No live regions detected on a page that appears to have dynamic content."}),e}const oe=360,ie=10;function re(t=document){if(typeof window>"u"||typeof document>"u")return[];if(window.innerWidth>oe)return[];const e=t.documentElement;if(!e)return[];const a=e.scrollWidth-e.clientWidth;return a<=ie?[]:[{ruleId:"wcc-reflow-horizontal-scroll",wcagCriterion:"1.4.10",wcagLevel:"AA",impact:"serious",description:`Page produces horizontal scroll at narrow viewport (${window.innerWidth}px wide; documentElement.scrollWidth=${e.scrollWidth}px vs clientWidth=${e.clientWidth}px, overflow=${a}px). WCAG 1.4.10 requires content to reflow at 320 CSS pixels without requiring scrolling in two dimensions. Common causes: fixed-width container exceeding the viewport, image without max-width:100%, white-space:nowrap on a long string, table without overflow handling, or a third-party widget that doesn't respect responsive constraints.`,helpUrl:`${c}reflow.html`,element:e,failureSummary:"Horizontal scroll detected at narrow viewport. Find the element producing the overflow (try Chrome DevTools → Elements → 3-dots → Capture full-size screenshot at 320px width) and add max-width / overflow-x:auto / responsive layout to keep content within 320px."}]}function ae(t=document){return[]}function se(t=document){const e=[],a=t.querySelectorAll("form");if(a.length===0)return e;let i=!1,n=!1;for(const o of Array.from(a)){const r=o.querySelectorAll('input[required], input[pattern], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]');if(r.length!==0){i=!0;for(const s of Array.from(r))if(s.getAttribute("aria-describedby")||s.getAttribute("aria-errormessage")||s.getAttribute("list")){n=!0;break}if(n)break}}return!i||n||e.push({ruleId:"wcc-form-no-error-suggestion",wcagCriterion:"3.3.3",wcagLevel:"AA",impact:"moderate",description:"Form has inputs with required / pattern / typed (email / url / tel / number) constraints but none of them wire to a `<datalist>`, `aria-describedby`, or `aria-errormessage`. WCAG 3.3.3 requires error suggestions when corrections are knowable. Add either a `<datalist>` of valid values OR an `aria-describedby` pointing at a visible help / error region with example-correct values.",helpUrl:`${c}error-suggestion.html`,element:a[0],failureSummary:"Form has constrained inputs but no suggestion-infrastructure (datalist / aria-describedby / aria-errormessage)."}),e}function le(t=document){var d;const e=[],a=t.querySelectorAll("form");if(a.length===0)return e;const i=/\b(pay|payment|order|purchase|checkout|charge|transfer|donate|subscribe|agree|sign|delete account|cancel subscription|consent|enroll|legally)\b/i,n=/\b(free|demo|trial|preview|sample)\b/i,o=/\b(review|preview|confirm|verify|are you sure|please confirm|double-check)\b/i,r=/\b(undo|reverse|cancel|edit|update later|change later)\b/i;let s=!1,l=null;for(const p of Array.from(a)){const u=(p.textContent??"")+" "+(p.getAttribute("aria-label")??"");if(!i.test(u)||n.test(u)&&!/\$|usd|eur|gbp/i.test(u))continue;s=!0,l=p;const m=u+" "+(((d=p.closest("section, main, body"))==null?void 0:d.textContent)??"");if(o.test(m)||r.test(m))return e}return s&&e.push({ruleId:"wcc-commit-form-no-review-step",wcagCriterion:"3.3.4",wcagLevel:"AA",impact:"serious",description:"Form appears to commit a legal, financial, or data-modification action (matched keywords: pay / order / purchase / transfer / agree / sign / delete-account / etc.) but no review, confirm, or reversibility step was detected nearby. WCAG 3.3.4 requires ONE of: (a) reversibility (undo / cancel after submit), (b) error-checking with explicit confirmation, OR (c) a review-and-confirm screen before final submit. Add at least one. Heuristic — verify manually if this form is actually a no-commitment lead-capture.",helpUrl:`${c}error-prevention-legal-financial-data.html`,element:l??t.documentElement,failureSummary:"Commit-style form with no detected review/confirm/reversibility cue."}),e}function ce(t=document){const e=[],a=t.querySelectorAll("audio[autoplay], video[autoplay]");if(a.length===0)return e;for(const i of Array.from(a))i.hasAttribute("controls")||i.tagName.toLowerCase()==="video"&&i.hasAttribute("muted")||e.push({ruleId:"wcc-autoplay-no-controls",wcagCriterion:"1.4.2",wcagLevel:"A",impact:"serious",description:`${i.tagName.toLowerCase()} element has \`autoplay\` but no \`controls\` attribute and is not muted. WCAG 1.4.2 (A) requires a mechanism to pause / stop / control volume for any audio playing automatically for more than 3 seconds. EITHER add the \`controls\` attribute, OR add \`muted\` to the element, OR provide a visible custom control adjacent to the media.`,helpUrl:`${c}audio-control.html`,element:i,failureSummary:"Autoplay media without controls — needs pause/stop/volume mechanism."});return e}function ue(t=document){const e=[],a=t.querySelectorAll('header, footer, [class*="banner" i], [class*="cookie" i], [class*="modal" i], [class*="chat" i], [class*="sticky" i], [class*="fixed" i], [class*="overlay" i], [role="banner"], [role="contentinfo"]');for(const i of Array.from(a).slice(0,30)){const n=getComputedStyle(i),o=n.position;if(o!=="sticky"&&o!=="fixed"||n.display==="none"||n.visibility==="hidden"||n.opacity==="0")continue;const r=Number(n.opacity);if(Number.isFinite(r)&&r<=.1)continue;const s=n.zIndex;if(s&&s!=="auto"){const g=Number(s);if(Number.isFinite(g)&&g<0)continue}const l=n.backgroundColor,d=/rgba?\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\s*\)/.exec(l);if((d?Number(d[1]):1)<.5&&!n.backgroundImage.match(/url\(/))continue;const u=i.getBoundingClientRect();if(u.width===0||u.height===0)continue;const m=window.innerHeight||document.documentElement.clientHeight,f=u.top<80&&u.bottom>0,b=u.bottom>m-80&&u.top<m;if(!f&&!b)continue;const C=getComputedStyle(document.documentElement),w=t.body?getComputedStyle(t.body):null;if(f){const g=parseFloat(C.scrollPaddingTop)||0,A=w&&parseFloat(w.scrollPaddingTop)||0;if(Math.max(g,A)>=u.height-2)continue}if(b){const g=parseFloat(C.scrollPaddingBottom)||0,A=w&&parseFloat(w.scrollPaddingBottom)||0;if(Math.max(g,A)>=u.height-2)continue}e.push({ruleId:"wcc-sticky-may-obscure-focus",wcagCriterion:"2.4.11",wcagLevel:"AA",impact:"moderate",description:`Element is ${o}-positioned and overlaps the viewport's ${f?"top":"bottom"} edge with an opaque background, and we did NOT detect a sufficient ${f?"`scroll-padding-top`":"`scroll-padding-bottom`"} on \`html\` / \`body\` to push focused elements out from under it. WCAG 2.4.11 (AA, WCAG 2.2) requires that focused components NOT be entirely hidden by such overlays. Common fixes (any one is sufficient): (1) \`${f?"scroll-padding-top":"scroll-padding-bottom"}: ${Math.ceil(u.height)}px\` on the scroll container (\`html\` or \`body\`) — the wcagcheckr rule detects this automatically and will stop firing once present; (2) \`${f?"scroll-margin-top":"scroll-margin-bottom"}\` on each focusable element below the overlay; (3) a JS focus-aware reveal that hides the sticky overlay when focus lands beneath it (this one the rule CANNOT detect; mark this finding as acknowledged in the per-page panel once it's in place).`,helpUrl:`${c}focus-not-obscured-minimum.html`,element:i,failureSummary:`${o} element overlapping viewport edge — verify focus visibility.`})}return e}function de(t=document){const e=[],a=t.querySelectorAll("form");if(a.length===0)return e;const i=/\b(step\s*\d|wizard|stage\s*\d|page\s*\d\s*of\s*\d|continue|next|previous|back to (cart|review))\b/i,n='[role="progressbar"], [class*="step" i], [class*="wizard" i], [class*="progress" i]';let o=!1;for(const r of Array.from(a)){const s=r.textContent??"";if(i.test(s)){o=!0;break}}return!o&&t.querySelectorAll(n).length>0&&(o=!0),o&&e.push({ruleId:"wcc-redundant-entry-needs-site-crawl",wcagCriterion:"3.3.7",wcagLevel:"A",impact:"minor",description:'This page appears to be part of a multi-step flow (step indicators, "continue"/"next" cues, progress markers). WCAG 3.3.7 (A, WCAG 2.2) requires that previously-entered information be auto-populated or selectable across steps — except when re-entry is essential (password confirmation, security). A single-page audit cannot verify cross-step redundancy. Run the Site Crawl flow on the full step sequence, then re-export — the crawler will compare field names across pages and flag duplicates.',helpUrl:`${c}redundant-entry.html`,element:t.documentElement,failureSummary:"Multi-step flow signals detected — needs site-crawl coverage."}),e}function me(t=document){return[]}const pe=/\b(click|tap|press|select|find|see|hear|use|choose) (?:the |a |an )?(red|orange|yellow|green|blue|purple|pink|black|white|round|square|triangular|left|right|top|bottom|upper|lower) (button|link|icon|tab|box|arrow|menu|control|element|item)\b/gi,he=/\b(listen for|hear) (?:the |a |an )?(chime|beep|click|ding|sound|tone)\b/gi;function fe(t=document){const e=[],a=t.body;if(!a)return e;const i=(a.innerText||a.textContent||"").slice(0,5e4);if(i.trim().length<40)return e;const n=new Set;for(const o of[pe,he]){o.lastIndex=0;let r;for(;(r=o.exec(i))!==null;){const s=r[0].toLowerCase().trim();if(!n.has(s)){if(n.add(s),n.size>5)break;e.push({ruleId:"wcc-sensory-only-directive",wcagCriterion:"1.3.3",wcagLevel:"A",impact:"moderate",description:`Found an instruction that appears to rely on sensory characteristics alone: "${s}". WCAG 1.3.3 requires that directions to users NOT depend solely on color, shape, size, position, orientation, or sound — there must also be a text alternative ("click the Submit button" rather than just "click the green button"). Verify the instruction includes a non-sensory cue (text label, ordinal position, accessible name). If the page provides additional context elsewhere, this can be marked verified.`,helpUrl:`${c}sensory-characteristics.html`,element:a,failureSummary:`Sensory-only directive phrase: "${s}"`})}}}return e}const ge=/\b(logo|icon|avatar|photo|portrait|headshot|illustration|chart|graph|diagram|map|screenshot|thumbnail)\b/i;function be(t=document){const e=[];let a=0;const i=t.querySelectorAll("img[alt]");for(const o of Array.from(i)){if(a>=5)break;const r=(o.getAttribute("alt")??"").trim();if(r.length<40||ge.test(r))continue;const s=/[a-z][.,;:]\s+[A-Z]/.test(r),l=(r.match(/\b[A-Z][a-z]+/g)??[]).length;!s&&l<4||(a++,e.push({ruleId:"wcc-image-of-text-candidate",wcagCriterion:"1.4.5",wcagLevel:"AA",impact:"moderate",description:`<img> alt text reads like prose content ("${r.slice(0,80)}${r.length>80?"…":""}"), suggesting the image may be RENDERING text instead of describing a non-text image. WCAG 1.4.5 prefers actual text (HTML + CSS) over text-baked-into-images so users can resize / restyle it. Common exceptions: logos, charts where the visual IS the information, screenshots of UI. If this image is one of those, mark verified.`,helpUrl:`${c}images-of-text.html`,element:o,failureSummary:`<img alt="${r.slice(0,60)}${r.length>60?"…":""}"> — long sentence-like alt suggests image-of-text.`}))}const n=t.querySelectorAll("svg");for(const o of Array.from(n)){if(a>=8)break;const r=o.querySelectorAll("text");if(r.length===0)continue;let s=0;for(const l of Array.from(r))s+=(l.textContent??"").trim().length;s<20||(a++,e.push({ruleId:"wcc-svg-text-as-content",wcagCriterion:"1.4.5",wcagLevel:"AA",impact:"minor",description:`<svg> contains <text> elements totaling ${s} characters. Stylized SVG text often substitutes for HTML text + CSS; users can't resize / restyle it the same way. Confirm the SVG text is genuinely graphical (chart axis labels, decorative logo text) rather than substantive content that should be HTML.`,helpUrl:`${c}images-of-text.html`,element:o,failureSummary:`SVG with ${r.length} <text> element(s), ${s} chars total.`}))}return e}function ye(t=document){const e=[];let a=0;const i=t.querySelectorAll("div, section, article, p, span, li, td, th, button, a");for(const n of Array.from(i)){if(a>=5)break;let o="";for(const u of n.childNodes)u.nodeType===Node.TEXT_NODE&&(o+=(u.textContent??"").trim());if(o.length<20)continue;const r=getComputedStyle(n);if(r.overflow!=="hidden"&&r.overflowY!=="hidden")continue;const s=n.style.height||r.height||"";if(!/\d+(?:px|rem|em|%|vh)/.test(s))continue;const d=n.getBoundingClientRect(),p=parseFloat(r.lineHeight||"0")||parseFloat(r.fontSize||"16");d.height<=p*1.6||(a++,e.push({ruleId:"wcc-text-spacing-clip-risk",wcagCriterion:"1.4.12",wcagLevel:"AA",impact:"minor",description:`Element has fixed height + overflow: hidden, with text content (${o.slice(0,60)}${o.length>60?"…":""}). WCAG 1.4.12 requires content remain visible when users increase line-height (1.5), letter-spacing (0.12em), word-spacing (0.16em), and paragraph-spacing (2× font-size). Fixed-height containers clip content as spacing grows. Either remove the fixed height, replace with min-height, or set overflow to visible. Strict verification requires CSS-injection probe (see WCAG technique C36).`,helpUrl:`${c}text-spacing.html`,element:n,failureSummary:"Fixed-height + overflow:hidden text container — likely clips under user spacing overrides."}))}return e}function k(t){if(t.id)return`#${t.id}`;const e=t.classList[0];return`${t.tagName.toLowerCase()}${e?"."+e:""}`}function we(t=document){return[...M(t),..._(t),...B(t),...j(t),...V(t),...K(t),...Y(t),...X(t),...Z(t),...J(t),...Q(t),...ee(t),...te(t),...ne(t),...me(t),...ae(t),...se(t),...re(t),...le(t),...ce(t),...ue(t),...de(t),...U(t),...O(t),...fe(t),...be(t),...ye(t),...D(t)]}const Ae=["2.2.1","2.2.2","2.3.1","2.5.1","2.5.7","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","3.2.1","3.2.2","3.3.8","1.3.4","1.4.4","1.4.13","2.4.5","2.5.2","2.5.4","3.3.1","2.1.4","4.1.3","4.1.1","3.3.3","1.4.10","3.3.4","1.4.2","2.4.11","3.3.7","1.3.3","1.4.5","1.4.12"];export{Ae as DOM_ANALYZER_COVERED_CRITERIA,_ as analyzeAnimation,ce as analyzeAudioControl,te as analyzeCharacterKeyShortcuts,K as analyzeCognitiveAuth,ae as analyzeConsistencyHints,X as analyzeContentOnHover,V as analyzeContextChangeOnFocusInput,ee as analyzeErrorIdentification,le as analyzeErrorPreventionLegalFinancial,se as analyzeErrorSuggestion,ue as analyzeFocusNotObscured,be as analyzeImagesOfText,j as analyzeMedia,Q as analyzeMotionActuation,Z as analyzeMultipleWays,Y as analyzeOrientation,me as analyzeParsingObsoleted,J as analyzePointerCancellation,B as analyzePointerGestures,de as analyzeRedundantEntry,re as analyzeReflow,fe as analyzeSensoryCharacteristics,ne as analyzeStatusMessages,ye as analyzeTextSpacing,M as analyzeTimingAdjustable,H as hasReducedMotionMitigation,we as runAllDomCriterionAnalyzers};
|
|
1
|
+
const I=/\b(cookie|cookies|consent|gdpr|privacy|tracker|analytics|preference)s?\b/i,$=/\b(accept|agree|allow|got it|ok|okay|continue|confirm|accept all|allow all)\b/i,L=/\b(reject|decline|deny|no thanks|cancel|disagree|reject all|deny all)\b/i,y="https://www.w3.org/WAI/WCAG21/Understanding/";function x(t){let e=0;const a=window.getComputedStyle(t),r=a.position;if(r!=="fixed"&&r!=="sticky")return 0;const n=parseInt(a.zIndex,10);Number.isFinite(n)&&n>=100&&(e+=2),Number.isFinite(n)&&n>=1e3&&(e+=1);const o=(t.textContent??"").slice(0,2e3);I.test(o)&&(e+=3),$.test(o)&&(e+=1),L.test(o)&&(e+=1);const s=t.getBoundingClientRect();return s.width>=100&&s.height>=40&&(e+=1),a.display==="none"||a.visibility==="hidden"||t.getAttribute("aria-hidden")==="true"?0:e}function E(t=document){const e=[],a=t.querySelectorAll('[class*="cookie" i], [class*="consent" i], [id*="cookie" i], [id*="consent" i], dialog[open], div[role="dialog"], div[role="alertdialog"]');for(const o of a){const s=x(o);s>0&&e.push({el:o,score:s})}const r=t.querySelectorAll("body *");for(const o of r){if(e.some(u=>u.el===o))continue;const s=window.getComputedStyle(o);if(s.position!=="fixed"&&s.position!=="sticky")continue;const i=(o.textContent??"").slice(0,2e3);if(!I.test(i))continue;const l=x(o);l>0&&e.push({el:o,score:l})}if(e.length===0)return null;e.sort((o,s)=>s.score-o.score);const n=e[0];return n.score>=5?n.el:null}function v(t){var n,o,s,i;const e=(n=t.getAttribute("aria-label"))==null?void 0:n.trim();if(e)return e;const a=t.getAttribute("aria-labelledby");if(a){const l=(o=t.ownerDocument)==null?void 0:o.getElementById(a),u=(s=l==null?void 0:l.textContent)==null?void 0:s.trim();if(u)return u}const r=(i=t.getAttribute("title"))==null?void 0:i.trim();return r||(t.textContent??"").trim()}function R(t){const e=t.tagName;if(e==="BUTTON"||e==="A"||e==="INPUT"||e==="SELECT"||e==="TEXTAREA")return!0;const a=t.getAttribute("role");if(a==="button"||a==="link"||a==="checkbox"||a==="radio"||a==="switch")return!0;const r=t.getAttribute("tabindex");return r!==null&&parseInt(r,10)>=0}function T(t){const e=[],a=Array.from(t.querySelectorAll('button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [tabindex]'));for(const i of a){if(!R(i)||i.type==="hidden")continue;const l=v(i);(!l||l.length<2)&&e.push({ruleId:"cookie-banner-button-name",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:"Cookie banner: interactive element has no accessible name. Screen-reader users cannot tell what this button does (accept, reject, manage preferences, etc.).",helpUrl:`${y}name-role-value`,element:i,failureSummary:'Add an aria-label, visible text content, or aria-labelledby to label this control. "Accept all cookies" / "Reject all cookies" / "Manage preferences" are the conventional labels.'})}a.some(i=>{const l=v(i);return $.test(l)})||e.push({ruleId:"cookie-banner-no-accept",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"moderate",description:'Cookie banner: no clearly-labeled "accept" control found. Users (especially with cognitive disabilities or screen-reader users skimming labels) need a consistent, recognizable label.',helpUrl:`${y}consistent-identification`,element:t,failureSummary:'Add a button with text or aria-label containing "Accept" / "Allow" / "Agree" so the consent action is identifiable from the label alone.'}),a.some(i=>{const l=v(i);return L.test(l)})||e.push({ruleId:"cookie-banner-no-reject",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"serious",description:'Cookie banner: no clearly-labeled "reject all" control found. GDPR Art. 7 requires consent withdrawal to be as easy as consent giving; for accessibility this also means the reject control must be discoverable from its label, not buried in a "manage preferences" sub-flow.',helpUrl:`${y}consistent-identification`,element:t,failureSummary:'Add a button with text or aria-label containing "Reject" / "Decline" / "Reject all" alongside the accept control at the same hierarchy level.'});const o=t.getAttribute("role");t.tagName==="DIALOG"||o==="dialog"||o==="alertdialog"||e.push({ruleId:"cookie-banner-no-dialog-role",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:"Cookie banner: no dialog semantics. Screen-reader users will not be alerted that an interrupting modal is on-screen; keyboard users do not get the standard Escape-to-dismiss affordance.",helpUrl:`${y}info-and-relationships`,element:t,failureSummary:'Use <dialog open> or role="dialog" / role="alertdialog" plus aria-labelledby pointing to the banner heading. With the dialog role, screen readers announce "dialog: <name>" when the banner mounts.'});for(const i of a){const l=i.getAttribute("tabindex");l&&parseInt(l,10)<0&&e.push({ruleId:"cookie-banner-unreachable-control",wcagCriterion:"2.1.1",wcagLevel:"A",impact:"critical",description:"Cookie banner: interactive control has tabindex < 0 so keyboard users cannot reach it. If this is the accept/reject button, consent cannot be given without a mouse.",helpUrl:`${y}keyboard`,element:i,failureSummary:'Remove the negative tabindex, or replace with tabindex="0" so the control participates in the keyboard tab order.'})}return e}function U(t=document){const e=E(t);return e?T(e):[]}const h="https://www.w3.org/WAI/ARIA/apg/patterns/";function W(t){var n,o,s,i;const e=(n=t.getAttribute("aria-label"))==null?void 0:n.trim();if(e)return e;const a=t.getAttribute("aria-labelledby");if(a){const l=(o=t.ownerDocument)==null?void 0:o.getElementById(a),u=(s=l==null?void 0:l.textContent)==null?void 0:s.trim();if(u)return u}const r=(i=t.getAttribute("title"))==null?void 0:i.trim();return r||""}function P(t=document){const e=[],a=Array.from(t.querySelectorAll('[role="dialog"], [role="alertdialog"], dialog[open]'));for(const r of a){const n=r.getAttribute("role")??(r.tagName==="DIALOG"?"dialog":"");W(r)||e.push({ruleId:"aria-pattern-dialog-no-name",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:`${n} has no accessible name. Screen readers will announce "dialog" with no label, leaving the user unable to identify what's open.`,helpUrl:`${h}dialog-modal/`,element:r,failureSummary:'Add aria-labelledby pointing to the dialog heading, or aria-label with a short title (e.g. aria-labelledby="dlg-title" + <h2 id="dlg-title">Confirm deletion</h2>).'});const o=r.getAttribute("aria-modal");!(r.tagName==="DIALOG")&&o!=="true"&&o!=="false"&&e.push({ruleId:"aria-pattern-dialog-no-modal-attr",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"moderate",description:`${n} has no aria-modal attribute. If this dialog blocks the rest of the page, set aria-modal="true" so assistive technology can mute the background. If it's non-modal, set aria-modal="false" explicitly.`,helpUrl:`${h}dialog-modal/`,element:r,failureSummary:'Set aria-modal="true" on modal dialogs (most common). Native <dialog open> handles this automatically; custom div-based dialogs need the attribute.'})}return e}function N(t=document){const e=[],a=Array.from(t.querySelectorAll('[role="tablist"]'));for(const r of a){const n=Array.from(r.querySelectorAll('[role="tab"]'));if(n.length===0){e.push({ruleId:"aria-pattern-tablist-no-tabs",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:'role="tablist" contains no role="tab" descendants. Screen readers announce a tab interface but provide nothing to navigate.',helpUrl:`${h}tabs/`,element:r,failureSummary:'Every tablist must directly contain role="tab" elements as children. Restructure the markup so the tabs are descendants of the tablist.'});continue}const o=n.filter(s=>s.getAttribute("aria-selected")==="true").length;o===0?e.push({ruleId:"aria-pattern-tabs-no-selected",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:'No tab in this tablist has aria-selected="true". Screen-reader users hear all tabs as inactive, with no indication which panel is currently shown.',helpUrl:`${h}tabs/`,element:r,failureSummary:'Exactly one tab must have aria-selected="true" at any time. Update the active tab when the user picks a new one.'}):o>1&&e.push({ruleId:"aria-pattern-tabs-multiple-selected",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:`${o} tabs have aria-selected="true" at once. Only one tab in a tablist may be selected at a time.`,helpUrl:`${h}tabs/`,element:r,failureSummary:"Clear aria-selected on all other tabs when one is activated. Use the keyboard-handler that updates aria-selected to also clear the previously-active tab."});for(const s of n){const i=s.getAttribute("aria-controls");if(!i){e.push({ruleId:"aria-pattern-tab-no-controls",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:'role="tab" has no aria-controls pointing to its tabpanel. Programmatic relationship between tab and panel is missing.',helpUrl:`${h}tabs/`,element:s,failureSummary:'Set aria-controls to the id of the corresponding tabpanel: <button role="tab" aria-controls="panel-1">Tab 1</button> + <div role="tabpanel" id="panel-1">…</div>'});continue}const l=t.getElementById(i);l?l.getAttribute("role")!=="tabpanel"&&e.push({ruleId:"aria-pattern-tab-wrong-target-role",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:`role="tab" aria-controls target has role="${l.getAttribute("role")??"(none)"}" instead of role="tabpanel".`,helpUrl:`${h}tabs/`,element:s,failureSummary:'The element a tab controls must have role="tabpanel".'}):e.push({ruleId:"aria-pattern-tab-broken-controls",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:`role="tab" aria-controls="${i}" points to an id that doesn't exist on the page.`,helpUrl:`${h}tabs/`,element:s,failureSummary:`Add an element with id="${i}" + role="tabpanel" containing the tab's content, or fix the aria-controls value to match an existing panel.`})}}return e}function z(t=document){const e=[],a=Array.from(t.querySelectorAll('[role="combobox"]'));for(const r of a){const n=r.getAttribute("aria-expanded");n===null?e.push({ruleId:"aria-pattern-combobox-no-expanded",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"serious",description:`role="combobox" has no aria-expanded attribute. Screen-reader users can't tell whether the popup is open.`,helpUrl:`${h}combobox/`,element:r,failureSummary:'Set aria-expanded="false" when the popup is closed (default) and aria-expanded="true" when open. Toggle on user interaction.'}):n!=="true"&&n!=="false"&&e.push({ruleId:"aria-pattern-combobox-invalid-expanded",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"moderate",description:`role="combobox" aria-expanded="${n}" is not a valid value. Must be "true" or "false".`,helpUrl:`${h}combobox/`,element:r,failureSummary:'Use aria-expanded="true" or aria-expanded="false" (lowercase, exact match).'});const o=r.getAttribute("aria-controls")||r.getAttribute("aria-owns");if(!o)e.push({ruleId:"aria-pattern-combobox-no-popup",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:'role="combobox" has no aria-controls (or aria-owns) pointing to the popup container.',helpUrl:`${h}combobox/`,element:r,failureSummary:"Set aria-controls to the id of the listbox / tree / grid / dialog that opens. The popup container needs the corresponding role."});else{const s=t.getElementById(o);if(!s)e.push({ruleId:"aria-pattern-combobox-broken-popup",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"serious",description:`role="combobox" aria-controls="${o}" points to an id that doesn't exist.`,helpUrl:`${h}combobox/`,element:r,failureSummary:`Add the popup element with id="${o}" and the appropriate role (listbox, tree, grid, or dialog).`});else{const i=s.getAttribute("role");(!i||!new Set(["listbox","tree","grid","dialog"]).has(i))&&e.push({ruleId:"aria-pattern-combobox-wrong-popup-role",wcagCriterion:"1.3.1",wcagLevel:"A",impact:"moderate",description:`role="combobox" popup target has role="${i??"(none)"}". Must be listbox, tree, grid, or dialog per APG.`,helpUrl:`${h}combobox/`,element:r,failureSummary:'Set role="listbox" on the popup container (most common). Use tree / grid / dialog only when the popup is structured that way.'})}}if(n==="true"){const s=r.getAttribute("aria-activedescendant");s&&!t.getElementById(s)&&e.push({ruleId:"aria-pattern-combobox-broken-activedescendant",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"moderate",description:`role="combobox" aria-activedescendant="${s}" references an id that doesn't exist.`,helpUrl:`${h}combobox/`,element:r,failureSummary:"Update aria-activedescendant to the id of the currently-highlighted option, or remove it and rely on DOM focus."})}}return e}function O(t=document){return[...P(t),...N(t),...z(t)]}const S="https://www.w3.org/WAI/WCAG21/Understanding/non-text-content";function G(t){const e=t.getAttribute("href")??"";return!!(/\.pdf(\?|#|$)/i.test(e)||t.getAttribute("type")==="application/pdf")}function F(t){const e=(t.textContent??"").toLowerCase();return!!(/\bpdf\b/.test(e)||/\(pdf/i.test(t.getAttribute("aria-label")??"")||t.querySelector('[aria-label*="PDF" i], [aria-label*="pdf" i], [title*="PDF" i]'))}function D(t=document){const e=[],a=Array.from(t.querySelectorAll("a[href]"));for(const r of a){if(!G(r))continue;const n=r.getAttribute("href")??"";e.push({ruleId:"pdf-needs-cli-audit",wcagCriterion:"1.1.1",wcagLevel:"A",impact:"moderate",description:`Linked PDF at ${n}. PDF accessibility cannot be verified from a browser audit — run \`wcagcheckr-ci pdf ${n}\` (or pass the local file path) to check tagging, language, title, and structure tree.`,helpUrl:S,element:r,failureSummary:"PDFs are a separate accessibility document type with their own structural requirements (PDF/UA-1, WCAG via EN 301 549 §10). The wcagcheckr CLI runs a metadata audit on the PDF binary; see PDF column in the deposition packet for the result."}),F(r)||e.push({ruleId:"pdf-link-no-format-hint",wcagCriterion:"2.4.4",wcagLevel:"A",impact:"serious",description:`Link points to a PDF but the visible label gives no "(PDF)" hint. Screen-reader users hear the link text and won't know the activation will download a document.`,helpUrl:S,element:r,failureSummary:'Add "(PDF)" to the link text, or use an aria-label that includes "PDF" / "PDF document". Bonus: include the file size — "Annual report (PDF, 2.4 MB)".'})}return e}const c="https://www.w3.org/WAI/WCAG21/Understanding/";function H(t=document){const e=[],a=t.querySelectorAll('meta[http-equiv="refresh" i]');for(const n of Array.from(a)){const o=n.getAttribute("content")??"",s=parseInt(o.split(";")[0]??"",10);Number.isFinite(s)&&s>0&&e.push({ruleId:"wcc-meta-refresh",wcagCriterion:"2.2.1",wcagLevel:"A",impact:"serious",description:`Page uses <meta http-equiv="refresh"> to auto-refresh after ${s}s. WCAG 2.2.1 requires that users can turn off, adjust, or extend any time limit. Auto-refresh without user control fails this criterion.`,helpUrl:`${c}timing-adjustable.html`,element:n,failureSummary:`Auto-refresh interval: ${s}s. No user control mechanism on the page.`})}const r=t.querySelectorAll("script:not([src])");for(const n of Array.from(r)){const o=n.textContent??"";o.length===0||!/\b(setTimeout|setInterval)\s*\(/.test(o)||!/\b(innerHTML|textContent|replaceChildren|window\.location|document\.location|location\.href|location\.replace)\b/.test(o)||e.push({ruleId:"wcc-timer-mutates-content",wcagCriterion:"2.2.1",wcagLevel:"A",impact:"moderate",description:"Inline script uses setTimeout/setInterval to mutate page content. WCAG 2.2.1 requires that users can turn off, adjust, or extend any time-limited content change. Verify a pause / extend / disable mechanism exists in the UI.",helpUrl:`${c}timing-adjustable.html`,element:n,failureSummary:"Script combines timer + DOM mutation. Without a user control to pause/extend/disable, fails 2.2.1."})}return e}function M(t=document){var e,a;try{const r=t.styleSheets;for(let n=0;n<r.length;n++){const o=r[n];if(!o)continue;let s=null;try{s=o.cssRules}catch{continue}if(s)for(let i=0;i<s.length;i++){const l=s[i];if(l&&l.constructor&&l.constructor.name==="CSSMediaRule"){const u=l,p=u.conditionText??((e=u.media)==null?void 0:e.mediaText)??"";if(!/prefers-reduced-motion\s*:\s*reduce/i.test(p))continue;for(let d=0;d<u.cssRules.length;d++){const m=u.cssRules[d];if(m&&m.constructor&&m.constructor.name==="CSSStyleRule"){const f=((a=m.style)==null?void 0:a.cssText)??"";if(/animation|transition/i.test(f))return!0}}}}}}catch{}return!1}function _(t=document){const e=[],a=M(t),r=t.querySelectorAll("marquee, blink");for(const i of Array.from(r))e.push({ruleId:"wcc-marquee-blink",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"serious",description:`<${i.tagName.toLowerCase()}> creates continuous motion with no built-in pause control. WCAG 2.2.2 requires a way to pause, stop, or hide auto-moving content.`,helpUrl:`${c}pause-stop-hide.html`,element:i,failureSummary:`Deprecated <${i.tagName.toLowerCase()}> element auto-animates without pause control.`});const n=t.querySelectorAll("video[autoplay]");for(const i of Array.from(n))!i.hasAttribute("controls")&&!i.muted&&e.push({ruleId:"wcc-autoplay-no-controls",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"serious",description:"<video autoplay> without controls attribute (or muted) creates moving content the user cannot pause. WCAG 2.2.2 requires a pause / stop / hide mechanism.",helpUrl:`${c}pause-stop-hide.html`,element:i,failureSummary:"Autoplay video lacks user-accessible pause control."});const o=t.querySelectorAll("*");let s=0;for(const i of Array.from(o)){if(s>1500)break;s++;const l=window.getComputedStyle(i);if(l.animationName==="none"||!l.animationName)continue;const u=l.animationDuration.split(",").map(d=>parseFloat(d)),p=l.animationIterationCount.split(",").map(d=>d.trim());for(let d=0;d<u.length;d++){const m=u[d]??0,b=(p[d]??"1")==="infinite";b&&m>0&&m<.34&&e.push({ruleId:"wcc-flash-risk",wcagCriterion:"2.3.1",wcagLevel:"A",impact:"critical",description:`Element animates with duration ${m.toFixed(2)}s on infinite repeat — that's > 3 cycles/sec, which can trigger photosensitive seizures. WCAG 2.3.1 prohibits content that flashes more than 3 times per second unless the flashing area is below threshold.`,helpUrl:`${c}three-flashes-or-below-threshold.html`,element:i,failureSummary:`Infinite CSS animation @ ${m.toFixed(2)}s/cycle = ${(1/m).toFixed(1)} flashes/sec.`}),b&&m>=5&&!a&&e.push({ruleId:"wcc-long-infinite-animation",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"moderate",description:`Element runs an infinite CSS animation (${m.toFixed(1)}s/cycle). WCAG 2.2.2 requires a way to pause, stop, or hide animation longer than 5 seconds. The canonical mitigation is a \`@media (prefers-reduced-motion: reduce)\` block that disables or shortens the animation — the wcagcheckr rule detects this automatically and stops firing once present. Alternative: a visible pause button.`,helpUrl:`${c}pause-stop-hide.html`,element:i,failureSummary:`Infinite animation @ ${m.toFixed(1)}s/cycle. Confirm user can pause.`})}}return e}function B(t=document){const e=[],a=new Set,r=t.querySelectorAll("[ontouchstart], [ontouchmove], [ongesturestart], [ongesturechange]");for(const o of Array.from(r)){const s=k(o);a.has(s)||(a.add(s),e.push({ruleId:"wcc-gesture-no-alternative",wcagCriterion:"2.5.1",wcagLevel:"A",impact:"serious",description:"Element handles multi-point or path-based gestures (touch/gesture events). WCAG 2.5.1 requires that all functionality operable with a path-based or multi-point gesture is ALSO operable with a single-pointer activation. Verify a button / link / single-tap alternative exists.",helpUrl:`${c}pointer-gestures.html`,element:o,failureSummary:"Touch/gesture handler on this element. Confirm single-pointer alternative."}))}const n=t.querySelectorAll('[draggable="true"], [ondragstart]');for(const o of Array.from(n)){const s=k(o);a.has(s)||(a.add(s),e.push({ruleId:"wcc-drag-no-alternative",wcagCriterion:"2.5.7",wcagLevel:"AA",impact:"serious",description:'Element uses HTML5 drag-and-drop (`draggable="true"` or `ondragstart`). WCAG 2.5.7 requires that any function achieved via a dragging movement is ALSO operable by a single-pointer action (click, button, arrow keys). Confirm an alternative exists.',helpUrl:`${c}dragging-movements.html`,element:o,failureSummary:"Drag-and-drop interaction. Confirm non-drag alternative for keyboard / touch users."}))}return e}function j(t=document){const e=[],a=t.querySelectorAll("video");for(const n of Array.from(a)){const o=Array.from(n.querySelectorAll("track")),s=o.some(l=>(l.kind??"").toLowerCase()==="captions"||(l.kind??"").toLowerCase()==="subtitles"),i=o.some(l=>(l.kind??"").toLowerCase()==="descriptions");s||e.push({ruleId:"wcc-video-no-captions",wcagCriterion:"1.2.2",wcagLevel:"A",impact:"serious",description:'<video> element without `<track kind="captions">` (or `subtitles`). WCAG 1.2.2 requires captions for all prerecorded audio content in synchronized media. If this video has no audio, mark this finding as not-applicable.',helpUrl:`${c}captions-prerecorded.html`,element:n,failureSummary:'No <track kind="captions"> child. Captions required unless video is silent.'}),i||(e.push({ruleId:"wcc-video-no-descriptions",wcagCriterion:"1.2.5",wcagLevel:"AA",impact:"moderate",description:'<video> element without `<track kind="descriptions">`. WCAG 1.2.5 (AA) requires audio description of important visual content in prerecorded video. If all video content is conveyed by the audio track alone, mark this finding as not-applicable.',helpUrl:`${c}audio-description-prerecorded.html`,element:n,failureSummary:'No <track kind="descriptions"> child. Audio description required when visuals carry info not in audio.'}),e.push({ruleId:"wcc-video-no-audio-desc-or-alt",wcagCriterion:"1.2.3",wcagLevel:"A",impact:"serious",description:'<video> element with no audio description track AND no obvious text-alternative link nearby. WCAG 1.2.3 (A) requires EITHER audio description OR a full text alternative for prerecorded video content. Add a <track kind="descriptions"> OR link a transcript/synopsis adjacent to the video.',helpUrl:`${c}audio-description-or-media-alternative-prerecorded.html`,element:n,failureSummary:'No <track kind="descriptions"> and no detectable text alternative. Need at least one.'}))}const r=t.querySelectorAll("audio");for(const n of Array.from(r))e.push({ruleId:"wcc-audio-needs-transcript",wcagCriterion:"1.2.1",wcagLevel:"A",impact:"moderate",description:"<audio> element present. WCAG 1.2.1 requires a text alternative (transcript) for prerecorded audio-only content. Confirm a transcript is linked nearby.",helpUrl:`${c}audio-only-and-video-only-prerecorded.html`,element:n,failureSummary:"Audio element detected. Verify nearby transcript link."});return e}function V(t=document){const e=[],a=/\b(window\.location|document\.location|location\.href|location\.replace|window\.open|\.submit\s*\()/,r=t.querySelectorAll("[onfocus]");for(const o of Array.from(r)){const s=o.getAttribute("onfocus")??"";a.test(s)&&e.push({ruleId:"wcc-onfocus-context-change",wcagCriterion:"3.2.1",wcagLevel:"A",impact:"serious",description:"Element's `onfocus` handler causes a context change (navigation, submit, popup). WCAG 3.2.1 forbids context changes triggered solely by focus — they disorient keyboard and screen-reader users who land on the element while scanning.",helpUrl:`${c}on-focus.html`,element:o,failureSummary:"Focus handler navigates / submits / opens window. Move trigger to explicit click/keypress."})}const n=t.querySelectorAll("select[onchange], input[onchange], textarea[onchange]");for(const o of Array.from(n)){const s=o.getAttribute("onchange")??"";a.test(s)&&e.push({ruleId:"wcc-oninput-context-change",wcagCriterion:"3.2.2",wcagLevel:"A",impact:"serious",description:"Form control's `onchange` handler causes a context change without explicit user request. WCAG 3.2.2 requires that changing a setting in a form control does NOT automatically navigate or submit — the user must explicitly confirm via a button.",helpUrl:`${c}on-input.html`,element:o,failureSummary:"Change handler navigates / submits. Replace with a separate submit button."})}return e}function K(t=document){const e=[],a=[".g-recaptcha","[data-sitekey]",".h-captcha",".cf-turnstile",".frc-captcha",'iframe[src*="recaptcha"]','iframe[src*="hcaptcha"]','iframe[src*="turnstile"]'].join(", "),r=t.querySelectorAll(a),n=new Set;for(const o of Array.from(r)){if(n.has(o))continue;n.add(o);const s=o.closest("form"),i=((s==null?void 0:s.textContent)??"").toLowerCase(),l=/audio captcha|alternative|sign in with|continue with|use email|magic link/.test(i);e.push({ruleId:"wcc-cognitive-auth-challenge",wcagCriterion:"3.3.8",wcagLevel:"AA",impact:l?"moderate":"serious",description:l?"CAPTCHA / cognitive-function challenge detected. An alternative authentication mechanism appears available — verify it actually bypasses the CAPTCHA. WCAG 3.3.8 requires that the alternative not also require a cognitive function test.":'CAPTCHA / cognitive-function challenge detected with no apparent alternative. WCAG 3.3.8 requires an alternative authentication mechanism that does NOT depend on a cognitive function test (recognizing distorted text, solving puzzles, identifying objects in images, etc.). Add a "Sign in with [SSO]" or magic-link option.',helpUrl:`${c}accessible-authentication-minimum.html`,element:o,failureSummary:l?"CAPTCHA detected; alternative may exist — verify it skips the cognitive challenge.":"CAPTCHA detected without an alternative authentication path."})}return e}function X(t=document){const e=[],a=t.querySelectorAll('meta[name="viewport" i]');for(const n of Array.from(a)){const o=n.getAttribute("content")??"";(/user-scalable\s*=\s*(no|0)\b/i.test(o)||/maximum-scale\s*=\s*1(\.0+)?\b/i.test(o))&&e.push({ruleId:"wcc-viewport-locks-zoom",wcagCriterion:"1.4.4",wcagLevel:"AA",impact:"serious",description:'<meta name="viewport"> disables user zoom (`user-scalable=no` or `maximum-scale=1`). WCAG 1.4.4 requires that users can scale text up to 200% without loss of content or functionality. Remove the zoom-disabling tokens.',helpUrl:`${c}resize-text.html`,element:n,failureSummary:"Viewport meta disables zoom. Remove user-scalable=no and maximum-scale<2."})}let r=0;for(const n of Array.from(t.styleSheets)){if(r>40)break;r++;try{const o=n.cssRules;if(!o)continue;for(const s of Array.from(o)){if(!(s instanceof CSSMediaRule))continue;const i=s.media.mediaText.toLowerCase(),l=/\(\s*orientation\s*:\s*portrait\s*\)/.test(i),u=/\(\s*orientation\s*:\s*landscape\s*\)/.test(i);if(!(!l&&!u)){for(const p of Array.from(s.cssRules))if(p instanceof CSSStyleRule&&/display\s*:\s*none/i.test(p.style.cssText)){e.push({ruleId:"wcc-orientation-locked-content",wcagCriterion:"1.3.4",wcagLevel:"AA",impact:"serious",description:`Stylesheet hides content via "display: none" inside an @media (orientation: ${l?"portrait":"landscape"}) block. WCAG 1.3.4 requires content not be restricted to a single display orientation unless that orientation is essential. Provide content in both orientations or document the essential-orientation exception.`,helpUrl:`${c}orientation.html`,element:t.documentElement,failureSummary:`Orientation-locked display:none on selector "${p.selectorText}".`});break}}}}catch{}}return e}function Y(t=document){const e=[];let a=0;const r=t.querySelectorAll("[title]");for(const n of Array.from(r)){if(a>200)break;a++;const o=n.getAttribute("title");!o||o.trim().length<8||n.hasAttribute("aria-describedby")||n.tagName==="A"&&(n.textContent??"").includes(o)||e.push({ruleId:"wcc-title-tooltip-no-persistence",wcagCriterion:"1.4.13",wcagLevel:"AA",impact:"moderate",description:'Element uses the `title` attribute for a tooltip-style description. Browser title tooltips auto-dismiss when the pointer moves, violating WCAG 1.4.13\'s "hoverable" and "persistent" requirements (content triggered on hover must remain visible until the user dismisses it). Use a proper aria-describedby pattern with a hoverable, dismissible tooltip element.',helpUrl:`${c}content-on-hover-or-focus.html`,element:n,failureSummary:"title-attribute tooltip is not dismissible / hoverable."})}return e}function Z(t=document){const e=[],a=typeof window<"u"?window.location.pathname:"/";if(a==="/"||a===""||/\/(search|results)\b/.test(a))return e;const r=t.querySelector('nav, [role="navigation"]')!==null,n=t.querySelector('input[type="search"], [role="search"]')!==null||Array.from(t.querySelectorAll('input[type="text"]')).some(l=>/search/i.test(l.name)||/search/i.test(l.placeholder??"")),o=t.querySelector('a[href*="sitemap" i]')!==null,s=Array.from(t.querySelectorAll('a[href^="#"]')).length>=3,i=[r,n,o,s].filter(Boolean).length;return i<2&&e.push({ruleId:"wcc-only-one-way-to-find",wcagCriterion:"2.4.5",wcagLevel:"AA",impact:"moderate",description:`Page provides only ${i} navigation aid${i===1?"":"s"}. WCAG 2.4.5 requires at least TWO ways to locate a page within a set: typically navigation menu + site search, OR navigation + sitemap link. Home pages and search results are exempt.`,helpUrl:`${c}multiple-ways.html`,element:t.documentElement,failureSummary:`Found ${i} of 4 affordances (nav=${r}, search=${n}, sitemap=${o}, toc=${s}).`}),e}function J(t=document){const e=[],a=/\b(window\.location|location\.href|\.submit\s*\(|window\.open|fetch\s*\(|XMLHttpRequest)\b/,r=t.querySelectorAll("[onmousedown], [onpointerdown]");for(const n of Array.from(r)){const o=(n.getAttribute("onmousedown")??"")+";"+(n.getAttribute("onpointerdown")??"");a.test(o)&&e.push({ruleId:"wcc-down-event-fires-action",wcagCriterion:"2.5.2",wcagLevel:"A",impact:"serious",description:"Element's mousedown/pointerdown handler fires a navigation, submit, or network action. WCAG 2.5.2 requires that single-pointer actions execute on UP-EVENT (mouseup, click), not down-event, so users can abort by dragging away. Move the action to the click handler or onmouseup.",helpUrl:`${c}pointer-cancellation.html`,element:n,failureSummary:"Action fires on pointer-down; user cannot abort by dragging away before release."})}return e}function Q(t=document){const e=[],a=t.querySelectorAll("script:not([src])");for(const r of Array.from(a)){const n=r.textContent??"";if(/\b(devicemotion|deviceorientation)\b/.test(n)&&/\baddEventListener\s*\(\s*['"`](devicemotion|deviceorientation)/.test(n)){e.push({ruleId:"wcc-motion-actuation-no-alt",wcagCriterion:"2.5.4",wcagLevel:"A",impact:"serious",description:"Page registers a `devicemotion` or `deviceorientation` listener (shake / tilt to act). WCAG 2.5.4 requires that any function triggered by device motion is ALSO operable via standard UI controls AND can be disabled to prevent accidental actuation. Add an on-screen button equivalent.",helpUrl:`${c}motion-actuation.html`,element:r,failureSummary:"Device-motion listener present. Confirm UI-control alternative + disable mechanism."});break}}return e}function ee(t=document){const e=[],a=t.querySelectorAll("form");for(const r of Array.from(a)){const n=r.querySelectorAll("input[required], select[required], textarea[required]");if(n.length===0||Array.from(n).some(i=>i.hasAttribute("aria-describedby")||i.hasAttribute("aria-invalid")||i.hasAttribute("aria-errormessage")))continue;const s=/\b(required|please|must|\*)\b/i.test(r.textContent??"");e.push({ruleId:"wcc-form-no-aria-error-pattern",wcagCriterion:"3.3.1",wcagLevel:"A",impact:s?"moderate":"serious",description:`Form has ${n.length} required field${n.length===1?"":"s"} but no fields use ARIA error patterns (\`aria-invalid\`, \`aria-describedby\`, \`aria-errormessage\`). WCAG 3.3.1 requires that input errors be identified in text the user can perceive. Browser-default HTML5 validation bubbles aren't reliably announced by all screen readers; wire each required field to a visible error region via aria-describedby and toggle aria-invalid when validation fails.`,helpUrl:`${c}error-identification.html`,element:r,failureSummary:`${n.length} required input(s) without ARIA error pattern.`})}return e}function te(t=document){const e=[],a=t.querySelectorAll("[onkeydown], [onkeypress], [onkeyup]");if(a.length===0)return e;const r=Array.from(a).slice(0,5);for(const n of r)e.push({ruleId:"wcc-inline-key-handler",wcagCriterion:"2.1.4",wcagLevel:"A",impact:"moderate",description:"Element has an inline keyboard event handler (onkeydown / onkeypress / onkeyup). WCAG 2.1.4 requires single-character key shortcuts to be togglable, remappable, or only-on-focus. Verify this handler complies, OR — preferred — replace inline handlers with addEventListener-attached handlers gated by a focused-element check.",helpUrl:`${c}character-key-shortcuts.html`,element:n,failureSummary:"Inline keyboard handler detected — verify shortcuts are togglable / scoped."});return e}function ne(t=document){const e=[];if(t.querySelectorAll('[role="status"], [role="alert"], [role="log"], [aria-live]').length>0)return e;const r=t.querySelector("form input[required], form input[pattern]")!=null,n=t.getElementById("root")!=null||t.getElementById("app")!=null||t.querySelector("[data-reactroot], [data-vue-app]")!=null,o=t.querySelector('[class*="toast" i], [class*="snackbar" i], [class*="notif" i]')!=null;return(r||n||o)&&e.push({ruleId:"wcc-no-live-regions",wcagCriterion:"4.1.3",wcagLevel:"AA",impact:"moderate",description:'Page appears to contain dynamic content (form validation, SPA mount, toast/snackbar container) but has no `role="status"` / `role="alert"` / `aria-live` regions. WCAG 4.1.3 requires status updates that don\'t cause a focus change to be announced via a live region. Add `role="status"` (polite) or `role="alert"` (assertive) to your status / error / toast containers. If your dynamic updates already steal focus, this finding may be N/A.',helpUrl:`${c}status-messages.html`,element:t.documentElement,failureSummary:"No live regions detected on a page that appears to have dynamic content."}),e}const oe=360,ie=10;function re(t=document){if(typeof window>"u"||typeof document>"u")return[];if(window.innerWidth>oe)return[];const e=t.documentElement;if(!e)return[];const a=e.scrollWidth-e.clientWidth;return a<=ie?[]:[{ruleId:"wcc-reflow-horizontal-scroll",wcagCriterion:"1.4.10",wcagLevel:"AA",impact:"serious",description:`Page produces horizontal scroll at narrow viewport (${window.innerWidth}px wide; documentElement.scrollWidth=${e.scrollWidth}px vs clientWidth=${e.clientWidth}px, overflow=${a}px). WCAG 1.4.10 requires content to reflow at 320 CSS pixels without requiring scrolling in two dimensions. Common causes: fixed-width container exceeding the viewport, image without max-width:100%, white-space:nowrap on a long string, table without overflow handling, or a third-party widget that doesn't respect responsive constraints.`,helpUrl:`${c}reflow.html`,element:e,failureSummary:"Horizontal scroll detected at narrow viewport. Find the element producing the overflow (try Chrome DevTools → Elements → 3-dots → Capture full-size screenshot at 320px width) and add max-width / overflow-x:auto / responsive layout to keep content within 320px."}]}function ae(t=document){return[]}function se(t=document){const e=[],a=t.querySelectorAll("form");if(a.length===0)return e;let r=!1,n=!1;for(const o of Array.from(a)){const s=o.querySelectorAll('input[required], input[pattern], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]');if(s.length!==0){r=!0;for(const i of Array.from(s))if(i.getAttribute("aria-describedby")||i.getAttribute("aria-errormessage")||i.getAttribute("list")){n=!0;break}if(n)break}}return!r||n||e.push({ruleId:"wcc-form-no-error-suggestion",wcagCriterion:"3.3.3",wcagLevel:"AA",impact:"moderate",description:"Form has inputs with required / pattern / typed (email / url / tel / number) constraints but none of them wire to a `<datalist>`, `aria-describedby`, or `aria-errormessage`. WCAG 3.3.3 requires error suggestions when corrections are knowable. Add either a `<datalist>` of valid values OR an `aria-describedby` pointing at a visible help / error region with example-correct values.",helpUrl:`${c}error-suggestion.html`,element:a[0],failureSummary:"Form has constrained inputs but no suggestion-infrastructure (datalist / aria-describedby / aria-errormessage)."}),e}function le(t=document){var u;const e=[],a=t.querySelectorAll("form");if(a.length===0)return e;const r=/\b(pay|payment|order|purchase|checkout|charge|transfer|donate|subscribe|agree|sign|delete account|cancel subscription|consent|enroll|legally)\b/i,n=/\b(free|demo|trial|preview|sample)\b/i,o=/\b(review|preview|confirm|verify|are you sure|please confirm|double-check)\b/i,s=/\b(undo|reverse|cancel|edit|update later|change later)\b/i;let i=!1,l=null;for(const p of Array.from(a)){const d=(p.textContent??"")+" "+(p.getAttribute("aria-label")??"");if(!r.test(d)||n.test(d)&&!/\$|usd|eur|gbp/i.test(d))continue;i=!0,l=p;const m=d+" "+(((u=p.closest("section, main, body"))==null?void 0:u.textContent)??"");if(o.test(m)||s.test(m))return e}return i&&e.push({ruleId:"wcc-commit-form-no-review-step",wcagCriterion:"3.3.4",wcagLevel:"AA",impact:"serious",description:"Form appears to commit a legal, financial, or data-modification action (matched keywords: pay / order / purchase / transfer / agree / sign / delete-account / etc.) but no review, confirm, or reversibility step was detected nearby. WCAG 3.3.4 requires ONE of: (a) reversibility (undo / cancel after submit), (b) error-checking with explicit confirmation, OR (c) a review-and-confirm screen before final submit. Add at least one. Heuristic — verify manually if this form is actually a no-commitment lead-capture.",helpUrl:`${c}error-prevention-legal-financial-data.html`,element:l??t.documentElement,failureSummary:"Commit-style form with no detected review/confirm/reversibility cue."}),e}function ce(t=document){const e=[],a=t.querySelectorAll("audio[autoplay], video[autoplay]");if(a.length===0)return e;for(const r of Array.from(a))r.hasAttribute("controls")||r.tagName.toLowerCase()==="video"&&r.hasAttribute("muted")||e.push({ruleId:"wcc-autoplay-no-controls",wcagCriterion:"1.4.2",wcagLevel:"A",impact:"serious",description:`${r.tagName.toLowerCase()} element has \`autoplay\` but no \`controls\` attribute and is not muted. WCAG 1.4.2 (A) requires a mechanism to pause / stop / control volume for any audio playing automatically for more than 3 seconds. EITHER add the \`controls\` attribute, OR add \`muted\` to the element, OR provide a visible custom control adjacent to the media.`,helpUrl:`${c}audio-control.html`,element:r,failureSummary:"Autoplay media without controls — needs pause/stop/volume mechanism."});return e}function ue(t=document){const e=[],a=t.querySelectorAll('header, footer, [class*="banner" i], [class*="cookie" i], [class*="modal" i], [class*="chat" i], [class*="sticky" i], [class*="fixed" i], [class*="overlay" i], [role="banner"], [role="contentinfo"]');for(const r of Array.from(a).slice(0,30)){const n=getComputedStyle(r),o=n.position;if(o!=="sticky"&&o!=="fixed"||n.display==="none"||n.visibility==="hidden"||n.opacity==="0")continue;const s=Number(n.opacity);if(Number.isFinite(s)&&s<=.1)continue;const i=n.zIndex;if(i&&i!=="auto"){const g=Number(i);if(Number.isFinite(g)&&g<0)continue}const l=n.backgroundColor,u=/rgba?\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\s*\)/.exec(l);if((u?Number(u[1]):1)<.5&&!n.backgroundImage.match(/url\(/))continue;const d=r.getBoundingClientRect();if(d.width===0||d.height===0)continue;const m=window.innerHeight||document.documentElement.clientHeight,f=d.top<80&&d.bottom>0,b=d.bottom>m-80&&d.top<m;if(!f&&!b)continue;const C=getComputedStyle(document.documentElement),w=t.body?getComputedStyle(t.body):null;if(f){const g=parseFloat(C.scrollPaddingTop)||0,A=w&&parseFloat(w.scrollPaddingTop)||0;if(Math.max(g,A)>=d.height-2)continue}if(b){const g=parseFloat(C.scrollPaddingBottom)||0,A=w&&parseFloat(w.scrollPaddingBottom)||0;if(Math.max(g,A)>=d.height-2)continue}e.push({ruleId:"wcc-sticky-may-obscure-focus",wcagCriterion:"2.4.11",wcagLevel:"AA",impact:"moderate",description:`Element is ${o}-positioned and overlaps the viewport's ${f?"top":"bottom"} edge with an opaque background, and we did NOT detect a sufficient ${f?"`scroll-padding-top`":"`scroll-padding-bottom`"} on \`html\` / \`body\` to push focused elements out from under it. WCAG 2.4.11 (AA, WCAG 2.2) requires that focused components NOT be entirely hidden by such overlays. Common fixes (any one is sufficient): (1) \`${f?"scroll-padding-top":"scroll-padding-bottom"}: ${Math.ceil(d.height)}px\` on the scroll container (\`html\` or \`body\`) — the wcagcheckr rule detects this automatically and will stop firing once present; (2) \`${f?"scroll-margin-top":"scroll-margin-bottom"}\` on each focusable element below the overlay; (3) a JS focus-aware reveal that hides the sticky overlay when focus lands beneath it (this one the rule CANNOT detect; mark this finding as acknowledged in the per-page panel once it's in place).`,helpUrl:`${c}focus-not-obscured-minimum.html`,element:r,failureSummary:`${o} element overlapping viewport edge — verify focus visibility.`})}return e}function de(t=document){const e=[],a=t.querySelectorAll("form");if(a.length===0)return e;const r=/\b(step\s*\d|wizard|stage\s*\d|page\s*\d\s*of\s*\d|continue|next|previous|back to (cart|review))\b/i,n='[role="progressbar"], [class*="step" i], [class*="wizard" i], [class*="progress" i]';let o=!1;for(const s of Array.from(a)){const i=s.textContent??"";if(r.test(i)){o=!0;break}}return!o&&t.querySelectorAll(n).length>0&&(o=!0),o&&e.push({ruleId:"wcc-redundant-entry-needs-site-crawl",wcagCriterion:"3.3.7",wcagLevel:"A",impact:"minor",description:'This page appears to be part of a multi-step flow (step indicators, "continue"/"next" cues, progress markers). WCAG 3.3.7 (A, WCAG 2.2) requires that previously-entered information be auto-populated or selectable across steps — except when re-entry is essential (password confirmation, security). A single-page audit cannot verify cross-step redundancy. Run the Site Crawl flow on the full step sequence, then re-export — the crawler will compare field names across pages and flag duplicates.',helpUrl:`${c}redundant-entry.html`,element:t.documentElement,failureSummary:"Multi-step flow signals detected — needs site-crawl coverage."}),e}function me(t=document){return[]}const pe=/\b(click|tap|press|select|find|see|hear|use|choose) (?:the |a |an )?(red|orange|yellow|green|blue|purple|pink|black|white|round|square|triangular|left|right|top|bottom|upper|lower) (button|link|icon|tab|box|arrow|menu|control|element|item)\b/gi,he=/\b(listen for|hear) (?:the |a |an )?(chime|beep|click|ding|sound|tone)\b/gi;function fe(t=document){const e=[],a=t.body;if(!a)return e;const r=(a.innerText||a.textContent||"").slice(0,5e4);if(r.trim().length<40)return e;const n=new Set;for(const o of[pe,he]){o.lastIndex=0;let s;for(;(s=o.exec(r))!==null;){const i=s[0].toLowerCase().trim();if(!n.has(i)){if(n.add(i),n.size>5)break;e.push({ruleId:"wcc-sensory-only-directive",wcagCriterion:"1.3.3",wcagLevel:"A",impact:"moderate",description:`Found an instruction that appears to rely on sensory characteristics alone: "${i}". WCAG 1.3.3 requires that directions to users NOT depend solely on color, shape, size, position, orientation, or sound — there must also be a text alternative ("click the Submit button" rather than just "click the green button"). Verify the instruction includes a non-sensory cue (text label, ordinal position, accessible name). If the page provides additional context elsewhere, this can be marked verified.`,helpUrl:`${c}sensory-characteristics.html`,element:a,failureSummary:`Sensory-only directive phrase: "${i}"`})}}}return e}const ge=/\b(logo|icon|avatar|photo|portrait|headshot|illustration|chart|graph|diagram|map|screenshot|thumbnail)\b/i;function be(t=document){const e=[];let a=0;const r=t.querySelectorAll("img[alt]");for(const o of Array.from(r)){if(a>=5)break;const s=(o.getAttribute("alt")??"").trim();if(s.length<40||ge.test(s))continue;const i=/[a-z][.,;:]\s+[A-Z]/.test(s),l=(s.match(/\b[A-Z][a-z]+/g)??[]).length;!i&&l<4||(a++,e.push({ruleId:"wcc-image-of-text-candidate",wcagCriterion:"1.4.5",wcagLevel:"AA",impact:"moderate",description:`<img> alt text reads like prose content ("${s.slice(0,80)}${s.length>80?"…":""}"), suggesting the image may be RENDERING text instead of describing a non-text image. WCAG 1.4.5 prefers actual text (HTML + CSS) over text-baked-into-images so users can resize / restyle it. Common exceptions: logos, charts where the visual IS the information, screenshots of UI. If this image is one of those, mark verified.`,helpUrl:`${c}images-of-text.html`,element:o,failureSummary:`<img alt="${s.slice(0,60)}${s.length>60?"…":""}"> — long sentence-like alt suggests image-of-text.`}))}const n=t.querySelectorAll("svg");for(const o of Array.from(n)){if(a>=8)break;const s=o.querySelectorAll("text");if(s.length===0)continue;let i=0;for(const l of Array.from(s))i+=(l.textContent??"").trim().length;i<20||(a++,e.push({ruleId:"wcc-svg-text-as-content",wcagCriterion:"1.4.5",wcagLevel:"AA",impact:"minor",description:`<svg> contains <text> elements totaling ${i} characters. Stylized SVG text often substitutes for HTML text + CSS; users can't resize / restyle it the same way. Confirm the SVG text is genuinely graphical (chart axis labels, decorative logo text) rather than substantive content that should be HTML.`,helpUrl:`${c}images-of-text.html`,element:o,failureSummary:`SVG with ${s.length} <text> element(s), ${i} chars total.`}))}return e}function ye(t=document){const e=[];if(t!==document)return e;const a=Array.from(t.querySelectorAll("div, section, article, p, li, td, th, button, a")).filter(i=>{let l="";for(const p of i.childNodes)p.nodeType===Node.TEXT_NODE&&(l+=(p.textContent??"").trim());if(l.length<20)return!1;const u=getComputedStyle(i);return!(u.visibility==="hidden"||u.display==="none"||u.overflow!=="hidden"&&u.overflowY!=="hidden")});if(a.length===0)return e;const r=a.map(i=>({el:i,scrollHeight:i.scrollHeight,clientHeight:i.clientHeight,scrollWidth:i.scrollWidth,clientWidth:i.clientWidth})),n="__wcagcheckr_c36_probe__",o=document.createElement("style");o.id=n,o.textContent=`body *:not(script):not(style) {
|
|
2
|
+
line-height: 1.5 !important;
|
|
3
|
+
letter-spacing: 0.12em !important;
|
|
4
|
+
word-spacing: 0.16em !important;
|
|
5
|
+
}
|
|
6
|
+
body p:not(script):not(style) {
|
|
7
|
+
margin-bottom: 2em !important;
|
|
8
|
+
}`,document.head.appendChild(o),document.body.scrollHeight;const s=[];for(const i of r){const l=i.scrollHeight>i.clientHeight||i.scrollWidth>i.clientWidth;(i.el.scrollHeight>i.el.clientHeight||i.el.scrollWidth>i.el.clientWidth)&&!l&&s.push(i)}document.head.removeChild(o),document.body.scrollHeight;for(const i of s.slice(0,5)){let l="";for(const u of i.el.childNodes)u.nodeType===Node.TEXT_NODE&&(l+=(u.textContent??"").trim());e.push({ruleId:"wcc-text-spacing-clip-risk",wcagCriterion:"1.4.12",wcagLevel:"AA",impact:"minor",description:`WCAG technique C36 probe confirmed clipping: when WCAG 1.4.12 text-spacing values (line-height 1.5, letter-spacing 0.12em, word-spacing 0.16em, paragraph-spacing 2em) are applied, this container's content overflows the box (scrollHeight > clientHeight). Text content: "${l.slice(0,60)}${l.length>60?"…":""}". Remove fixed height, replace with min-height, or set overflow to visible.`,helpUrl:`${c}text-spacing.html`,element:i.el,failureSummary:"Container clips under WCAG 1.4.12 text-spacing overrides (verified via C36 CSS injection probe)."})}return e}function k(t){if(t.id)return`#${t.id}`;const e=t.classList[0];return`${t.tagName.toLowerCase()}${e?"."+e:""}`}function we(t=document){return[...H(t),..._(t),...B(t),...j(t),...V(t),...K(t),...X(t),...Y(t),...Z(t),...J(t),...Q(t),...ee(t),...te(t),...ne(t),...me(t),...ae(t),...se(t),...re(t),...le(t),...ce(t),...ue(t),...de(t),...U(t),...O(t),...fe(t),...be(t),...ye(t),...D(t)]}const Ae=["2.2.1","2.2.2","2.3.1","2.5.1","2.5.7","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","3.2.1","3.2.2","3.3.8","1.3.4","1.4.4","1.4.13","2.4.5","2.5.2","2.5.4","3.3.1","2.1.4","4.1.3","4.1.1","3.3.3","1.4.10","3.3.4","1.4.2","2.4.11","3.3.7","1.3.3","1.4.5","1.4.12"];export{Ae as DOM_ANALYZER_COVERED_CRITERIA,_ as analyzeAnimation,ce as analyzeAudioControl,te as analyzeCharacterKeyShortcuts,K as analyzeCognitiveAuth,ae as analyzeConsistencyHints,Y as analyzeContentOnHover,V as analyzeContextChangeOnFocusInput,ee as analyzeErrorIdentification,le as analyzeErrorPreventionLegalFinancial,se as analyzeErrorSuggestion,ue as analyzeFocusNotObscured,be as analyzeImagesOfText,j as analyzeMedia,Q as analyzeMotionActuation,Z as analyzeMultipleWays,X as analyzeOrientation,me as analyzeParsingObsoleted,J as analyzePointerCancellation,B as analyzePointerGestures,de as analyzeRedundantEntry,re as analyzeReflow,fe as analyzeSensoryCharacteristics,ne as analyzeStatusMessages,ye as analyzeTextSpacing,H as analyzeTimingAdjustable,M as hasReducedMotionMitigation,we as runAllDomCriterionAnalyzers};
|
package/dist/manifest.json
CHANGED
|
@@ -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.
|
|
6
|
-
"version_name": "1.0.0-rc.
|
|
5
|
+
"version": "1.0.0.273",
|
|
6
|
+
"version_name": "1.0.0-rc.273",
|
|
7
7
|
"author": "Locustware",
|
|
8
8
|
"homepage_url": "https://wcagcheckr.com",
|
|
9
9
|
"icons": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"content_scripts": [
|
|
38
38
|
{
|
|
39
39
|
"js": [
|
|
40
|
-
"assets/content-script.ts-loader-
|
|
40
|
+
"assets/content-script.ts-loader-DOSB_ugu.js"
|
|
41
41
|
],
|
|
42
42
|
"matches": [
|
|
43
43
|
"<all_urls>"
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"assets/custom-rules-CAe0nbES.js",
|
|
67
67
|
"assets/design-system-audit-DpxJrxnb.js",
|
|
68
68
|
"assets/reflow-analyzer-DNgBX8N_.js",
|
|
69
|
-
"assets/dom-criterion-analyzers-
|
|
70
|
-
"assets/content-script.ts-
|
|
69
|
+
"assets/dom-criterion-analyzers-DoUaJV5C.js",
|
|
70
|
+
"assets/content-script.ts-CX6ON5Ua.js"
|
|
71
71
|
],
|
|
72
72
|
"use_dynamic_url": false
|
|
73
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcag-checkr/ci",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.273",
|
|
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",
|