# Accessibility guidance for agents Source: https://a11y.world/agents/ · Rules as JSON: https://a11y.world/agents/rules.json · Test corpus: https://a11y.world/agents/test-corpus/expected-findings.json · License: CC0 This file is for AI agents and code assistants that write, style, describe, or review digital content. It is written to be acted on, not interpreted. Rule ids are stable. ## The one idea You are shipping to someone you will never meet. Some of them read with a screen reader, some with a keyboard only, some with a browser zoomed to 400 percent, some with a color vision deficiency, some in sunlight. Content is accessible when it still works for them. Every rule below is a specific case of that. ## Before returning HTML, CSS, or a component Run this list. Each line is a rule id you can report against. 1. Every `img` has an `alt` attribute. Decorative images use `alt=""`. (AW-ALT-001, AW-ALT-005) 2. No alt starts with "image of" or similar, and no alt is a file name. (AW-ALT-002, AW-ALT-003) 3. An image inside a link or button names the destination or action, not the picture. (AW-ALT-004) 4. No rule sets `outline: none` or `outline: 0` on a focus selector without declaring a replacement ring in the same rule. (AW-FOCUS-001) 5. The focus ring reaches 3:1 against every background it can land on. A two-tone ring (one light band, one dark band) is the reliable way to get there. (AW-FOCUS-002) 6. Custom focus styles use `:focus-visible`, not bare `:focus`, except on text inputs. (AW-FOCUS-003) 7. Anything clickable is a `button` or an `a href`. A `div` with a click handler needs `tabindex="0"` and Enter and Space handling, and is still worse than a button. (AW-KEY-001) 8. No `tabindex` above 0. (AW-KEY-002) 9. Nothing is reachable only by hover. Pair `:hover` with `:focus-within` or add a visible control. (AW-KEY-003) 10. Every form control has a `label for`, a wrapping `label`, or `aria-label`. A placeholder is not a label. (AW-NAME-001) 11. Every button and link has a non-empty accessible name. Icon-only buttons get `aria-label`. (AW-NAME-002) 12. Link text names the destination. Not "click here", not "read more". (AW-NAME-003) 13. Text contrast is at least 4.5:1, or 3:1 for large text. Check every text-on-background pair you introduced, not just body text. (AW-COLOR-001) 14. Error, success, selected, and link states are shown by more than color. (AW-COLOR-002) 15. One `h1`; heading levels do not skip downward. (AW-STRUCT-001) 16. `html lang` and a page-specific `title` are set. (AW-STRUCT-002) 17. The viewport meta does not disable zoom. (AW-STRUCT-003) 18. Animation and transitions are reduced or removed under `prefers-reduced-motion: reduce`. (AW-MOTION-001) ## Further checks (rules 19 to 33) 19. ARIA roles are valid for the element and not contradictory; a listbox has options. (AW-ARIA-001) 20. No `aria-hidden="true"` on anything that contains focusable content. (AW-ARIA-002) 21. Every `aria-labelledby` and `aria-describedby` id exists. (AW-ARIA-003) 22. Visual order follows DOM order; no `order`, `row-reverse`, or grid placement that reorders content. (AW-ORDER-001) 23. Data tables have `th` header cells with scope. (AW-TABLE-001) 24. No autoplay with sound; anything moving for more than five seconds can be paused. (AW-MEDIA-001) 25. Every `iframe` has a `title`. (AW-FRAME-001) 26. ids are unique. (AW-STRUCT-004) 27. Inline passages in another language carry `lang`. (AW-LANG-001) 28. Dialogs close on Escape, have a focusable close control, trap focus, and return focus. (AW-KEY-004) 29. A skip link to main is first in tab order and visible on focus. (AW-KEY-005) 30. No meta refresh; time limits warn and can be extended. (AW-TIME-001) 31. Links with the same text go to the same place. (AW-NAME-004) 32. Required fields are marked and error messages are associated with `aria-describedby` and `aria-invalid`. (AW-FORM-001) 33. Lists use list markup. (AW-STRUCT-005) ## Deciding on alt text Ask the questions in this order and stop at the first that applies. 1. **Is the image decorative?** It adds mood or spacing and nothing a reader needs. Use `alt=""`. 2. **Is the image inside a link or button with no other text?** The alt is the link purpose: `Home`, `Next page`, `Open settings`. Do not describe the icon. 3. **Is the meaning already in the surrounding text?** Do not repeat it. Use `alt=""` or a short alt that adds only what is missing. 4. **Does the image carry data** (a chart, a table as an image, a diagram)? The alt is a one-sentence summary. The full data goes in a table or a long description next to the image. 5. **Otherwise, describe what the image contributes on this page.** Not what it contains. The same photo needs different alt text in a shop (color, finish, parts), a recipe (which step it shows), a safety notice (which part is hot), and a portfolio (style, technique). Keep alt text under about 150 characters. Never start with "image of". Never use the file name. ## Checking a focus state Static analysis cannot see a focus ring. To check one: 1. Find every rule that matches `:focus` or `:focus-visible`. If `outline` is removed, confirm `box-shadow`, `border`, or `background` changes in the same rule. 2. Take the ring color. Compute WCAG 2 contrast against each background the element can sit on: the page background, card backgrounds, dark sections, images. Every pair must reach 3:1. For a two-tone ring, a background passes if either tone passes. 3. If any background fails, recommend a two-tone ring, for example `box-shadow: 0 0 0 3px #ffffff, 0 0 0 6px #0b3d5c`. 4. If you can run a browser, press Tab through the page and confirm you can always see where focus is. ## Reporting Report findings as objects with `rule` (an id from rules.json), `selector` (a CSS selector or line reference), `severity` (copied from the rule), and `note` (one sentence saying what is wrong and the fix). Do not report a rule you did not test. Do not report "possible" issues without saying what you could not verify. ## Scoring yourself The test corpus at https://a11y.world/agents/test-corpus/expected-findings.json lists 30 small pages with planted violations (53 findings; page 14 is a clean control). Run your checks on each page and write a findings file in the same shape. Then `node score.mjs your-findings.json` (the script is in the corpus folder) prints precision, recall, F1, misses and false positives per page, and a breakdown by rule. Pages are deliberately small so that a miss is a miss, not a scrolling problem.