@wcag-checkr/ci 1.0.0-rc.105 → 1.0.0-rc.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{content-script.ts-DaLbQgaA.js → content-script.ts-3-biN-xk.js} +1 -1
- package/dist/assets/{content-script.ts-loader-BHryrwSa.js → content-script.ts-loader-Czc3Zduw.js} +1 -1
- package/dist/assets/{dom-criterion-analyzers-Bdtobl22.js → dom-criterion-analyzers-DsEV9zo7.js} +1 -1
- package/dist/manifest.json +5 -5
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@ See: https://github.com/dequelabs/axe-core/blob/master/doc/context.md`))}functio
|
|
|
36
36
|
transition-delay: 0ms !important;
|
|
37
37
|
caret-color: transparent !important;
|
|
38
38
|
}
|
|
39
|
-
`,oO=.85,Dh=80,sO=300,uO=4e3;async function lO(){var f;if(!(typeof document>"u"||typeof window>"u"))try{const h=window.scrollY,p=window.scrollX,D=Math.max(document.documentElement.scrollHeight,((f=document.body)==null?void 0:f.scrollHeight)??0),b=window.innerHeight||0;if(D<=b)return;const g=Math.max(100,b*oO),S=performance.now();for(let I=0;I<D&&!(performance.now()-S>uO);I+=g)window.scrollTo({top:I,left:0,behavior:"instant"}),await new Promise(Y=>setTimeout(Y,Dh));window.scrollTo({top:D,left:0,behavior:"instant"}),await new Promise(I=>setTimeout(I,Dh)),window.scrollTo({top:h,left:p,behavior:"instant"}),await new Promise(I=>setTimeout(I,sO))}catch(h){er.warn("scroll-reveal warmup failed (continuing with audit)",h)}}function cO(){if(typeof document>"u")return()=>{};if(document.getElementById(wh))return()=>{};const f=document.createElement("style");return f.id=wh,f.textContent=iO,document.head.appendChild(f),()=>f.remove()}function dO(f){return f==="minor"||f==="moderate"||f==="serious"||f==="critical"?f:"moderate"}function fO(f){return f.some(h=>h.endsWith("aaa"))?"AAA":f.some(h=>h.endsWith("aa"))?"AA":"A"}function Do(f){for(const h of f){const p=/^wcag(\d)(\d)(\d{1,2})$/.exec(h);if(p)return`${p[1]}.${p[2]}.${parseInt(p[3],10)}`}return""}function pO(f,h){const D=[];for(let b=0;b<h.length&&D.length<25;b++){const g=h[b],S=Array.isArray(g.target)?g.target.join(" "):g.target;if(!S)continue;const I=(()=>{try{return document.querySelector(S)}catch{return null}})(),Y={selector:S,failureSummary:g.failureSummary};if(I instanceof HTMLElement){const K=I.outerHTML;if(Y.outerHTMLSnippet=K.length>1200?K.slice(0,1200)+"…":K,f==="color-contrast"||f==="color-contrast-enhanced"){const z=window.getComputedStyle(I);Y.styles={foreground:z.color||void 0,background:z.backgroundColor||void 0,fontSize:parseFloat(z.fontSize)||void 0,fontWeight:parseInt(z.fontWeight,10)||void 0,textSample:(I.textContent??"").trim().slice(0,120)||void 0,effectiveBackground:mO(I)}}}D.push(Y)}return D}function mO(f){const h=new Set(["rgba(0, 0, 0, 0)","transparent",""]);let p=f,D,b=!1;for(;p;){const g=window.getComputedStyle(p),S=g.backgroundImage;S&&S!=="none"&&!b&&(b=!0,D=Eh(p));const I=g.backgroundColor;if(I&&!h.has(I))return{color:I,fromAncestor:p===f?void 0:Eh(p),hasBackgroundImageInChain:b,imageAncestor:D};p=p.parentElement}return{color:"rgb(255, 255, 255)",fromAncestor:"document default",hasBackgroundImageInChain:b,imageAncestor:D}}function Eh(f){const h=f.tagName.toLowerCase(),p=f.id?`#${f.id}`:"",D=f.classList.length>0?`.${f.classList[0]}`:"";return`${h}${p}${D}`}function hO(f,h,p){var S,I;const D=((S=f.textContent)==null?void 0:S.trim().slice(0,50))??null,b=(I=f.getBoundingClientRect)==null?void 0:I.call(f),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(h.target)?h.target.join(" "):String(h.target),outerHTML:(f.outerHTML??"").slice(0,500),failureSummary:h.failureSummary??"",tagName:f.tagName,role:f.getAttribute("role"),accessibleName:f.getAttribute("aria-label")??D,textSnippet:D,attrId:f.id||null,attrTestid:f.getAttribute("data-testid"),boundingRect:g,opacityContext:vO(f),cascadeChain:yO(f,p)}}function vO(f){if(typeof getComputedStyle!="function")return;let h=1,p=1,D;try{const b=getComputedStyle(f);h=parseFloat(b.opacity||"1");let g=f.parentElement;for(;g&&g!==document.documentElement;){const S=getComputedStyle(g),I=parseFloat(S.opacity||"1");I<p&&(p=I,D=gO(g)),g=g.parentElement}}catch{return}if(!(h>=.99&&p>=.99))return{self:Math.round(h*100)/100,ancestor:Math.round(p*100)/100,ancestorSelector:D}}function gO(f){const h=f.tagName.toLowerCase();if(f.id)return`${h}#${f.id}`;const p=f.classList.length>0?"."+Array.from(f.classList).slice(0,2).join("."):"";return`${h}${p}`}const bO=/var\(\s*(--[A-Za-z0-9_-]+)/g;function yO(f,h){if(!(h!=="color-contrast"&&h!=="color-contrast-enhanced")&&typeof getComputedStyle=="function")try{const p=getComputedStyle(f);return{color:_h(f,"color",p.color),backgroundColor:_h(f,"background-color",p.backgroundColor)}}catch{return}}function _h(f,h,p){let D,b;for(const S of Array.from(document.styleSheets)){let I=null;try{I=S.cssRules}catch{continue}I&&Qh(I,f,h,(Y,K)=>{D=Y,b=K})}if(!D)return;const g=[...D.matchAll(bO)].map(S=>S[1]).filter(S=>!!S);return{authored:D,rendered:p,vars:g,ruleSelector:b}}function Qh(f,h,p,D){for(const b of Array.from(f)){if(b instanceof CSSStyleRule){try{if(!h.matches(b.selectorText))continue}catch{continue}const g=b.style.getPropertyValue(p);g&&D(g,b.selectorText);continue}"cssRules"in b&&b.cssRules&&Qh(b.cssRules,h,p,D)}}async function wO(f,h,p,D){var Na,mr,_r,Pn,Ln,Bn,ia,$n;const b=cO();await new Promise(oe=>setTimeout(oe,aO)),await nO();const g=new Date().toISOString(),S=performance.now(),I=document.querySelector(f);if(!I)throw b(),new Error(`scope not found: ${f}`);const Y=Uh({element:I,url:window.location.href}),K=await Qk(),z=await eO();let ue=!1;try{const oe=await ck();oe.length>0&&(Zk(K,oe),ue=!0)}catch(oe){er.warn("custom-rule registration failed; continuing with built-ins only",oe)}const De=p??Jk,ce=ue&&De.runOnly&&typeof De.runOnly=="object"&&Array.isArray(De.runOnly.values)&&!De.runOnly.values.includes(yh)?{...De,runOnly:{...De.runOnly,values:[...De.runOnly.values,yh]}}:De,xe=Object.keys(z).length>0?{...ce,rules:{...ce.rules??{},...z}}:ce,ke=D&&D.length>0?{...xe,runOnly:{type:"rule",values:D}}:xe;let Ye;try{Ye=await K.run(I,ke)}catch(oe){throw b(),oe}const ht=K.version??"unknown",zt=[];for(const oe of Ye.violations??[])if(!(oe.tags??[]).some(Je=>wo.test(Je)))for(const Je of oe.nodes??[]){const Te=Array.isArray(Je.target)?Je.target.join(" "):Je.target,rt=document.querySelector(Te);if(!rt)continue;const Be=hO(rt,Je,oe.id),qe=await yl({ruleId:oe.id,componentId:Y.id,currentState:h,target:Be});zt.push({ruleId:oe.id,wcagCriterion:Do(oe.tags??[]),wcagLevel:fO(oe.tags??[]),impact:dO(oe.impact),description:oe.description??"",helpUrl:oe.helpUrl??"",target:Be,componentId:Y.id,currentState:h,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}const rr=[];if(h.breakpoint.width<=320)try{const{analyzeReflow:oe}=await ch(async()=>{const{analyzeReflow:Te}=await import("./reflow-analyzer-DNgBX8N_.js");return{analyzeReflow:Te}},[]),Je=oe(h.breakpoint.width,I);Je.findings.length===0&&rr.push({ruleId:"wcc-reflow-clean",wcagCriterion:"1.4.10"});for(const Te of Je.findings){const rt=document.querySelector(Te.selector)??I,Be={selector:Te.selector,outerHTML:Te.outerHtml||(rt.outerHTML??"").slice(0,500),failureSummary:Te.kind==="document-overflow"?`Page scrolls horizontally at ${h.breakpoint.width}px (content width ${Je.documentScrollWidthPx}px, overflow ${Te.overflowPx}px).`:Te.kind==="element-overflow"?`Element extends ${Te.overflowPx}px past the ${h.breakpoint.width}px viewport edge.`:`Element scrolls horizontally (${Te.overflowPx}px past its container).`,tagName:rt.tagName,role:((Na=rt.getAttribute)==null?void 0:Na.call(rt,"role"))??null,accessibleName:((mr=rt.getAttribute)==null?void 0:mr.call(rt,"aria-label"))??null,textSnippet:((_r=rt.textContent)==null?void 0:_r.trim().slice(0,50))??null,attrId:rt.id||null,attrTestid:((Pn=rt.getAttribute)==null?void 0:Pn.call(rt,"data-testid"))??null},qe=await yl({ruleId:"wcagcheckr-reflow",componentId:Y.id,currentState:h,target:Be});zt.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:Y.id,currentState:h,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}}catch(oe){console.warn("[audit-engine] reflow analyzer failed:",oe)}if(h.pseudoState==="default"&&h.theme==="light"&&h.direction==="ltr"&&h.breakpoint.id==="desktop")try{const{runAllDomCriterionAnalyzers:oe,DOM_ANALYZER_COVERED_CRITERIA:Je}=await ch(async()=>{const{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}=await import("./dom-criterion-analyzers-Bdtobl22.js");return{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}},[]),Te=oe(),rt=new Set(Te.map(Be=>Be.wcagCriterion));for(const Be of Je)rt.has(Be)||rr.push({ruleId:`wcc-clean::${Be}`,wcagCriterion:Be});for(const Be of Te){const qe=Be.element,oa={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:((Ln=qe.getAttribute)==null?void 0:Ln.call(qe,"role"))??null,accessibleName:((Bn=qe.getAttribute)==null?void 0:Bn.call(qe,"aria-label"))??null,textSnippet:((ia=qe.textContent)==null?void 0:ia.trim().slice(0,50))??null,attrId:qe.id||null,attrTestid:(($n=qe.getAttribute)==null?void 0:$n.call(qe,"data-testid"))??null},jr=await yl({ruleId:Be.ruleId,componentId:Y.id,currentState:h,target:oa});zt.push({ruleId:Be.ruleId,wcagCriterion:Be.wcagCriterion,wcagLevel:Be.wcagLevel,impact:Be.impact,description:Be.description,helpUrl:Be.helpUrl,target:oa,componentId:Y.id,currentState:h,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:jr})}}catch(oe){console.warn("[audit-engine] dom-criterion-analyzers failed:",oe)}return b(),{componentId:Y.id,scope:f,pageUrl:typeof window<"u"&&window.location?window.location.href:void 0,state:h,violations:zt,passes:(Ye.passes??[]).length,incomplete:(Ye.incomplete??[]).length,inapplicable:(Ye.inapplicable??[]).length,axeVersion:ht,startedAt:g,durationMs:performance.now()-S,axeRulesEvaluated:{passed:[...(Ye.passes??[]).filter(oe=>!(oe.tags??[]).some(Je=>wo.test(Je))).map(oe=>({ruleId:oe.id,wcagCriterion:Do(oe.tags??[])})).filter(oe=>oe.wcagCriterion),...rr],inapplicable:(Ye.inapplicable??[]).filter(oe=>!(oe.tags??[]).some(Je=>wo.test(Je))).map(oe=>({ruleId:oe.id,wcagCriterion:Do(oe.tags??[])})).filter(oe=>oe.wcagCriterion),incomplete:(Ye.incomplete??[]).filter(oe=>!(oe.tags??[]).some(Je=>wo.test(Je))).map(oe=>({ruleId:oe.id,wcagCriterion:Do(oe.tags??[]),elements:pO(oe.id,oe.nodes??[])})).filter(oe=>oe.wcagCriterion)}}}function DO(){const f=_e("SCOPE_FINGERPRINT_REQUEST",async K=>{try{const z=K.selector==="html"?document.documentElement:document.querySelector(K.selector);if(!z)return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null};const ue=z.outerHTML,De=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(ue));return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:Array.from(new Uint8Array(De)).map(xe=>xe.toString(16).padStart(2,"0")).join("")}}catch(z){return er.warn("scope fingerprint failed",z),{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null}}}),h=_e("AUDIT_REQUEST",async K=>{try{return{type:"AUDIT_RESPONSE",success:!0,result:await wO(K.selector,K.currentState,K.axeConfig,K.rulesToRun)}}catch(z){er.error("audit failed",z);const ue=z instanceof Error?z.message:String(z),De=ue.toLowerCase().includes("paint")?"PAINT_TIMEOUT":"AXE_FAILED";return{type:"AUDIT_RESPONSE",success:!1,error:sk(De,ue,!1)}}}),p=_e("CUSTOM_PROPERTY_REQUEST",async()=>{try{return{type:"CUSTOM_PROPERTY_RESPONSE",undefined:Hk()}}catch(K){return er.warn("custom-property analysis failed",K),{type:"CUSTOM_PROPERTY_RESPONSE",undefined:[]}}}),D=_e("THEME_AWARENESS_REQUEST",async()=>{try{const K=Yk();return{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:K.hasLightModeCss,hasDarkModeCss:K.hasDarkModeCss,hasUnreadableSheets:K.hasUnreadableSheets}}catch(K){return er.warn("theme-awareness probe failed",K),{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:!0,hasDarkModeCss:!0,hasUnreadableSheets:!0}}}),b=_e("READING_ORDER_REQUEST",async K=>{try{const z=document.querySelector(K.selector)??document;return{type:"READING_ORDER_RESPONSE",issues:Kh(z)}}catch(z){return er.warn("reading-order analysis failed",z),{type:"READING_ORDER_RESPONSE",issues:[]}}}),g=_e("TYPOGRAPHY_ANALYZE_REQUEST",async K=>{try{const z=document.querySelector(K.selector)??document;return{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:jk(z)}}catch(z){return er.warn("typography analysis failed",z),{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:[]}}}),S=_e("TAB_ORDER_ANALYZE_REQUEST",async()=>{try{return{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:Mk()}}catch(K){return er.warn("tab-order analysis failed",K),{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:[]}}}),I=_e("WARMUP_REQUEST",async()=>(await lO(),{type:"WARMUP_RESPONSE"})),Y=_e("AI_CANDIDATES_REQUEST",async K=>{try{return{type:"AI_CANDIDATES_RESPONSE",images:EO(K.selector),headings:AO(K.selector),instructions:CO(K.selector),ariaElements:RO(K.selector),links:TO(K.selector),longTextBlocks:FO(K.selector),languageInfo:SO(),colorOnlyRegions:kO(K.selector)}}catch(z){return er.warn("ai-candidates enumeration failed",z),{type:"AI_CANDIDATES_RESPONSE",images:[],headings:[],instructions:[],ariaElements:[],links:[],longTextBlocks:[],languageInfo:null,colorOnlyRegions:[]}}});return()=>{h(),f(),b(),S(),g(),I(),p(),D(),Y()}}function EO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('img, [role="img"]');for(const b of Array.from(D)){const g=b.currentSrc||b.src||b.getAttribute("src")||"";if(!g||g.startsWith("data:"))continue;const S=b.getBoundingClientRect();if(S.width<16||S.height<16)continue;const I=b.getAttribute("alt")??"";_O(b,I)||p.push({imageUrl:g,alt:I,selector:na(b),outerHTML:(b.outerHTML??"").slice(0,500),surroundingContext:xO(b),pixelArea:Math.round(S.width*S.height)})}return p}function _O(f,h){if(aa(f))return!0;const p=f.getAttribute("role");if(p==="presentation"||p==="none")return!0;if(h===""){let D=f.parentElement,b=0;for(;D&&b<3;){const g=D.tagName.toLowerCase(),S=D.getAttribute("role");if(g==="a"||g==="button"||S==="link"||S==="button"){if((D.textContent??"").trim().length>0)return!0;break}D=D.parentElement,b++}}if(h===""){const D=(f.getAttribute("class")??"").toLowerCase();if(/\b(logo|brand|icon|emblem|crest|wordmark)\b/.test(D)&&f.parentElement&&(f.parentElement.textContent??"").trim().length>0)return!0}return!1}function aa(f){let h=f;for(;h&&h!==document.body;){if(h.getAttribute("aria-hidden")==="true")return!0;h=h.parentElement}return!1}function na(f){if(f.id)return`#${CSS.escape(f.id)}`;const h=f.tagName.toLowerCase(),p=f.parentElement;if(!p)return h;const D=Array.from(p.children).filter(g=>g.tagName===f.tagName),b=D.indexOf(f);return D.length===1?`${p.tagName.toLowerCase()} > ${h}`:`${p.tagName.toLowerCase()} > ${h}:nth-of-type(${b+1})`}function xO(f){let h=f.parentElement;for(;h&&h!==document.body;){const p=(h.textContent??"").trim();if(p.length>20&&p.length<800)return p;h=h.parentElement}}function AO(f){var b,g;const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');for(const S of Array.from(D)){if(aa(S))continue;const I=(S.textContent??"").trim();if(!I||I.length>200)continue;const Y=parseInt(((b=S.tagName.match(/^H(\d)$/))==null?void 0:b[1])??"6",10);let K="",z=S.nextElementSibling;for(;z&&K.length<800;){const ue=parseInt(((g=z.tagName.match(/^H(\d)$/))==null?void 0:g[1])??"0",10);if(ue>0&&ue<=Y)break;const De=(z.textContent??"").trim();De&&(K+=(K?" ":"")+De),z=z.nextElementSibling}K.length<30||p.push({level:Y,text:I,sectionContent:K.slice(0,800),selector:na(S),outerHTML:(S.outerHTML??"").slice(0,300)})}return p}function CO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('p, li, div[role="note"], aside');for(const b of Array.from(D)){if(aa(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:na(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20))break}return p}function RO(f){var b;const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll("[role]");for(const g of Array.from(D)){if(aa(g))continue;const S=g.getAttribute("role")??"";if(S&&(p.push({role:S,selector:na(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 TO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll("a[href]");for(const b of Array.from(D)){if(aa(b))continue;const g=(b.textContent??"").trim();if(!g||g.length>60)continue;let S="",I=b.parentElement;for(;I&&I!==document.body;){const Y=I.tagName;if(Y==="P"||Y==="LI"||Y==="DD"||Y==="FIGCAPTION"||/^H[1-6]$/.test(Y)){const K=(I.textContent??"").trim();if(K.length>=g.length){S=K;break}}I=I.parentElement}if(p.push({linkText:g,surroundingText:S.slice(0,800),selector:na(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=25)break}return p}function FO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=new Set,b=h.querySelectorAll("article, section, main, div, aside");for(const g of Array.from(b)){if(aa(g))continue;let S=g.parentElement,I=!1;for(;S;){if(D.has(S)){I=!0;break}S=S.parentElement}if(I)continue;const Y=(g.textContent??"").trim();if(!Y||Y.split(/\s+/).filter(Boolean).length<300)continue;const z=g.querySelectorAll("h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, figure, table, button, input").length;if(!(z>4)&&(p.push({blockText:Y.slice(0,1500),structuralChildrenCount:z,selector:na(g),outerHTML:(g.outerHTML??"").slice(0,400)}),D.add(g),p.length>=5))break}return p}function SO(){const f=document.documentElement.getAttribute("lang")??"",h=document.body;if(!h)return null;const p=(h.textContent??"").trim().replace(/\s+/g," ").slice(0,1500);return p.length<60?null:{declaredLang:f,bodyTextSample:p,selector:"html",outerHTML:`<html lang="${f}">`}}function kO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('[class*="status"], [class*="badge"], [class*="dot"], [class*="indicator"], [aria-invalid="true"], [class*="pill"]');for(const b of Array.from(D)){if(aa(b))continue;const g=(b.textContent??"").trim(),S=window.getComputedStyle(b),I=S.backgroundColor,Y=S.borderColor;if(!(I&&I!=="rgba(0, 0, 0, 0)"&&I!=="transparent"||Y&&Y!=="rgba(0, 0, 0, 0)"&&Y!=="transparent"))continue;const z=b.querySelector("svg, [aria-label], [aria-labelledby], [title]")!==null;if(g.length>0&&z)continue;const ue=b.getAttribute("aria-label")??b.getAttribute("title")??"",De=[g?`text: "${g.slice(0,60)}"`:"no text content",ue?`label: "${ue}"`:"no aria-label",`bg: ${I}`,z?"has icon child":"no icon child"].join("; ");if(p.push({elementHtml:(b.outerHTML??"").slice(0,400),contextDescription:De.slice(0,400),selector:na(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20)break}return p}const OO=Rt("storybook-integration"),IO=3e3;function NO(){const f=window.__STORYBOOK_PREVIEW__;return f&&typeof f=="object"?{detected:!0,version:f.version??"unknown"}:{detected:!1}}async function MO(){var b,g;const f=window.__STORYBOOK_PREVIEW__;if(!f)return[];const h=f.storyStoreValue??f.storyStore;if(!h)return[];typeof h.cacheAllCSFFiles=="function"&&await h.cacheAllCSFFiles();const p=((b=h.extract)==null?void 0:b.call(h))??((g=h.cachedCSFFiles)==null?void 0:g.call(h));if(!p)return[];const D=[];if(p instanceof Map)for(const[S,I]of p)D.push({id:S,name:I.name??S,kind:I.title??I.kind??""});else if(typeof p=="object")for(const[S,I]of Object.entries(p))D.push({id:S,name:I.name??S,kind:I.title??I.kind??""});return D}async function PO(f){const h=new URL(window.location.href);h.searchParams.set("id",f),h.searchParams.set("viewMode","story"),window.location.href!==h.href&&(window.history.pushState({},"",h.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(p=>{const D=setTimeout(()=>p(),IO),b=()=>{clearTimeout(D),p()};window.addEventListener("storyrendered",b,{once:!0})}),Sl({type:"STORYBOOK_NAVIGATED_EVENT",storyId:f,ready:!0})}function LO(){return document.getElementById("storybook-root")?"#storybook-root":document.getElementById("root")?"#root":"body"}function BO(){const f=[];return f.push(_e("STORYBOOK_DETECT_REQUEST",async()=>{const h=NO();return await chrome.storage.local.set({"storybook:lastDetected":{detected:h.detected,version:h.version,detectedAt:new Date().toISOString()}}),{type:"STORYBOOK_DETECT_RESPONSE",detected:h.detected,version:h.version}})),f.push(_e("STORYBOOK_LIST_STORIES_REQUEST",async()=>({type:"STORYBOOK_LIST_STORIES_RESPONSE",stories:await MO()}))),f.push(_e("STORYBOOK_NAVIGATE_REQUEST",async h=>{await PO(h.storyId)})),f.push(_e("STORYBOOK_GET_STORY_TARGET_REQUEST",()=>({type:"STORYBOOK_GET_STORY_TARGET_RESPONSE",selector:LO()}))),OO.info("handlers registered"),()=>f.forEach(h=>h())}const Jh=Rt("element-picker");let tr=null,Oa=!1;const $O=`
|
|
39
|
+
`,oO=.85,Dh=80,sO=300,uO=4e3;async function lO(){var f;if(!(typeof document>"u"||typeof window>"u"))try{const h=window.scrollY,p=window.scrollX,D=Math.max(document.documentElement.scrollHeight,((f=document.body)==null?void 0:f.scrollHeight)??0),b=window.innerHeight||0;if(D<=b)return;const g=Math.max(100,b*oO),S=performance.now();for(let I=0;I<D&&!(performance.now()-S>uO);I+=g)window.scrollTo({top:I,left:0,behavior:"instant"}),await new Promise(Y=>setTimeout(Y,Dh));window.scrollTo({top:D,left:0,behavior:"instant"}),await new Promise(I=>setTimeout(I,Dh)),window.scrollTo({top:h,left:p,behavior:"instant"}),await new Promise(I=>setTimeout(I,sO))}catch(h){er.warn("scroll-reveal warmup failed (continuing with audit)",h)}}function cO(){if(typeof document>"u")return()=>{};if(document.getElementById(wh))return()=>{};const f=document.createElement("style");return f.id=wh,f.textContent=iO,document.head.appendChild(f),()=>f.remove()}function dO(f){return f==="minor"||f==="moderate"||f==="serious"||f==="critical"?f:"moderate"}function fO(f){return f.some(h=>h.endsWith("aaa"))?"AAA":f.some(h=>h.endsWith("aa"))?"AA":"A"}function Do(f){for(const h of f){const p=/^wcag(\d)(\d)(\d{1,2})$/.exec(h);if(p)return`${p[1]}.${p[2]}.${parseInt(p[3],10)}`}return""}function pO(f,h){const D=[];for(let b=0;b<h.length&&D.length<25;b++){const g=h[b],S=Array.isArray(g.target)?g.target.join(" "):g.target;if(!S)continue;const I=(()=>{try{return document.querySelector(S)}catch{return null}})(),Y={selector:S,failureSummary:g.failureSummary};if(I instanceof HTMLElement){const K=I.outerHTML;if(Y.outerHTMLSnippet=K.length>1200?K.slice(0,1200)+"…":K,f==="color-contrast"||f==="color-contrast-enhanced"){const z=window.getComputedStyle(I);Y.styles={foreground:z.color||void 0,background:z.backgroundColor||void 0,fontSize:parseFloat(z.fontSize)||void 0,fontWeight:parseInt(z.fontWeight,10)||void 0,textSample:(I.textContent??"").trim().slice(0,120)||void 0,effectiveBackground:mO(I)}}}D.push(Y)}return D}function mO(f){const h=new Set(["rgba(0, 0, 0, 0)","transparent",""]);let p=f,D,b=!1;for(;p;){const g=window.getComputedStyle(p),S=g.backgroundImage;S&&S!=="none"&&!b&&(b=!0,D=Eh(p));const I=g.backgroundColor;if(I&&!h.has(I))return{color:I,fromAncestor:p===f?void 0:Eh(p),hasBackgroundImageInChain:b,imageAncestor:D};p=p.parentElement}return{color:"rgb(255, 255, 255)",fromAncestor:"document default",hasBackgroundImageInChain:b,imageAncestor:D}}function Eh(f){const h=f.tagName.toLowerCase(),p=f.id?`#${f.id}`:"",D=f.classList.length>0?`.${f.classList[0]}`:"";return`${h}${p}${D}`}function hO(f,h,p){var S,I;const D=((S=f.textContent)==null?void 0:S.trim().slice(0,50))??null,b=(I=f.getBoundingClientRect)==null?void 0:I.call(f),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(h.target)?h.target.join(" "):String(h.target),outerHTML:(f.outerHTML??"").slice(0,500),failureSummary:h.failureSummary??"",tagName:f.tagName,role:f.getAttribute("role"),accessibleName:f.getAttribute("aria-label")??D,textSnippet:D,attrId:f.id||null,attrTestid:f.getAttribute("data-testid"),boundingRect:g,opacityContext:vO(f),cascadeChain:yO(f,p)}}function vO(f){if(typeof getComputedStyle!="function")return;let h=1,p=1,D;try{const b=getComputedStyle(f);h=parseFloat(b.opacity||"1");let g=f.parentElement;for(;g&&g!==document.documentElement;){const S=getComputedStyle(g),I=parseFloat(S.opacity||"1");I<p&&(p=I,D=gO(g)),g=g.parentElement}}catch{return}if(!(h>=.99&&p>=.99))return{self:Math.round(h*100)/100,ancestor:Math.round(p*100)/100,ancestorSelector:D}}function gO(f){const h=f.tagName.toLowerCase();if(f.id)return`${h}#${f.id}`;const p=f.classList.length>0?"."+Array.from(f.classList).slice(0,2).join("."):"";return`${h}${p}`}const bO=/var\(\s*(--[A-Za-z0-9_-]+)/g;function yO(f,h){if(!(h!=="color-contrast"&&h!=="color-contrast-enhanced")&&typeof getComputedStyle=="function")try{const p=getComputedStyle(f);return{color:_h(f,"color",p.color),backgroundColor:_h(f,"background-color",p.backgroundColor)}}catch{return}}function _h(f,h,p){let D,b;for(const S of Array.from(document.styleSheets)){let I=null;try{I=S.cssRules}catch{continue}I&&Qh(I,f,h,(Y,K)=>{D=Y,b=K})}if(!D)return;const g=[...D.matchAll(bO)].map(S=>S[1]).filter(S=>!!S);return{authored:D,rendered:p,vars:g,ruleSelector:b}}function Qh(f,h,p,D){for(const b of Array.from(f)){if(b instanceof CSSStyleRule){try{if(!h.matches(b.selectorText))continue}catch{continue}const g=b.style.getPropertyValue(p);g&&D(g,b.selectorText);continue}"cssRules"in b&&b.cssRules&&Qh(b.cssRules,h,p,D)}}async function wO(f,h,p,D){var Na,mr,_r,Pn,Ln,Bn,ia,$n;const b=cO();await new Promise(oe=>setTimeout(oe,aO)),await nO();const g=new Date().toISOString(),S=performance.now(),I=document.querySelector(f);if(!I)throw b(),new Error(`scope not found: ${f}`);const Y=Uh({element:I,url:window.location.href}),K=await Qk(),z=await eO();let ue=!1;try{const oe=await ck();oe.length>0&&(Zk(K,oe),ue=!0)}catch(oe){er.warn("custom-rule registration failed; continuing with built-ins only",oe)}const De=p??Jk,ce=ue&&De.runOnly&&typeof De.runOnly=="object"&&Array.isArray(De.runOnly.values)&&!De.runOnly.values.includes(yh)?{...De,runOnly:{...De.runOnly,values:[...De.runOnly.values,yh]}}:De,xe=Object.keys(z).length>0?{...ce,rules:{...ce.rules??{},...z}}:ce,ke=D&&D.length>0?{...xe,runOnly:{type:"rule",values:D}}:xe;let Ye;try{Ye=await K.run(I,ke)}catch(oe){throw b(),oe}const ht=K.version??"unknown",zt=[];for(const oe of Ye.violations??[])if(!(oe.tags??[]).some(Je=>wo.test(Je)))for(const Je of oe.nodes??[]){const Te=Array.isArray(Je.target)?Je.target.join(" "):Je.target,rt=document.querySelector(Te);if(!rt)continue;const Be=hO(rt,Je,oe.id),qe=await yl({ruleId:oe.id,componentId:Y.id,currentState:h,target:Be});zt.push({ruleId:oe.id,wcagCriterion:Do(oe.tags??[]),wcagLevel:fO(oe.tags??[]),impact:dO(oe.impact),description:oe.description??"",helpUrl:oe.helpUrl??"",target:Be,componentId:Y.id,currentState:h,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}const rr=[];if(h.breakpoint.width<=320)try{const{analyzeReflow:oe}=await ch(async()=>{const{analyzeReflow:Te}=await import("./reflow-analyzer-DNgBX8N_.js");return{analyzeReflow:Te}},[]),Je=oe(h.breakpoint.width,I);Je.findings.length===0&&rr.push({ruleId:"wcc-reflow-clean",wcagCriterion:"1.4.10"});for(const Te of Je.findings){const rt=document.querySelector(Te.selector)??I,Be={selector:Te.selector,outerHTML:Te.outerHtml||(rt.outerHTML??"").slice(0,500),failureSummary:Te.kind==="document-overflow"?`Page scrolls horizontally at ${h.breakpoint.width}px (content width ${Je.documentScrollWidthPx}px, overflow ${Te.overflowPx}px).`:Te.kind==="element-overflow"?`Element extends ${Te.overflowPx}px past the ${h.breakpoint.width}px viewport edge.`:`Element scrolls horizontally (${Te.overflowPx}px past its container).`,tagName:rt.tagName,role:((Na=rt.getAttribute)==null?void 0:Na.call(rt,"role"))??null,accessibleName:((mr=rt.getAttribute)==null?void 0:mr.call(rt,"aria-label"))??null,textSnippet:((_r=rt.textContent)==null?void 0:_r.trim().slice(0,50))??null,attrId:rt.id||null,attrTestid:((Pn=rt.getAttribute)==null?void 0:Pn.call(rt,"data-testid"))??null},qe=await yl({ruleId:"wcagcheckr-reflow",componentId:Y.id,currentState:h,target:Be});zt.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:Y.id,currentState:h,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:qe})}}catch(oe){console.warn("[audit-engine] reflow analyzer failed:",oe)}if(h.pseudoState==="default"&&h.theme==="light"&&h.direction==="ltr"&&h.breakpoint.id==="desktop")try{const{runAllDomCriterionAnalyzers:oe,DOM_ANALYZER_COVERED_CRITERIA:Je}=await ch(async()=>{const{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}=await import("./dom-criterion-analyzers-DsEV9zo7.js");return{runAllDomCriterionAnalyzers:Be,DOM_ANALYZER_COVERED_CRITERIA:qe}},[]),Te=oe(),rt=new Set(Te.map(Be=>Be.wcagCriterion));for(const Be of Je)rt.has(Be)||rr.push({ruleId:`wcc-clean::${Be}`,wcagCriterion:Be});for(const Be of Te){const qe=Be.element,oa={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:((Ln=qe.getAttribute)==null?void 0:Ln.call(qe,"role"))??null,accessibleName:((Bn=qe.getAttribute)==null?void 0:Bn.call(qe,"aria-label"))??null,textSnippet:((ia=qe.textContent)==null?void 0:ia.trim().slice(0,50))??null,attrId:qe.id||null,attrTestid:(($n=qe.getAttribute)==null?void 0:$n.call(qe,"data-testid"))??null},jr=await yl({ruleId:Be.ruleId,componentId:Y.id,currentState:h,target:oa});zt.push({ruleId:Be.ruleId,wcagCriterion:Be.wcagCriterion,wcagLevel:Be.wcagLevel,impact:Be.impact,description:Be.description,helpUrl:Be.helpUrl,target:oa,componentId:Y.id,currentState:h,axeVersion:ht,detectedAt:new Date().toISOString(),matchKey:jr})}}catch(oe){console.warn("[audit-engine] dom-criterion-analyzers failed:",oe)}return b(),{componentId:Y.id,scope:f,pageUrl:typeof window<"u"&&window.location?window.location.href:void 0,state:h,violations:zt,passes:(Ye.passes??[]).length,incomplete:(Ye.incomplete??[]).length,inapplicable:(Ye.inapplicable??[]).length,axeVersion:ht,startedAt:g,durationMs:performance.now()-S,axeRulesEvaluated:{passed:[...(Ye.passes??[]).filter(oe=>!(oe.tags??[]).some(Je=>wo.test(Je))).map(oe=>({ruleId:oe.id,wcagCriterion:Do(oe.tags??[])})).filter(oe=>oe.wcagCriterion),...rr],inapplicable:(Ye.inapplicable??[]).filter(oe=>!(oe.tags??[]).some(Je=>wo.test(Je))).map(oe=>({ruleId:oe.id,wcagCriterion:Do(oe.tags??[])})).filter(oe=>oe.wcagCriterion),incomplete:(Ye.incomplete??[]).filter(oe=>!(oe.tags??[]).some(Je=>wo.test(Je))).map(oe=>({ruleId:oe.id,wcagCriterion:Do(oe.tags??[]),elements:pO(oe.id,oe.nodes??[])})).filter(oe=>oe.wcagCriterion)}}}function DO(){const f=_e("SCOPE_FINGERPRINT_REQUEST",async K=>{try{const z=K.selector==="html"?document.documentElement:document.querySelector(K.selector);if(!z)return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null};const ue=z.outerHTML,De=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(ue));return{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:Array.from(new Uint8Array(De)).map(xe=>xe.toString(16).padStart(2,"0")).join("")}}catch(z){return er.warn("scope fingerprint failed",z),{type:"SCOPE_FINGERPRINT_RESPONSE",fingerprint:null}}}),h=_e("AUDIT_REQUEST",async K=>{try{return{type:"AUDIT_RESPONSE",success:!0,result:await wO(K.selector,K.currentState,K.axeConfig,K.rulesToRun)}}catch(z){er.error("audit failed",z);const ue=z instanceof Error?z.message:String(z),De=ue.toLowerCase().includes("paint")?"PAINT_TIMEOUT":"AXE_FAILED";return{type:"AUDIT_RESPONSE",success:!1,error:sk(De,ue,!1)}}}),p=_e("CUSTOM_PROPERTY_REQUEST",async()=>{try{return{type:"CUSTOM_PROPERTY_RESPONSE",undefined:Hk()}}catch(K){return er.warn("custom-property analysis failed",K),{type:"CUSTOM_PROPERTY_RESPONSE",undefined:[]}}}),D=_e("THEME_AWARENESS_REQUEST",async()=>{try{const K=Yk();return{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:K.hasLightModeCss,hasDarkModeCss:K.hasDarkModeCss,hasUnreadableSheets:K.hasUnreadableSheets}}catch(K){return er.warn("theme-awareness probe failed",K),{type:"THEME_AWARENESS_RESPONSE",hasLightModeCss:!0,hasDarkModeCss:!0,hasUnreadableSheets:!0}}}),b=_e("READING_ORDER_REQUEST",async K=>{try{const z=document.querySelector(K.selector)??document;return{type:"READING_ORDER_RESPONSE",issues:Kh(z)}}catch(z){return er.warn("reading-order analysis failed",z),{type:"READING_ORDER_RESPONSE",issues:[]}}}),g=_e("TYPOGRAPHY_ANALYZE_REQUEST",async K=>{try{const z=document.querySelector(K.selector)??document;return{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:jk(z)}}catch(z){return er.warn("typography analysis failed",z),{type:"TYPOGRAPHY_ANALYZE_RESPONSE",issues:[]}}}),S=_e("TAB_ORDER_ANALYZE_REQUEST",async()=>{try{return{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:Mk()}}catch(K){return er.warn("tab-order analysis failed",K),{type:"TAB_ORDER_ANALYZE_RESPONSE",issues:[]}}}),I=_e("WARMUP_REQUEST",async()=>(await lO(),{type:"WARMUP_RESPONSE"})),Y=_e("AI_CANDIDATES_REQUEST",async K=>{try{return{type:"AI_CANDIDATES_RESPONSE",images:EO(K.selector),headings:AO(K.selector),instructions:CO(K.selector),ariaElements:RO(K.selector),links:TO(K.selector),longTextBlocks:FO(K.selector),languageInfo:SO(),colorOnlyRegions:kO(K.selector)}}catch(z){return er.warn("ai-candidates enumeration failed",z),{type:"AI_CANDIDATES_RESPONSE",images:[],headings:[],instructions:[],ariaElements:[],links:[],longTextBlocks:[],languageInfo:null,colorOnlyRegions:[]}}});return()=>{h(),f(),b(),S(),g(),I(),p(),D(),Y()}}function EO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('img, [role="img"]');for(const b of Array.from(D)){const g=b.currentSrc||b.src||b.getAttribute("src")||"";if(!g||g.startsWith("data:"))continue;const S=b.getBoundingClientRect();if(S.width<16||S.height<16)continue;const I=b.getAttribute("alt")??"";_O(b,I)||p.push({imageUrl:g,alt:I,selector:na(b),outerHTML:(b.outerHTML??"").slice(0,500),surroundingContext:xO(b),pixelArea:Math.round(S.width*S.height)})}return p}function _O(f,h){if(aa(f))return!0;const p=f.getAttribute("role");if(p==="presentation"||p==="none")return!0;if(h===""){let D=f.parentElement,b=0;for(;D&&b<3;){const g=D.tagName.toLowerCase(),S=D.getAttribute("role");if(g==="a"||g==="button"||S==="link"||S==="button"){if((D.textContent??"").trim().length>0)return!0;break}D=D.parentElement,b++}}if(h===""){const D=(f.getAttribute("class")??"").toLowerCase();if(/\b(logo|brand|icon|emblem|crest|wordmark)\b/.test(D)&&f.parentElement&&(f.parentElement.textContent??"").trim().length>0)return!0}return!1}function aa(f){let h=f;for(;h&&h!==document.body;){if(h.getAttribute("aria-hidden")==="true")return!0;h=h.parentElement}return!1}function na(f){if(f.id)return`#${CSS.escape(f.id)}`;const h=f.tagName.toLowerCase(),p=f.parentElement;if(!p)return h;const D=Array.from(p.children).filter(g=>g.tagName===f.tagName),b=D.indexOf(f);return D.length===1?`${p.tagName.toLowerCase()} > ${h}`:`${p.tagName.toLowerCase()} > ${h}:nth-of-type(${b+1})`}function xO(f){let h=f.parentElement;for(;h&&h!==document.body;){const p=(h.textContent??"").trim();if(p.length>20&&p.length<800)return p;h=h.parentElement}}function AO(f){var b,g;const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');for(const S of Array.from(D)){if(aa(S))continue;const I=(S.textContent??"").trim();if(!I||I.length>200)continue;const Y=parseInt(((b=S.tagName.match(/^H(\d)$/))==null?void 0:b[1])??"6",10);let K="",z=S.nextElementSibling;for(;z&&K.length<800;){const ue=parseInt(((g=z.tagName.match(/^H(\d)$/))==null?void 0:g[1])??"0",10);if(ue>0&&ue<=Y)break;const De=(z.textContent??"").trim();De&&(K+=(K?" ":"")+De),z=z.nextElementSibling}K.length<30||p.push({level:Y,text:I,sectionContent:K.slice(0,800),selector:na(S),outerHTML:(S.outerHTML??"").slice(0,300)})}return p}function CO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('p, li, div[role="note"], aside');for(const b of Array.from(D)){if(aa(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:na(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20))break}return p}function RO(f){var b;const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll("[role]");for(const g of Array.from(D)){if(aa(g))continue;const S=g.getAttribute("role")??"";if(S&&(p.push({role:S,selector:na(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 TO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll("a[href]");for(const b of Array.from(D)){if(aa(b))continue;const g=(b.textContent??"").trim();if(!g||g.length>60)continue;let S="",I=b.parentElement;for(;I&&I!==document.body;){const Y=I.tagName;if(Y==="P"||Y==="LI"||Y==="DD"||Y==="FIGCAPTION"||/^H[1-6]$/.test(Y)){const K=(I.textContent??"").trim();if(K.length>=g.length){S=K;break}}I=I.parentElement}if(p.push({linkText:g,surroundingText:S.slice(0,800),selector:na(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=25)break}return p}function FO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=new Set,b=h.querySelectorAll("article, section, main, div, aside");for(const g of Array.from(b)){if(aa(g))continue;let S=g.parentElement,I=!1;for(;S;){if(D.has(S)){I=!0;break}S=S.parentElement}if(I)continue;const Y=(g.textContent??"").trim();if(!Y||Y.split(/\s+/).filter(Boolean).length<300)continue;const z=g.querySelectorAll("h1, h2, h3, h4, h5, h6, ul, ol, dl, p, blockquote, figure, table, button, input").length;if(!(z>4)&&(p.push({blockText:Y.slice(0,1500),structuralChildrenCount:z,selector:na(g),outerHTML:(g.outerHTML??"").slice(0,400)}),D.add(g),p.length>=5))break}return p}function SO(){const f=document.documentElement.getAttribute("lang")??"",h=document.body;if(!h)return null;const p=(h.textContent??"").trim().replace(/\s+/g," ").slice(0,1500);return p.length<60?null:{declaredLang:f,bodyTextSample:p,selector:"html",outerHTML:`<html lang="${f}">`}}function kO(f){const h=document.querySelector(f)??document.body;if(!h)return[];const p=[],D=h.querySelectorAll('[class*="status"], [class*="badge"], [class*="dot"], [class*="indicator"], [aria-invalid="true"], [class*="pill"]');for(const b of Array.from(D)){if(aa(b))continue;const g=(b.textContent??"").trim(),S=window.getComputedStyle(b),I=S.backgroundColor,Y=S.borderColor;if(!(I&&I!=="rgba(0, 0, 0, 0)"&&I!=="transparent"||Y&&Y!=="rgba(0, 0, 0, 0)"&&Y!=="transparent"))continue;const z=b.querySelector("svg, [aria-label], [aria-labelledby], [title]")!==null;if(g.length>0&&z)continue;const ue=b.getAttribute("aria-label")??b.getAttribute("title")??"",De=[g?`text: "${g.slice(0,60)}"`:"no text content",ue?`label: "${ue}"`:"no aria-label",`bg: ${I}`,z?"has icon child":"no icon child"].join("; ");if(p.push({elementHtml:(b.outerHTML??"").slice(0,400),contextDescription:De.slice(0,400),selector:na(b),outerHTML:(b.outerHTML??"").slice(0,400)}),p.length>=20)break}return p}const OO=Rt("storybook-integration"),IO=3e3;function NO(){const f=window.__STORYBOOK_PREVIEW__;return f&&typeof f=="object"?{detected:!0,version:f.version??"unknown"}:{detected:!1}}async function MO(){var b,g;const f=window.__STORYBOOK_PREVIEW__;if(!f)return[];const h=f.storyStoreValue??f.storyStore;if(!h)return[];typeof h.cacheAllCSFFiles=="function"&&await h.cacheAllCSFFiles();const p=((b=h.extract)==null?void 0:b.call(h))??((g=h.cachedCSFFiles)==null?void 0:g.call(h));if(!p)return[];const D=[];if(p instanceof Map)for(const[S,I]of p)D.push({id:S,name:I.name??S,kind:I.title??I.kind??""});else if(typeof p=="object")for(const[S,I]of Object.entries(p))D.push({id:S,name:I.name??S,kind:I.title??I.kind??""});return D}async function PO(f){const h=new URL(window.location.href);h.searchParams.set("id",f),h.searchParams.set("viewMode","story"),window.location.href!==h.href&&(window.history.pushState({},"",h.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(p=>{const D=setTimeout(()=>p(),IO),b=()=>{clearTimeout(D),p()};window.addEventListener("storyrendered",b,{once:!0})}),Sl({type:"STORYBOOK_NAVIGATED_EVENT",storyId:f,ready:!0})}function LO(){return document.getElementById("storybook-root")?"#storybook-root":document.getElementById("root")?"#root":"body"}function BO(){const f=[];return f.push(_e("STORYBOOK_DETECT_REQUEST",async()=>{const h=NO();return await chrome.storage.local.set({"storybook:lastDetected":{detected:h.detected,version:h.version,detectedAt:new Date().toISOString()}}),{type:"STORYBOOK_DETECT_RESPONSE",detected:h.detected,version:h.version}})),f.push(_e("STORYBOOK_LIST_STORIES_REQUEST",async()=>({type:"STORYBOOK_LIST_STORIES_RESPONSE",stories:await MO()}))),f.push(_e("STORYBOOK_NAVIGATE_REQUEST",async h=>{await PO(h.storyId)})),f.push(_e("STORYBOOK_GET_STORY_TARGET_REQUEST",()=>({type:"STORYBOOK_GET_STORY_TARGET_RESPONSE",selector:LO()}))),OO.info("handlers registered"),()=>f.forEach(h=>h())}const Jh=Rt("element-picker");let tr=null,Oa=!1;const $O=`
|
|
40
40
|
position: fixed;
|
|
41
41
|
pointer-events: none;
|
|
42
42
|
background: rgba(99, 102, 241, 0.20);
|
package/dist/assets/{content-script.ts-loader-BHryrwSa.js → content-script.ts-loader-Czc3Zduw.js}
RENAMED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
(async () => {
|
|
6
6
|
const { onExecute } = await import(
|
|
7
7
|
/* @vite-ignore */
|
|
8
|
-
chrome.runtime.getURL("assets/content-script.ts-
|
|
8
|
+
chrome.runtime.getURL("assets/content-script.ts-3-biN-xk.js")
|
|
9
9
|
);
|
|
10
10
|
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
|
|
11
11
|
})().catch(console.error);
|
package/dist/assets/{dom-criterion-analyzers-Bdtobl22.js → dom-criterion-analyzers-DsEV9zo7.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
const c="https://www.w3.org/WAI/WCAG21/Understanding/";function g(e=document){const t=[],i=e.querySelectorAll('meta[http-equiv="refresh" i]');for(const n of Array.from(i)){const r=n.getAttribute("content")??"",a=parseInt(r.split(";")[0]??"",10);Number.isFinite(a)&&a>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 ${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 o=e.querySelectorAll("script:not([src])");for(const n of Array.from(o)){const r=n.textContent??"";r.length===0||!/\b(setTimeout|setInterval)\s*\(/.test(r)||!/\b(innerHTML|textContent|replaceChildren|window\.location|document\.location|location\.href|location\.replace)\b/.test(r)||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:`${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 t}function y(e=document){const t=[],i=e.querySelectorAll("marquee, blink");for(const a of Array.from(i))t.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 o=e.querySelectorAll("video[autoplay]");for(const a of Array.from(o))!a.hasAttribute("controls")&&!a.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:`${c}pause-stop-hide.html`,element:a,failureSummary:"Autoplay video lacks user-accessible pause control."});const n=e.querySelectorAll("*");let r=0;for(const a of Array.from(n)){if(r>1500)break;r++;const s=window.getComputedStyle(a);if(s.animationName==="none"||!s.animationName)continue;const l=s.animationDuration.split(",").map(u=>parseFloat(u)),m=s.animationIterationCount.split(",").map(u=>u.trim());for(let u=0;u<l.length;u++){const d=l[u]??0,h=(m[u]??"1")==="infinite";h&&d>0&&d<.34&&t.push({ruleId:"wcc-flash-risk",wcagCriterion:"2.3.1",wcagLevel:"A",impact:"critical",description:`Element animates with duration ${d.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 @ ${d.toFixed(2)}s/cycle = ${(1/d).toFixed(1)} flashes/sec.`}),h&&d>=5&&t.push({ruleId:"wcc-long-infinite-animation",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"moderate",description:`Element runs an infinite CSS animation (${d.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 @ ${d.toFixed(1)}s/cycle. Confirm user can pause.`})}}return t}function b(e=document){const t=[],i=new Set,o=e.querySelectorAll("[ontouchstart], [ontouchmove], [ongesturestart], [ongesturechange]");for(const r of Array.from(o)){const a=f(r);i.has(a)||(i.add(a),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:`${c}pointer-gestures.html`,element:r,failureSummary:"Touch/gesture handler on this element. Confirm single-pointer alternative."}))}const n=e.querySelectorAll('[draggable="true"], [ondragstart]');for(const r of Array.from(n)){const a=f(r);i.has(a)||(i.add(a),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:`${c}dragging-movements.html`,element:r,failureSummary:"Drag-and-drop interaction. Confirm non-drag alternative for keyboard / touch users."}))}return t}function w(e=document){const t=[],i=e.querySelectorAll("video");for(const n of Array.from(i)){const r=Array.from(n.querySelectorAll("track")),a=r.some(l=>(l.kind??"").toLowerCase()==="captions"||(l.kind??"").toLowerCase()==="subtitles"),s=r.some(l=>(l.kind??"").toLowerCase()==="descriptions");a||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:`${c}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:`${c}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:`${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 o=e.querySelectorAll("audio");for(const n of Array.from(o))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:`${c}audio-only-and-video-only-prerecorded.html`,element:n,failureSummary:"Audio element detected. Verify nearby transcript link."});return t}function v(e=document){const t=[],i=/\b(window\.location|document\.location|location\.href|location\.replace|window\.open|\.submit\s*\()/,o=e.querySelectorAll("[onfocus]");for(const r of Array.from(o)){const a=r.getAttribute("onfocus")??"";i.test(a)&&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:`${c}on-focus.html`,element:r,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 r of Array.from(n)){const a=r.getAttribute("onchange")??"";i.test(a)&&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:`${c}on-input.html`,element:r,failureSummary:"Change handler navigates / submits. Replace with a separate submit button."})}return t}function A(e=document){const t=[],i=[".g-recaptcha","[data-sitekey]",".h-captcha",".cf-turnstile",".frc-captcha",'iframe[src*="recaptcha"]','iframe[src*="hcaptcha"]','iframe[src*="turnstile"]'].join(", "),o=e.querySelectorAll(i),n=new Set;for(const r of Array.from(o)){if(n.has(r))continue;n.add(r);const a=r.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);t.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:r,failureSummary:l?"CAPTCHA detected; alternative may exist — verify it skips the cognitive challenge.":"CAPTCHA detected without an alternative authentication path."})}return t}function C(e=document){const t=[],i=e.querySelectorAll('meta[name="viewport" i]');for(const n of Array.from(i)){const r=n.getAttribute("content")??"";(/user-scalable\s*=\s*(no|0)\b/i.test(r)||/maximum-scale\s*=\s*1(\.0+)?\b/i.test(r))&&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:`${c}resize-text.html`,element:n,failureSummary:"Viewport meta disables zoom. Remove user-scalable=no and maximum-scale<2."})}let o=0;for(const n of Array.from(e.styleSheets)){if(o>40)break;o++;try{const r=n.cssRules;if(!r)continue;for(const a of Array.from(r)){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 u of Array.from(a.cssRules))if(u instanceof CSSStyleRule&&/display\s*:\s*none/i.test(u.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: ${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:e.documentElement,failureSummary:`Orientation-locked display:none on selector "${u.selectorText}".`});break}}}}catch{}}return t}function S(e=document){const t=[];let i=0;const o=e.querySelectorAll("[title]");for(const n of Array.from(o)){if(i>200)break;i++;const r=n.getAttribute("title");!r||r.trim().length<8||n.hasAttribute("aria-describedby")||n.tagName==="A"&&(n.textContent??"").includes(r)||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:`${c}content-on-hover-or-focus.html`,element:n,failureSummary:"title-attribute tooltip is not dismissible / hoverable."})}return t}function k(e=document){const t=[],i=typeof window<"u"?window.location.pathname:"/";if(i==="/"||i===""||/\/(search|results)\b/.test(i))return t;const o=e.querySelector('nav, [role="navigation"]')!==null,n=e.querySelector('input[type="search"], [role="search"]')!==null||Array.from(e.querySelectorAll('input[type="text"]')).some(l=>/search/i.test(l.name)||/search/i.test(l.placeholder??"")),r=e.querySelector('a[href*="sitemap" i]')!==null,a=Array.from(e.querySelectorAll('a[href^="#"]')).length>=3,s=[o,n,r,a].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:`${c}multiple-ways.html`,element:e.documentElement,failureSummary:`Found ${s} of 4 affordances (nav=${o}, search=${n}, sitemap=${r}, toc=${a}).`}),t}function q(e=document){const t=[],i=/\b(window\.location|location\.href|\.submit\s*\(|window\.open|fetch\s*\(|XMLHttpRequest)\b/,o=e.querySelectorAll("[onmousedown], [onpointerdown]");for(const n of Array.from(o)){const r=(n.getAttribute("onmousedown")??"")+";"+(n.getAttribute("onpointerdown")??"");i.test(r)&&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:`${c}pointer-cancellation.html`,element:n,failureSummary:"Action fires on pointer-down; user cannot abort by dragging away before release."})}return t}function x(e=document){const t=[],i=e.querySelectorAll("script:not([src])");for(const o of Array.from(i)){const n=o.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:`${c}motion-actuation.html`,element:o,failureSummary:"Device-motion listener present. Confirm UI-control alternative + disable mechanism."});break}}return t}function I(e=document){const t=[],i=e.querySelectorAll("form");for(const o of Array.from(i)){const n=o.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(o.textContent??"");t.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:o,failureSummary:`${n.length} required input(s) without ARIA error pattern.`})}return t}function L(e=document){const t=[],i=e.querySelectorAll("[onkeydown], [onkeypress], [onkeyup]");if(i.length===0)return t;const o=Array.from(i).slice(0,5);for(const n of o)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:`${c}character-key-shortcuts.html`,element:n,failureSummary:"Inline keyboard handler detected — verify shortcuts are togglable / scoped."});return t}function $(e=document){const t=[];if(e.querySelectorAll('[role="status"], [role="alert"], [role="log"], [aria-live]').length>0)return t;const o=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,r=e.querySelector('[class*="toast" i], [class*="snackbar" i], [class*="notif" i]')!=null;return(o||n||r)&&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:`${c}status-messages.html`,element:e.documentElement,failureSummary:"No live regions detected on a page that appears to have dynamic content."}),t}function E(e=document){const t=[],i=e.querySelector('nav, [role="navigation"]')!=null,o=e.querySelector('footer, [role="contentinfo"]')!=null,n=e.querySelector('header a[href="/"], [role="banner"] a[href="/"]')!=null;return!i&&!o&&!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:`${c}consistent-navigation.html`,element:e.documentElement,failureSummary:"Multi-page consistency requires site-crawl coverage."}),t}function R(e=document){const t=[],i=e.querySelectorAll("form");if(i.length===0)return t;let o=!1,n=!1;for(const r of Array.from(i)){const a=r.querySelectorAll('input[required], input[pattern], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]');if(a.length!==0){o=!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!o||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:`${c}error-suggestion.html`,element:i[0],failureSummary:"Form has constrained inputs but no suggestion-infrastructure (datalist / aria-describedby / aria-errormessage)."}),t}function W(e=document){var m;const t=[],i=e.querySelectorAll("form");if(i.length===0)return t;const o=/\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,r=/\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 u of Array.from(i)){const d=(u.textContent??"")+" "+(u.getAttribute("aria-label")??"");if(!o.test(d)||n.test(d)&&!/\$|usd|eur|gbp/i.test(d))continue;s=!0,l=u;const p=d+" "+(((m=u.closest("section, main, body"))==null?void 0:m.textContent)??"");if(r.test(p)||a.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:`${c}error-prevention-legal-financial-data.html`,element:l??e.documentElement,failureSummary:"Commit-style form with no detected review/confirm/reversibility cue."}),t}function T(e=document){const t=[],i=e.querySelectorAll("audio[autoplay], video[autoplay]");if(i.length===0)return t;for(const o of Array.from(i))o.hasAttribute("controls")||o.tagName.toLowerCase()==="video"&&o.hasAttribute("muted")||t.push({ruleId:"wcc-autoplay-no-controls",wcagCriterion:"1.4.2",wcagLevel:"A",impact:"serious",description:`${o.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:o,failureSummary:"Autoplay media without controls — needs pause/stop/volume mechanism."});return t}function G(e=document){const t=[],i=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 o of Array.from(i).slice(0,30)){const n=getComputedStyle(o),r=n.position;if(r!=="sticky"&&r!=="fixed"||n.display==="none"||n.visibility==="hidden"||n.opacity==="0")continue;const a=n.backgroundColor,s=/rgba?\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\s*\)/.exec(a);if((s?Number(s[1]):1)<.5&&!n.backgroundImage.match(/url\(/))continue;const m=o.getBoundingClientRect();if(m.width===0||m.height===0)continue;const u=window.innerHeight||document.documentElement.clientHeight,d=m.top<80&&m.bottom>0,p=m.bottom>u-80&&m.top<u;!d&&!p||t.push({ruleId:"wcc-sticky-may-obscure-focus",wcagCriterion:"2.4.11",wcagLevel:"AA",impact:"moderate",description:`Element is ${r}-positioned and overlaps the viewport's ${d?"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:o,failureSummary:`${r} element overlapping viewport edge — verify focus visibility.`})}return t}function U(e=document){const t=[],i=e.querySelectorAll("form");if(i.length===0)return t;const o=/\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 r=!1;for(const a of Array.from(i)){const s=a.textContent??"";if(o.test(s)){r=!0;break}}return!r&&e.querySelectorAll(n).length>0&&(r=!0),r&&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:`${c}redundant-entry.html`,element:e.documentElement,failureSummary:"Multi-step flow signals detected — needs site-crawl coverage."}),t}function z(e=document){return[]}function f(e){if(e.id)return`#${e.id}`;const t=e.classList[0];return`${e.tagName.toLowerCase()}${t?"."+t:""}`}function O(e=document){return[...g(e),...y(e),...b(e),...w(e),...v(e),...A(e),...C(e),...S(e),...k(e),...q(e),...x(e),...I(e),...L(e),...$(e),...z(e),...E(e),...R(e),...W(e),...T(e),...G(e),...U(e)]}const N=["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{N as DOM_ANALYZER_COVERED_CRITERIA,y as analyzeAnimation,T as analyzeAudioControl,L as analyzeCharacterKeyShortcuts,A as analyzeCognitiveAuth,E as analyzeConsistencyHints,S as analyzeContentOnHover,v as analyzeContextChangeOnFocusInput,I as analyzeErrorIdentification,W as analyzeErrorPreventionLegalFinancial,R as analyzeErrorSuggestion,G as analyzeFocusNotObscured,w as analyzeMedia,x as analyzeMotionActuation,k as analyzeMultipleWays,C as analyzeOrientation,z as analyzeParsingObsoleted,q as analyzePointerCancellation,b as analyzePointerGestures,U as analyzeRedundantEntry,$ as analyzeStatusMessages,g as analyzeTimingAdjustable,O as runAllDomCriterionAnalyzers};
|
|
1
|
+
const c="https://www.w3.org/WAI/WCAG21/Understanding/";function b(e=document){const t=[],o=e.querySelectorAll('meta[http-equiv="refresh" i]');for(const n of Array.from(o)){const r=n.getAttribute("content")??"",i=parseInt(r.split(";")[0]??"",10);Number.isFinite(i)&&i>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 ${i}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: ${i}s. No user control mechanism on the page.`})}const a=e.querySelectorAll("script:not([src])");for(const n of Array.from(a)){const r=n.textContent??"";r.length===0||!/\b(setTimeout|setInterval)\s*\(/.test(r)||!/\b(innerHTML|textContent|replaceChildren|window\.location|document\.location|location\.href|location\.replace)\b/.test(r)||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:`${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 t}function w(e=document){const t=[],o=e.querySelectorAll("marquee, blink");for(const i of Array.from(o))t.push({ruleId:"wcc-marquee-blink",wcagCriterion:"2.2.2",wcagLevel:"A",impact:"serious",description:`<${i.tagName.toLowerCase()}> creates continuous motion with no built-in pause control. WCAG 2.2.2 requires a way to pause, stop, or hide auto-moving content.`,helpUrl:`${c}pause-stop-hide.html`,element:i,failureSummary:`Deprecated <${i.tagName.toLowerCase()}> element auto-animates without pause control.`});const a=e.querySelectorAll("video[autoplay]");for(const i of Array.from(a))!i.hasAttribute("controls")&&!i.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:`${c}pause-stop-hide.html`,element:i,failureSummary:"Autoplay video lacks user-accessible pause control."});const n=e.querySelectorAll("*");let r=0;for(const i of Array.from(n)){if(r>1500)break;r++;const s=window.getComputedStyle(i);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,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:`${c}three-flashes-or-below-threshold.html`,element:i,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:`${c}pause-stop-hide.html`,element:i,failureSummary:`Infinite animation @ ${u.toFixed(1)}s/cycle. Confirm user can pause.`})}}return t}function v(e=document){const t=[],o=new Set,a=e.querySelectorAll("[ontouchstart], [ontouchmove], [ongesturestart], [ongesturechange]");for(const r of Array.from(a)){const i=g(r);o.has(i)||(o.add(i),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:`${c}pointer-gestures.html`,element:r,failureSummary:"Touch/gesture handler on this element. Confirm single-pointer alternative."}))}const n=e.querySelectorAll('[draggable="true"], [ondragstart]');for(const r of Array.from(n)){const i=g(r);o.has(i)||(o.add(i),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:`${c}dragging-movements.html`,element:r,failureSummary:"Drag-and-drop interaction. Confirm non-drag alternative for keyboard / touch users."}))}return t}function A(e=document){const t=[],o=e.querySelectorAll("video");for(const n of Array.from(o)){const r=Array.from(n.querySelectorAll("track")),i=r.some(l=>(l.kind??"").toLowerCase()==="captions"||(l.kind??"").toLowerCase()==="subtitles"),s=r.some(l=>(l.kind??"").toLowerCase()==="descriptions");i||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:`${c}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:`${c}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:`${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 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:`${c}audio-only-and-video-only-prerecorded.html`,element:n,failureSummary:"Audio element detected. Verify nearby transcript link."});return t}function C(e=document){const t=[],o=/\b(window\.location|document\.location|location\.href|location\.replace|window\.open|\.submit\s*\()/,a=e.querySelectorAll("[onfocus]");for(const r of Array.from(a)){const i=r.getAttribute("onfocus")??"";o.test(i)&&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:`${c}on-focus.html`,element:r,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 r of Array.from(n)){const i=r.getAttribute("onchange")??"";o.test(i)&&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:`${c}on-input.html`,element:r,failureSummary:"Change handler navigates / submits. Replace with a separate submit button."})}return t}function S(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 r of Array.from(a)){if(n.has(r))continue;n.add(r);const i=r.closest("form"),s=((i==null?void 0:i.textContent)??"").toLowerCase(),l=/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: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:r,failureSummary:l?"CAPTCHA detected; alternative may exist — verify it skips the cognitive challenge.":"CAPTCHA detected without an alternative authentication path."})}return t}function k(e=document){const t=[],o=e.querySelectorAll('meta[name="viewport" i]');for(const n of Array.from(o)){const r=n.getAttribute("content")??"";(/user-scalable\s*=\s*(no|0)\b/i.test(r)||/maximum-scale\s*=\s*1(\.0+)?\b/i.test(r))&&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:`${c}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 r=n.cssRules;if(!r)continue;for(const i of Array.from(r)){if(!(i instanceof CSSMediaRule))continue;const s=i.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(i.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: ${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:e.documentElement,failureSummary:`Orientation-locked display:none on selector "${d.selectorText}".`});break}}}}catch{}}return t}function q(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 r=n.getAttribute("title");!r||r.trim().length<8||n.hasAttribute("aria-describedby")||n.tagName==="A"&&(n.textContent??"").includes(r)||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:`${c}content-on-hover-or-focus.html`,element:n,failureSummary:"title-attribute tooltip is not dismissible / hoverable."})}return t}function x(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(l=>/search/i.test(l.name)||/search/i.test(l.placeholder??"")),r=e.querySelector('a[href*="sitemap" i]')!==null,i=Array.from(e.querySelectorAll('a[href^="#"]')).length>=3,s=[a,n,r,i].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:`${c}multiple-ways.html`,element:e.documentElement,failureSummary:`Found ${s} of 4 affordances (nav=${a}, search=${n}, sitemap=${r}, toc=${i}).`}),t}function I(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 r=(n.getAttribute("onmousedown")??"")+";"+(n.getAttribute("onpointerdown")??"");o.test(r)&&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:`${c}pointer-cancellation.html`,element:n,failureSummary:"Action fires on pointer-down; user cannot abort by dragging away before release."})}return t}function L(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:`${c}motion-actuation.html`,element:a,failureSummary:"Device-motion listener present. Confirm UI-control alternative + disable mechanism."});break}}return t}function $(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 i=/\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:i?"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:a,failureSummary:`${n.length} required input(s) without ARIA error pattern.`})}return t}function E(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:`${c}character-key-shortcuts.html`,element:n,failureSummary:"Inline keyboard handler detected — verify shortcuts are togglable / scoped."});return t}function R(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,r=e.querySelector('[class*="toast" i], [class*="snackbar" i], [class*="notif" i]')!=null;return(a||n||r)&&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:`${c}status-messages.html`,element:e.documentElement,failureSummary:"No live regions detected on a page that appears to have dynamic content."}),t}function W(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:`${c}consistent-navigation.html`,element:e.documentElement,failureSummary:"Multi-page consistency requires site-crawl coverage."}),t}function z(e=document){const t=[],o=e.querySelectorAll("form");if(o.length===0)return t;let a=!1,n=!1;for(const r of Array.from(o)){const i=r.querySelectorAll('input[required], input[pattern], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]');if(i.length!==0){a=!0;for(const s of Array.from(i))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:`${c}error-suggestion.html`,element:o[0],failureSummary:"Form has constrained inputs but no suggestion-infrastructure (datalist / aria-describedby / aria-errormessage)."}),t}function T(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,r=/\b(review|preview|confirm|verify|are you sure|please confirm|double-check)\b/i,i=/\b(undo|reverse|cancel|edit|update later|change later)\b/i;let s=!1,l=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,l=d;const p=u+" "+(((m=d.closest("section, main, body"))==null?void 0:m.textContent)??"");if(r.test(p)||i.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:`${c}error-prevention-legal-financial-data.html`,element:l??e.documentElement,failureSummary:"Commit-style form with no detected review/confirm/reversibility cue."}),t}function G(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:`${c}audio-control.html`,element:a,failureSummary:"Autoplay media without controls — needs pause/stop/volume mechanism."});return t}function U(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),r=n.position;if(r!=="sticky"&&r!=="fixed"||n.display==="none"||n.visibility==="hidden"||n.opacity==="0")continue;const i=Number(n.opacity);if(Number.isFinite(i)&&i<=.1)continue;const s=n.zIndex;if(s&&s!=="auto"){const f=Number(s);if(Number.isFinite(f)&&f<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=a.getBoundingClientRect();if(u.width===0||u.height===0)continue;const p=window.innerHeight||document.documentElement.clientHeight,h=u.top<80&&u.bottom>0,y=u.bottom>p-80&&u.top<p;!h&&!y||t.push({ruleId:"wcc-sticky-may-obscure-focus",wcagCriterion:"2.4.11",wcagLevel:"AA",impact:"moderate",description:`Element is ${r}-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:`${c}focus-not-obscured-minimum.html`,element:a,failureSummary:`${r} element overlapping viewport edge — verify focus visibility.`})}return t}function N(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 r=!1;for(const i of Array.from(o)){const s=i.textContent??"";if(a.test(s)){r=!0;break}}return!r&&e.querySelectorAll(n).length>0&&(r=!0),r&&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:`${c}redundant-entry.html`,element:e.documentElement,failureSummary:"Multi-step flow signals detected — needs site-crawl coverage."}),t}function O(e=document){return[]}function g(e){if(e.id)return`#${e.id}`;const t=e.classList[0];return`${e.tagName.toLowerCase()}${t?"."+t:""}`}function F(e=document){return[...b(e),...w(e),...v(e),...A(e),...C(e),...S(e),...k(e),...q(e),...x(e),...I(e),...L(e),...$(e),...E(e),...R(e),...O(e),...W(e),...z(e),...T(e),...G(e),...U(e),...N(e)]}const H=["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{H as DOM_ANALYZER_COVERED_CRITERIA,w as analyzeAnimation,G as analyzeAudioControl,E as analyzeCharacterKeyShortcuts,S as analyzeCognitiveAuth,W as analyzeConsistencyHints,q as analyzeContentOnHover,C as analyzeContextChangeOnFocusInput,$ as analyzeErrorIdentification,T as analyzeErrorPreventionLegalFinancial,z as analyzeErrorSuggestion,U as analyzeFocusNotObscured,A as analyzeMedia,L as analyzeMotionActuation,x as analyzeMultipleWays,k as analyzeOrientation,O as analyzeParsingObsoleted,I as analyzePointerCancellation,v as analyzePointerGestures,N as analyzeRedundantEntry,R as analyzeStatusMessages,b as analyzeTimingAdjustable,F as runAllDomCriterionAnalyzers};
|
package/dist/manifest.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "wcagcheckr",
|
|
4
4
|
"description": "Audit components across hover, focus, dark mode, forced colors, RTL — every state your users actually encounter. Per-component baselines surface only NEW violations.",
|
|
5
|
-
"version": "1.0.0.
|
|
6
|
-
"version_name": "1.0.0-rc.
|
|
5
|
+
"version": "1.0.0.106",
|
|
6
|
+
"version_name": "1.0.0-rc.106",
|
|
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-
|
|
41
|
+
"assets/content-script.ts-loader-Czc3Zduw.js"
|
|
42
42
|
],
|
|
43
43
|
"matches": [
|
|
44
44
|
"<all_urls>"
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"assets/_commonjsHelpers-Cpj98o6Y.js",
|
|
68
68
|
"assets/custom-rules-CAe0nbES.js",
|
|
69
69
|
"assets/reflow-analyzer-DNgBX8N_.js",
|
|
70
|
-
"assets/dom-criterion-analyzers-
|
|
71
|
-
"assets/content-script.ts-
|
|
70
|
+
"assets/dom-criterion-analyzers-DsEV9zo7.js",
|
|
71
|
+
"assets/content-script.ts-3-biN-xk.js"
|
|
72
72
|
],
|
|
73
73
|
"use_dynamic_url": false
|
|
74
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcag-checkr/ci",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.106",
|
|
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",
|