@wcag-checkr/ci 1.0.21 → 1.0.23
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/ErrorBoundary-CAo1EDqF.js +594 -0
- package/dist/assets/ai-usage-log-DVzy9AU1.js +1 -0
- package/dist/assets/{copy-ai-fixer-prompt-CL_yFzec.js → copy-ai-fixer-prompt-CoTMEYa8.js} +2 -2
- package/dist/assets/{devtools-panel-hFt35zzD.js → devtools-panel-BZ5-1wG6.js} +1 -1
- package/dist/assets/{options-Q406rAw1.js → options-C7TJwA_a.js} +3 -3
- package/dist/assets/{parallel-tab-flow-DN_z0IGA.js → parallel-tab-flow-B8XxnbrB.js} +1 -1
- package/dist/assets/{rubric-config-client-Ck8nJHda.js → rubric-config-client-DhPZ3tGE.js} +1 -1
- package/dist/assets/scheduled-audit-runner-Dek_J9pU.js +2105 -0
- package/dist/assets/{service-worker.ts-BZg0XiJG.js → service-worker.ts-BGN4N7Yn.js} +2 -2
- package/dist/assets/{side-panel-BjfjV2qz.js → side-panel-DvxfwgMS.js} +3 -3
- package/dist/assets/{site-report-renderer-Cdd4E5RB.js → site-report-renderer-Bw9jYmSg.js} +1 -1
- package/dist/assets/{styles-clWOl0K-.js → styles-Dg6rHpW6.js} +13 -13
- package/dist/assets/{tier-config-client-D1GMRTQW.js → tier-config-client-CimEdyEg.js} +1 -1
- package/dist/devtools/panel.html +5 -5
- package/dist/manifest.json +1 -1
- package/dist/options/options.html +4 -4
- package/dist/service-worker-loader.js +1 -1
- package/dist/side-panel/side-panel.html +8 -8
- package/package.json +1 -1
- package/dist/assets/ErrorBoundary-CzV_1Ln4.js +0 -594
- package/dist/assets/ai-usage-log-HdIilbbf.js +0 -1
- package/dist/assets/scheduled-audit-runner-DO-z335b.js +0 -2101
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as U}from"./crash-reporter-DAZnNefT.js";const I="autoExportSettings",f="lastAutoExportAt",be=["deposition-packet","defense-bundle","html-print","ai-prompt"],v={enabled:!1,formats:["defense-bundle"]};function C(o){return o==="deposition-packet"||o==="defense-bundle"||o==="html-print"||o==="ai-prompt"}async function ye(){var l;if(typeof chrome>"u"||!((l=chrome.storage)!=null&&l.local))return{...v,formats:[...v.formats]};const e=(await chrome.storage.local.get(I))[I];if(!e||typeof e!="object")return{...v,formats:[...v.formats]};const n=typeof e.enabled=="boolean"?e.enabled:!1;let a=[];return Array.isArray(e.formats)?a=e.formats.filter(C):C(e.format)&&(a=[e.format]),a.length===0&&(a=[...v.formats]),{enabled:n,formats:a}}async function Ae(o){var e;typeof chrome>"u"||!((e=chrome.storage)!=null&&e.local)||await chrome.storage.local.set({[I]:o})}async function we(o){var a;return typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local)?null:((await chrome.storage.local.get(f))[f]??{})[o]??null}async function Ie(o){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return;const n=(await chrome.storage.local.get(f))[f]??{};n[o.url]=o,await chrome.storage.local.set({[f]:n})}const S="autoDownloadReports";async function Se(){return(await chrome.storage.local.get(S))[S]===!0}const xe=S;function M(o,e){const a=/^text\/(markdown|plain|csv)\b/i.test(e)?"\uFEFF"+o:o,l=/charset=/i.test(e)?e:`${e};charset=utf-8`;return{body:a,type:l}}function ke(o,e,n="text/html"){const a=(e.split(".").pop()??"").toLowerCase(),l=n==="text/html"&&(a==="md"||a==="markdown")?"text/markdown":n==="text/html"&&a==="txt"?"text/plain":n,{body:r,type:t}=M(o,l),d=new Blob([r],{type:t}),i=URL.createObjectURL(d),s=document.createElement("a");s.href=i,s.download=e,document.body.appendChild(s),s.click(),document.body.removeChild(s),setTimeout(()=>URL.revokeObjectURL(i),5e3)}function De(o){var e;typeof chrome<"u"&&((e=chrome.tabs)!=null&&e.create)?chrome.tabs.create({url:o,active:!0}).catch(n=>{console.warn("[download-helper] chrome.tabs.create failed; falling back to window.open",n),window.open(o,"_blank","noopener,noreferrer")}):window.open(o,"_blank","noopener,noreferrer")}function Ce(o,e="text/html"){var l;const n=new Blob([o],{type:e}),a=URL.createObjectURL(n);return typeof chrome<"u"&&((l=chrome.tabs)!=null&&l.create)?chrome.tabs.create({url:a,active:!0}).catch(r=>{console.warn("[download-helper] chrome.tabs.create failed; falling back to window.open",r),window.open(a,"_blank","noopener,noreferrer")}):window.open(a,"_blank","noopener,noreferrer"),setTimeout(()=>URL.revokeObjectURL(a),6e4),a}function qe(o,e){const n=new Date().toISOString().slice(0,10),a=(e??"audit").replace(/[^a-z0-9]/gi,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").slice(0,24).toLowerCase()||"audit";return`wcagcheckr-${o}-${n}-${a}.${o==="ai-prompt"?"md":"html"}`}function Re(o){return o<1024?`${o} B`:o<1024*1024?`${Math.round(o/1024)} KB`:`${(o/(1024*1024)).toFixed(1)} MB`}const x="tierConfig:cache",P=60*60*1e3,N=24*60*60*1e3,W=7*24*60*60*1e3;async function Te(){return(await chrome.storage.local.get(x))[x]??null}async function Oe(o){await chrome.storage.local.set({[x]:o})}function Ee(o,e=Date.now()){const n=e-o.fetchedAt;return n<P?"fresh":n<N?"stale":n<W?"grace":"expired"}function Le(o,e){const n={...o},a=s=>{if(typeof s=="number")return s===-1?1/0:s},l=s=>typeof s=="boolean"?s:void 0,r=s=>{if(s==="all"||s==="configurable"||s==="default-only")return s},t=(s,c)=>{const u=l(c);u!==void 0&&(n[s]=u)},d=(s,c)=>{const u=a(c);u!==void 0&&(n[s]=u)};d("maxComponents",e.maxComponents),d("maxBaselines",e.maxBaselines);const i=r(e.stateMatrix);return i!==void 0&&(n.stateMatrix=i),d("siteCrawlMaxPages",e.siteCrawlMaxPages),d("scheduledAuditsMaxCount",e.scheduledAuditsMaxCount),d("aiCostCapDefaultUsd",e.aiCostCapDefaultUsd),d("aiMaxCandidatesPerCheck",e.aiMaxCandidatesPerCheck),t("storybookAutoIterate",e.storybookAutoIterate),t("parallelScan",e.parallelScan),t("allFramesAudit",e.allFramesAudit),t("siteCrawl",e.siteCrawl),t("scheduledAudits",e.scheduledAudits),t("emailAlerts",e.emailAlerts),t("webhookAlerts",e.webhookAlerts),t("autoExportAuditReports",e.autoExportAuditReports),t("aiJudgment",e.aiJudgment),t("aiWalkthroughs",e.aiWalkthroughs),t("aiCopilot",e.aiCopilot),t("aiColorFix",e.aiColorFix),t("aiComplianceSummary",e.aiComplianceSummary),t("aiIncompleteResolver",e.aiIncompleteResolver),t("aiConsistencySuppression",e.aiConsistencySuppression),t("visualizers",e.visualizers),t("flowRecorder",e.flowRecorder),t("igtWorkflows",e.igtWorkflows),t("axTreeViewer",e.axTreeViewer),t("pdfAudit",e.pdfAudit),t("exportJson",e.exportJson),t("exportSarif",e.exportSarif),t("exportJunit",e.exportJunit),t("exportHtmlPrint",e.exportHtmlPrint),t("exportVpat",e.exportVpat),t("exportDefenseBundle",e.exportDefenseBundle),t("exportEvidenceBundle",e.exportEvidenceBundle),t("exportDeveloperLetter",e.exportDeveloperLetter),t("exportOwnerReport",e.exportOwnerReport),t("exportAiPrompts",e.exportAiPrompts),t("exportDepositionPacket",e.exportDepositionPacket),t("exportMethodologyDoc",e.exportMethodologyDoc),t("exportConformanceCrosswalk",e.exportConformanceCrosswalk),t("exportEaaStatement",e.exportEaaStatement),t("exportTicketBundle",e.exportTicketBundle),t("exportExecutiveReport",e.exportExecutiveReport),t("exportPreLitigationReport",e.exportPreLitigationReport),t("forensicLog",e.forensicLog),t("forensicAnchoring",e.forensicAnchoring),t("wcagEmReport",e.wcagEmReport),t("wcagEmAnchor",e.wcagEmAnchor),t("statusBadge",e.statusBadge),t("riskDashboard",e.riskDashboard),t("lawfareRisk",e.lawfareRisk),t("wcag3Preview",e.wcag3Preview),t("cloudSync",e.cloudSync),t("issueTrackers",e.issueTrackers),t("cliRunner",e.cliRunner),t("mcpServer",e.mcpServer),t("vscodeExtension",e.vscodeExtension),t("devtoolsPanel",e.devtoolsPanel),t("designSystemAudit",e.designSystemAudit),t("customRules",e.customRules),n}function Fe(o,e){const n=e.plans.find(a=>a.code===o||a.code.startsWith(o+"-"));return(n==null?void 0:n.features)??null}const w=[{id:"mobile",label:"Mobile (375×667)",width:375,height:667,deviceScaleFactor:2,mobile:!0},{id:"tablet",label:"Tablet (768×1024)",width:768,height:1024,deviceScaleFactor:2,mobile:!0},{id:"desktop",label:"Desktop (1280×800)",width:1280,height:800,deviceScaleFactor:1,mobile:!1}],T={pseudoStates:["default","hover","focus","focus-visible","active","disabled"],ariaVariations:[],themes:["light","dark","forced-colors-active","forced-colors-none"],directions:["ltr"],breakpoints:["desktop","mobile","tablet"],breakpointPresets:w},O={pseudoStates:["default","hover","focus","focus-visible","active","disabled"],ariaVariations:[],themes:["light","dark","forced-colors-active","forced-colors-none"],directions:["ltr","rtl"],breakpoints:["desktop","mobile","tablet"],breakpointPresets:w},V={pseudoStates:["default"],ariaVariations:[],themes:["light"],directions:["ltr"],breakpoints:["desktop"],breakpointPresets:w},$=[{id:"default-subset",name:"Default subset",matrix:T},{id:"full-matrix",name:"Full matrix",matrix:O}],_e=Object.freeze(Object.defineProperty({__proto__:null,BUILT_IN_PRESETS:$,DEFAULT_BREAKPOINT_PRESETS:w,DEFAULT_STATE_MATRIX_SETTINGS:T,FULL_STATE_MATRIX_SETTINGS:O,QUICK_SCAN_MATRIX_SETTINGS:V},Symbol.toStringTag,{value:"Module"})),z={id:"keyboard",name:"Keyboard navigation",blurb:"Verify everyone who relies on the keyboard can reach, operate, and escape every interactive element. WCAG 2.1.1, 2.1.2, 2.4.3, 2.4.7.",steps:[{id:"tab-reaches-all",prompt:"Tab through the component from start to finish. Compare against the Tab-order overlay.",question:"Did Tab reach every interactive element (button, link, form field, custom widget)?",wcag:"2.1.1",visualizer:"tab-order",severity:"required"},{id:"tab-order-logical",prompt:"Watch the focus indicator as you Tab. Compare visual order vs the Tab-order numbered overlay.",question:"Does focus move in a logical order matching the visual layout (left-to-right, top-to-bottom)? Red badges indicate visual mismatches.",wcag:"2.4.3",visualizer:"tab-order",severity:"required",autoDismissActivity:"tab-order"},{id:"focus-visible",prompt:"On each focused element, confirm a visible focus indicator (outline, ring, color change).",question:"Is the focus indicator clearly visible on every interactive element?",wcag:"2.4.7",relatedAxeRule:"focus-order-semantics",severity:"required"},{id:"no-trap",prompt:"Tab forward and Shift+Tab backward through any modal, dropdown, or custom widget.",question:"Can you escape the widget with Tab/Shift+Tab/Esc — no trapping?",wcag:"2.1.2",severity:"required"},{id:"enter-activates",prompt:"Focus a button or link and press Enter (and Space for buttons).",question:"Did Enter/Space activate the control as expected?",wcag:"2.1.1",severity:"required"},{id:"esc-closes",prompt:"Open any modal, dropdown, or popover and press Escape.",question:"Did Escape close the overlay and return focus to the trigger?",wcag:"2.1.2",severity:"required"},{id:"arrow-keys-grouped",prompt:"For grouped widgets (radio, tabs, listbox, menu): focus into the group, then use arrow keys.",question:"Do arrow keys move within the group as expected (and Tab moves out of it)?",wcag:"2.1.1",severity:"advisory"},{id:"character-key-shortcuts",prompt:'Identify any single-character keyboard shortcuts (e.g., "S" to open search, "/" to focus the search box, J/K for next/prev). If none exist, mark N/A.',question:"For every single-character shortcut: can it be turned off, remapped to a modifier-key combo, OR is it only active when the relevant control has focus? (Prevents accidental activation by speech-input users.)",wcag:"2.1.4",severity:"required"}]},K={id:"screen-reader",name:"Screen reader readiness",blurb:"Verify the component is operable and meaningful via a screen reader. Use NVDA (Windows), VoiceOver (macOS/iOS), or TalkBack (Android). WCAG 1.3.1, 4.1.2.",steps:[{id:"sr-announces-on-focus",prompt:"Activate your screen reader and Tab to each interactive element.",question:"Does the SR announce a meaningful name + role + state for every focused element?",wcag:"4.1.2",relatedAxeRule:"aria-required-attr",severity:"required"},{id:"sr-headings-navigate",prompt:"Use the SR's heading-navigation shortcut (NVDA: H, VoiceOver: VO+Cmd+H). Compare against the Outline overlay.",question:"Are all logical sections reachable via heading navigation, in correct hierarchy order?",wcag:"1.3.1",relatedAxeRule:"heading-order",visualizer:"outline",severity:"required"},{id:"sr-reading-order",prompt:"Read the page top-to-bottom using the SR's read-all command (NVDA: NumPad+Down, VoiceOver: VO+A). Compare against the Reading-order numbered overlay.",question:"Does the SR read content in the meaningful order implied by the visual layout (1.3.2 Meaningful Sequence)? Red badges in the Reading-order overlay show DOM-vs-visual divergences.",wcag:"1.3.2",visualizer:"reading-order",severity:"required",autoDismissActivity:"reading-order"},{id:"sr-landmarks",prompt:"Use the SR's landmark-navigation shortcut (NVDA: D, VoiceOver: VO+U → Landmarks). Compare against the Outline overlay.",question:"Can the user jump to main, nav, banner, contentinfo via landmark navigation?",wcag:"1.3.1",relatedAxeRule:"region",visualizer:"outline",severity:"required"},{id:"sr-live-region",prompt:"Trigger any dynamic update (form submission, status change, toast).",question:'Does the SR announce the change automatically (via aria-live or role="status"/role="alert")?',wcag:"4.1.3",severity:"required"},{id:"sr-form-errors",prompt:"Submit a form with an invalid field.",question:"Does the SR announce the error and which field it relates to?",wcag:"3.3.1",severity:"required"},{id:"sr-images-meaningful",prompt:"Navigate over each image (NVDA: G, VoiceOver: VO+Cmd+G).",question:"Does each meaningful image have descriptive alt text? Are decorative images silent?",wcag:"1.1.1",relatedAxeRule:"image-alt",severity:"required"}]},G={id:"focus-management",name:"Focus management",blurb:"Verify focus moves and restores correctly across overlays, navigations, and dynamic content. WCAG 2.4.3, 2.4.7, 3.2.2.",steps:[{id:"modal-initial-focus",prompt:"Open a modal/dialog.",question:"Did focus move into the modal automatically (typically to the first focusable element or a heading)?",severity:"required"},{id:"modal-restored-on-close",prompt:"Close a modal/dialog (via close button or Esc).",question:"Was focus restored to the element that opened it?",severity:"required"},{id:"modal-trap-while-open",prompt:"While a modal is open, Tab/Shift+Tab through it.",question:"Is focus trapped inside the modal until it closes?",severity:"required"},{id:"route-change-focus",prompt:"Trigger a client-side route change.",question:"Did focus move sensibly (to the new page heading, or main landmark) — not to <body>?",severity:"required"},{id:"expanded-focus",prompt:"Open an expandable section (accordion, dropdown, collapsible).",question:"Is focus moved into or near the newly-revealed content (or kept on the trigger appropriately)?",severity:"advisory"},{id:"no-unexpected-focus-move",prompt:"Type or click in form fields without explicitly tabbing.",question:"Does focus stay where you put it (no auto-jump on input)?",wcag:"3.2.2",severity:"required"},{id:"no-context-change-on-focus",prompt:"Tab into each interactive element on the page.",question:"Does merely RECEIVING focus on an element NOT trigger a context change (auto-submit, open modal, navigate elsewhere, change focus to a different field)? (Context change on focus violates 3.2.1; activating a control via click/Enter is fine.)",wcag:"3.2.1",severity:"required"}]},B={id:"forms",name:"Forms",blurb:"Verify form fields are properly labeled, errors are clear, and the experience works for assistive technology. WCAG 1.3.1, 3.3.1, 3.3.2, 4.1.2.",steps:[{id:"visible-labels",prompt:"Look at every form field.",question:"Does every field have a visible label (not just placeholder text)?",wcag:"3.3.2",relatedAxeRule:"label",severity:"required"},{id:"label-association",prompt:"Click each visible label.",question:"Did clicking the label focus the associated input?",wcag:"1.3.1",relatedAxeRule:"label",severity:"required"},{id:"required-marked",prompt:"Identify required fields.",question:"Are required fields marked both visually AND programmatically (aria-required or required attribute)?",wcag:"3.3.2",severity:"required"},{id:"autocomplete-set",prompt:"For personal-info fields (name, email, phone, address): inspect the autocomplete attribute.",question:'Is autocomplete set with a valid HTML5 token (e.g., "email", "given-name", "tel")?',wcag:"1.3.5",relatedAxeRule:"autocomplete-valid",severity:"required"},{id:"error-text",prompt:"Submit with invalid input.",question:'Are error messages specific (not just "invalid"), shown near the field, and programmatically associated (aria-describedby)?',wcag:"3.3.1",severity:"required"},{id:"no-color-only-error",prompt:"Look at how errors are signaled.",question:"Is the error indicated by more than just color (also icon, text, or border style)?",wcag:"1.4.1",severity:"required"},{id:"success-announced",prompt:"Submit a form successfully.",question:'Is the success state announced to screen readers (via aria-live or role="status")?',wcag:"4.1.3",severity:"advisory"},{id:"error-suggestion",prompt:"Submit a form with an obvious invalid input (wrong email format, password too short, missing required field).",question:'Does the error message INCLUDE a specific suggestion for fixing it (not just "invalid")?',wcag:"3.3.3",severity:"required"},{id:"redundant-entry",prompt:"Identify any multi-step or multi-form flow where the user re-enters previously provided information (shipping = billing, profile data repeated in another form).",question:"Is previously-entered information auto-populated, selectable from a list, or otherwise NOT manually re-entered?",wcag:"3.3.7",severity:"required"},{id:"accessible-authentication",prompt:"Identify the site's authentication flow (login, password reset, multi-factor).",question:'Does auth NOT require a cognitive function test (e.g., remembering / transcribing characters), OR is an accessible alternative offered (passkey, magic link, password-manager autofill, "show password" toggle)?',wcag:"3.3.8",severity:"required"}]},H={id:"page-structure",name:"Page structure",blurb:"Verify the page declares its language, has a meaningful title, and exposes a sensible heading + landmark hierarchy. WCAG 2.4.2, 3.1.1, 3.1.2, 1.3.1, 2.4.6.",steps:[{id:"page-title",prompt:"Look at the browser tab — is the page title set and meaningful?",question:'Does the title describe the page topic / purpose (not just "Untitled" or the site name)?',wcag:"2.4.2",relatedAxeRule:"document-title",severity:"required"},{id:"page-lang",prompt:"Inspect the <html> element's lang attribute.",question:'Is lang set to a valid language code (e.g., "en", "es-MX")?',wcag:"3.1.1",relatedAxeRule:"html-has-lang",severity:"required"},{id:"lang-of-parts",prompt:"For any text not in the page's primary language (foreign quote, brand name in another language), inspect the parent element.",question:"Is lang attribute set on those elements to indicate the language change?",wcag:"3.1.2",severity:"advisory"},{id:"h1-present",prompt:"Use the Outline visualizer or SR heading shortcut to find the h1.",question:"Is there exactly one h1, and does it describe the main content of the page?",wcag:"1.3.1",relatedAxeRule:"page-has-heading-one",visualizer:"outline",severity:"required"},{id:"descriptive-headings",prompt:"Read each heading. Imagine someone listening only to the heading text.",question:"Does each heading describe its section meaningfully on its own?",wcag:"2.4.6",severity:"required"},{id:"main-landmark",prompt:"Use the Outline visualizer or SR landmark shortcut.",question:"Is there exactly one <main> landmark wrapping the page's primary content?",wcag:"1.3.1",relatedAxeRule:"landmark-one-main",visualizer:"outline",severity:"required"},{id:"parsing-well-formed",prompt:"Open DevTools → Console. Reload the page and check for HTML parse errors. Or run W3C validator on the URL.",question:"Is the page's markup well-formed (no duplicate IDs in scope, no invalid nesting, no missing required attributes)? WCAG 4.1.1 was obsoleted in 2.2 but still applies to 2.1 conformance claims.",wcag:"4.1.1",severity:"required"}]},j={id:"visual-indicators",name:"Visual indicators",blurb:"Verify nothing relies on color/shape/position alone, and non-text contrast meets thresholds for UI components and focus rings. WCAG 1.3.3, 1.4.1, 1.4.11.",steps:[{id:"no-color-only-info",prompt:"Scan instructions, error messages, charts, status indicators.",question:"Is information conveyed by something OTHER than color alone (text, icon, pattern, label)?",wcag:"1.4.1",severity:"required"},{id:"no-sensory-only",prompt:'Look for instructions referencing shape ("the round button"), location ("on the left"), sound ("the chime"), or visual cues alone.',question:"Are those instructions backed up by a programmatic identifier (label, role, accessible name)?",wcag:"1.3.3",severity:"required"},{id:"non-text-contrast",prompt:"For UI components (form-field borders, focus rings, icons-as-buttons), verify contrast vs adjacent colors. Use a contrast checker if needed.",question:"Do non-text UI components meet ≥3:1 contrast against adjacent colors?",wcag:"1.4.11",severity:"required"},{id:"focus-ring-contrast",prompt:"Tab through; for each focused element, verify the focus ring is visible against both the element and the page background.",question:"Does the focus ring meet ≥3:1 contrast on every focusable surface?",wcag:"1.4.11",visualizer:"tab-order",severity:"required"},{id:"state-changes-visible",prompt:"For toggleable controls (checkboxes, switches, expanded states): change them and observe.",question:"Is the state change conveyed visibly AND programmatically (aria-pressed/expanded/checked)?",wcag:"4.1.2",severity:"required"}]},Y={id:"reflow",name:"Reflow & text spacing",blurb:"Verify the layout survives narrow viewports, browser zoom, and user-overridden text spacing. WCAG 1.4.4, 1.4.10, 1.4.12.",steps:[{id:"resize-320",prompt:"Resize the browser to 320px wide (or set DevTools device toolbar to 320×256).",question:"Does content reflow without two-dimensional scrolling? (Vertical scroll OK; horizontal scroll within content blocks NOT OK.)",wcag:"1.4.10",relatedAxeRule:"wcagcheckr-reflow",severity:"required"},{id:"zoom-200",prompt:"In a desktop viewport, zoom the browser to 200%.",question:"Is all content + functionality available, with no text clipping or unusable controls?",wcag:"1.4.4",severity:"required"},{id:"text-spacing-override",prompt:"Apply text-spacing override (line-height: 1.5, paragraph spacing 2x font-size, letter-spacing 0.12em, word-spacing 0.16em) via a bookmarklet or DevTools.",question:"Does content stay readable with no overlap or clipping?",wcag:"1.4.12",severity:"required"},{id:"no-fixed-pixel-text",prompt:"Inspect text styles for fixed px-based font sizes, line-heights.",question:"Are text dimensions set in relative units (rem/em/%) so they scale with user settings?",severity:"advisory"},{id:"orientation-not-locked",prompt:"Rotate a mobile device (or use DevTools device toolbar to rotate) between portrait and landscape.",question:"Does the layout adapt to both orientations? (Content not locked to one orientation unless essential.)",wcag:"1.3.4",severity:"required"},{id:"images-of-text",prompt:"Look for marketing banners, hero images, or any image that contains substantial text content as part of the raster.",question:"Is real HTML text used in preference to images-of-text, except for logos / decorative purposes? (Images-of-text fail to scale with zoom, can't be selected, and don't respect user text settings.)",wcag:"1.4.5",severity:"required"}]},J={id:"hover-focus-content",name:"Content on hover or focus",blurb:"Tooltips, dropdown previews, and any content revealed on hover or focus must be dismissable, hoverable, and persistent. WCAG 1.4.13.",steps:[{id:"dismissable",prompt:"Hover or focus to reveal a tooltip / popover. Press Escape.",question:"Did Escape dismiss the revealed content without moving pointer or focus?",wcag:"1.4.13",severity:"required"},{id:"hoverable",prompt:"Hover to reveal a tooltip. Move the pointer over the tooltip itself (without losing the trigger's hover).",question:"Does the revealed content stay visible when the pointer enters it?",wcag:"1.4.13",severity:"required"},{id:"persistent",prompt:"Reveal a tooltip and don't do anything else.",question:"Does the content remain visible until the user dismisses it, moves focus/pointer away, or the trigger info is no longer valid?",wcag:"1.4.13",severity:"required"}]},X={id:"pointer",name:"Pointer interactions",blurb:"Verify gestures, target sizes, and pointer cancellation respect users with motor limitations. WCAG 2.5.1, 2.5.2, 2.5.5/2.5.8.",steps:[{id:"no-multi-point-gesture",prompt:"Identify any feature that requires a multi-point gesture (pinch, two-finger swipe) or path-based gesture (drag along a line).",question:"Is there a single-tap / single-click alternative for that feature?",wcag:"2.5.1",severity:"required"},{id:"pointer-cancellation",prompt:"Mouse-down on a button, drag the pointer OFF the button, then release.",question:"Was the action canceled (the button did NOT activate)? Activation should fire on up-event over the target, not down-event.",wcag:"2.5.2",severity:"required"},{id:"target-size-aa",prompt:"Toggle the Targets visualizer. Look for any red overlays (≥24×24 fail).",question:"Are all interactive targets ≥24×24 CSS px (or have sufficient spacing per WCAG 2.5.8)?",wcag:"2.5.8",relatedAxeRule:"target-size",visualizer:"targets",severity:"required"},{id:"label-in-name",prompt:"For every visually-labeled control (button, link), inspect its accessible name (use the SR or DevTools).",question:"Does the accessible name START with — or fully contain — the visible label text?",wcag:"2.5.3",relatedAxeRule:"label-content-name-mismatch",severity:"required"},{id:"motion-actuation-alt",prompt:"Identify any feature that triggers on device motion (shake to undo, tilt to scroll, step counter).",question:"Is there a non-motion alternative (button / control) for every motion-triggered function, AND can motion be disabled?",wcag:"2.5.4",severity:"required"},{id:"dragging-alternative",prompt:"Identify any feature that requires a dragging movement (slider thumb, drag-to-rearrange, swipe-to-delete).",question:"Is there a single-tap / single-click alternative for every dragging action (e.g., +/- buttons next to a slider, up/down buttons next to draggable list items)?",wcag:"2.5.7",severity:"required"}]},Q={id:"time-based",name:"Time-based behaviors",blurb:"Verify time limits, auto-updating content, and motion can be controlled by users. WCAG 2.2.1, 2.2.2, 2.3.1.",steps:[{id:"time-limits-adjustable",prompt:"Identify any time-limited interaction (session timeout, form auto-submit, OTP expiration).",question:"Can the user turn off, adjust, or extend the time limit before it expires (warning + control before the deadline)?",wcag:"2.2.1",severity:"required"},{id:"pause-moving-content",prompt:"Look for content that moves, blinks, scrolls, or auto-updates for >5 seconds (carousels, animated banners, news tickers, auto-refreshing feeds).",question:"Is there a control to pause / stop / hide the moving content?",wcag:"2.2.2",severity:"required"},{id:"no-flash",prompt:"Identify any flashing or blinking content (>3 flashes per second).",question:"Is flashing absent, OR limited below the general/red flash thresholds?",wcag:"2.3.1",severity:"required"},{id:"reduced-motion-honored",prompt:'Enable system "reduce motion" preference. Reload the page.',question:"Does the site honor prefers-reduced-motion (parallax / animations / spinners minimized or disabled)?",wcag:"2.3.3",severity:"advisory"}]},Z={id:"error-prevention",name:"Error prevention",blurb:"Verify destructive, financial, or legal actions are reversible, checked, or confirmed. WCAG 3.3.4, 3.3.6.",steps:[{id:"destructive-confirm",prompt:"Identify destructive actions (delete account, cancel subscription, destroy data, send irreversible message).",question:'Is each destructive action reversible (undo), or checked (validation), or confirmed (explicit "Are you sure?" dialog)?',wcag:"3.3.4",severity:"required"},{id:"financial-confirm",prompt:"Identify financial / legal commitments (purchase, contract sign, transfer).",question:"Before submission, does the user see a review screen and an explicit confirmation step?",wcag:"3.3.4",severity:"required"},{id:"undo-available",prompt:'Test "destructive" actions on user content (delete a note, archive an item).',question:'Is there an undo / restore mechanism, even briefly (toast with "undo" button)?',wcag:"3.3.6",severity:"advisory"}]},ee={id:"consistency",name:"Consistency",blurb:"Verify navigation, identification, and user paths are consistent across pages. WCAG 2.4.5, 3.2.3, 3.2.4.",steps:[{id:"consistent-nav",prompt:"Compare the primary navigation across 3+ pages.",question:"Does the same navigation appear in the same order on every page?",wcag:"3.2.3",severity:"required"},{id:"consistent-id",prompt:'Compare the same UI element appearing on multiple pages (e.g., a search icon, a "Help" link).',question:"Does the same component have the same accessible name everywhere it appears?",wcag:"3.2.4",severity:"required"},{id:"multiple-ways",prompt:"Beyond the primary navigation, is there a second way to find a page (search, sitemap, related links, breadcrumbs)?",question:"Are there at least 2 ways to locate any page (single-process pages exempt)?",wcag:"2.4.5",severity:"required"},{id:"skip-link",prompt:"Tab once when the page loads.",question:'Does the first focusable element offer a "Skip to main content" link (or equivalent bypass)?',wcag:"2.4.1",relatedAxeRule:"bypass",severity:"required"},{id:"consistent-help",prompt:"Identify pages that surface a help mechanism (contact info, help link, FAQ link, chatbot trigger). Compare position + order across pages.",question:"When the same help mechanism appears on multiple pages, does it appear in the same relative order (e.g., always last in the header)?",wcag:"3.2.6",severity:"required"}]},te={id:"time-based-media",name:"Time-based media",blurb:'Verify audio + video have the alternatives required by WCAG. If the site has no time-based media, mark every step "Not applicable." WCAG 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.4.2.',steps:[{id:"prerecorded-audio-only-alt",prompt:"Identify any prerecorded audio-only content (podcast embed, audio quote, voice message).",question:"Does each prerecorded audio-only file have a text transcript readable on the page (or linked from it)?",wcag:"1.2.1",severity:"required"},{id:"prerecorded-video-only-alt",prompt:"Identify any prerecorded silent video (animated GIF with content, silent screencast, animated explainer).",question:"Does each prerecorded video-only file have an equivalent text or audio description?",wcag:"1.2.1",severity:"required"},{id:"prerecorded-captions",prompt:"Identify any prerecorded video that includes audio (interviews, tutorials, marketing videos).",question:"Does each video provide synchronized captions for ALL spoken content + meaningful non-speech sound?",wcag:"1.2.2",severity:"required"},{id:"prerecorded-alt-for-video",prompt:"For each prerecorded video with audio, check if there's a full text transcript on the page (covering both audio dialog AND visual action) OR if audio description is provided.",question:"Does each prerecorded video have either a full text alternative OR an audio-description track?",wcag:"1.2.3",severity:"required"},{id:"prerecorded-audio-description",prompt:"Watch each prerecorded video without looking at it. Listen only.",question:"Is all visually-conveyed information also conveyed in the audio (either built into the narration OR via a separate audio-description track)?",wcag:"1.2.5",severity:"required"},{id:"live-captions",prompt:"Identify any LIVE audio/video stream (live shopping, webinar, conference broadcast).",question:"Does each live broadcast provide real-time captions?",wcag:"1.2.4",severity:"required"},{id:"audio-control",prompt:"Reload the page. Listen for audio that starts automatically and plays for >3 seconds (background music, autoplay video with sound).",question:"Is there a mechanism to pause / stop the audio, OR can the volume be controlled INDEPENDENTLY from the system volume?",wcag:"1.4.2",severity:"required"}]},oe=[z,K,G,B,H,j,Y,J,X,Q,te,Z,ee];function Ue(o,e){let n=0,a=0,l=0,r=0,t=0;for(const d of e.steps){const i=o.steps[d.id];if(!i){t++;continue}i.status==="pass"?n++:i.status==="fail"?a++:i.status==="not-applicable"?r++:i.status==="skip"?l++:t++}return{passed:n,failed:a,skipped:l,notApplicable:r,unanswered:t,total:e.steps.length,fullyAnswered:t===0}}const ie=[{id:"1.1.1",title:"Non-text Content",level:"A",addedIn:"2.0",understandingSlug:"non-text-content",shortDescription:"All non-text content has a text alternative."},{id:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",addedIn:"2.0",understandingSlug:"audio-only-and-video-only-prerecorded",shortDescription:"Alternatives for prerecorded audio-only or video-only."},{id:"1.2.2",title:"Captions (Prerecorded)",level:"A",addedIn:"2.0",understandingSlug:"captions-prerecorded",shortDescription:"Captions for prerecorded audio in synchronized media."},{id:"1.2.3",title:"Audio Description or Media Alternative (Prerecorded)",level:"A",addedIn:"2.0",understandingSlug:"audio-description-or-media-alternative-prerecorded",shortDescription:"Alternative for time-based media (prerecorded)."},{id:"1.2.4",title:"Captions (Live)",level:"AA",addedIn:"2.0",understandingSlug:"captions-live",shortDescription:"Captions for live synchronized media."},{id:"1.2.5",title:"Audio Description (Prerecorded)",level:"AA",addedIn:"2.0",understandingSlug:"audio-description-prerecorded",shortDescription:"Audio description for prerecorded video in synchronized media."},{id:"1.2.6",title:"Sign Language (Prerecorded)",level:"AAA",addedIn:"2.0",understandingSlug:"sign-language-prerecorded",shortDescription:"Sign-language interpretation for prerecorded audio."},{id:"1.2.7",title:"Extended Audio Description (Prerecorded)",level:"AAA",addedIn:"2.0",understandingSlug:"extended-audio-description-prerecorded",shortDescription:"Extended audio description for prerecorded video."},{id:"1.2.8",title:"Media Alternative (Prerecorded)",level:"AAA",addedIn:"2.0",understandingSlug:"media-alternative-prerecorded",shortDescription:"Alternative for time-based media (prerecorded)."},{id:"1.2.9",title:"Audio-only (Live)",level:"AAA",addedIn:"2.0",understandingSlug:"audio-only-live",shortDescription:"Alternative for live audio-only content."},{id:"1.3.1",title:"Info and Relationships",level:"A",addedIn:"2.0",understandingSlug:"info-and-relationships",shortDescription:"Information and structural relationships are programmatically determinable."},{id:"1.3.2",title:"Meaningful Sequence",level:"A",addedIn:"2.0",understandingSlug:"meaningful-sequence",shortDescription:"Reading sequence is programmatically determinable."},{id:"1.3.3",title:"Sensory Characteristics",level:"A",addedIn:"2.0",understandingSlug:"sensory-characteristics",shortDescription:"Instructions don't rely solely on shape, color, size, or sound."},{id:"1.3.4",title:"Orientation",level:"AA",addedIn:"2.1",understandingSlug:"orientation",shortDescription:"Content doesn't restrict view to portrait or landscape."},{id:"1.3.5",title:"Identify Input Purpose",level:"AA",addedIn:"2.1",understandingSlug:"identify-input-purpose",shortDescription:"Purpose of input fields is programmatically identifiable."},{id:"1.3.6",title:"Identify Purpose",level:"AAA",addedIn:"2.1",understandingSlug:"identify-purpose",shortDescription:"Purpose of UI components, icons, and regions is identifiable."},{id:"1.4.1",title:"Use of Color",level:"A",addedIn:"2.0",understandingSlug:"use-of-color",shortDescription:"Color is not the only means of conveying information."},{id:"1.4.2",title:"Audio Control",level:"A",addedIn:"2.0",understandingSlug:"audio-control",shortDescription:"Mechanism to pause/stop auto-playing audio over 3s."},{id:"1.4.3",title:"Contrast (Minimum)",level:"AA",addedIn:"2.0",understandingSlug:"contrast-minimum",shortDescription:"Text has 4.5:1 contrast (3:1 for large text)."},{id:"1.4.4",title:"Resize Text",level:"AA",addedIn:"2.0",understandingSlug:"resize-text",shortDescription:"Text resizable to 200% without loss of content/function."},{id:"1.4.5",title:"Images of Text",level:"AA",addedIn:"2.0",understandingSlug:"images-of-text",shortDescription:"Text used in preference to images of text where possible."},{id:"1.4.6",title:"Contrast (Enhanced)",level:"AAA",addedIn:"2.0",understandingSlug:"contrast-enhanced",shortDescription:"Text has 7:1 contrast (4.5:1 for large text)."},{id:"1.4.7",title:"Low or No Background Audio",level:"AAA",addedIn:"2.0",understandingSlug:"low-or-no-background-audio",shortDescription:"Background audio at least 20dB lower than speech."},{id:"1.4.8",title:"Visual Presentation",level:"AAA",addedIn:"2.0",understandingSlug:"visual-presentation",shortDescription:"Configurable visual presentation for blocks of text."},{id:"1.4.9",title:"Images of Text (No Exception)",level:"AAA",addedIn:"2.0",understandingSlug:"images-of-text-no-exception",shortDescription:"Images of text used only for decoration / essential."},{id:"1.4.10",title:"Reflow",level:"AA",addedIn:"2.1",understandingSlug:"reflow",shortDescription:"Content reflows at 320 CSS px without 2D scrolling."},{id:"1.4.11",title:"Non-text Contrast",level:"AA",addedIn:"2.1",understandingSlug:"non-text-contrast",shortDescription:"UI components and graphical objects have 3:1 contrast."},{id:"1.4.12",title:"Text Spacing",level:"AA",addedIn:"2.1",understandingSlug:"text-spacing",shortDescription:"Text spacing overrides don't cause content loss."},{id:"1.4.13",title:"Content on Hover or Focus",level:"AA",addedIn:"2.1",understandingSlug:"content-on-hover-or-focus",shortDescription:"Hover/focus tooltips are dismissible, hoverable, persistent."},{id:"2.1.1",title:"Keyboard",level:"A",addedIn:"2.0",understandingSlug:"keyboard",shortDescription:"All functionality available from the keyboard."},{id:"2.1.2",title:"No Keyboard Trap",level:"A",addedIn:"2.0",understandingSlug:"no-keyboard-trap",shortDescription:"Keyboard focus can move away from any component."},{id:"2.1.3",title:"Keyboard (No Exception)",level:"AAA",addedIn:"2.0",understandingSlug:"keyboard-no-exception",shortDescription:"All functionality is keyboard-operable without timing."},{id:"2.1.4",title:"Character Key Shortcuts",level:"A",addedIn:"2.1",understandingSlug:"character-key-shortcuts",shortDescription:"Single-key shortcuts can be turned off or remapped."},{id:"2.2.1",title:"Timing Adjustable",level:"A",addedIn:"2.0",understandingSlug:"timing-adjustable",shortDescription:"User can turn off, adjust, or extend time limits."},{id:"2.2.2",title:"Pause, Stop, Hide",level:"A",addedIn:"2.0",understandingSlug:"pause-stop-hide",shortDescription:"Moving/blinking/scrolling content can be paused."},{id:"2.2.3",title:"No Timing",level:"AAA",addedIn:"2.0",understandingSlug:"no-timing",shortDescription:"No timing essential to content/function (with exceptions)."},{id:"2.2.4",title:"Interruptions",level:"AAA",addedIn:"2.0",understandingSlug:"interruptions",shortDescription:"User can postpone/suppress non-emergency interruptions."},{id:"2.2.5",title:"Re-authenticating",level:"AAA",addedIn:"2.0",understandingSlug:"re-authenticating",shortDescription:"Authenticated session can be resumed without data loss."},{id:"2.2.6",title:"Timeouts",level:"AAA",addedIn:"2.1",understandingSlug:"timeouts",shortDescription:"Inactivity timeouts warned in advance."},{id:"2.3.1",title:"Three Flashes or Below Threshold",level:"A",addedIn:"2.0",understandingSlug:"three-flashes-or-below-threshold",shortDescription:"No content flashes more than 3 times per second."},{id:"2.3.2",title:"Three Flashes",level:"AAA",addedIn:"2.0",understandingSlug:"three-flashes",shortDescription:"No content flashes more than 3 times per second (no exceptions)."},{id:"2.3.3",title:"Animation from Interactions",level:"AAA",addedIn:"2.1",understandingSlug:"animation-from-interactions",shortDescription:"Motion animation from interactions can be disabled."},{id:"2.4.1",title:"Bypass Blocks",level:"A",addedIn:"2.0",understandingSlug:"bypass-blocks",shortDescription:"Mechanism to skip blocks of repeated content."},{id:"2.4.2",title:"Page Titled",level:"A",addedIn:"2.0",understandingSlug:"page-titled",shortDescription:"Pages have titles that describe topic/purpose."},{id:"2.4.3",title:"Focus Order",level:"A",addedIn:"2.0",understandingSlug:"focus-order",shortDescription:"Components receive focus in a meaningful order."},{id:"2.4.4",title:"Link Purpose (In Context)",level:"A",addedIn:"2.0",understandingSlug:"link-purpose-in-context",shortDescription:"Link purpose is clear from the link text + context."},{id:"2.4.5",title:"Multiple Ways",level:"AA",addedIn:"2.0",understandingSlug:"multiple-ways",shortDescription:"Two or more ways to locate a page within a set."},{id:"2.4.6",title:"Headings and Labels",level:"AA",addedIn:"2.0",understandingSlug:"headings-and-labels",shortDescription:"Headings and labels describe topic or purpose."},{id:"2.4.7",title:"Focus Visible",level:"AA",addedIn:"2.0",understandingSlug:"focus-visible",shortDescription:"Keyboard focus indicator is visible."},{id:"2.4.8",title:"Location",level:"AAA",addedIn:"2.0",understandingSlug:"location",shortDescription:"Information about user's location within a set is available."},{id:"2.4.9",title:"Link Purpose (Link Only)",level:"AAA",addedIn:"2.0",understandingSlug:"link-purpose-link-only",shortDescription:"Link purpose clear from the link text alone."},{id:"2.4.10",title:"Section Headings",level:"AAA",addedIn:"2.0",understandingSlug:"section-headings",shortDescription:"Section headings are used to organize content."},{id:"2.4.11",title:"Focus Not Obscured (Minimum)",level:"AA",addedIn:"2.2",understandingSlug:"focus-not-obscured-minimum",shortDescription:"Focused component not entirely hidden by other content."},{id:"2.4.12",title:"Focus Not Obscured (Enhanced)",level:"AAA",addedIn:"2.2",understandingSlug:"focus-not-obscured-enhanced",shortDescription:"Focused component fully visible."},{id:"2.4.13",title:"Focus Appearance",level:"AAA",addedIn:"2.2",understandingSlug:"focus-appearance",shortDescription:"Focus indicator meets specific size/contrast requirements."},{id:"2.5.1",title:"Pointer Gestures",level:"A",addedIn:"2.1",understandingSlug:"pointer-gestures",shortDescription:"Multi-point or path-based gestures have single-pointer alternatives."},{id:"2.5.2",title:"Pointer Cancellation",level:"A",addedIn:"2.1",understandingSlug:"pointer-cancellation",shortDescription:"Pointer actions can be aborted or undone."},{id:"2.5.3",title:"Label in Name",level:"A",addedIn:"2.1",understandingSlug:"label-in-name",shortDescription:"Accessible name includes the visible label text."},{id:"2.5.4",title:"Motion Actuation",level:"A",addedIn:"2.1",understandingSlug:"motion-actuation",shortDescription:"Motion-triggered functions have UI alternatives + can be disabled."},{id:"2.5.5",title:"Target Size (Enhanced)",level:"AAA",addedIn:"2.1",understandingSlug:"target-size-enhanced",shortDescription:"Pointer target size at least 44×44 CSS px."},{id:"2.5.6",title:"Concurrent Input Mechanisms",level:"AAA",addedIn:"2.1",understandingSlug:"concurrent-input-mechanisms",shortDescription:"Content doesn't restrict input modality."},{id:"2.5.7",title:"Dragging Movements",level:"AA",addedIn:"2.2",understandingSlug:"dragging-movements",shortDescription:"Dragging actions have single-pointer alternatives."},{id:"2.5.8",title:"Target Size (Minimum)",level:"AA",addedIn:"2.2",understandingSlug:"target-size-minimum",shortDescription:"Pointer target size at least 24×24 CSS px."},{id:"3.1.1",title:"Language of Page",level:"A",addedIn:"2.0",understandingSlug:"language-of-page",shortDescription:"Default human language of the page is programmatically set."},{id:"3.1.2",title:"Language of Parts",level:"AA",addedIn:"2.0",understandingSlug:"language-of-parts",shortDescription:"Language of each passage is programmatically set."},{id:"3.1.3",title:"Unusual Words",level:"AAA",addedIn:"2.0",understandingSlug:"unusual-words",shortDescription:"Definitions provided for jargon, idioms, etc."},{id:"3.1.4",title:"Abbreviations",level:"AAA",addedIn:"2.0",understandingSlug:"abbreviations",shortDescription:"Expanded form or meaning of abbreviations is available."},{id:"3.1.5",title:"Reading Level",level:"AAA",addedIn:"2.0",understandingSlug:"reading-level",shortDescription:"Supplemental content provided for advanced reading levels."},{id:"3.1.6",title:"Pronunciation",level:"AAA",addedIn:"2.0",understandingSlug:"pronunciation",shortDescription:"Mechanism for pronunciation of words where meaning is ambiguous."},{id:"3.2.1",title:"On Focus",level:"A",addedIn:"2.0",understandingSlug:"on-focus",shortDescription:"Focus does not trigger an unexpected context change."},{id:"3.2.2",title:"On Input",level:"A",addedIn:"2.0",understandingSlug:"on-input",shortDescription:"Input does not trigger an unexpected context change."},{id:"3.2.3",title:"Consistent Navigation",level:"AA",addedIn:"2.0",understandingSlug:"consistent-navigation",shortDescription:"Navigation is consistent across a set of pages."},{id:"3.2.4",title:"Consistent Identification",level:"AA",addedIn:"2.0",understandingSlug:"consistent-identification",shortDescription:"Components with the same function are identified consistently."},{id:"3.2.5",title:"Change on Request",level:"AAA",addedIn:"2.0",understandingSlug:"change-on-request",shortDescription:"Changes of context only on user request."},{id:"3.2.6",title:"Consistent Help",level:"A",addedIn:"2.2",understandingSlug:"consistent-help",shortDescription:"Help mechanisms appear in consistent order across pages."},{id:"3.3.1",title:"Error Identification",level:"A",addedIn:"2.0",understandingSlug:"error-identification",shortDescription:"Form errors are identified and described in text."},{id:"3.3.2",title:"Labels or Instructions",level:"A",addedIn:"2.0",understandingSlug:"labels-or-instructions",shortDescription:"Form fields have labels or instructions."},{id:"3.3.3",title:"Error Suggestion",level:"AA",addedIn:"2.0",understandingSlug:"error-suggestion",shortDescription:"Error suggestions provided when known."},{id:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",addedIn:"2.0",understandingSlug:"error-prevention-legal-financial-data",shortDescription:"Submissions are reversible, checked, or confirmed."},{id:"3.3.5",title:"Help",level:"AAA",addedIn:"2.0",understandingSlug:"help",shortDescription:"Context-sensitive help is available."},{id:"3.3.6",title:"Error Prevention (All)",level:"AAA",addedIn:"2.0",understandingSlug:"error-prevention-all",shortDescription:"All submissions reversible, checked, or confirmed."},{id:"3.3.7",title:"Redundant Entry",level:"A",addedIn:"2.2",understandingSlug:"redundant-entry",shortDescription:"Previously-entered information is auto-populated or selectable."},{id:"3.3.8",title:"Accessible Authentication (Minimum)",level:"AA",addedIn:"2.2",understandingSlug:"accessible-authentication-minimum",shortDescription:"Auth doesn't require cognitive function test."},{id:"3.3.9",title:"Accessible Authentication (Enhanced)",level:"AAA",addedIn:"2.2",understandingSlug:"accessible-authentication-enhanced",shortDescription:"Auth doesn't require cognitive function test (no exceptions)."},{id:"4.1.1",title:"Parsing",level:"A",addedIn:"2.0",removedIn:"2.2",understandingSlug:"parsing",shortDescription:"Markup is well-formed (obsoleted in WCAG 2.2)."},{id:"4.1.2",title:"Name, Role, Value",level:"A",addedIn:"2.0",understandingSlug:"name-role-value",shortDescription:"UI components have programmatically determinable name + role + state."},{id:"4.1.3",title:"Status Messages",level:"AA",addedIn:"2.1",understandingSlug:"status-messages",shortDescription:"Status messages programmatically announced without focus change."}];function ne(o,e){const n={A:1,AA:2,AAA:3},a={"2.0":1,"2.1":2,"2.2":3};return ie.filter(l=>a[l.addedIn]>a[o]||l.removedIn&&a[l.removedIn]<=a[o]?!1:n[l.level]<=n[e])}function Me(o,e){return`https://www.w3.org/WAI/WCAG${e.replace(".","")}/Understanding/${o.understandingSlug}`}let y="2.1";const re="AA";function Pe(o){y=o}async function Ne(){var o,e;try{const a=(await chrome.storage.local.get("wcagTargetVersion")).wcagTargetVersion;(a==="2.1"||a==="2.2")&&(y=a),(e=(o=chrome.storage.onChanged)==null?void 0:o.addListener)==null||e.call(o,(l,r)=>{if(r!=="local")return;const t=l.wcagTargetVersion;if(!t)return;const d=t.newValue;(d==="2.1"||d==="2.2")&&(y=d)})}catch{}}const ae=["3.2.3","3.2.4","3.2.6","3.3.7"];function We(o){const e=ne(y,re),n=new Map;for(const r of e)n.set(r.id,{criterionId:r.id,level:r.level,title:r.title,state:"not-evaluated",evidence:[]});const a=new Map;function l(r,t){let d=a.get(r);return d||(d=new Set,a.set(r,d)),d.has(t)?!0:(d.add(t),!1)}for(const r of o.audits){const t=r.pageUrl??r.scope;for(const i of r.violations){if(!i.wcagCriterion)continue;const s=n.get(i.wcagCriterion);if(!s)continue;const c=i.ruleId.startsWith("ai-"),u=i.target.selector;if(o.acknowledgedMatchKeys.has(i.matchKey)){const h=`ack::${i.ruleId}::${t}::${u}`;if(l(i.wcagCriterion,h))continue;s.evidence.push({source:c?"ai":"axe",ruleOrStepId:i.ruleId,outcome:"ack",pageUrl:t,selector:u});continue}if(i.needsReview){const h=`needsReview::${i.ruleId}::${t}::${u}`;if(l(i.wcagCriterion,h))continue;s.evidence.push({source:"ai",ruleOrStepId:i.ruleId,outcome:"incomplete",pageUrl:t,selector:u,resolutionHint:"Acknowledge in the Matrix view if not an issue, or fix the underlying alt text."});continue}const p=`fail::${i.ruleId}::${t}::${u}`;l(i.wcagCriterion,p)||s.evidence.push({source:c?"ai":"axe",ruleOrStepId:i.ruleId,outcome:"fail",pageUrl:t,selector:u,resolutionHint:`Fix ${i.ruleId} on ${u}.`})}const d=r.axeRulesEvaluated;if(d){for(const i of d.passed){const s=n.get(i.wcagCriterion);if(!s)continue;const c=`axe::${i.ruleId}::${t}::pass`;l(i.wcagCriterion,c)||s.evidence.push({source:"axe",ruleOrStepId:i.ruleId,outcome:"pass",pageUrl:t})}for(const i of d.inapplicable){const s=n.get(i.wcagCriterion);if(!s)continue;const c=`axe::${i.ruleId}::${t}::inapplicable`;l(i.wcagCriterion,c)||s.evidence.push({source:"axe",ruleOrStepId:i.ruleId,outcome:"inapplicable",pageUrl:t})}for(const i of d.incomplete){const s=n.get(i.wcagCriterion);if(!s)continue;const c=`axe::${i.ruleId}::${t}::incomplete`,u=s.evidence.findIndex(p=>p.source==="axe"&&p.outcome==="incomplete"&&p.ruleOrStepId===i.ruleId&&p.pageUrl===t);if(u>=0){if(i.elements&&i.elements.length>0){const p=s.evidence[u],h=new Set((p.elements??[]).map(m=>m.selector));for(const m of i.elements)h.has(m.selector)||((p.elements??(p.elements=[])).push(m),h.add(m.selector))}continue}l(i.wcagCriterion,c)||s.evidence.push({source:"axe",ruleOrStepId:i.ruleId,outcome:"incomplete",pageUrl:t,elements:i.elements?[...i.elements]:void 0,resolutionHint:"axe-core couldn't fully determine. Common causes: text against an image/gradient background, or aria-role on a hidden element. Review the page manually."})}}}if(o.incompleteResolutions&&o.incompleteResolutions.length>0){const r=new Map;for(const t of o.incompleteResolutions)r.set(`${t.pageUrl}::${t.ruleId}::${t.selector}`,t);for(const t of n.values()){const d=[];for(const i of t.evidence){if(i.source!=="axe"||i.outcome!=="incomplete"||!i.elements||i.elements.length===0){d.push(i);continue}const s=[];for(const c of i.elements){const u=`${i.pageUrl}::${i.ruleOrStepId}::${c.selector}`,p=r.get(u);if(!p){s.push(c);continue}p.verdict==="pass"?d.push({source:"ai",ruleOrStepId:`ai-resolved::${i.ruleOrStepId}`,outcome:"pass",pageUrl:i.pageUrl,selector:c.selector,notes:p.reasoning}):p.verdict==="fail"?d.push({source:"ai",ruleOrStepId:`ai-resolved::${i.ruleOrStepId}`,outcome:"fail",pageUrl:i.pageUrl,selector:c.selector,notes:p.reasoning,resolutionHint:`AI determined ${i.ruleOrStepId} fails on this element. Review and fix or override.`}):s.push(c)}s.length>0&&d.push({...i,elements:s})}t.evidence=d}}if(o.interactiveAuditResults&&o.interactiveAuditResults.length>0)for(const r of o.interactiveAuditResults){const t=n.get(r.criterionId);t&&t.evidence.push({source:"ai",ruleOrStepId:r.ruleId,outcome:r.verdict==="pass"?"pass":r.verdict==="fail"?"fail":"incomplete",pageUrl:r.pageUrl,notes:r.reasoning,resolutionHint:r.verdict==="fail"?"Review the AI walkthrough in the Compliance tab; fix the underlying focus-order issue and re-run.":r.verdict==="uncertain"?"Re-run the interactive audit, or manually verify via Guided Tests.":void 0})}for(const r of o.workflows)for(const t of r.steps){if(!t.wcag)continue;const d=n.get(t.wcag);if(d)for(const i of o.igtRuns){if(i.workflowId!==r.id)continue;const s=i.steps[t.id],c=`${r.id}.${t.id}`;if(!s){d.evidence.push({source:"igt",ruleOrStepId:c,outcome:"skip",resolutionHint:`Open Guided Tests → ${r.name} → answer "${t.id}" as Pass / Fail / N/A.`});continue}s.status==="pass"?d.evidence.push({source:"igt",ruleOrStepId:c,outcome:"pass",notes:s.notes}):s.status==="fail"?d.evidence.push({source:"igt",ruleOrStepId:c,outcome:"fail",notes:s.notes,resolutionHint:`Address the failing manual check: ${t.question}`}):s.status==="not-applicable"?d.evidence.push({source:"igt",ruleOrStepId:c,outcome:"n/a",notes:s.notes}):s.status==="skip"&&d.evidence.push({source:"igt",ruleOrStepId:c,outcome:"skip",notes:s.notes,resolutionHint:`Open Guided Tests → ${r.name} → re-answer "${t.id}" as Pass / Fail / N/A.`})}}for(const r of n.values())r.state=se(r.evidence);if(o.auditScope==="single-page")for(const r of ae){const t=n.get(r);t&&t.state==="not-evaluated"&&(t.state="not-applicable",t.evidence.push({source:"axe",ruleOrStepId:"wcc-multi-page-only-na",outcome:"n/a",notes:"Single-page audit cannot evaluate this cross-page criterion. Treated as N/A for this audit; run Site Crawl to evaluate."}))}if(o.humanCriterionVerdicts&&o.humanCriterionVerdicts.length>0)for(const r of o.humanCriterionVerdicts){const t=n.get(r.criterionId);if(!t)continue;const d=r.verdict==="na"?"n/a":r.verdict,i=r.verdict==="na"?"not-applicable":r.verdict==="fail"?"fail":"pass";t.state=i,t.evidence.push({source:"human",ruleOrStepId:"human-criterion-verdict",outcome:d,notes:`Human verified ${r.verdict.toUpperCase()} on ${r.verifiedAt}: ${r.note}`})}return n}function se(o){return o.length===0?"not-evaluated":o.some(n=>n.outcome==="fail")?"fail":o.some(n=>n.outcome==="incomplete"||n.outcome==="skip")?"inconclusive":o.some(n=>n.outcome==="pass"||n.outcome==="inapplicable"||n.outcome==="ack")?"pass":"not-applicable"}function Ve(o){const e={pass:[],fail:[],inconclusive:[],notApplicable:[],notEvaluated:[]};for(const a of o.values())e[de(a.state)].push(a.criterionId);const n=o.size;return{pass:e.pass.length,fail:e.fail.length,inconclusive:e.inconclusive.length,notApplicable:e.notApplicable.length,notEvaluated:e.notEvaluated.length,totalApplicable:n,canClaimConformance:e.fail.length===0&&e.inconclusive.length===0&&e.notEvaluated.length===0,byState:e}}function de(o){return o==="not-applicable"?"notApplicable":o==="not-evaluated"?"notEvaluated":o}function $e(o,e=oe){const n=new Set,a=new Set,l=new Set;for(const t of o.values())for(const d of t.evidence)d.source==="axe"?n.add(t.criterionId):d.source==="ai"?a.add(t.criterionId):d.source==="igt"&&l.add(t.criterionId);for(const t of e)for(const d of t.steps)d.wcag&&o.has(d.wcag)&&l.add(d.wcag);function r(t,d){const i=Array.from(d).sort();let s=0;const c=[],u=[],p=[];for(const h of i){const m=o.get(h);if(!m)continue;const b=m.evidence.filter(g=>t==="automation"&&g.source==="axe"||t==="ai"&&g.source==="ai"||t==="human"&&g.source==="igt");if(b.length===0){p.push(h);continue}const L=b.some(g=>g.outcome==="fail"),F=b.some(g=>g.outcome==="incomplete"||g.outcome==="skip"),_=b.some(g=>g.outcome==="pass"||g.outcome==="inapplicable"||g.outcome==="ack"||g.outcome==="n/a");L?c.push(h):F?u.push(h):_?s++:p.push(h)}return{layer:t,coveredCriteria:i,cleared:s,failing:c,inconclusive:u,blocked:[...c,...u].sort(),unevaluated:p}}return{automation:r("automation",n),ai:r("ai",a),human:r("human",l)}}const k="auditCache:",le=24*60*60*1e3;async function D(o){const e=new TextEncoder().encode(o),n=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(n)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function ce(o){const e=k+o,a=(await chrome.storage.local.get(e))[e];return!a||Date.now()-a.cachedAt>le?null:a}async function ue(o){const e=k+o.lookupKey;await chrome.storage.local.set({[e]:o})}async function pe(){const o=await chrome.storage.local.get(null),e=Object.keys(o).filter(n=>n.startsWith(k));e.length>0&&await chrome.storage.local.remove(e)}async function ge(o){return D(JSON.stringify(o))}async function he(o){return D(JSON.stringify([...o??[]].sort()))}const ze=Object.freeze(Object.defineProperty({__proto__:null,clearAllCachedAudits:pe,getCachedAudit:ce,hashDisabledRules:he,hashMatrixConfig:ge,setCachedAudit:ue,sha256Hex:D},Symbol.toStringTag,{value:"Module"}));function Ke(o){return{...q,...o??{},enabledChecks:{...q.enabledChecks,...(o==null?void 0:o.enabledChecks)??{}}}}const q={enabled:!1,provider:"anthropic",apiKey:"",costCapUsd:5,model:"",enabledChecks:{altText:!0,headings:!0,sensory:!0,aria:!0,imageOfText:!0,colorOnlyMeaning:!0,genericLinkText:!0,wallOfText:!0,languageMismatch:!0},imageOfTextMaxImages:5,maxCandidatesPerCheck:10},me=U("ai-usage-log"),A="aiUsageLog",R=200;async function Ge(o){try{const e=await E();e.unshift(o),e.length>R&&(e.length=R),await chrome.storage.local.set({[A]:e})}catch(e){me.warn("failed to append ai usage entry",e)}}async function E(){try{const e=(await chrome.storage.local.get(A))[A];return Array.isArray(e)?e:[]}catch{return[]}}function ve(o,e=10){const n=new Date,a=`${n.getUTCFullYear()}-${n.getUTCMonth()}`;let l=0,r=0,t=0;const d={};for(const i of o){l+=i.totalCostUsd,`${new Date(i.at).getUTCFullYear()}-${new Date(i.at).getUTCMonth()}`===a&&(r+=i.totalCostUsd,t++);for(const[c,u]of Object.entries(i.byCheck))u!=null&&(d[c]=(d[c]??0)+u)}return{totalSpentUsd:l,totalAudits:o.length,spentThisMonthUsd:r,auditsThisMonth:t,averageCostUsd:o.length===0?null:l/o.length,byCheck:d,recent:o.slice(0,e)}}async function Be(o=10){const e=await E();return ve(e,o)}async function He(){await chrome.storage.local.remove(A)}export{xe as A,$ as B,Ne as C,w as D,Ee as E,Oe as F,Pe as G,Fe as H,Le as I,Ve as J,D as K,ge as L,he as M,ce as N,ue as O,Ge as P,_e as Q,ze as R,re as T,ie as W,be as a,q as b,pe as c,He as d,T as e,qe as f,ye as g,$e as h,We as i,oe as j,Re as k,Be as l,Se as m,De as n,Ce as o,we as p,Ue as q,Ie as r,Ae as s,ke as t,M as u,ne as v,Me as w,y as x,Te as y,Ke as z};
|