@wcag-checkr/ci 1.0.0-rc.147 → 1.0.0-rc.148

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.
@@ -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
- `,FO=.85,Bh=80,SO=300,kO=4e3;async function OO(){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*FO),F=performance.now();for(let S=0;S<w&&!(performance.now()-F>kO);S+=g)window.scrollTo({top:S,left:0,behavior:"instant"}),await new Promise(B=>setTimeout(B,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,SO))}catch(m){rr.warn("scroll-reveal warmup failed (continuing with audit)",m)}}function IO(){if(typeof document>"u")return()=>{};if(document.getElementById(Lh))return()=>{};const d=document.createElement("style");return d.id=Lh,d.textContent=TO,document.head.appendChild(d),()=>d.remove()}function NO(d){return d==="minor"||d==="moderate"||d==="serious"||d==="critical"?d:"moderate"}function MO(d){return d.some(m=>m.endsWith("aaa"))?"AAA":d.some(m=>m.endsWith("aa"))?"AA":"A"}function Ro(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 PO(d,m){const w=[];for(let b=0;b<m.length&&w.length<25;b++){const g=m[b],F=Array.isArray(g.target)?g.target.join(" "):g.target;if(!F)continue;const S=(()=>{try{return document.querySelector(F)}catch{return null}})(),B={selector:F,failureSummary:g.failureSummary};if(S instanceof HTMLElement){const z=S.outerHTML;if(B.outerHTMLSnippet=z.length>1200?z.slice(0,1200)+"…":z,d==="color-contrast"||d==="color-contrast-enhanced"){const q=window.getComputedStyle(S);B.styles={foreground:q.color||void 0,background:q.backgroundColor||void 0,fontSize:parseFloat(q.fontSize)||void 0,fontWeight:parseInt(q.fontWeight,10)||void 0,textSample:(S.textContent??"").trim().slice(0,120)||void 0,effectiveBackground:LO(S)}}}w.push(B)}return w}function LO(d){const m=new Set(["rgba(0, 0, 0, 0)","transparent",""]);let p=d,w,b=!1;for(;p;){const g=window.getComputedStyle(p),F=g.backgroundImage;F&&F!=="none"&&!b&&(b=!0,w=$h(p));const S=g.backgroundColor;if(S&&!m.has(S))return{color:S,fromAncestor:p===d?void 0:$h(p),hasBackgroundImageInChain:b,imageAncestor:w};p=p.parentElement}return{color:"rgb(255, 255, 255)",fromAncestor:"document default",hasBackgroundImageInChain:b,imageAncestor:w}}function $h(d){const m=d.tagName.toLowerCase(),p=d.id?`#${d.id}`:"",w=d.classList.length>0?`.${d.classList[0]}`:"";return`${m}${p}${w}`}function BO(d,m,p){var F,S;const w=((F=d.textContent)==null?void 0:F.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:$O(d),cascadeChain:HO(d,p)}}function $O(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 F=getComputedStyle(g),S=parseFloat(F.opacity||"1");S<p&&(p=S,w=qO(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 qO(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 jO=/var\(\s*(--[A-Za-z0-9_-]+)/g;function HO(d,m){if(!(m!=="color-contrast"&&m!=="color-contrast-enhanced")&&typeof getComputedStyle=="function")try{const p=getComputedStyle(d);return{color:qh(d,"color",p.color),backgroundColor:qh(d,"background-color",p.backgroundColor)}}catch{return}}function qh(d,m,p){let w,b;for(const F of Array.from(document.styleSheets)){let S=null;try{S=F.cssRules}catch{continue}S&&wv(S,d,m,(B,z)=>{w=B,b=z})}if(!w)return;const g=[...w.matchAll(jO)].map(F=>F[1]).filter(F=>!!F);return{authored:w,rendered:p,vars:g,ruleSelector:b}}function wv(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&&wv(b.cssRules,m,p,w)}}async function zO(d,m,p,w){var Ba,vr,Ar,Hn,zn,Vn,ua,Gn;const b=IO();await new Promise(se=>setTimeout(se,CO)),await RO();const g=new Date().toISOString(),F=performance.now(),S=document.querySelector(d);if(!S)throw b(),new Error(`scope not found: ${d}`);const B=mv({element:S,url:window.location.href}),z=await DO(),q=await _O();let re=!1;try{const se=await Ok();se.length>0&&(wO(z,se),re=!0)}catch(se){rr.warn("custom-rule registration failed; continuing with built-ins only",se)}const ve=p??EO,ce=re&&ve.runOnly&&typeof ve.runOnly=="object"&&Array.isArray(ve.runOnly.values)&&!ve.runOnly.values.includes(Ph)?{...ve,runOnly:{...ve.runOnly,values:[...ve.runOnly.values,Ph]}}:ve,xe=Object.keys(q).length>0?{...ce,rules:{...ce.rules??{},...q}}:ce,ke=w&&w.length>0?{...xe,runOnly:{type:"rule",values:w}}:xe;let Ye;try{Ye=await z.run(S,ke)}catch(se){throw b(),se}const ht=z.version??"unknown",Vt=[];for(const se of Ye.violations??[])if(!(se.tags??[]).some(Je=>Co.test(Je)))for(const Je of se.nodes??[]){const Te=Array.isArray(Je.target)?Je.target.join(" "):Je.target,rt=document.querySelector(Te);if(!rt)continue;const Be=BO(rt,Je,se.id),qe=await Ol({ruleId:se.id,componentId:B.id,currentState:m,target:Be});Vt.push({ruleId:se.id,wcagCriterion:Ro(se.tags??[]),wcagLevel:MO(se.tags??[]),impact:NO(se.impact),description:se.description??"",helpUrl:se.helpUrl??"",target:Be,componentId:B.id,currentState:m,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}const nr=[];if(m.breakpoint.width<=320)try{const{analyzeReflow:se}=await Rh(async()=>{const{analyzeReflow:Te}=await import("./reflow-analyzer-DNgBX8N_.js");return{analyzeReflow:Te}},[]),Je=se(m.breakpoint.width,S);Je.findings.length===0&&nr.push({ruleId:"wcc-reflow-clean",wcagCriterion:"1.4.10"});for(const Te of Je.findings){const rt=document.querySelector(Te.selector)??S,Be={selector:Te.selector,outerHTML:Te.outerHtml||(rt.outerHTML??"").slice(0,500),failureSummary:Te.kind==="document-overflow"?`Page scrolls horizontally at ${m.breakpoint.width}px (content width ${Je.documentScrollWidthPx}px, overflow ${Te.overflowPx}px).`:Te.kind==="element-overflow"?`Element extends ${Te.overflowPx}px past the ${m.breakpoint.width}px viewport edge.`:`Element scrolls horizontally (${Te.overflowPx}px past its container).`,tagName:rt.tagName,role:((Ba=rt.getAttribute)==null?void 0:Ba.call(rt,"role"))??null,accessibleName:((vr=rt.getAttribute)==null?void 0:vr.call(rt,"aria-label"))??null,textSnippet:((Ar=rt.textContent)==null?void 0:Ar.trim().slice(0,50))??null,attrId:rt.id||null,attrTestid:((Hn=rt.getAttribute)==null?void 0:Hn.call(rt,"data-testid"))??null},qe=await Ol({ruleId:"wcagcheckr-reflow",componentId:B.id,currentState:m,target:Be});Vt.push({ruleId:"wcagcheckr-reflow",wcagCriterion:"1.4.10",wcagLevel:"AA",impact:"serious",description:Te.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:Be,componentId:B.id,currentState:m,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}}catch(se){console.warn("[audit-engine] reflow analyzer failed:",se)}if(m.pseudoState==="default"&&m.theme==="light"&&m.direction==="ltr"&&m.breakpoint.id==="desktop")try{const{runAllDomCriterionAnalyzers:se,DOM_ANALYZER_COVERED_CRITERIA:Je}=await Rh(async()=>{const{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}=await import("./dom-criterion-analyzers-DCSIVGo4.js");return{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}},[]),Te=se(),rt=new Set(Te.map(Be=>Be.wcagCriterion));for(const Be of Je)rt.has(Be)||nr.push({ruleId:`wcc-clean::${Be}`,wcagCriterion:Be});for(const Be of Te){const qe=Be.element,la={selector:qe.id?`#${qe.id}`:qe===document.documentElement?"html":(qe.tagName||"unknown").toLowerCase()+(qe.classList[0]?"."+qe.classList[0]:""),outerHTML:(qe.outerHTML??"").slice(0,500),failureSummary:Be.failureSummary,tagName:qe.tagName,role:((zn=qe.getAttribute)==null?void 0:zn.call(qe,"role"))??null,accessibleName:((Vn=qe.getAttribute)==null?void 0:Vn.call(qe,"aria-label"))??null,textSnippet:((ua=qe.textContent)==null?void 0:ua.trim().slice(0,50))??null,attrId:qe.id||null,attrTestid:((Gn=qe.getAttribute)==null?void 0:Gn.call(qe,"data-testid"))??null},zr=await Ol({ruleId:Be.ruleId,componentId:B.id,currentState:m,target:la});Vt.push({ruleId:Be.ruleId,wcagCriterion:Be.wcagCriterion,wcagLevel:Be.wcagLevel,impact:Be.impact,description:Be.description,helpUrl:Be.helpUrl,target:la,componentId:B.id,currentState:m,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:zr})}}catch(se){console.warn("[audit-engine] dom-criterion-analyzers failed:",se)}return b(),{componentId:B.id,scope:d,pageUrl:typeof window<"u"&&window.location?window.location.href:void 0,state:m,violations:Vt,passes:(Ye.passes??[]).length,incomplete:(Ye.incomplete??[]).length,inapplicable:(Ye.inapplicable??[]).length,axeVersion:ht,startedAt:g,durationMs:performance.now()-F,axeRulesEvaluated:{passed:[...(Ye.passes??[]).filter(se=>!(se.tags??[]).some(Je=>Co.test(Je))).map(se=>({ruleId:se.id,wcagCriterion:Ro(se.tags??[])})).filter(se=>se.wcagCriterion),...nr],inapplicable:(Ye.inapplicable??[]).filter(se=>!(se.tags??[]).some(Je=>Co.test(Je))).map(se=>({ruleId:se.id,wcagCriterion:Ro(se.tags??[])})).filter(se=>se.wcagCriterion),incomplete:(Ye.incomplete??[]).filter(se=>!(se.tags??[]).some(Je=>Co.test(Je))).map(se=>({ruleId:se.id,wcagCriterion:Ro(se.tags??[]),elements:PO(se.id,se.nodes??[])})).filter(se=>se.wcagCriterion)}}}function VO(){const d=ye("SCOPE_FINGERPRINT_REQUEST",async z=>{try{const q=z.selector==="html"?document.documentElement:document.querySelector(z.selector);if(!q)return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null};const re=q.outerHTML,ve=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(re));return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:Array.from(new Uint8Array(ve)).map(xe=>xe.toString(16).padStart(2,"0")).join("")}}catch(q){return rr.warn("scope fingerprint failed",q),{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null}}}),m=ye("AUDIT_REQUEST",async z=>{try{return{type:"AUDIT_RESPONSE",success:!0,result:await zO(z.selector,z.currentState,z.axeConfig,z.rulesToRun)}}catch(q){rr.error("audit failed",q);const re=q instanceof Error?q.message:String(q),ve=re.toLowerCase().includes("paint")?"PAINT_TIMEOUT":"AXE_FAILED";return{type:"AUDIT_RESPONSE",success:!1,error:Tk(ve,re,!1)}}}),p=ye("CUSTOM_PROPERTY_REQUEST",async()=>{try{return{type:"CUSTOM_PROPERTY_RESPONSE",undefined:fO()}}catch(z){return rr.warn("custom-property analysis failed",z),{type:"CUSTOM_PROPERTY_RESPONSE",undefined:[]}}}),w=ye("THEME_AWARENESS_REQUEST",async()=>{try{const z=gO();return{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:z.hasLightModeCss,hasDarkModeCss:z.hasDarkModeCss,hasUnreadableSheets:z.hasUnreadableSheets}}catch(z){return rr.warn("theme-awareness probe failed",z),{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:!0,hasDarkModeCss:!0,hasUnreadableSheets:!0}}}),b=ye("READING_ORDER_REQUEST",async z=>{try{const q=document.querySelector(z.selector)??document;return{type:"READING_ORDER_RESPONSE",issues:gv(q)}}catch(q){return rr.warn("reading-order analysis failed",q),{type:"READING_ORDER_RESPONSE",issues:[]}}}),g=ye("TYPOGRAPHY_ANALYZE_REQUEST",async z=>{try{const q=document.querySelector(z.selector)??document;return{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:lO(q)}}catch(q){return rr.warn("typography analysis failed",q),{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:[]}}}),F=ye("TAB_ORDER_ANALYZE_REQUEST",async()=>{try{return{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:aO()}}catch(z){return rr.warn("tab-order analysis failed",z),{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:[]}}}),S=ye("WARMUP_REQUEST",async()=>(await OO(),{type:"WARMUP_RESPONSE"})),B=ye("AI_CANDIDATES_REQUEST",async z=>{try{return{type:"AI_CANDIDATES_RESPONSE",images:GO(z.selector),headings:YO(z.selector),instructions:KO(z.selector),ariaElements:XO(z.selector),links:ZO(z.selector),longTextBlocks:QO(z.selector),languageInfo:JO(),colorOnlyRegions:e4(z.selector)}}catch(q){return rr.warn("ai-candidates enumeration failed",q),{type:"AI_CANDIDATES_RESPONSE",images:[],headings:[],instructions:[],ariaElements:[],links:[],longTextBlocks:[],languageInfo:null,colorOnlyRegions:[]}}});return()=>{m(),d(),b(),F(),g(),S(),p(),w(),B()}}function GO(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 F=b.getBoundingClientRect();if(F.width<16||F.height<16)continue;const S=b.getAttribute("alt")??"";UO(b,S)||p.push({imageUrl:g,alt:S,selector:sa(b),outerHTML:(b.outerHTML??"").slice(0,500),surroundingContext:WO(b),pixelArea:Math.round(F.width*F.height)})}return p}function UO(d,m){if(oa(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(),F=w.getAttribute("role");if(g==="a"||g==="button"||F==="link"||F==="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 oa(d){let m=d;for(;m&&m!==document.body;){if(m.getAttribute("aria-hidden")==="true")return!0;m=m.parentElement}return!1}function sa(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 WO(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 YO(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 F of Array.from(w)){if(oa(F))continue;const S=(F.textContent??"").trim();if(!S||S.length>200)continue;const B=parseInt(((b=F.tagName.match(/^H(\d)$/))==null?void 0:b[1])??"6",10);let z="",q=F.nextElementSibling;for(;q&&z.length<800;){const re=parseInt(((g=q.tagName.match(/^H(\d)$/))==null?void 0:g[1])??"0",10);if(re>0&&re<=B)break;const ve=(q.textContent??"").trim();ve&&(z+=(z?" ":"")+ve),q=q.nextElementSibling}z.length<30||p.push({level:B,text:S,sectionContent:z.slice(0,800),selector:sa(F),outerHTML:(F.outerHTML??"").slice(0,300)})}return p}function KO(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(oa(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:sa(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20))break}return p}function XO(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(oa(g))continue;const F=g.getAttribute("role")??"";if(F&&(p.push({role:F,selector:sa(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 ZO(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(oa(b))continue;const g=(b.textContent??"").trim();if(!g||g.length>60)continue;let F="",S=b.parentElement;for(;S&&S!==document.body;){const B=S.tagName;if(B==="P"||B==="LI"||B==="DD"||B==="FIGCAPTION"||/^H[1-6]$/.test(B)){const z=(S.textContent??"").trim();if(z.length>=g.length){F=z;break}}S=S.parentElement}if(p.push({linkText:g,surroundingText:F.slice(0,800),selector:sa(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=25)break}return p}function QO(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(oa(g))continue;let F=g.parentElement,S=!1;for(;F;){if(w.has(F)){S=!0;break}F=F.parentElement}if(S)continue;const B=(g.textContent??"").trim();if(!B||B.split(/\s+/).filter(Boolean).length<300)continue;const q=g.querySelectorAll("h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, figure, table, button, input").length;if(!(q>4)&&(p.push({blockText:B.slice(0,1500),structuralChildrenCount:q,selector:sa(g),outerHTML:(g.outerHTML??"").slice(0,400)}),w.add(g),p.length>=5))break}return p}function JO(){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 e4(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(oa(b))continue;const g=(b.textContent??"").trim(),F=window.getComputedStyle(b),S=F.backgroundColor,B=F.borderColor;if(!(S&&S!=="rgba(0, 0, 0, 0)"&&S!=="transparent"||B&&B!=="rgba(0, 0, 0, 0)"&&B!=="transparent"))continue;const q=b.querySelector("svg, [aria-label], [aria-labelledby], [title]")!==null;if(g.length>0&&q)continue;const re=b.getAttribute("aria-label")??b.getAttribute("title")??"",ve=[g?`text: "${g.slice(0,60)}"`:"no text content",re?`label: "${re}"`:"no aria-label",`bg: ${S}`,q?"has icon child":"no icon child"].join("; ");if(p.push({elementHtml:(b.outerHTML??"").slice(0,400),contextDescription:ve.slice(0,400),selector:sa(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20)break}return p}const t4=Dt("storybook-integration"),r4=3e3;function a4(){const d=window.__STORYBOOK_PREVIEW__;return d&&typeof d=="object"?{detected:!0,version:d.version??"unknown"}:{detected:!1}}async function n4(){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[F,S]of p)w.push({id:F,name:S.name??F,kind:S.title??S.kind??""});else if(typeof p=="object")for(const[F,S]of Object.entries(p))w.push({id:F,name:S.name??F,kind:S.title??S.kind??""});return w}async function i4(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(),r4),b=()=>{clearTimeout(w),p()};window.addEventListener("storyrendered",b,{once:!0})}),rs({type:"STORYBOOK_NAVIGATED_EVENT",storyId:d,ready:!0})}function o4(){return document.getElementById("storybook-root")?"#storybook-root":document.getElementById("root")?"#root":"body"}function s4(){const d=[];return d.push(ye("STORYBOOK_DETECT_REQUEST",async()=>{const m=a4();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(ye("STORYBOOK_LIST_STORIES_REQUEST",async()=>({type:"STORYBOOK_LIST_STORIES_RESPONSE",stories:await n4()}))),d.push(ye("STORYBOOK_NAVIGATE_REQUEST",async m=>{await i4(m.storyId)})),d.push(ye("STORYBOOK_GET_STORY_TARGET_REQUEST",()=>({type:"STORYBOOK_GET_STORY_TARGET_RESPONSE",selector:o4()}))),t4.info("handlers registered"),()=>d.forEach(m=>m())}const Dv=Dt("element-picker");let ar=null,Pa=!1;const u4=`
39
+ `,FO=.85,Bh=80,SO=300,kO=4e3;async function OO(){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*FO),F=performance.now();for(let S=0;S<w&&!(performance.now()-F>kO);S+=g)window.scrollTo({top:S,left:0,behavior:"instant"}),await new Promise(B=>setTimeout(B,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,SO))}catch(m){rr.warn("scroll-reveal warmup failed (continuing with audit)",m)}}function IO(){if(typeof document>"u")return()=>{};if(document.getElementById(Lh))return()=>{};const d=document.createElement("style");return d.id=Lh,d.textContent=TO,document.head.appendChild(d),()=>d.remove()}function NO(d){return d==="minor"||d==="moderate"||d==="serious"||d==="critical"?d:"moderate"}function MO(d){return d.some(m=>m.endsWith("aaa"))?"AAA":d.some(m=>m.endsWith("aa"))?"AA":"A"}function Ro(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 PO(d,m){const w=[];for(let b=0;b<m.length&&w.length<25;b++){const g=m[b],F=Array.isArray(g.target)?g.target.join(" "):g.target;if(!F)continue;const S=(()=>{try{return document.querySelector(F)}catch{return null}})(),B={selector:F,failureSummary:g.failureSummary};if(S instanceof HTMLElement){const z=S.outerHTML;if(B.outerHTMLSnippet=z.length>1200?z.slice(0,1200)+"…":z,d==="color-contrast"||d==="color-contrast-enhanced"){const q=window.getComputedStyle(S);B.styles={foreground:q.color||void 0,background:q.backgroundColor||void 0,fontSize:parseFloat(q.fontSize)||void 0,fontWeight:parseInt(q.fontWeight,10)||void 0,textSample:(S.textContent??"").trim().slice(0,120)||void 0,effectiveBackground:LO(S)}}}w.push(B)}return w}function LO(d){const m=new Set(["rgba(0, 0, 0, 0)","transparent",""]);let p=d,w,b=!1;for(;p;){const g=window.getComputedStyle(p),F=g.backgroundImage;F&&F!=="none"&&!b&&(b=!0,w=$h(p));const S=g.backgroundColor;if(S&&!m.has(S))return{color:S,fromAncestor:p===d?void 0:$h(p),hasBackgroundImageInChain:b,imageAncestor:w};p=p.parentElement}return{color:"rgb(255, 255, 255)",fromAncestor:"document default",hasBackgroundImageInChain:b,imageAncestor:w}}function $h(d){const m=d.tagName.toLowerCase(),p=d.id?`#${d.id}`:"",w=d.classList.length>0?`.${d.classList[0]}`:"";return`${m}${p}${w}`}function BO(d,m,p){var F,S;const w=((F=d.textContent)==null?void 0:F.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:$O(d),cascadeChain:HO(d,p)}}function $O(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 F=getComputedStyle(g),S=parseFloat(F.opacity||"1");S<p&&(p=S,w=qO(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 qO(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 jO=/var\(\s*(--[A-Za-z0-9_-]+)/g;function HO(d,m){if(!(m!=="color-contrast"&&m!=="color-contrast-enhanced")&&typeof getComputedStyle=="function")try{const p=getComputedStyle(d);return{color:qh(d,"color",p.color),backgroundColor:qh(d,"background-color",p.backgroundColor)}}catch{return}}function qh(d,m,p){let w,b;for(const F of Array.from(document.styleSheets)){let S=null;try{S=F.cssRules}catch{continue}S&&wv(S,d,m,(B,z)=>{w=B,b=z})}if(!w)return;const g=[...w.matchAll(jO)].map(F=>F[1]).filter(F=>!!F);return{authored:w,rendered:p,vars:g,ruleSelector:b}}function wv(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&&wv(b.cssRules,m,p,w)}}async function zO(d,m,p,w){var Ba,vr,Ar,Hn,zn,Vn,ua,Gn;const b=IO();await new Promise(se=>setTimeout(se,CO)),await RO();const g=new Date().toISOString(),F=performance.now(),S=document.querySelector(d);if(!S)throw b(),new Error(`scope not found: ${d}`);const B=mv({element:S,url:window.location.href}),z=await DO(),q=await _O();let re=!1;try{const se=await Ok();se.length>0&&(wO(z,se),re=!0)}catch(se){rr.warn("custom-rule registration failed; continuing with built-ins only",se)}const ve=p??EO,ce=re&&ve.runOnly&&typeof ve.runOnly=="object"&&Array.isArray(ve.runOnly.values)&&!ve.runOnly.values.includes(Ph)?{...ve,runOnly:{...ve.runOnly,values:[...ve.runOnly.values,Ph]}}:ve,xe=Object.keys(q).length>0?{...ce,rules:{...ce.rules??{},...q}}:ce,ke=w&&w.length>0?{...xe,runOnly:{type:"rule",values:w}}:xe;let Ye;try{Ye=await z.run(S,ke)}catch(se){throw b(),se}const ht=z.version??"unknown",Vt=[];for(const se of Ye.violations??[])if(!(se.tags??[]).some(Je=>Co.test(Je)))for(const Je of se.nodes??[]){const Te=Array.isArray(Je.target)?Je.target.join(" "):Je.target,rt=document.querySelector(Te);if(!rt)continue;const Be=BO(rt,Je,se.id),qe=await Ol({ruleId:se.id,componentId:B.id,currentState:m,target:Be});Vt.push({ruleId:se.id,wcagCriterion:Ro(se.tags??[]),wcagLevel:MO(se.tags??[]),impact:NO(se.impact),description:se.description??"",helpUrl:se.helpUrl??"",target:Be,componentId:B.id,currentState:m,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}const nr=[];if(m.breakpoint.width<=320)try{const{analyzeReflow:se}=await Rh(async()=>{const{analyzeReflow:Te}=await import("./reflow-analyzer-DNgBX8N_.js");return{analyzeReflow:Te}},[]),Je=se(m.breakpoint.width,S);Je.findings.length===0&&nr.push({ruleId:"wcc-reflow-clean",wcagCriterion:"1.4.10"});for(const Te of Je.findings){const rt=document.querySelector(Te.selector)??S,Be={selector:Te.selector,outerHTML:Te.outerHtml||(rt.outerHTML??"").slice(0,500),failureSummary:Te.kind==="document-overflow"?`Page scrolls horizontally at ${m.breakpoint.width}px (content width ${Je.documentScrollWidthPx}px, overflow ${Te.overflowPx}px).`:Te.kind==="element-overflow"?`Element extends ${Te.overflowPx}px past the ${m.breakpoint.width}px viewport edge.`:`Element scrolls horizontally (${Te.overflowPx}px past its container).`,tagName:rt.tagName,role:((Ba=rt.getAttribute)==null?void 0:Ba.call(rt,"role"))??null,accessibleName:((vr=rt.getAttribute)==null?void 0:vr.call(rt,"aria-label"))??null,textSnippet:((Ar=rt.textContent)==null?void 0:Ar.trim().slice(0,50))??null,attrId:rt.id||null,attrTestid:((Hn=rt.getAttribute)==null?void 0:Hn.call(rt,"data-testid"))??null},qe=await Ol({ruleId:"wcagcheckr-reflow",componentId:B.id,currentState:m,target:Be});Vt.push({ruleId:"wcagcheckr-reflow",wcagCriterion:"1.4.10",wcagLevel:"AA",impact:"serious",description:Te.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:Be,componentId:B.id,currentState:m,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}}catch(se){console.warn("[audit-engine] reflow analyzer failed:",se)}if(m.pseudoState==="default"&&m.theme==="light"&&m.direction==="ltr"&&m.breakpoint.id==="desktop")try{const{runAllDomCriterionAnalyzers:se,DOM_ANALYZER_COVERED_CRITERIA:Je}=await Rh(async()=>{const{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}=await import("./dom-criterion-analyzers-CGIVGgPR.js");return{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}},[]),Te=se(),rt=new Set(Te.map(Be=>Be.wcagCriterion));for(const Be of Je)rt.has(Be)||nr.push({ruleId:`wcc-clean::${Be}`,wcagCriterion:Be});for(const Be of Te){const qe=Be.element,la={selector:qe.id?`#${qe.id}`:qe===document.documentElement?"html":(qe.tagName||"unknown").toLowerCase()+(qe.classList[0]?"."+qe.classList[0]:""),outerHTML:(qe.outerHTML??"").slice(0,500),failureSummary:Be.failureSummary,tagName:qe.tagName,role:((zn=qe.getAttribute)==null?void 0:zn.call(qe,"role"))??null,accessibleName:((Vn=qe.getAttribute)==null?void 0:Vn.call(qe,"aria-label"))??null,textSnippet:((ua=qe.textContent)==null?void 0:ua.trim().slice(0,50))??null,attrId:qe.id||null,attrTestid:((Gn=qe.getAttribute)==null?void 0:Gn.call(qe,"data-testid"))??null},zr=await Ol({ruleId:Be.ruleId,componentId:B.id,currentState:m,target:la});Vt.push({ruleId:Be.ruleId,wcagCriterion:Be.wcagCriterion,wcagLevel:Be.wcagLevel,impact:Be.impact,description:Be.description,helpUrl:Be.helpUrl,target:la,componentId:B.id,currentState:m,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:zr})}}catch(se){console.warn("[audit-engine] dom-criterion-analyzers failed:",se)}return b(),{componentId:B.id,scope:d,pageUrl:typeof window<"u"&&window.location?window.location.href:void 0,state:m,violations:Vt,passes:(Ye.passes??[]).length,incomplete:(Ye.incomplete??[]).length,inapplicable:(Ye.inapplicable??[]).length,axeVersion:ht,startedAt:g,durationMs:performance.now()-F,axeRulesEvaluated:{passed:[...(Ye.passes??[]).filter(se=>!(se.tags??[]).some(Je=>Co.test(Je))).map(se=>({ruleId:se.id,wcagCriterion:Ro(se.tags??[])})).filter(se=>se.wcagCriterion),...nr],inapplicable:(Ye.inapplicable??[]).filter(se=>!(se.tags??[]).some(Je=>Co.test(Je))).map(se=>({ruleId:se.id,wcagCriterion:Ro(se.tags??[])})).filter(se=>se.wcagCriterion),incomplete:(Ye.incomplete??[]).filter(se=>!(se.tags??[]).some(Je=>Co.test(Je))).map(se=>({ruleId:se.id,wcagCriterion:Ro(se.tags??[]),elements:PO(se.id,se.nodes??[])})).filter(se=>se.wcagCriterion)}}}function VO(){const d=ye("SCOPE_FINGERPRINT_REQUEST",async z=>{try{const q=z.selector==="html"?document.documentElement:document.querySelector(z.selector);if(!q)return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null};const re=q.outerHTML,ve=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(re));return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:Array.from(new Uint8Array(ve)).map(xe=>xe.toString(16).padStart(2,"0")).join("")}}catch(q){return rr.warn("scope fingerprint failed",q),{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null}}}),m=ye("AUDIT_REQUEST",async z=>{try{return{type:"AUDIT_RESPONSE",success:!0,result:await zO(z.selector,z.currentState,z.axeConfig,z.rulesToRun)}}catch(q){rr.error("audit failed",q);const re=q instanceof Error?q.message:String(q),ve=re.toLowerCase().includes("paint")?"PAINT_TIMEOUT":"AXE_FAILED";return{type:"AUDIT_RESPONSE",success:!1,error:Tk(ve,re,!1)}}}),p=ye("CUSTOM_PROPERTY_REQUEST",async()=>{try{return{type:"CUSTOM_PROPERTY_RESPONSE",undefined:fO()}}catch(z){return rr.warn("custom-property analysis failed",z),{type:"CUSTOM_PROPERTY_RESPONSE",undefined:[]}}}),w=ye("THEME_AWARENESS_REQUEST",async()=>{try{const z=gO();return{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:z.hasLightModeCss,hasDarkModeCss:z.hasDarkModeCss,hasUnreadableSheets:z.hasUnreadableSheets}}catch(z){return rr.warn("theme-awareness probe failed",z),{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:!0,hasDarkModeCss:!0,hasUnreadableSheets:!0}}}),b=ye("READING_ORDER_REQUEST",async z=>{try{const q=document.querySelector(z.selector)??document;return{type:"READING_ORDER_RESPONSE",issues:gv(q)}}catch(q){return rr.warn("reading-order analysis failed",q),{type:"READING_ORDER_RESPONSE",issues:[]}}}),g=ye("TYPOGRAPHY_ANALYZE_REQUEST",async z=>{try{const q=document.querySelector(z.selector)??document;return{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:lO(q)}}catch(q){return rr.warn("typography analysis failed",q),{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:[]}}}),F=ye("TAB_ORDER_ANALYZE_REQUEST",async()=>{try{return{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:aO()}}catch(z){return rr.warn("tab-order analysis failed",z),{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:[]}}}),S=ye("WARMUP_REQUEST",async()=>(await OO(),{type:"WARMUP_RESPONSE"})),B=ye("AI_CANDIDATES_REQUEST",async z=>{try{return{type:"AI_CANDIDATES_RESPONSE",images:GO(z.selector),headings:YO(z.selector),instructions:KO(z.selector),ariaElements:XO(z.selector),links:ZO(z.selector),longTextBlocks:QO(z.selector),languageInfo:JO(),colorOnlyRegions:e4(z.selector)}}catch(q){return rr.warn("ai-candidates enumeration failed",q),{type:"AI_CANDIDATES_RESPONSE",images:[],headings:[],instructions:[],ariaElements:[],links:[],longTextBlocks:[],languageInfo:null,colorOnlyRegions:[]}}});return()=>{m(),d(),b(),F(),g(),S(),p(),w(),B()}}function GO(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 F=b.getBoundingClientRect();if(F.width<16||F.height<16)continue;const S=b.getAttribute("alt")??"";UO(b,S)||p.push({imageUrl:g,alt:S,selector:sa(b),outerHTML:(b.outerHTML??"").slice(0,500),surroundingContext:WO(b),pixelArea:Math.round(F.width*F.height)})}return p}function UO(d,m){if(oa(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(),F=w.getAttribute("role");if(g==="a"||g==="button"||F==="link"||F==="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 oa(d){let m=d;for(;m&&m!==document.body;){if(m.getAttribute("aria-hidden")==="true")return!0;m=m.parentElement}return!1}function sa(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 WO(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 YO(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 F of Array.from(w)){if(oa(F))continue;const S=(F.textContent??"").trim();if(!S||S.length>200)continue;const B=parseInt(((b=F.tagName.match(/^H(\d)$/))==null?void 0:b[1])??"6",10);let z="",q=F.nextElementSibling;for(;q&&z.length<800;){const re=parseInt(((g=q.tagName.match(/^H(\d)$/))==null?void 0:g[1])??"0",10);if(re>0&&re<=B)break;const ve=(q.textContent??"").trim();ve&&(z+=(z?" ":"")+ve),q=q.nextElementSibling}z.length<30||p.push({level:B,text:S,sectionContent:z.slice(0,800),selector:sa(F),outerHTML:(F.outerHTML??"").slice(0,300)})}return p}function KO(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(oa(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:sa(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20))break}return p}function XO(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(oa(g))continue;const F=g.getAttribute("role")??"";if(F&&(p.push({role:F,selector:sa(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 ZO(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(oa(b))continue;const g=(b.textContent??"").trim();if(!g||g.length>60)continue;let F="",S=b.parentElement;for(;S&&S!==document.body;){const B=S.tagName;if(B==="P"||B==="LI"||B==="DD"||B==="FIGCAPTION"||/^H[1-6]$/.test(B)){const z=(S.textContent??"").trim();if(z.length>=g.length){F=z;break}}S=S.parentElement}if(p.push({linkText:g,surroundingText:F.slice(0,800),selector:sa(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=25)break}return p}function QO(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(oa(g))continue;let F=g.parentElement,S=!1;for(;F;){if(w.has(F)){S=!0;break}F=F.parentElement}if(S)continue;const B=(g.textContent??"").trim();if(!B||B.split(/\s+/).filter(Boolean).length<300)continue;const q=g.querySelectorAll("h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, figure, table, button, input").length;if(!(q>4)&&(p.push({blockText:B.slice(0,1500),structuralChildrenCount:q,selector:sa(g),outerHTML:(g.outerHTML??"").slice(0,400)}),w.add(g),p.length>=5))break}return p}function JO(){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 e4(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(oa(b))continue;const g=(b.textContent??"").trim(),F=window.getComputedStyle(b),S=F.backgroundColor,B=F.borderColor;if(!(S&&S!=="rgba(0, 0, 0, 0)"&&S!=="transparent"||B&&B!=="rgba(0, 0, 0, 0)"&&B!=="transparent"))continue;const q=b.querySelector("svg, [aria-label], [aria-labelledby], [title]")!==null;if(g.length>0&&q)continue;const re=b.getAttribute("aria-label")??b.getAttribute("title")??"",ve=[g?`text: "${g.slice(0,60)}"`:"no text content",re?`label: "${re}"`:"no aria-label",`bg: ${S}`,q?"has icon child":"no icon child"].join("; ");if(p.push({elementHtml:(b.outerHTML??"").slice(0,400),contextDescription:ve.slice(0,400),selector:sa(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20)break}return p}const t4=Dt("storybook-integration"),r4=3e3;function a4(){const d=window.__STORYBOOK_PREVIEW__;return d&&typeof d=="object"?{detected:!0,version:d.version??"unknown"}:{detected:!1}}async function n4(){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[F,S]of p)w.push({id:F,name:S.name??F,kind:S.title??S.kind??""});else if(typeof p=="object")for(const[F,S]of Object.entries(p))w.push({id:F,name:S.name??F,kind:S.title??S.kind??""});return w}async function i4(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(),r4),b=()=>{clearTimeout(w),p()};window.addEventListener("storyrendered",b,{once:!0})}),rs({type:"STORYBOOK_NAVIGATED_EVENT",storyId:d,ready:!0})}function o4(){return document.getElementById("storybook-root")?"#storybook-root":document.getElementById("root")?"#root":"body"}function s4(){const d=[];return d.push(ye("STORYBOOK_DETECT_REQUEST",async()=>{const m=a4();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(ye("STORYBOOK_LIST_STORIES_REQUEST",async()=>({type:"STORYBOOK_LIST_STORIES_RESPONSE",stories:await n4()}))),d.push(ye("STORYBOOK_NAVIGATE_REQUEST",async m=>{await i4(m.storyId)})),d.push(ye("STORYBOOK_GET_STORY_TARGET_REQUEST",()=>({type:"STORYBOOK_GET_STORY_TARGET_RESPONSE",selector:o4()}))),t4.info("handlers registered"),()=>d.forEach(m=>m())}const Dv=Dt("element-picker");let ar=null,Pa=!1;const u4=`
40
40
  position: fixed;
41
41
  pointer-events: none;
42
42
  background: rgba(99, 102, 241, 0.20);
@@ -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-Db-lY3u2.js")
8
+ chrome.runtime.getURL("assets/content-script.ts-AibSfQOE.js")
9
9
  );
10
10
  onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
11
11
  })().catch(console.error);
@@ -0,0 +1 @@
1
+ const v=/\b(cookie|cookies|consent|gdpr|privacy|tracker|analytics|preference)s?\b/i,C=/\b(accept|agree|allow|got it|ok|okay|continue|confirm|accept all|allow all)\b/i,S=/\b(reject|decline|deny|no thanks|cancel|disagree|reject all|deny all)\b/i,g="https://www.w3.org/WAI/WCAG21/Understanding/";function w(t){let e=0;const i=window.getComputedStyle(t),r=i.position;if(r!=="fixed"&&r!=="sticky")return 0;const n=parseInt(i.zIndex,10);Number.isFinite(n)&&n>=100&&(e+=2),Number.isFinite(n)&&n>=1e3&&(e+=1);const o=(t.textContent??"").slice(0,2e3);v.test(o)&&(e+=3),C.test(o)&&(e+=1),S.test(o)&&(e+=1);const a=t.getBoundingClientRect();return a.width>=100&&a.height>=40&&(e+=1),i.display==="none"||i.visibility==="hidden"||t.getAttribute("aria-hidden")==="true"?0:e}function k(t=document){const e=[],i=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 i){const a=w(o);a>0&&e.push({el:o,score:a})}const r=t.querySelectorAll("body *");for(const o of r){if(e.some(m=>m.el===o))continue;const a=window.getComputedStyle(o);if(a.position!=="fixed"&&a.position!=="sticky")continue;const s=(o.textContent??"").slice(0,2e3);if(!v.test(s))continue;const l=w(o);l>0&&e.push({el:o,score:l})}if(e.length===0)return null;e.sort((o,a)=>a.score-o.score);const n=e[0];return n.score>=5?n.el:null}function b(t){var n,o,a,s;const e=(n=t.getAttribute("aria-label"))==null?void 0:n.trim();if(e)return e;const i=t.getAttribute("aria-labelledby");if(i){const l=(o=t.ownerDocument)==null?void 0:o.getElementById(i),m=(a=l==null?void 0:l.textContent)==null?void 0:a.trim();if(m)return m}const r=(s=t.getAttribute("title"))==null?void 0:s.trim();return r||(t.textContent??"").trim()}function I(t){const e=t.tagName;if(e==="BUTTON"||e==="A"||e==="INPUT"||e==="SELECT"||e==="TEXTAREA")return!0;const i=t.getAttribute("role");if(i==="button"||i==="link"||i==="checkbox"||i==="radio"||i==="switch")return!0;const r=t.getAttribute("tabindex");return r!==null&&parseInt(r,10)>=0}function q(t){const e=[],i=Array.from(t.querySelectorAll('button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [tabindex]'));for(const s of i){if(!I(s)||s.type==="hidden")continue;const l=b(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:`${g}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.'})}i.some(s=>{const l=b(s);return C.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:`${g}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.'}),i.some(s=>{const l=b(s);return S.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:`${g}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:`${g}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 i){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:`${g}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 $(t=document){const e=k(t);return e?q(e):[]}const p="https://www.w3.org/WAI/ARIA/apg/patterns/";function L(t){var n,o,a,s;const e=(n=t.getAttribute("aria-label"))==null?void 0:n.trim();if(e)return e;const i=t.getAttribute("aria-labelledby");if(i){const l=(o=t.ownerDocument)==null?void 0:o.getElementById(i),m=(a=l==null?void 0:l.textContent)==null?void 0:a.trim();if(m)return m}const r=(s=t.getAttribute("title"))==null?void 0:s.trim();return r||""}function E(t=document){const e=[],i=Array.from(t.querySelectorAll('[role="dialog"], [role="alertdialog"], dialog[open]'));for(const r of i){const n=r.getAttribute("role")??(r.tagName==="DIALOG"?"dialog":"");L(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:`${p}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:`${p}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 R(t=document){const e=[],i=Array.from(t.querySelectorAll('[role="tablist"]'));for(const r of i){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:`${p}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(a=>a.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:`${p}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:`${p}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 a of n){const s=a.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:`${p}tabs/`,element:a,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:`${p}tabs/`,element:a,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:`${p}tabs/`,element:a,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 U(t=document){const e=[],i=Array.from(t.querySelectorAll('[role="combobox"]'));for(const r of i){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:`${p}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:`${p}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:`${p}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 a=t.getElementById(o);if(!a)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:`${p}combobox/`,element:r,failureSummary:`Add the popup element with id="${o}" and the appropriate role (listbox, tree, grid, or dialog).`});else{const s=a.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:`${p}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 a=r.getAttribute("aria-activedescendant");a&&!t.getElementById(a)&&e.push({ruleId:"aria-pattern-combobox-broken-activedescendant",wcagCriterion:"4.1.2",wcagLevel:"A",impact:"moderate",description:`role="combobox" aria-activedescendant="${a}" references an id that doesn't exist.`,helpUrl:`${p}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 T(t=document){return[...E(t),...R(t),...U(t)]}const c="https://www.w3.org/WAI/WCAG21/Understanding/";function N(t=document){const e=[],i=t.querySelectorAll('meta[http-equiv="refresh" i]');for(const n of Array.from(i)){const o=n.getAttribute("content")??"",a=parseInt(o.split(";")[0]??"",10);Number.isFinite(a)&&a>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 ${a}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: ${a}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 O(t=document){const e=[],i=t.querySelectorAll("marquee, blink");for(const a of Array.from(i))e.push({ruleId:"wcc-marquee-blink",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"serious",description:`<${a.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:a,failureSummary:`Deprecated <${a.tagName.toLowerCase()}> element auto-animates without pause control.`});const r=t.querySelectorAll("video[autoplay]");for(const a of Array.from(r))!a.hasAttribute("controls")&&!a.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:a,failureSummary:"Autoplay video lacks user-accessible pause control."});const n=t.querySelectorAll("*");let o=0;for(const a of Array.from(n)){if(o>1500)break;o++;const s=window.getComputedStyle(a);if(s.animationName==="none"||!s.animationName)continue;const l=s.animationDuration.split(",").map(d=>parseFloat(d)),m=s.animationIterationCount.split(",").map(d=>d.trim());for(let d=0;d<l.length;d++){const u=l[d]??0,f=(m[d]??"1")==="infinite";f&&u>0&&u<.34&&e.push({ruleId:"wcc-flash-risk",wcagCriterion:"2.3.1",wcagLevel:"A",impact:"critical",description:`Element animates with duration ${u.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:a,failureSummary:`Infinite CSS animation @ ${u.toFixed(2)}s/cycle = ${(1/u).toFixed(1)} flashes/sec.`}),f&&u>=5&&e.push({ruleId:"wcc-long-infinite-animation",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"moderate",description:`Element runs an infinite CSS animation (${u.toFixed(1)}s/cycle). WCAG 2.2.2 requires a way to pause, stop, or hide animation longer than 5 seconds. Verify a pause mechanism exists.`,helpUrl:`${c}pause-stop-hide.html`,element:a,failureSummary:`Infinite animation @ ${u.toFixed(1)}s/cycle. Confirm user can pause.`})}}return e}function z(t=document){const e=[],i=new Set,r=t.querySelectorAll("[ontouchstart], [ontouchmove], [ongesturestart], [ongesturechange]");for(const o of Array.from(r)){const a=A(o);i.has(a)||(i.add(a),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 a=A(o);i.has(a)||(i.add(a),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 W(t=document){const e=[],i=t.querySelectorAll("video");for(const n of Array.from(i)){const o=Array.from(n.querySelectorAll("track")),a=o.some(l=>(l.kind??"").toLowerCase()==="captions"||(l.kind??"").toLowerCase()==="subtitles"),s=o.some(l=>(l.kind??"").toLowerCase()==="descriptions");a||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 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 G(t=document){const e=[],i=/\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 a=o.getAttribute("onfocus")??"";i.test(a)&&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 a=o.getAttribute("onchange")??"";i.test(a)&&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 P(t=document){const e=[],i=[".g-recaptcha","[data-sitekey]",".h-captcha",".cf-turnstile",".frc-captcha",'iframe[src*="recaptcha"]','iframe[src*="hcaptcha"]','iframe[src*="turnstile"]'].join(", "),r=t.querySelectorAll(i),n=new Set;for(const o of Array.from(r)){if(n.has(o))continue;n.add(o);const a=o.closest("form"),s=((a==null?void 0:a.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 M(t=document){const e=[],i=t.querySelectorAll('meta[name="viewport" i]');for(const n of Array.from(i)){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 a of Array.from(o)){if(!(a instanceof CSSMediaRule))continue;const s=a.media.mediaText.toLowerCase(),l=/\(\s*orientation\s*:\s*portrait\s*\)/.test(s),m=/\(\s*orientation\s*:\s*landscape\s*\)/.test(s);if(!(!l&&!m)){for(const d of Array.from(a.cssRules))if(d instanceof CSSStyleRule&&/display\s*:\s*none/i.test(d.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 "${d.selectorText}".`});break}}}}catch{}}return e}function F(t=document){const e=[];let i=0;const r=t.querySelectorAll("[title]");for(const n of Array.from(r)){if(i>200)break;i++;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 H(t=document){const e=[],i=typeof window<"u"?window.location.pathname:"/";if(i==="/"||i===""||/\/(search|results)\b/.test(i))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,a=Array.from(t.querySelectorAll('a[href^="#"]')).length>=3,s=[r,n,o,a].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=${r}, search=${n}, sitemap=${o}, toc=${a}).`}),e}function D(t=document){const e=[],i=/\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")??"");i.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 j(t=document){const e=[],i=t.querySelectorAll("script:not([src])");for(const r of Array.from(i)){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 B(t=document){const e=[],i=t.querySelectorAll("form");for(const r of Array.from(i)){const n=r.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 a=/\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:a?"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 _(t=document){const e=[],i=t.querySelectorAll("[onkeydown], [onkeypress], [onkeyup]");if(i.length===0)return e;const r=Array.from(i).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 V(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}function K(t=document){const e=[],i=t.querySelector('nav, [role="navigation"]')!=null,r=t.querySelector('footer, [role="contentinfo"]')!=null,n=t.querySelector('header a[href="/"], [role="banner"] a[href="/"]')!=null;return!i&&!r&&!n||e.push({ruleId:"wcc-consistency-needs-site-crawl",wcagCriterion:"3.2.3",wcagLevel:"AA",impact:"minor",description:"This page has navigation / footer / brand-link structure that WCAG 3.2.3 / 3.2.4 / 3.2.6 require to be consistent ACROSS pages. A single-page audit cannot verify multi-page consistency. Run the Site Crawl flow (Crawl tab) on a representative sample of pages, then re-export the report — the crawler will emit per-divergence findings.",helpUrl:`${c}consistent-navigation.html`,element:t.documentElement,failureSummary:"Multi-page consistency requires site-crawl coverage."}),e}function X(t=document){const e=[],i=t.querySelectorAll("form");if(i.length===0)return e;let r=!1,n=!1;for(const o of Array.from(i)){const a=o.querySelectorAll('input[required], input[pattern], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]');if(a.length!==0){r=!0;for(const s of Array.from(a))if(s.getAttribute("aria-describedby")||s.getAttribute("aria-errormessage")||s.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:i[0],failureSummary:"Form has constrained inputs but no suggestion-infrastructure (datalist / aria-describedby / aria-errormessage)."}),e}function J(t=document){var m;const e=[],i=t.querySelectorAll("form");if(i.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,a=/\b(undo|reverse|cancel|edit|update later|change later)\b/i;let s=!1,l=null;for(const d of Array.from(i)){const u=(d.textContent??"")+" "+(d.getAttribute("aria-label")??"");if(!r.test(u)||n.test(u)&&!/\$|usd|eur|gbp/i.test(u))continue;s=!0,l=d;const h=u+" "+(((m=d.closest("section, main, body"))==null?void 0:m.textContent)??"");if(o.test(h)||a.test(h))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 Y(t=document){const e=[],i=t.querySelectorAll("audio[autoplay], video[autoplay]");if(i.length===0)return e;for(const r of Array.from(i))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 Z(t=document){const e=[],i=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(i).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 a=Number(n.opacity);if(Number.isFinite(a)&&a<=.1)continue;const s=n.zIndex;if(s&&s!=="auto"){const y=Number(s);if(Number.isFinite(y)&&y<0)continue}const l=n.backgroundColor,m=/rgba?\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\s*\)/.exec(l);if((m?Number(m[1]):1)<.5&&!n.backgroundImage.match(/url\(/))continue;const u=r.getBoundingClientRect();if(u.width===0||u.height===0)continue;const h=window.innerHeight||document.documentElement.clientHeight,f=u.top<80&&u.bottom>0,x=u.bottom>h-80&&u.top<h;!f&&!x||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. WCAG 2.4.11 (AA, WCAG 2.2) requires that focused components NOT be entirely hidden by such overlays. Verify that tabbing to an element near this overlay still scrolls the element into a visible position. Common fixes: scroll-margin-top: 80px on focusable elements, scroll-padding-top on the scroll container, or a focus-aware reveal that hides the sticky overlay when focus lands beneath it.`,helpUrl:`${c}focus-not-obscured-minimum.html`,element:r,failureSummary:`${o} element overlapping viewport edge — verify focus visibility.`})}return e}function Q(t=document){const e=[],i=t.querySelectorAll("form");if(i.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 a of Array.from(i)){const s=a.textContent??"";if(r.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 ee(t=document){return[]}function A(t){if(t.id)return`#${t.id}`;const e=t.classList[0];return`${t.tagName.toLowerCase()}${e?"."+e:""}`}function te(t=document){return[...N(t),...O(t),...z(t),...W(t),...G(t),...P(t),...M(t),...F(t),...H(t),...D(t),...j(t),...B(t),..._(t),...V(t),...ee(t),...K(t),...X(t),...J(t),...Y(t),...Z(t),...Q(t),...$(t),...T(t)]}const ne=["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.2.3","3.2.4","3.2.6","3.3.3","3.3.4","1.4.2","2.4.11","3.3.7"];export{ne as DOM_ANALYZER_COVERED_CRITERIA,O as analyzeAnimation,Y as analyzeAudioControl,_ as analyzeCharacterKeyShortcuts,P as analyzeCognitiveAuth,K as analyzeConsistencyHints,F as analyzeContentOnHover,G as analyzeContextChangeOnFocusInput,B as analyzeErrorIdentification,J as analyzeErrorPreventionLegalFinancial,X as analyzeErrorSuggestion,Z as analyzeFocusNotObscured,W as analyzeMedia,j as analyzeMotionActuation,H as analyzeMultipleWays,M as analyzeOrientation,ee as analyzeParsingObsoleted,D as analyzePointerCancellation,z as analyzePointerGestures,Q as analyzeRedundantEntry,V as analyzeStatusMessages,N as analyzeTimingAdjustable,te as runAllDomCriterionAnalyzers};
@@ -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.147",
6
- "version_name": "1.0.0-rc.147",
5
+ "version": "1.0.0.148",
6
+ "version_name": "1.0.0-rc.148",
7
7
  "author": "Locustware",
8
8
  "homepage_url": "https://wcagcheckr.com",
9
9
  "icons": {
@@ -38,7 +38,7 @@
38
38
  "content_scripts": [
39
39
  {
40
40
  "js": [
41
- "assets/content-script.ts-loader-Bcb1BN4u.js"
41
+ "assets/content-script.ts-loader-DkZUgl7Z.js"
42
42
  ],
43
43
  "matches": [
44
44
  "<all_urls>"
@@ -66,8 +66,8 @@
66
66
  "assets/_commonjsHelpers-Cpj98o6Y.js",
67
67
  "assets/custom-rules-CAe0nbES.js",
68
68
  "assets/reflow-analyzer-DNgBX8N_.js",
69
- "assets/dom-criterion-analyzers-DCSIVGo4.js",
70
- "assets/content-script.ts-Db-lY3u2.js"
69
+ "assets/dom-criterion-analyzers-CGIVGgPR.js",
70
+ "assets/content-script.ts-AibSfQOE.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.147",
3
+ "version": "1.0.0-rc.148",
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",
@@ -1 +0,0 @@
1
- const A=/\b(cookie|cookies|consent|gdpr|privacy|tracker|analytics|preference)s?\b/i,v=/\b(accept|agree|allow|got it|ok|okay|continue|confirm|accept all|allow all)\b/i,C=/\b(reject|decline|deny|no thanks|cancel|disagree|reject all|deny all)\b/i,f="https://www.w3.org/WAI/WCAG21/Understanding/";function b(e){let t=0;const o=window.getComputedStyle(e),a=o.position;if(a!=="fixed"&&a!=="sticky")return 0;const n=parseInt(o.zIndex,10);Number.isFinite(n)&&n>=100&&(t+=2),Number.isFinite(n)&&n>=1e3&&(t+=1);const i=(e.textContent??"").slice(0,2e3);A.test(i)&&(t+=3),v.test(i)&&(t+=1),C.test(i)&&(t+=1);const r=e.getBoundingClientRect();return r.width>=100&&r.height>=40&&(t+=1),o.display==="none"||o.visibility==="hidden"||e.getAttribute("aria-hidden")==="true"?0:t}function k(e=document){const t=[],o=e.querySelectorAll('[class*="cookie" i], [class*="consent" i], [id*="cookie" i], [id*="consent" i], dialog[open], div[role="dialog"], div[role="alertdialog"]');for(const i of o){const r=b(i);r>0&&t.push({el:i,score:r})}const a=e.querySelectorAll("body *");for(const i of a){if(t.some(m=>m.el===i))continue;const r=window.getComputedStyle(i);if(r.position!=="fixed"&&r.position!=="sticky")continue;const s=(i.textContent??"").slice(0,2e3);if(!A.test(s))continue;const c=b(i);c>0&&t.push({el:i,score:c})}if(t.length===0)return null;t.sort((i,r)=>r.score-i.score);const n=t[0];return n.score>=5?n.el:null}function g(e){var n,i,r,s;const t=(n=e.getAttribute("aria-label"))==null?void 0:n.trim();if(t)return t;const o=e.getAttribute("aria-labelledby");if(o){const c=(i=e.ownerDocument)==null?void 0:i.getElementById(o),m=(r=c==null?void 0:c.textContent)==null?void 0:r.trim();if(m)return m}const a=(s=e.getAttribute("title"))==null?void 0:s.trim();return a||(e.textContent??"").trim()}function x(e){const t=e.tagName;if(t==="BUTTON"||t==="A"||t==="INPUT"||t==="SELECT"||t==="TEXTAREA")return!0;const o=e.getAttribute("role");if(o==="button"||o==="link"||o==="checkbox"||o==="radio"||o==="switch")return!0;const a=e.getAttribute("tabindex");return a!==null&&parseInt(a,10)>=0}function q(e){const t=[],o=Array.from(e.querySelectorAll('button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [tabindex]'));for(const s of o){if(!x(s)||s.type==="hidden")continue;const c=g(s);(!c||c.length<2)&&t.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:`${f}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.'})}o.some(s=>{const c=g(s);return v.test(c)})||t.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:`${f}consistent-identification`,element:e,failureSummary:'Add a button with text or aria-label containing "Accept" / "Allow" / "Agree" so the consent action is identifiable from the label alone.'}),o.some(s=>{const c=g(s);return C.test(c)})||t.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:`${f}consistent-identification`,element:e,failureSummary:'Add a button with text or aria-label containing "Reject" / "Decline" / "Reject all" alongside the accept control at the same hierarchy level.'});const i=e.getAttribute("role");e.tagName==="DIALOG"||i==="dialog"||i==="alertdialog"||t.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:`${f}info-and-relationships`,element:e,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 o){const c=s.getAttribute("tabindex");c&&parseInt(c,10)<0&&t.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:`${f}keyboard`,element:s,failureSummary:'Remove the negative tabindex, or replace with tabindex="0" so the control participates in the keyboard tab order.'})}return t}function I(e=document){const t=k(e);return t?q(t):[]}const l="https://www.w3.org/WAI/WCAG21/Understanding/";function E(e=document){const t=[],o=e.querySelectorAll('meta[http-equiv="refresh" i]');for(const n of Array.from(o)){const i=n.getAttribute("content")??"",r=parseInt(i.split(";")[0]??"",10);Number.isFinite(r)&&r>0&&t.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:`${l}timing-adjustable.html`,element:n,failureSummary:`Auto-refresh interval: ${r}s. No user control mechanism on the page.`})}const a=e.querySelectorAll("script:not([src])");for(const n of Array.from(a)){const i=n.textContent??"";i.length===0||!/\b(setTimeout|setInterval)\s*\(/.test(i)||!/\b(innerHTML|textContent|replaceChildren|window\.location|document\.location|location\.href|location\.replace)\b/.test(i)||t.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:`${l}timing-adjustable.html`,element:n,failureSummary:"Script combines timer + DOM mutation. Without a user control to pause/extend/disable, fails 2.2.1."})}return t}function L(e=document){const t=[],o=e.querySelectorAll("marquee, blink");for(const r of Array.from(o))t.push({ruleId:"wcc-marquee-blink",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"serious",description:`<${r.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:`${l}pause-stop-hide.html`,element:r,failureSummary:`Deprecated <${r.tagName.toLowerCase()}> element auto-animates without pause control.`});const a=e.querySelectorAll("video[autoplay]");for(const r of Array.from(a))!r.hasAttribute("controls")&&!r.muted&&t.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:`${l}pause-stop-hide.html`,element:r,failureSummary:"Autoplay video lacks user-accessible pause control."});const n=e.querySelectorAll("*");let i=0;for(const r of Array.from(n)){if(i>1500)break;i++;const s=window.getComputedStyle(r);if(s.animationName==="none"||!s.animationName)continue;const c=s.animationDuration.split(",").map(d=>parseFloat(d)),m=s.animationIterationCount.split(",").map(d=>d.trim());for(let d=0;d<c.length;d++){const u=c[d]??0,h=(m[d]??"1")==="infinite";h&&u>0&&u<.34&&t.push({ruleId:"wcc-flash-risk",wcagCriterion:"2.3.1",wcagLevel:"A",impact:"critical",description:`Element animates with duration ${u.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:`${l}three-flashes-or-below-threshold.html`,element:r,failureSummary:`Infinite CSS animation @ ${u.toFixed(2)}s/cycle = ${(1/u).toFixed(1)} flashes/sec.`}),h&&u>=5&&t.push({ruleId:"wcc-long-infinite-animation",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"moderate",description:`Element runs an infinite CSS animation (${u.toFixed(1)}s/cycle). WCAG 2.2.2 requires a way to pause, stop, or hide animation longer than 5 seconds. Verify a pause mechanism exists.`,helpUrl:`${l}pause-stop-hide.html`,element:r,failureSummary:`Infinite animation @ ${u.toFixed(1)}s/cycle. Confirm user can pause.`})}}return t}function $(e=document){const t=[],o=new Set,a=e.querySelectorAll("[ontouchstart], [ontouchmove], [ongesturestart], [ongesturechange]");for(const i of Array.from(a)){const r=w(i);o.has(r)||(o.add(r),t.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:`${l}pointer-gestures.html`,element:i,failureSummary:"Touch/gesture handler on this element. Confirm single-pointer alternative."}))}const n=e.querySelectorAll('[draggable="true"], [ondragstart]');for(const i of Array.from(n)){const r=w(i);o.has(r)||(o.add(r),t.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:`${l}dragging-movements.html`,element:i,failureSummary:"Drag-and-drop interaction. Confirm non-drag alternative for keyboard / touch users."}))}return t}function R(e=document){const t=[],o=e.querySelectorAll("video");for(const n of Array.from(o)){const i=Array.from(n.querySelectorAll("track")),r=i.some(c=>(c.kind??"").toLowerCase()==="captions"||(c.kind??"").toLowerCase()==="subtitles"),s=i.some(c=>(c.kind??"").toLowerCase()==="descriptions");r||t.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:`${l}captions-prerecorded.html`,element:n,failureSummary:'No <track kind="captions"> child. Captions required unless video is silent.'}),s||(t.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:`${l}audio-description-prerecorded.html`,element:n,failureSummary:'No <track kind="descriptions"> child. Audio description required when visuals carry info not in audio.'}),t.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:`${l}audio-description-or-media-alternative-prerecorded.html`,element:n,failureSummary:'No <track kind="descriptions"> and no detectable text alternative. Need at least one.'}))}const a=e.querySelectorAll("audio");for(const n of Array.from(a))t.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:`${l}audio-only-and-video-only-prerecorded.html`,element:n,failureSummary:"Audio element detected. Verify nearby transcript link."});return t}function T(e=document){const t=[],o=/\b(window\.location|document\.location|location\.href|location\.replace|window\.open|\.submit\s*\()/,a=e.querySelectorAll("[onfocus]");for(const i of Array.from(a)){const r=i.getAttribute("onfocus")??"";o.test(r)&&t.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:`${l}on-focus.html`,element:i,failureSummary:"Focus handler navigates / submits / opens window. Move trigger to explicit click/keypress."})}const n=e.querySelectorAll("select[onchange], input[onchange], textarea[onchange]");for(const i of Array.from(n)){const r=i.getAttribute("onchange")??"";o.test(r)&&t.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:`${l}on-input.html`,element:i,failureSummary:"Change handler navigates / submits. Replace with a separate submit button."})}return t}function U(e=document){const t=[],o=[".g-recaptcha","[data-sitekey]",".h-captcha",".cf-turnstile",".frc-captcha",'iframe[src*="recaptcha"]','iframe[src*="hcaptcha"]','iframe[src*="turnstile"]'].join(", "),a=e.querySelectorAll(o),n=new Set;for(const i of Array.from(a)){if(n.has(i))continue;n.add(i);const r=i.closest("form"),s=((r==null?void 0:r.textContent)??"").toLowerCase(),c=/audio captcha|alternative|sign in with|continue with|use email|magic link/.test(s);t.push({ruleId:"wcc-cognitive-auth-challenge",wcagCriterion:"3.3.8",wcagLevel:"AA",impact:c?"moderate":"serious",description:c?"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:`${l}accessible-authentication-minimum.html`,element:i,failureSummary:c?"CAPTCHA detected; alternative may exist — verify it skips the cognitive challenge.":"CAPTCHA detected without an alternative authentication path."})}return t}function W(e=document){const t=[],o=e.querySelectorAll('meta[name="viewport" i]');for(const n of Array.from(o)){const i=n.getAttribute("content")??"";(/user-scalable\s*=\s*(no|0)\b/i.test(i)||/maximum-scale\s*=\s*1(\.0+)?\b/i.test(i))&&t.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:`${l}resize-text.html`,element:n,failureSummary:"Viewport meta disables zoom. Remove user-scalable=no and maximum-scale<2."})}let a=0;for(const n of Array.from(e.styleSheets)){if(a>40)break;a++;try{const i=n.cssRules;if(!i)continue;for(const r of Array.from(i)){if(!(r instanceof CSSMediaRule))continue;const s=r.media.mediaText.toLowerCase(),c=/\(\s*orientation\s*:\s*portrait\s*\)/.test(s),m=/\(\s*orientation\s*:\s*landscape\s*\)/.test(s);if(!(!c&&!m)){for(const d of Array.from(r.cssRules))if(d instanceof CSSStyleRule&&/display\s*:\s*none/i.test(d.style.cssText)){t.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: ${c?"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:`${l}orientation.html`,element:e.documentElement,failureSummary:`Orientation-locked display:none on selector "${d.selectorText}".`});break}}}}catch{}}return t}function z(e=document){const t=[];let o=0;const a=e.querySelectorAll("[title]");for(const n of Array.from(a)){if(o>200)break;o++;const i=n.getAttribute("title");!i||i.trim().length<8||n.hasAttribute("aria-describedby")||n.tagName==="A"&&(n.textContent??"").includes(i)||t.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:`${l}content-on-hover-or-focus.html`,element:n,failureSummary:"title-attribute tooltip is not dismissible / hoverable."})}return t}function N(e=document){const t=[],o=typeof window<"u"?window.location.pathname:"/";if(o==="/"||o===""||/\/(search|results)\b/.test(o))return t;const a=e.querySelector('nav, [role="navigation"]')!==null,n=e.querySelector('input[type="search"], [role="search"]')!==null||Array.from(e.querySelectorAll('input[type="text"]')).some(c=>/search/i.test(c.name)||/search/i.test(c.placeholder??"")),i=e.querySelector('a[href*="sitemap" i]')!==null,r=Array.from(e.querySelectorAll('a[href^="#"]')).length>=3,s=[a,n,i,r].filter(Boolean).length;return s<2&&t.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:`${l}multiple-ways.html`,element:e.documentElement,failureSummary:`Found ${s} of 4 affordances (nav=${a}, search=${n}, sitemap=${i}, toc=${r}).`}),t}function O(e=document){const t=[],o=/\b(window\.location|location\.href|\.submit\s*\(|window\.open|fetch\s*\(|XMLHttpRequest)\b/,a=e.querySelectorAll("[onmousedown], [onpointerdown]");for(const n of Array.from(a)){const i=(n.getAttribute("onmousedown")??"")+";"+(n.getAttribute("onpointerdown")??"");o.test(i)&&t.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:`${l}pointer-cancellation.html`,element:n,failureSummary:"Action fires on pointer-down; user cannot abort by dragging away before release."})}return t}function G(e=document){const t=[],o=e.querySelectorAll("script:not([src])");for(const a of Array.from(o)){const n=a.textContent??"";if(/\b(devicemotion|deviceorientation)\b/.test(n)&&/\baddEventListener\s*\(\s*['"`](devicemotion|deviceorientation)/.test(n)){t.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:`${l}motion-actuation.html`,element:a,failureSummary:"Device-motion listener present. Confirm UI-control alternative + disable mechanism."});break}}return t}function F(e=document){const t=[],o=e.querySelectorAll("form");for(const a of Array.from(o)){const n=a.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(a.textContent??"");t.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:`${l}error-identification.html`,element:a,failureSummary:`${n.length} required input(s) without ARIA error pattern.`})}return t}function P(e=document){const t=[],o=e.querySelectorAll("[onkeydown], [onkeypress], [onkeyup]");if(o.length===0)return t;const a=Array.from(o).slice(0,5);for(const n of a)t.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:`${l}character-key-shortcuts.html`,element:n,failureSummary:"Inline keyboard handler detected — verify shortcuts are togglable / scoped."});return t}function M(e=document){const t=[];if(e.querySelectorAll('[role="status"], [role="alert"], [role="log"], [aria-live]').length>0)return t;const a=e.querySelector("form input[required], form input[pattern]")!=null,n=e.getElementById("root")!=null||e.getElementById("app")!=null||e.querySelector("[data-reactroot], [data-vue-app]")!=null,i=e.querySelector('[class*="toast" i], [class*="snackbar" i], [class*="notif" i]')!=null;return(a||n||i)&&t.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:`${l}status-messages.html`,element:e.documentElement,failureSummary:"No live regions detected on a page that appears to have dynamic content."}),t}function H(e=document){const t=[],o=e.querySelector('nav, [role="navigation"]')!=null,a=e.querySelector('footer, [role="contentinfo"]')!=null,n=e.querySelector('header a[href="/"], [role="banner"] a[href="/"]')!=null;return!o&&!a&&!n||t.push({ruleId:"wcc-consistency-needs-site-crawl",wcagCriterion:"3.2.3",wcagLevel:"AA",impact:"minor",description:"This page has navigation / footer / brand-link structure that WCAG 3.2.3 / 3.2.4 / 3.2.6 require to be consistent ACROSS pages. A single-page audit cannot verify multi-page consistency. Run the Site Crawl flow (Crawl tab) on a representative sample of pages, then re-export the report — the crawler will emit per-divergence findings.",helpUrl:`${l}consistent-navigation.html`,element:e.documentElement,failureSummary:"Multi-page consistency requires site-crawl coverage."}),t}function j(e=document){const t=[],o=e.querySelectorAll("form");if(o.length===0)return t;let a=!1,n=!1;for(const i of Array.from(o)){const r=i.querySelectorAll('input[required], input[pattern], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]');if(r.length!==0){a=!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!a||n||t.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:`${l}error-suggestion.html`,element:o[0],failureSummary:"Form has constrained inputs but no suggestion-infrastructure (datalist / aria-describedby / aria-errormessage)."}),t}function _(e=document){var m;const t=[],o=e.querySelectorAll("form");if(o.length===0)return t;const a=/\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,i=/\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,c=null;for(const d of Array.from(o)){const u=(d.textContent??"")+" "+(d.getAttribute("aria-label")??"");if(!a.test(u)||n.test(u)&&!/\$|usd|eur|gbp/i.test(u))continue;s=!0,c=d;const p=u+" "+(((m=d.closest("section, main, body"))==null?void 0:m.textContent)??"");if(i.test(p)||r.test(p))return t}return s&&t.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:`${l}error-prevention-legal-financial-data.html`,element:c??e.documentElement,failureSummary:"Commit-style form with no detected review/confirm/reversibility cue."}),t}function D(e=document){const t=[],o=e.querySelectorAll("audio[autoplay], video[autoplay]");if(o.length===0)return t;for(const a of Array.from(o))a.hasAttribute("controls")||a.tagName.toLowerCase()==="video"&&a.hasAttribute("muted")||t.push({ruleId:"wcc-autoplay-no-controls",wcagCriterion:"1.4.2",wcagLevel:"A",impact:"serious",description:`${a.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:`${l}audio-control.html`,element:a,failureSummary:"Autoplay media without controls — needs pause/stop/volume mechanism."});return t}function B(e=document){const t=[],o=e.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 a of Array.from(o).slice(0,30)){const n=getComputedStyle(a),i=n.position;if(i!=="sticky"&&i!=="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 y=Number(s);if(Number.isFinite(y)&&y<0)continue}const c=n.backgroundColor,m=/rgba?\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\s*\)/.exec(c);if((m?Number(m[1]):1)<.5&&!n.backgroundImage.match(/url\(/))continue;const u=a.getBoundingClientRect();if(u.width===0||u.height===0)continue;const p=window.innerHeight||document.documentElement.clientHeight,h=u.top<80&&u.bottom>0,S=u.bottom>p-80&&u.top<p;!h&&!S||t.push({ruleId:"wcc-sticky-may-obscure-focus",wcagCriterion:"2.4.11",wcagLevel:"AA",impact:"moderate",description:`Element is ${i}-positioned and overlaps the viewport's ${h?"top":"bottom"} edge with an opaque background. WCAG 2.4.11 (AA, WCAG 2.2) requires that focused components NOT be entirely hidden by such overlays. Verify that tabbing to an element near this overlay still scrolls the element into a visible position. Common fixes: scroll-margin-top: 80px on focusable elements, scroll-padding-top on the scroll container, or a focus-aware reveal that hides the sticky overlay when focus lands beneath it.`,helpUrl:`${l}focus-not-obscured-minimum.html`,element:a,failureSummary:`${i} element overlapping viewport edge — verify focus visibility.`})}return t}function V(e=document){const t=[],o=e.querySelectorAll("form");if(o.length===0)return t;const a=/\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 i=!1;for(const r of Array.from(o)){const s=r.textContent??"";if(a.test(s)){i=!0;break}}return!i&&e.querySelectorAll(n).length>0&&(i=!0),i&&t.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:`${l}redundant-entry.html`,element:e.documentElement,failureSummary:"Multi-step flow signals detected — needs site-crawl coverage."}),t}function K(e=document){return[]}function w(e){if(e.id)return`#${e.id}`;const t=e.classList[0];return`${e.tagName.toLowerCase()}${t?"."+t:""}`}function X(e=document){return[...E(e),...L(e),...$(e),...R(e),...T(e),...U(e),...W(e),...z(e),...N(e),...O(e),...G(e),...F(e),...P(e),...M(e),...K(e),...H(e),...j(e),..._(e),...D(e),...B(e),...V(e),...I(e)]}const J=["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.2.3","3.2.4","3.2.6","3.3.3","3.3.4","1.4.2","2.4.11","3.3.7"];export{J as DOM_ANALYZER_COVERED_CRITERIA,L as analyzeAnimation,D as analyzeAudioControl,P as analyzeCharacterKeyShortcuts,U as analyzeCognitiveAuth,H as analyzeConsistencyHints,z as analyzeContentOnHover,T as analyzeContextChangeOnFocusInput,F as analyzeErrorIdentification,_ as analyzeErrorPreventionLegalFinancial,j as analyzeErrorSuggestion,B as analyzeFocusNotObscured,R as analyzeMedia,G as analyzeMotionActuation,N as analyzeMultipleWays,W as analyzeOrientation,K as analyzeParsingObsoleted,O as analyzePointerCancellation,$ as analyzePointerGestures,V as analyzeRedundantEntry,M as analyzeStatusMessages,E as analyzeTimingAdjustable,X as runAllDomCriterionAnalyzers};