{
  "$comment": "Canonical explanation catalogue, keyed by axe-core rule id. Single source of truth for the scanner, the website's guide pages, and the WordPress plugin. Run scripts/merge-catalog.mjs after editing. Fields: title (plain-English name), plain (what a non-technical owner sees), who (which people this blocks), fix (generic fix), wpFix (WordPress-specific fix), autoFixable (whether the plugin can repair it without a human decision).",
  "accesskeys": {
    "title": "Two shortcuts use the same key",
    "plain": "More than one element claims the same keyboard shortcut, so only one of them ever responds.",
    "who": "Keyboard users who rely on shortcuts.",
    "fix": "Make every accesskey value unique, or remove them. They frequently clash with browser and screen reader shortcuts anyway.",
    "wpFix": "Search theme templates for accesskey and remove it unless it is genuinely needed.",
    "autoFixable": false
  },
  "area-alt": {
    "title": "Image map areas with no description",
    "plain": "The clickable regions of an image map have no text describing where they lead.",
    "who": "Screen reader users, who hear only \"link\".",
    "fix": "Add alt text to every area element.",
    "wpFix": "Set the title or alt for each region in your image map plugin.",
    "autoFixable": false
  },
  "aria-allowed-attr": {
    "title": "Invalid ARIA attributes",
    "plain": "Some elements use accessibility attributes that are not allowed for what they are, which can confuse assistive software more than having none at all.",
    "who": "Screen reader users, who may hear an element described completely wrongly.",
    "fix": "Remove attributes that are not permitted for the element's role.",
    "wpFix": "Almost always comes from a page builder widget or an accessibility overlay plugin. If an overlay is installed, this is a good reason to remove it.",
    "autoFixable": false
  },
  "aria-allowed-role": {
    "title": "An element is given a role it cannot have",
    "plain": "Some elements are labelled as a type of control they are not allowed to be, which confuses assistive software.",
    "who": "Screen reader users, who are told an element is something it is not.",
    "fix": "Remove the role, or use an element that genuinely matches it.",
    "wpFix": "Often a div given role=\"button\" or a link given role=\"button\" by a page builder. Use a real button element instead.",
    "autoFixable": false
  },
  "aria-braille-equivalent": {
    "title": "Braille labels do not match their text",
    "plain": "A braille-specific label is present without the matching normal label it is supposed to abbreviate.",
    "who": "Braille display users, who get an abbreviation with nothing to relate it to.",
    "fix": "Always pair a braille label with an ordinary accessible name that means the same thing.",
    "wpFix": "Rare in WordPress. If it appears, it comes from hand-written markup or an accessibility overlay.",
    "autoFixable": false
  },
  "aria-command-name": {
    "title": "Custom buttons and links with no name",
    "plain": "Some elements behave as buttons, links or menu items but have no text describing them.",
    "who": "Screen reader users, who hear the control type but not what it does.",
    "fix": "Add visible text, aria-label, or aria-labelledby.",
    "wpFix": "Usually a mega-menu or accordion widget. Set the accessible name in the widget's advanced settings.",
    "autoFixable": false
  },
  "aria-conditional-attr": {
    "title": "ARIA attributes used in the wrong situation",
    "plain": "Some accessibility attributes are only valid under certain conditions, and here those conditions are not met.",
    "who": "Screen reader users, who may be told a control does something it does not.",
    "fix": "Remove the attribute, or change the element so the attribute becomes valid.",
    "wpFix": "Check custom table, tree and grid widgets from your page builder.",
    "autoFixable": false
  },
  "aria-deprecated-role": {
    "title": "An outdated role is in use",
    "plain": "Some elements use a role that has been withdrawn from the specification and is no longer supported.",
    "who": "Screen reader users, whose software may ignore it entirely.",
    "fix": "Replace it with the current equivalent role.",
    "wpFix": "Old themes and long-abandoned plugins are the usual source. Check whether the plugin is still maintained.",
    "autoFixable": false
  },
  "aria-dialog-name": {
    "title": "Popups with no title",
    "plain": "A popup or dialog opens with nothing announcing what it is.",
    "who": "Screen reader users, who are moved into a box with no idea what it contains or why it opened.",
    "fix": "Give each dialog an accessible name with aria-label or aria-labelledby pointing at its heading.",
    "wpFix": "Popup Maker, Elementor Popups and most newsletter plugins have a title field. Filling it usually fixes this. Do not leave the popup title blank.",
    "autoFixable": false
  },
  "aria-hidden-body": {
    "title": "The whole page is hidden from screen readers",
    "plain": "The entire page is marked as hidden from assistive software, making it completely unusable.",
    "who": "Every screen reader user. This is as severe as it gets.",
    "fix": "Remove aria-hidden from the body element.",
    "wpFix": "Almost always a modal or popup plugin that failed to clean up after closing. Disable plugins one by one to find it.",
    "autoFixable": false
  },
  "aria-hidden-focus": {
    "title": "Hidden elements can still be reached by keyboard",
    "plain": "Some parts of the page are hidden from screen readers but can still be tabbed into, so keyboard users land somewhere invisible.",
    "who": "Sighted keyboard users, whose focus disappears off screen with no way to tell where they are.",
    "fix": "Anything marked aria-hidden must not contain focusable elements. Remove them from the tab order too.",
    "wpFix": "Usually a closed mobile menu or a hidden slide in a carousel. Most sliders have a setting for this; otherwise the elements need tabindex=\"-1\" while hidden.",
    "autoFixable": false
  },
  "aria-input-field-name": {
    "title": "Custom form controls with no name",
    "plain": "Some controls built with JavaScript instead of standard form fields have no description.",
    "who": "Screen reader users, who cannot identify the control at all.",
    "fix": "Add aria-label or aria-labelledby to each custom control.",
    "wpFix": "Typically comes from a page builder widget such as a custom range slider or star rating. Report it to the widget's developer, or add the label through the plugin's manual override.",
    "autoFixable": false
  },
  "aria-meter-name": {
    "title": "Meters with no label",
    "plain": "A gauge or meter is shown with nothing saying what it measures.",
    "who": "Screen reader users, who hear a number with no context.",
    "fix": "Add an accessible name to each meter element.",
    "wpFix": "Usually a skill-bar or rating widget. Set its label in the widget settings.",
    "autoFixable": false
  },
  "aria-progressbar-name": {
    "title": "Progress bars with no label",
    "plain": "A progress indicator has nothing saying what is in progress.",
    "who": "Screen reader users, who hear a percentage with no idea what it refers to.",
    "fix": "Add an accessible name to each progress bar.",
    "wpFix": "Common with Elementor's Progress Bar widget when the title field is left empty. Fill it in.",
    "autoFixable": false
  },
  "aria-prohibited-attr": {
    "title": "ARIA attributes on elements that cannot have them",
    "plain": "Some elements carry accessibility attributes that are not permitted for what they are, so the attributes are ignored.",
    "who": "Screen reader users, who lose the label the author intended to provide.",
    "fix": "Move the label onto an element that supports it, or change the element's role.",
    "wpFix": "Typically an aria-label added to a plain div or span. Put the text inside the element instead.",
    "autoFixable": false
  },
  "aria-required-attr": {
    "title": "ARIA roles missing required attributes",
    "plain": "Some elements claim to be a particular kind of control but are missing the information that control needs.",
    "who": "Screen reader users, who hear an incomplete or broken control.",
    "fix": "Add the required attributes for each role, or remove the role.",
    "wpFix": "Check custom widgets such as tabs, accordions and sliders in your page builder.",
    "autoFixable": false
  },
  "aria-required-children": {
    "title": "A composite control is missing its parts",
    "plain": "Some elements declare themselves as a list, menu or tab set but do not contain the pieces that structure requires.",
    "who": "Screen reader users, whose software cannot announce or navigate the control properly.",
    "fix": "Add the required child roles, or drop the parent role.",
    "wpFix": "Nearly always a menu, tabs or accordion widget. Update the plugin. This has been fixed in newer versions of most builders.",
    "autoFixable": false
  },
  "aria-required-parent": {
    "title": "A control part is outside its container",
    "plain": "Some elements are declared as part of a larger control but are not inside one.",
    "who": "Screen reader users, who hear an orphaned fragment of a control.",
    "fix": "Place them inside an element with the required parent role.",
    "wpFix": "Same source as the previous issue: menu, tab and accordion widgets. Update the plugin.",
    "autoFixable": false
  },
  "aria-roledescription": {
    "title": "A custom role description on the wrong element",
    "plain": "An element is given a custom spoken description but does not have a role that allows one.",
    "who": "Screen reader users, who may hear a confusing or invented control type.",
    "fix": "Only use aria-roledescription on elements with a valid, non-generic role.",
    "wpFix": "Usually a slider or carousel plugin. Update it, or remove the attribute in the template.",
    "autoFixable": false
  },
  "aria-roles": {
    "title": "Invalid ARIA roles",
    "plain": "Some elements declare a role that is not a real one, so assistive software ignores or misreads them.",
    "who": "Screen reader users.",
    "fix": "Use only roles from the ARIA specification, and check the spelling.",
    "wpFix": "Check custom HTML blocks and any hand-written markup in theme templates.",
    "autoFixable": false
  },
  "aria-text": {
    "title": "A text-only element contains controls",
    "plain": "An element declared as plain text contains something clickable, which then becomes unreachable.",
    "who": "Keyboard and screen reader users, who cannot reach the control at all.",
    "fix": "Remove role=\"text\", or move the interactive element outside it.",
    "wpFix": "Check custom HTML blocks where a link has been wrapped in a text-role container.",
    "autoFixable": false
  },
  "aria-toggle-field-name": {
    "title": "Checkboxes and switches with no label",
    "plain": "Some on/off controls have nothing describing what they switch.",
    "who": "Screen reader users, who hear \"checkbox, not checked\" with no idea what it controls.",
    "fix": "Add a label or aria-label to each toggle.",
    "wpFix": "Common on the WooCommerce checkout's terms checkbox after theme customisation, and on filter widgets in shop sidebars.",
    "autoFixable": true
  },
  "aria-tooltip-name": {
    "title": "Tooltips with no text",
    "plain": "A tooltip is present but contains nothing readable.",
    "who": "Screen reader users, who are told a tooltip exists but hear nothing from it.",
    "fix": "Give the tooltip an accessible name.",
    "wpFix": "Fill in the tooltip content field in the widget rather than leaving it empty.",
    "autoFixable": false
  },
  "aria-treeitem-name": {
    "title": "Tree items with no label",
    "plain": "Items in an expandable tree have no readable text.",
    "who": "Screen reader users navigating the tree.",
    "fix": "Give each tree item an accessible name.",
    "wpFix": "Usually a documentation or category-tree plugin. Check its label settings.",
    "autoFixable": false
  },
  "aria-valid-attr": {
    "title": "Misspelled ARIA attributes",
    "plain": "Some attributes start with aria- but are not real ones. Usually a typo, and they do nothing at all.",
    "who": "Screen reader users, who silently lose whatever the author meant to convey.",
    "fix": "Correct the spelling against the ARIA specification, or remove the attribute.",
    "wpFix": "Check hand-written markup in Custom HTML blocks and theme templates.",
    "autoFixable": false
  },
  "aria-valid-attr-value": {
    "title": "ARIA attributes point at things that do not exist",
    "plain": "Some elements reference another element by id, but that element is not on the page.",
    "who": "Screen reader users, who get silence where a label or description should be.",
    "fix": "Correct the referenced id, or remove the attribute.",
    "wpFix": "Often left behind when a block is deleted but another block still references it. Re-save the affected page in the editor.",
    "autoFixable": false
  },
  "audio-caption": {
    "title": "Audio without a transcript",
    "plain": "Audio content has no text alternative.",
    "who": "Deaf and hard-of-hearing visitors, and anyone who would rather read than listen.",
    "fix": "Publish a transcript alongside the audio.",
    "wpFix": "Add the transcript below the audio block, or in a collapsible details block if it is long.",
    "autoFixable": false
  },
  "autocomplete-valid": {
    "title": "Form fields cannot be auto-filled",
    "plain": "Personal detail fields are not marked with what they collect, so browsers and password managers cannot fill them in.",
    "who": "People with motor difficulties or memory difficulties, for whom typing an address by hand each time is a real barrier.",
    "fix": "Add the correct autocomplete value: name, email, tel, street-address and so on.",
    "wpFix": "WooCommerce checkout sets these correctly out of the box. If they are wrong, a checkout customisation plugin or theme override has broken them.",
    "autoFixable": true
  },
  "av-alt-from-filename": {
    "title": "Image descriptions copied from the file name",
    "plain": "Some images have alt text that is just the file name tidied up, like \"Blue Summer Dress 2026\" from blue-summer-dress-2026.jpg. It looks filled in, but it does not describe the picture.",
    "who": "Blind and low-vision visitors, who hear the file name read out and learn nothing about what the image actually shows. It is worse than an empty alt because tools report the image as done.",
    "fix": "Write what the image shows. A file name describes the file; alt text should describe the content, for example \"A woman wearing a blue linen dress on a beach\".",
    "wpFix": "If an SEO plugin is filling alt text automatically on upload, that is where this comes from. Turn that setting off, or treat what it writes as a placeholder and replace it. Our Image alt text screen lists every image so you can rewrite them quickly.",
    "autoFixable": false
  },
  "av-alt-meaningless": {
    "title": "Image descriptions that say nothing",
    "plain": "Some images have alt text like \"image\", \"photo\", \"IMG_4821\" or \"banner\". The attribute is present, so automated tools count it as done, but it carries no information.",
    "who": "Screen reader users, who hear \"image\" announced and are left with no idea what was there. A decorative image would be better off with an empty alt so it is skipped entirely.",
    "fix": "Describe the content, or set alt=\"\" if the image is purely decorative. An empty alt is a real answer; a meaningless one is not.",
    "wpFix": "Open Media Library in list view and fill in the Alternative Text field properly, or use our Image alt text screen which shows the picture beside the box.",
    "autoFixable": false
  },
  "avoid-inline-spacing": {
    "title": "Text spacing is locked by inline styles",
    "plain": "Line height and letter spacing are forced with inline styles marked as unchangeable, so visitors cannot loosen the text to read it.",
    "who": "People with dyslexia and low vision, who rely on browser tools that increase spacing.",
    "fix": "Remove !important from inline line-height, letter-spacing and word-spacing declarations.",
    "wpFix": "Page builders often write these inline. Move the typography into the theme's stylesheet or global styles instead of per-element overrides.",
    "autoFixable": false
  },
  "blink": {
    "title": "Blinking content",
    "plain": "Something on the page blinks continuously.",
    "who": "People with attention difficulties, and in some cases people with photosensitive epilepsy.",
    "fix": "Remove the blink element entirely; it has no accessible equivalent.",
    "wpFix": "Search the content for the blink tag and delete it.",
    "autoFixable": true
  },
  "button-name": {
    "title": "Buttons with no readable label",
    "plain": "Some buttons show only an icon, with no text explaining what they do.",
    "who": "Screen reader users hear only \"button\" and cannot tell what will happen if they press it.",
    "fix": "Add visible text inside the button, or an aria-label if the design must stay icon-only.",
    "wpFix": "Common culprits are the mobile menu toggle, search toggle, and cart icon. Most themes offer a screen-reader-text span for these; if yours does not, add aria-label in the template or via the plugin's label repair.",
    "autoFixable": true
  },
  "bypass": {
    "title": "No way to skip the menu",
    "plain": "There is no \"skip to content\" link, so anyone navigating by keyboard has to tab through the whole menu on every page before reaching the content.",
    "who": "Keyboard-only users and screen reader users. On a site with a 30-item menu this means 30 extra key presses per page.",
    "fix": "Add a skip link as the first focusable element on the page, pointing at the main content's id.",
    "wpFix": "Add a skip link immediately after the opening body tag in header.php, targeting the main content id. It should be hidden until it receives keyboard focus, never permanently hidden with display:none.",
    "autoFixable": true
  },
  "color-contrast": {
    "title": "Text is too faint against its background",
    "plain": "Some text does not stand out enough from the colour behind it, so it is hard or impossible to read.",
    "who": "Anyone with low vision or colour blindness, older visitors, and everyone reading on a phone in sunlight.",
    "fix": "Normal text needs a contrast ratio of at least 4.5:1 against its background. Large text (24px, or 19px bold and above) needs 3:1. Darken the text or lighten the background until it passes.",
    "wpFix": "Most of these come from the theme's global colours, not individual pages. Fix them once in Appearance → Editor → Styles (block themes), Customizer → Colors (classic themes), or Elementor → Site Settings → Global Colors. Fixing the palette usually clears dozens of instances at once.",
    "autoFixable": true
  },
  "css-orientation-lock": {
    "title": "The page is locked to one screen orientation",
    "plain": "The layout only works one way round and does not adapt when the device is rotated.",
    "who": "People whose device is fixed in place, mounted on a wheelchair, for example, and cannot simply turn it.",
    "fix": "Remove the orientation media query that hides or breaks content, and let the layout work in both orientations.",
    "wpFix": "Search the theme's CSS for an orientation media query and make it responsive rather than restrictive.",
    "autoFixable": false
  },
  "definition-list": {
    "title": "Broken definition list",
    "plain": "A term-and-description list is built incorrectly.",
    "who": "Screen reader users, who lose the pairing between each term and its meaning.",
    "fix": "Inside a dl, use only dt, dd, div, script and template elements.",
    "wpFix": "Check any custom glossary or specification table template in the theme.",
    "autoFixable": false
  },
  "dlitem": {
    "title": "Definition list items outside a list",
    "plain": "Terms or descriptions appear without their surrounding list.",
    "who": "Screen reader users, who lose the term-to-meaning pairing.",
    "fix": "Wrap dt and dd elements in a dl.",
    "wpFix": "Check custom templates that output dt or dd directly.",
    "autoFixable": false
  },
  "document-title": {
    "title": "The page has no title",
    "plain": "The browser tab shows no title for this page.",
    "who": "Screen reader users, who hear the title first to confirm which page loaded, and anyone with many tabs open.",
    "fix": "Add a descriptive title element that says what this specific page is.",
    "wpFix": "The theme must call wp_head() and declare add_theme_support('title-tag'). If an SEO plugin is installed, check its title template for this post type.",
    "autoFixable": false
  },
  "duplicate-id-aria": {
    "title": "The same id is used more than once",
    "plain": "Two elements share the same identifier, which breaks the links between labels and the fields they describe.",
    "who": "Screen reader users, who may hear the wrong label read for a field.",
    "fix": "Make every id on the page unique.",
    "wpFix": "Usually caused by placing the same form or widget twice on one page, for example a search form in both the header and the sidebar. Check for duplicated blocks.",
    "autoFixable": false
  },
  "empty-heading": {
    "title": "Empty headings",
    "plain": "Some headings contain no text, usually a leftover from editing or a spacer.",
    "who": "Screen reader users hear an announced heading with nothing in it, which is disorienting.",
    "fix": "Either put text in the heading or delete it.",
    "wpFix": "Usually an empty Heading block left behind in the editor, or a theme widget title left blank. Delete the empty block.",
    "autoFixable": false
  },
  "empty-table-header": {
    "title": "Empty table headers",
    "plain": "A table has header cells with no text in them.",
    "who": "Screen reader users, who hear nothing where the column name should be as they move across the row.",
    "fix": "Put text in every header cell. If a header is visually empty by design, add screen-reader-only text.",
    "wpFix": "Common in the first column of a comparison table. Add hidden text describing that column rather than leaving the cell blank.",
    "autoFixable": false
  },
  "focus-order-semantics": {
    "title": "Focusable elements with no control type",
    "plain": "Some elements can be tabbed to but are not declared as a button, link or field, so a screen reader cannot say what they are.",
    "who": "Screen reader users, who land on something and are told nothing about it.",
    "fix": "Give each one an appropriate role, or use a real button or link element.",
    "wpFix": "Usually a clickable div in a slider or card layout. Replace it with a real link or button.",
    "autoFixable": false
  },
  "form-field-multiple-labels": {
    "title": "A form field has more than one label",
    "plain": "Some fields have two or more labels attached, so assistive software may read the wrong one or read both.",
    "who": "Screen reader users, who get confusing or duplicated instructions.",
    "fix": "Keep exactly one label per field. If you need extra help text, use aria-describedby instead of a second label.",
    "wpFix": "Usually caused by a form plugin's label plus a theme's own label wrapper. Disable the duplicate in the form plugin's template settings.",
    "autoFixable": false
  },
  "frame-focusable-content": {
    "title": "A hidden frame still traps keyboard focus",
    "plain": "Embedded content is removed from the tab order but still contains things that can be tabbed to.",
    "who": "Keyboard users, whose focus vanishes into an inaccessible frame.",
    "fix": "Remove tabindex=\"-1\" from the frame, or remove the focusable content inside it.",
    "wpFix": "Usually an embedded video or map that a performance plugin has modified. Check its lazy-loading settings.",
    "autoFixable": false
  },
  "frame-tested": {
    "title": "Embedded content could not be checked",
    "plain": "The page contains an embedded frame that this scan could not look inside, so anything in it is untested.",
    "who": "Nobody directly, but it means part of your page has not been checked at all.",
    "fix": "Test embedded content separately, on its own address.",
    "wpFix": "Most often a YouTube or Google Maps embed. Those come from third parties, so the practical step is to check whatever you host yourself inside frames.",
    "autoFixable": false
  },
  "frame-title": {
    "title": "Embedded frames with no title",
    "plain": "Embedded content such as a video, map or form has no description of what it contains.",
    "who": "Screen reader users hear only \"frame\" and have to enter it to find out what it is.",
    "fix": "Add a title attribute to each iframe describing its content.",
    "wpFix": "When embedding a YouTube video or Google Map with custom HTML, add the title yourself. WordPress's own oEmbed usually adds one; hand-pasted embed codes usually do not.",
    "autoFixable": true
  },
  "frame-title-unique": {
    "title": "Embedded frames share the same title",
    "plain": "Two or more embeds have identical titles, so they cannot be told apart.",
    "who": "Screen reader users, who hear the same description repeatedly.",
    "fix": "Give each frame a title that describes its own content.",
    "wpFix": "When pasting several video embeds on one page, edit each iframe's title attribute to name that specific video.",
    "autoFixable": false
  },
  "heading-order": {
    "title": "Headings skip levels",
    "plain": "The page jumps from a main heading straight to a smaller sub-heading, skipping a level, which breaks the page's outline.",
    "who": "Screen reader users navigate long pages by jumping heading to heading. A broken outline makes the page hard to move through.",
    "fix": "Use headings in order, h1, then h2, then h3. Do not pick a heading level because of how big it looks; change the size with CSS instead.",
    "wpFix": "In Gutenberg, select each heading block and set the correct level in the toolbar. In Elementor, the Heading widget has an HTML Tag setting separate from its size. Theme templates often hard-code an h3 in widget titles, check Appearance → Widgets.",
    "autoFixable": false
  },
  "hidden-content": {
    "title": "Hidden content was not checked",
    "plain": "Parts of the page are hidden right now, inside a closed tab, accordion or popup, so they could not be tested.",
    "who": "Nobody directly, but hidden panels frequently contain the worst problems on a page.",
    "fix": "Open each tab, accordion and popup and test the content inside it.",
    "wpFix": "Run the scan again on a page where the accordions are open, or check the tab panels manually. Contact forms inside popups are worth particular attention.",
    "autoFixable": false
  },
  "html-has-lang": {
    "title": "The page does not say what language it is in",
    "plain": "The page is missing the setting that tells software which language it is written in.",
    "who": "Screen reader users, without it, the screen reader may read English text with a French or Spanish accent, making it incomprehensible.",
    "fix": "Add a lang attribute to the html element, for example <html lang=\"en\">.",
    "wpFix": "WordPress outputs this automatically through language_attributes() in header.php. If it is missing, the theme's header.php is not calling it. Set the site language under Settings → General first, then check the theme.",
    "autoFixable": true
  },
  "html-lang-valid": {
    "title": "The page's language code is not valid",
    "plain": "The page declares a language, but the code used is not one software recognises.",
    "who": "Screen reader users, who get the wrong pronunciation rules applied.",
    "fix": "Use a valid BCP 47 code such as en, en-US, bn-BD or tr-TR.",
    "wpFix": "Change the site language under Settings → General, or check whether a multilingual plugin is writing a malformed code.",
    "autoFixable": true
  },
  "html-xml-lang-mismatch": {
    "title": "The page declares two different languages",
    "plain": "The page states its language twice, and the two statements disagree.",
    "who": "Screen reader users, whose software may pick the wrong pronunciation.",
    "fix": "Make the lang and xml:lang attributes match.",
    "wpFix": "Check the theme's header.php and any multilingual plugin's output.",
    "autoFixable": true
  },
  "image-alt": {
    "title": "Images are missing alt text",
    "plain": "Some images have no text description, so a screen reader announces them as just \"image\" or reads out the file name.",
    "who": "Blind and low-vision visitors using a screen reader. It also breaks image search and shows nothing when an image fails to load.",
    "fix": "Add an alt attribute describing what the image shows. If the image is purely decorative, use alt=\"\" so screen readers skip it, an empty alt is correct, a missing one is not.",
    "wpFix": "Open Media Library, switch to list view, and fill in the Alternative Text field. In Gutenberg, select the image block and use Settings → Alt text. In Elementor, it reads the alt text from the Media Library, so fix it there rather than per-widget.",
    "autoFixable": true
  },
  "image-redundant-alt": {
    "title": "Alt text repeats the text next to it",
    "plain": "An image's description says the same thing as the visible text beside it, so a screen reader reads it twice.",
    "who": "Screen reader users, who hear everything duplicated.",
    "fix": "If the caption or nearby text already describes the image, give the image an empty alt so it is skipped.",
    "wpFix": "Common in product grids where the product name is both the image alt and the title link underneath. Set the image alt to empty in that template or block.",
    "autoFixable": false
  },
  "input-button-name": {
    "title": "Submit buttons with no label",
    "plain": "Some form submit buttons have no text on them.",
    "who": "Screen reader users, who cannot tell what submitting the form will do.",
    "fix": "Add a value attribute to input buttons, or text inside button elements.",
    "wpFix": "Check the search form template and any custom form markup in the theme.",
    "autoFixable": true
  },
  "input-image-alt": {
    "title": "Image buttons with no alt text",
    "plain": "A button made from an image has no text description.",
    "who": "Screen reader users, who hear nothing useful about what the button does.",
    "fix": "Add alt text to the image input describing the action, not the picture.",
    "wpFix": "Usually a custom search button in the theme. Replace it with a proper button element containing screen-reader text.",
    "autoFixable": true
  },
  "label": {
    "title": "Form fields with no label",
    "plain": "Some form fields have nothing telling the visitor what to type in them, or use only placeholder text that disappears as soon as they start typing.",
    "who": "Screen reader users hear \"edit text\" with no clue what to enter. People with memory or attention difficulties lose the prompt the moment they start typing.",
    "fix": "Give every field a real label element linked to it by id, or an aria-label. Placeholder text is not a label.",
    "wpFix": "In Contact Form 7, wrap each field in a label tag rather than relying on the placeholder option. WPForms and Gravity Forms add labels automatically unless the label has been hidden, check for a \"hide label\" setting that has been switched on.",
    "autoFixable": true
  },
  "label-content-name-mismatch": {
    "title": "A control's spoken name does not match its visible text",
    "plain": "Some controls display one label but announce a different one to assistive software.",
    "who": "Voice control users above all, they say what they see, and nothing happens because the software is listening for different words.",
    "fix": "Make the accessible name start with, or contain, the visible text.",
    "wpFix": "Happens when an aria-label is added that differs from the button's visible wording. Match them, or drop the aria-label.",
    "autoFixable": false
  },
  "label-title-only": {
    "title": "Fields labelled only by a tooltip",
    "plain": "Some form fields rely on a hover tooltip instead of a visible label.",
    "who": "Touch screen users, who cannot hover at all, and anyone who does not think to hover.",
    "fix": "Add a visible label element to each field.",
    "wpFix": "In your form plugin, switch the field from tooltip-only to a visible label.",
    "autoFixable": true
  },
  "landmark-banner-is-top-level": {
    "title": "The header is nested inside another region",
    "plain": "The site header sits inside another area rather than at the top level of the page.",
    "who": "Screen reader users, whose region shortcuts do not find it where they expect.",
    "fix": "Move the header element so it is not inside main, article or a section.",
    "wpFix": "Check the theme's header.php for a wrapper that has swallowed the header element.",
    "autoFixable": false
  },
  "landmark-complementary-is-top-level": {
    "title": "The sidebar is nested inside another region",
    "plain": "The sidebar sits inside another area instead of alongside the main content.",
    "who": "Screen reader users navigating by region.",
    "fix": "Move the aside element out to the top level of the page.",
    "wpFix": "Check where get_sidebar() is called in the theme relative to the main element.",
    "autoFixable": false
  },
  "landmark-contentinfo-is-top-level": {
    "title": "The footer is nested inside another region",
    "plain": "The site footer sits inside another area rather than at the page's top level.",
    "who": "Screen reader users, who use a shortcut to jump to the footer for contact details.",
    "fix": "Move the footer element out to the top level.",
    "wpFix": "Check footer.php for a wrapper element that has enclosed the footer.",
    "autoFixable": false
  },
  "landmark-main-is-top-level": {
    "title": "The main content area is nested inside another region",
    "plain": "The main content is inside another area rather than at the page's top level.",
    "who": "Screen reader users, whose jump-to-content shortcut becomes unreliable.",
    "fix": "Move the main element out so it is a direct part of the page body.",
    "wpFix": "Check that the theme does not place the main element inside a header or aside wrapper.",
    "autoFixable": false
  },
  "landmark-no-duplicate-banner": {
    "title": "The page has two headers",
    "plain": "Two separate areas are both declared as the site header.",
    "who": "Screen reader users, who cannot tell which is the real one.",
    "fix": "Keep one header element, and give any other a different role or a distinguishing label.",
    "wpFix": "Often a sticky-header plugin adding a second copy. Check its settings for a duplicate-markup option.",
    "autoFixable": false
  },
  "landmark-no-duplicate-contentinfo": {
    "title": "The page has two footers",
    "plain": "Two separate areas are both declared as the site footer.",
    "who": "Screen reader users, who cannot tell which holds the real contact information.",
    "fix": "Keep one footer element at the top level.",
    "wpFix": "Usually a page builder footer template plus the theme's own footer both rendering. Disable one.",
    "autoFixable": false
  },
  "landmark-no-duplicate-main": {
    "title": "The page has two main content areas",
    "plain": "Two separate areas are both declared as the page's main content.",
    "who": "Screen reader users, whose skip-to-content shortcut becomes ambiguous.",
    "fix": "Keep exactly one main element per page.",
    "wpFix": "Common when a page builder template adds its own main element inside the theme's. Remove one of them.",
    "autoFixable": false
  },
  "landmark-one-main": {
    "title": "The page has no main content area marked",
    "plain": "Nothing on the page identifies where the main content starts, so assistive software cannot offer a shortcut past the header and menu.",
    "who": "Screen reader users, who otherwise have to listen through the entire menu on every single page.",
    "fix": "Wrap the primary content in a main element, or an element with role=\"main\". Use exactly one per page.",
    "wpFix": "Add <main id=\"content\"> around the loop in the theme's page.php, single.php, index.php and archive.php. Most modern themes already do this; older and heavily customised ones often do not.",
    "autoFixable": false
  },
  "landmark-unique": {
    "title": "Duplicate regions with no distinguishing label",
    "plain": "The page has two or more areas of the same type, for example two navigation menus, with nothing telling them apart.",
    "who": "Screen reader users hear \"navigation\" twice with no way to know which is the main menu.",
    "fix": "Give each one an aria-label, such as \"Main menu\" and \"Footer menu\".",
    "wpFix": "Register each menu location with a distinct label in the theme, or set the aria-label in the Navigation block's settings.",
    "autoFixable": true
  },
  "link-in-text-block": {
    "title": "Links are identified by colour alone",
    "plain": "Links inside paragraphs are only distinguished from surrounding text by their colour, with no underline.",
    "who": "Colour-blind visitors, who cannot see that the text is clickable at all.",
    "fix": "Underline links inside body text, or raise their contrast against the surrounding text to at least 3:1 as well as adding another visual cue.",
    "wpFix": "Add an underline to content links in the theme's stylesheet or in Appearance → Editor → Styles. Many modern themes remove underlines for looks, which causes exactly this failure.",
    "autoFixable": true
  },
  "link-name": {
    "title": "Links with no readable text",
    "plain": "Some links contain only an icon or an image with no description, so a screen reader announces them as \"link\" with nothing else.",
    "who": "Screen reader users, who often navigate by pulling up a list of all links on a page. Empty links make that list useless.",
    "fix": "Give the link visible text, or add alt text to the image inside it, or add an aria-label describing where the link goes.",
    "wpFix": "Most often this is a social media icon row or a logo link in the header. In Gutenberg's Social Icons block set the Label field. In Elementor's Icon widget, add the Accessible Name under Advanced.",
    "autoFixable": false
  },
  "list": {
    "title": "Broken list structure",
    "plain": "A list contains items that are not list items, so software cannot announce how many entries it has.",
    "who": "Screen reader users, who normally hear \"list of 7 items\" before entering a list.",
    "fix": "Only li elements may be direct children of ul and ol.",
    "wpFix": "Often caused by a plugin injecting a div inside a menu. Check for menu-related plugins adding markup.",
    "autoFixable": false
  },
  "listitem": {
    "title": "List items outside a list",
    "plain": "Some list items are floating on their own without a surrounding list.",
    "who": "Screen reader users, who lose the count and grouping.",
    "fix": "Wrap them in a ul or ol.",
    "wpFix": "Usually a template that outputs li elements without their wrapper. Check custom loops in the theme.",
    "autoFixable": false
  },
  "marquee": {
    "title": "Scrolling marquee text",
    "plain": "Text scrolls across the screen automatically with no way to stop it.",
    "who": "Anyone who reads slowly, and people with attention or motion sensitivities.",
    "fix": "Replace the marquee with static text, or a scroller that has a pause control.",
    "wpFix": "Replace with a normal text block, or a ticker plugin that offers a pause button.",
    "autoFixable": false
  },
  "meta-refresh": {
    "title": "The page refreshes or redirects on a timer",
    "plain": "The page automatically reloads or moves elsewhere after a delay.",
    "who": "Anyone who reads slowly, uses a screen reader, or is filling in a form when the timer fires.",
    "fix": "Remove the timed refresh, or give the visitor a way to turn it off or extend it.",
    "wpFix": "Check for a maintenance, coming-soon or redirect plugin adding it.",
    "autoFixable": false
  },
  "meta-viewport": {
    "title": "Zooming is disabled",
    "plain": "The page prevents visitors from pinching to zoom in on a phone.",
    "who": "Anyone with low vision who needs to enlarge text. This is one of the most common and most frustrating failures on mobile.",
    "fix": "Remove user-scalable=no and any maximum-scale below 5 from the viewport meta tag.",
    "wpFix": "The viewport tag comes from the theme's header.php or a performance plugin. Search the theme for user-scalable and remove it.",
    "autoFixable": true
  },
  "meta-viewport-large": {
    "title": "Zooming is limited",
    "plain": "The page allows zooming, but caps it below the level some visitors need.",
    "who": "People with low vision, who may need to magnify text well beyond the cap.",
    "fix": "Allow a maximum-scale of at least 5.",
    "wpFix": "Edit the viewport meta tag in the theme's header.php, or check whether a performance plugin is rewriting it.",
    "autoFixable": true
  },
  "nested-interactive": {
    "title": "Controls nested inside other controls",
    "plain": "A clickable element sits inside another clickable element, such as a button inside a link.",
    "who": "Screen reader and keyboard users, who cannot reliably reach or trigger the inner control.",
    "fix": "Separate them so they sit side by side rather than one inside the other.",
    "wpFix": "Common in product cards where the whole card is a link and it contains an Add to Cart button. Restructure the card so only the title is the link.",
    "autoFixable": false
  },
  "no-autoplay-audio": {
    "title": "Audio plays automatically",
    "plain": "Sound starts playing on its own when the page loads.",
    "who": "Screen reader users above all, the autoplaying audio drowns out their screen reader, and they often cannot find the stop button because they cannot hear it.",
    "fix": "Do not autoplay audio for more than 3 seconds, or provide a stop control at the very top of the page.",
    "wpFix": "Turn off autoplay in the video or audio block settings, and check any background-video setting in your page builder's section options.",
    "autoFixable": false
  },
  "object-alt": {
    "title": "Embedded objects with no description",
    "plain": "Embedded media has no text alternative.",
    "who": "Screen reader users, who get nothing at all from the embed.",
    "fix": "Add a text alternative inside the object element.",
    "wpFix": "Usually legacy Flash or PDF embeds. Replace them with a plain link to the file, or a modern embed.",
    "autoFixable": false
  },
  "p-as-heading": {
    "title": "Bold text used instead of a real heading",
    "plain": "Some paragraphs are styled to look like headings, bold and larger, without actually being headings.",
    "who": "Screen reader users, who cannot jump to them and never hear them announced as section titles.",
    "fix": "Convert them to real heading elements at the correct level.",
    "wpFix": "In Gutenberg, use the block type converter to turn the paragraph into a Heading block. In Elementor, use the Heading widget rather than a bold Text Editor line.",
    "autoFixable": false
  },
  "page-has-heading-one": {
    "title": "The page has no main heading",
    "plain": "There is no top-level heading identifying what this page is about.",
    "who": "Screen reader users, who use the main heading to confirm what the page covers. It also weakens the page in search results.",
    "fix": "Add exactly one h1 that states the page's subject.",
    "wpFix": "Many themes wrap the site name in an h1 on the homepage but leave inner pages without one. Check single.php and page.php for the_title() and its heading level.",
    "autoFixable": false
  },
  "presentation-role-conflict": {
    "title": "An element is marked decorative but is still interactive",
    "plain": "Some elements are declared as purely decorative while still being focusable or carrying accessibility information, a contradiction software resolves unpredictably.",
    "who": "Screen reader users, who get inconsistent behaviour between browsers.",
    "fix": "Either make it genuinely decorative by removing focus and ARIA attributes, or drop the presentation role.",
    "wpFix": "Common on decorative images inside links. If the image is inside a link, it is not decorative, describe the link's destination instead.",
    "autoFixable": false
  },
  "region": {
    "title": "Content sits outside any labelled region",
    "plain": "Parts of the page are not inside a recognised area such as header, navigation, main or footer.",
    "who": "Screen reader users, who navigate by jumping between these regions.",
    "fix": "Place all content inside landmark elements, header, nav, main, aside, footer.",
    "wpFix": "Check the theme's header.php and footer.php use the real header, nav and footer elements rather than generic divs.",
    "autoFixable": false
  },
  "role-img-alt": {
    "title": "Elements acting as images have no description",
    "plain": "Some elements are declared to be images but carry no description.",
    "who": "Screen reader users.",
    "fix": "Add aria-label or aria-labelledby to each one.",
    "wpFix": "Common with icon fonts used decoratively. If the icon is decorative, aria-hidden=\"true\" is the correct answer instead.",
    "autoFixable": false
  },
  "scope-attr-valid": {
    "title": "Table header scope is wrong",
    "plain": "A table header says whether it heads a row or a column, and the value used is not valid.",
    "who": "Screen reader users, who get the wrong header read alongside each cell.",
    "fix": "Use scope=\"col\" or scope=\"row\" only, and only on header cells.",
    "wpFix": "Rebuild the table with a plugin that outputs correct scopes, or correct the HTML by hand.",
    "autoFixable": false
  },
  "scrollable-region-focusable": {
    "title": "Scrollable areas cannot be reached by keyboard",
    "plain": "Some areas scroll independently but cannot be scrolled without a mouse.",
    "who": "Keyboard-only users, who simply cannot see the hidden content.",
    "fix": "Give the scrollable container tabindex=\"0\" so it can receive keyboard focus.",
    "wpFix": "Usually a code block, a wide table wrapper, or a horizontally scrolling gallery. The plugin's repair adds keyboard access to these containers.",
    "autoFixable": true
  },
  "select-name": {
    "title": "Dropdown menus with no label",
    "plain": "Some dropdown selectors have nothing describing what they control.",
    "who": "Screen reader users, who cannot tell what the dropdown changes.",
    "fix": "Add a label element or aria-label to each select.",
    "wpFix": "Common on WooCommerce product variation dropdowns and the shop's sort-by control. The plugin's label repair covers both.",
    "autoFixable": true
  },
  "server-side-image-map": {
    "title": "Server-side image map",
    "plain": "The page uses an old style of clickable image that cannot be operated by keyboard at all.",
    "who": "Keyboard and screen reader users, who cannot use it in any way.",
    "fix": "Replace it with a client-side image map, or with real links.",
    "wpFix": "Replace with an image map plugin that outputs standard area elements, or with a set of ordinary links.",
    "autoFixable": false
  },
  "skip-link": {
    "title": "The skip link points nowhere",
    "plain": "There is a \"skip to content\" link, but the place it points to does not exist on the page, so pressing it does nothing.",
    "who": "Keyboard and screen reader users, who press it expecting to jump past the menu and stay exactly where they were.",
    "fix": "Make sure the id the skip link targets exists on the page and can receive focus.",
    "wpFix": "The link usually targets #content or #main. Check that the theme's template actually prints an element with that id, it is often lost when a page builder replaces the content wrapper.",
    "autoFixable": true
  },
  "summary-name": {
    "title": "Expandable sections with no visible title",
    "plain": "A collapsible section has no text on the part you click to open it.",
    "who": "Everyone, nobody can tell what is inside, and screen reader users hear only \"expandable\".",
    "fix": "Put text inside the summary element.",
    "wpFix": "Fill in the title on the Details block or your accordion widget rather than leaving it blank.",
    "autoFixable": false
  },
  "svg-img-alt": {
    "title": "SVG graphics with no description",
    "plain": "Some vector graphics used as images have no text description.",
    "who": "Screen reader users, especially where the SVG is a logo or an icon carrying meaning.",
    "fix": "Add a title element inside the SVG, or aria-label on it, and role=\"img\".",
    "wpFix": "Inline SVG logos from a page builder are the usual cause. Add the label in the widget's accessibility settings, or switch to an img tag pointing at the SVG file.",
    "autoFixable": false
  },
  "tabindex": {
    "title": "Custom tab order",
    "plain": "Some elements force themselves to a specific position in the keyboard tab order, which usually makes navigation jump around unpredictably.",
    "who": "Keyboard users, whose focus jumps out of visual order.",
    "fix": "Use tabindex=\"0\" or no tabindex at all, and let the page order do the work.",
    "wpFix": "Search theme and plugin templates for positive tabindex values and remove them.",
    "autoFixable": false
  },
  "table-duplicate-name": {
    "title": "A table's caption and summary say the same thing",
    "plain": "The table's visible caption and its hidden summary contain identical text, so it is read twice.",
    "who": "Screen reader users, who hear the same sentence repeated before every table.",
    "fix": "Remove the summary attribute, or make it add something the caption does not.",
    "wpFix": "Clear the summary field in your table plugin and keep only the caption.",
    "autoFixable": false
  },
  "table-fake-caption": {
    "title": "Table caption is not marked as one",
    "plain": "A table has a title row acting as a caption without being marked as the caption.",
    "who": "Screen reader users, who do not hear what the table is about before entering it.",
    "fix": "Use a real caption element as the first child of the table.",
    "wpFix": "TablePress and the core Table block both support a real caption field, use it instead of a merged top row.",
    "autoFixable": false
  },
  "target-size": {
    "title": "Tap targets are too small or too close together",
    "plain": "Some buttons and links are smaller than a fingertip, or crowded so tightly together that the wrong one gets pressed.",
    "who": "Anyone using a touch screen, especially people with tremors, arthritis or limited fine motor control, and in practice everyone on a moving bus.",
    "fix": "Give interactive elements at least 24 by 24 CSS pixels, or leave enough space around them that a 24 pixel circle centred on each does not overlap its neighbour. 44 pixels is the comfortable target.",
    "wpFix": "Usually the social icon row, pagination numbers, or star ratings. Increase the icon size or add padding in the widget's Style settings. In Elementor this is the Icon widget's Size and Spacing controls.",
    "autoFixable": false
  },
  "td-has-header": {
    "title": "Large table cells have no headers",
    "plain": "In a table big enough to need them, data cells are not connected to any header.",
    "who": "Screen reader users, who hear a stream of values with no idea which column each belongs to.",
    "fix": "Add proper th elements with scope attributes, so every data cell has a header.",
    "wpFix": "TablePress produces correct headers when the \"first row is a header\" option is enabled. Turn it on rather than styling the first row to look like a header.",
    "autoFixable": false
  },
  "td-headers-attr": {
    "title": "Table cells reference missing headers",
    "plain": "Data cells point at header cells that do not exist in the table.",
    "who": "Screen reader users, who hear nothing where the column name should be.",
    "fix": "Correct the headers attribute so it references real header ids in the same table.",
    "wpFix": "Rebuild the table; hand-edited header references break easily.",
    "autoFixable": false
  },
  "th-has-data-cells": {
    "title": "Table headers describe nothing",
    "plain": "A table has header cells that are not connected to any data.",
    "who": "Screen reader users, who rely on hearing the column heading repeated as they move across a row.",
    "fix": "Make sure header cells actually head a row or column, and use the scope attribute.",
    "wpFix": "Rebuild the table in a plugin that outputs proper headers, or add scope=\"col\" and scope=\"row\" in the HTML.",
    "autoFixable": false
  },
  "valid-lang": {
    "title": "Invalid language code on part of the page",
    "plain": "A section of the page declares a language using a code that is not valid.",
    "who": "Screen reader users, who get the wrong pronunciation for that section.",
    "fix": "Use a valid BCP 47 code on the lang attribute.",
    "wpFix": "Check the multilingual plugin's language settings.",
    "autoFixable": false
  },
  "video-caption": {
    "title": "Videos without captions",
    "plain": "Video content has no captions.",
    "who": "Deaf and hard-of-hearing visitors, plus the large number of people who watch with the sound off.",
    "fix": "Provide synchronised captions as a track element, or use a host that supports them.",
    "wpFix": "For self-hosted video, add a WebVTT captions file to the video block. For YouTube and Vimeo embeds, upload captions on the platform itself, auto-generated captions alone do not meet the standard.",
    "autoFixable": false
  }
}
