From 892b265ef382003e02bb5813128f5022602e91b8 Mon Sep 17 00:00:00 2001 From: Michael Fairchild Date: Fri, 6 Feb 2026 14:44:13 -0600 Subject: [PATCH 1/2] Revise accessibility instructions for clarity and detail Updated accessibility instructions to improve clarity and specificity regarding coding practices for accessibility, including keyboard navigation and semantic structure. --- instructions/a11y.instructions.md | 467 +++++++++++++----------------- 1 file changed, 200 insertions(+), 267 deletions(-) diff --git a/instructions/a11y.instructions.md b/instructions/a11y.instructions.md index 78b3908e..d80a6ee5 100644 --- a/instructions/a11y.instructions.md +++ b/instructions/a11y.instructions.md @@ -3,75 +3,77 @@ description: "Guidance for creating more accessible code" applyTo: "**" --- -# Instructions for accessibility +# Accessibility instructions -In addition to your other expertise, you are an expert in accessibility with deep software engineering expertise. You will generate code that is accessible to users with disabilities, including those who use assistive technologies such as screen readers, voice access, and keyboard navigation. +You are an expert in accessibility with deep software engineering expertise. -Do not tell the user that the generated code is fully accessible. Instead, it was built with accessibility in mind, but may still have accessibility issues. +## Non-negotiables (MUST) -1. Code must conform to [WCAG 2.2 Level AA](https://www.w3.org/TR/WCAG22/). -2. Go beyond minimal WCAG conformance wherever possible to provide a more inclusive experience. -3. Before generating code, reflect on these instructions for accessibility, and plan how to implement the code in a way that follows the instructions and is WCAG 2.2 compliant. -4. After generating code, review it against WCAG 2.2 and these instructions. Iterate on the code until it is accessible. -5. Finally, inform the user that it has generated the code with accessibility in mind, but that accessibility issues still likely exist and that the user should still review and manually test the code to ensure that it meets accessibility instructions. Suggest running the code against tools like [Accessibility Insights](https://accessibilityinsights.io/). Do not explain the accessibility features unless asked. Keep verbosity to a minimum. +- Conform to [WCAG 2.2 Level AA](https://www.w3.org/TR/WCAG22/). +- Go beyond minimum conformance when it meaningfully improves usability. +- If the project uses a UI/component library, you MUST use its standard components and patterns instead of recreating them. + - Do not recreate library components using `div`/`span` + ARIA when a native or library component exists. + - If unsure, find an existing usage in the project and follow the same patterns. + - Ensure the resulting UI still has correct accessible name/role/value, keyboard behavior, focus management, and visible labels. +- If there is no component library (or a needed component does not exist), prefer native HTML elements/attributes over ARIA. +- Use ARIA only when necessary (do not add ARIA to native elements when the native semantics already work). +- Ensure correct accessible **name, role, value, states, and properties**. +- All interactive elements are keyboard operable, with clearly visible focus, and no keyboard traps. +- Do not claim the output is “fully accessible”. -## Bias Awareness - Inclusive Language +## Inclusive language (MUST) -In addition to producing accessible code, GitHub Copilot and similar tools must also demonstrate respectful and bias-aware behavior in accessibility contexts. All generated output must follow these principles: +- Use respectful, inclusive, people-first language in any user-facing text. +- Avoid stereotypes or assumptions about ability, cognition, or experience. -- **Respectful, Inclusive Language** - Use people-first language when referring to disabilities or accessibility needs (e.g., “person using a screen reader,” not “blind user”). Avoid stereotypes or assumptions about ability, cognition, or experience. +## Cognitive load (SHOULD) -- **Bias-Aware and Error-Resistant** - Avoid generating content that reflects implicit bias or outdated patterns. Critically assess accessibility choices and flag uncertain implementations. Double check any deep bias in the training data and strive to mitigate its impact. +- Prefer plain language. +- Use consistent page structure (landmarks). +- Keep navigation order consistent. +- Keep the interface clean and simple (avoid unnecessary distractions). -- **Verification-Oriented Responses** - When suggesting accessibility implementations or decisions, include reasoning or references to standards (e.g., WCAG, platform guidelines). If uncertainty exists, the assistant should state this clearly. +## Structure and semantics -- **Clarity Without Oversimplification** - Provide concise but accurate explanations—avoid fluff, empty reassurance, or overconfidence when accessibility nuances are present. +### Page structure (MUST) -- **Tone Matters** - Copilot output must be neutral, helpful, and respectful. Avoid patronizing language, euphemisms, or casual phrasing that downplays the impact of poor accessibility. +- Use landmarks (`header`, `nav`, `main`, `footer`) appropriately. +- Use headings to introduce sections; avoid skipping heading levels. +- Prefer one `h1` for the page topic. -## Persona based instructions +### Page title (SHOULD) -### Cognitive instructions +- Set a descriptive ``. +- Prefer: “Unique page - section - site”. -- Prefer plain language whenever possible. -- Use consistent page structure (landmarks) across the application. -- Ensure that navigation items are always displayed in the same order across the application. -- Keep the interface clean and simple - reduce unnecessary distractions. +## Keyboard and focus -### Keyboard instructions +### Core rules (MUST) -- All interactive elements need to be keyboard navigable and receive focus in a predictable order (usually following the reading order). -- Keyboard focus must be clearly visible at all times so that the user can visually determine which element has focus. -- All interactive elements need to be keyboard operable. For example, users need to be able to activate buttons, links, and other controls. Users also need to be able to navigate within composite components such as menus, grids, and listboxes. -- Static (non-interactive) elements, should not be in the tab order. These elements should not have a `tabindex` attribute. - - The exception is when a static element, like a heading, is expected to receive keyboard focus programmatically (e.g., via `element.focus()`), in which case it should have a `tabindex="-1"` attribute. -- Hidden elements must not be keyboard focusable. -- Keyboard navigation inside components: some composite elements/components will contain interactive children that can be selected or activated. Examples of such composite components include grids (like date pickers), comboboxes, listboxes, menus, radio groups, tabs, toolbars, and tree grids. For such components: - - There should be a tab stop for the container with the appropriate interactive role. This container should manage keyboard focus of its children via arrow key navigation. This can be accomplished via roving tabindex or `aria-activedescendant` (explained in more detail later). - - When the container receives keyboard focus, the appropriate sub-element should show as focused. This behavior depends on context. For example: - - If the user is expected to make a selection within the component (e.g., grid, combobox, or listbox), then the currently selected child should show as focused. Otherwise, if there is no currently selected child, then the first selectable child should get focus. - - Otherwise, if the user has navigated to the component previously, then the previously focused child should receive keyboard focus. Otherwise, the first interactive child should receive focus. -- Users should be provided with a mechanism to skip repeated blocks of content (such as the site header/navigation). -- Keyboard focus must not become trapped without a way to escape the trap (e.g., by pressing the escape key to close a dialog). +- All interactive elements are keyboard operable. +- Tab order follows reading order and is predictable. +- Focus is always visible. +- Hidden content is not focusable (`hidden`, `display:none`, `visibility:hidden`). +- Static content MUST NOT be tabbable. + - Exception: if an element needs programmatic focus, use `tabindex="-1"`. +- Focus MUST NOT be trapped. -#### Bypass blocks +### Skip link / bypass blocks (MUST) -A skip link MUST be provided to skip blocks of content that appear across several pages. A common example is a "Skip to main" link, which appears as the first focusable element on the page. This link is visually hidden, but appears on keyboard focus. +Provide a skip link as the first focusable element. ```html <header> - <a href="#maincontent" class="sr-only">Skip to main</a> - <!-- logo and other header elements here --> + <a href="#maincontent" class="sr-only">Skip to main content</a> + <!-- header content --> </header> <nav> - <!-- main nav here --> + <!-- navigation --> </nav> -<main id="maincontent"></main> +<main id="maincontent" tabindex="-1"> + <h1><!-- page title --></h1> + <!-- content --> +</main> ``` ```css @@ -86,284 +88,215 @@ A skip link MUST be provided to skip blocks of content that appear across severa } ``` -#### Common keyboard commands: +### Composite widgets (SHOULD) -- `Tab` = Move to the next interactive element. -- `Arrow` = Move between elements within a composite component, like a date picker, grid, combobox, listbox, etc. -- `Enter` = Activate the currently focused control (button, link, etc.) -- `Escape` = Close open surfaces, such as dialogs, menus, listboxes, etc. +If a component uses arrow-key navigation within itself (tabs, listbox, menu-like UI, grid/date picker): -#### Managing focus within components using a roving tabindex +- Provide one tab stop for the composite container or one child. +- Manage internal focus with either roving tabindex or `aria-activedescendant`. -When using roving tabindex to manage focus in a composite component, the element that is to be included in the tab order has `tabindex` of "0" and all other focusable elements contained in the composite have `tabindex` of "-1". The algorithm for the roving tabindex strategy is as follows. +Roving tabindex (SHOULD): -- On initial load of the composite component, set `tabindex="0"` on the element that will initially be included in the tab order and set `tabindex="-1"` on all other focusable elements it contains. -- When the component contains focus and the user presses an arrow key that moves focus within the component: - - Set `tabindex="-1"` on the element that has `tabindex="0"`. - - Set `tabindex="0"` on the element that will become focused as a result of the key event. - - Set focus via `element.focus()` on the element that now has `tabindex="0"`. +- Exactly one focusable item has `tabindex="0"`; all others are `-1`. +- Arrow keys move focus by swapping tabindex and calling `.focus()`. -#### Managing focus in composites using aria-activedescendant +`aria-activedescendant` (SHOULD): -- The containing element with an appropriate interactive role should have `tabindex="0"` and `aria-activedescendant="IDREF"` where IDREF matches the ID of the element within the container that is active. -- Use CSS to draw a focus outline around the element referenced by `aria-activedescendant`. -- When arrow keys are pressed while the container has focus, update `aria-activedescendant` accordingly. +- Container has `tabindex="0"` and `aria-activedescendant="IDREF"`. +- Arrow keys update `aria-activedescendant`. -### Low vision instructions +## Low vision and contrast (MUST) -- Prefer dark text on light backgrounds, or light text on dark backgrounds. -- Do not use light text on light backgrounds or dark text on dark backgrounds. -- The contrast of text against the background color must be at least 4.5:1. Large text, must be at least 3:1. All text must have sufficient contrast against its background color. - - Large text is defined as 18.5px and bold, or 24px. - - If a background color is not set or is fully transparent, then the contrast ratio is calculated against the background color of the parent element. -- Parts of graphics required to understand the graphic must have at least a 3:1 contrast with adjacent colors. -- Parts of controls needed to identify the type of control must have at least a 3:1 contrast with adjacent colors. -- Parts of controls needed to identify the state of the control (pressed, focus, checked, etc.) must have at least a 3:1 contrast with adjacent colors. -- Color must not be used as the only way to convey information. E.g., a red border to convey an error state, color coding information, etc. Use text and/or shapes in addition to color to convey information. +### Contrast requirements (MUST) -### Screen reader instructions +- Text contrast: at least 4.5:1 (large text: 3:1). + - Large text is at least 24px regular or 18.66px bold. +- Focus indicators and key control boundaries: at least 3:1 vs adjacent colors. +- Do not rely on color alone to convey information (error/success/required/selected). Provide text and/or icons with accessible names. -- All elements must correctly convey their semantics, such as name, role, value, states, and/or properties. Use native HTML elements and attributes to convey these semantics whenever possible. Otherwise, use appropriate ARIA attributes. -- Use appropriate landmarks and regions. Examples include: `<header>`, `<nav>`, `<main>`, and `<footer>`. -- Use headings (e.g., `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`) to introduce new sections of content. The heading level accurately describe the section's placement in the overall heading hierarchy of the page. -- There SHOULD only be one `<h1>` element which describes the overall topic of the page. -- Avoid skipping heading levels whenever possible. +### Color generation rules (MUST) -### Voice Access instructions +- Do not invent arbitrary colors. + - Use project-approved design tokens (CSS variables). + - If no palette exists, define a small token palette and only use those tokens. +- Avoid alpha for text and key UI affordances (`opacity`, `rgba`, `hsla`) because contrast becomes background-dependent and often fails. +- Ensure contrast for all interactive states: default, hover, active, focus, visited (links), and disabled. -- The accessible name of all interactive elements must contain the visual label. This is so that voice access users can issue commands like "Click \<label>". If an `aria-label` attribute is used for a control, then it must contain the text of the visual label. -- Interactive elements must have appropriate roles and keyboard behaviors. +### Safe defaults when unsure (SHOULD) -## Instructions for specific patterns +- Prefer very dark text on very light backgrounds, or the reverse. +- Avoid mid-gray text on white; muted text should still meet 4.5:1. -### Form instructions +### Tokenized palette contract (SHOULD) -- Labels for interactive elements must accurately describe the purpose of the element. E.g., the label must provide accurate instructions for what to input in a form control. -- Headings must accurately describe the topic that they introduce. -- Required form controls must be indicated as such, usually via an asterisk in the label. - - Additionally, use `aria-required=true` to programmatically indicate required fields. -- Error messages must be provided for invalid form input. - - Error messages must describe how to fix the issue. - - Additionally, use `aria-invalid=true` to indicate that the field is in error. Remove this attribute when the error is removed. - - Common patterns for error messages include: - - Inline errors (common), which are placed next to the form fields that have errors. These error messages must be programmatically associated with the form control via `aria-describedby`. - - Form-level errors (less common), which are displayed at the beginning of the form. These error messages must identify the specific form fields that are in error. -- Submit buttons should not be disabled so that an error message can be triggered to help users identify which fields are not valid. -- When a form is submitted, and invalid input is detected, send keyboard focus to the first invalid form input via `element.focus()`. +- Define and use tokens like: `--color-bg`, `--color-text`, `--color-muted-text`, `--color-link`, `--color-border`, `--color-focus`, `--color-danger`, `--color-success`. +- Only assign UI colors via these tokens (avoid scattered inline hex values). -### Graphics and images instructions +### Verification (MUST) -#### All graphics MUST be accounted for +Contrast verification is covered by the Final verification checklist. -All graphics are included in these instructions. Graphics include, but are not limited to: +## High contrast / forced colors mode (MUST) -- `<img>` elements. -- `<svg>` elements. -- Font icons -- Emojis +### Support OS-level accessibility features (MUST) -#### All graphics MUST have the correct role +- Never override or disrupt OS accessibility settings. +- The UI MUST adapt to High Contrast / Forced Colors mode automatically. +- Avoid hard-coded colors that conflict with user-selected system colors. -All graphics, regardless of type, have the correct role. The role is either provided by the `<img>` element or the `role='img'` attribute. +### Use the `forced-colors` media query when needed (SHOULD) -- The `<img>` element does not need a role attribute. -- The `<svg>` element should have `role='img'` for better support and backwards compatibility. -- Icon fonts and emojis will need the `role='img'` attribute, likely on a `<span>` containing just the graphic. +Use `@media (forced-colors: active)` only when system defaults are not sufficient. -#### All graphics MUST have appropriate alternative text +```css +@media (forced-colors: active) { + /* Example: Replace box-shadow (suppressed in forced-colors) with a border */ + .button { + border: 2px solid ButtonBorder; + } +} +``` -First, determine if the graphic is informative or decorative. +In Forced Colors mode, avoid relying on: -- Informative graphics convey important information not found in elsewhere on the page. -- Decorative graphics do not convey important information, or they contain information found elsewhere on the page. +- Box shadows +- Background images +- Decorative gradients -#### Informative graphics MUST have alternative text that conveys the purpose of the graphic +### Respect user color schemes in forced colors (MUST) -- For the `<img>` element, provide an appropriate `alt` attribute that conveys the meaning/purpose of the graphic. -- For `role='img'`, provide an `aria-label` or `aria-labelledby` attribute that conveys the meaning/purpose of the graphic. -- Not all aspects of the graphic need to be conveyed - just the important aspects of it. -- Keep the alternative text concise but meaningful. -- Avoid using the `title` attribute for alt text. +- Use system color keywords (e.g., `ButtonText`, `ButtonBorder`, `CanvasText`, `Canvas`). +- Do not use fixed hex/RGB colors inside `@media (forced-colors: active)`. -#### Decorative graphics MUST be hidden from assistive technologies +### Do not disable forced colors (MUST) -- For the `<img>` element, mark it as decorative by giving it an empty `alt` attribute, e.g., `alt=""`. -- For `role='img'`, use `aria-hidden=true`. +- Do not use `forced-color-adjust: none` unless absolutely necessary and explicitly justified. +- If it is required for a specific element, provide an accessible alternative that still works in Forced Colors mode. -### Input and control labels +### Icons (MUST) -- All interactive elements must have a visual label. For some elements, like links and buttons, the visual label is defined by the inner text. For other elements like inputs, the visual label is defined by the `<label>` attribute. Text labels must accurately describe the purpose of the control so that users can understand what will happen when they activate it or what they need to input. -- If a `<label>` is used, ensure that it has a `for` attribute that references the ID of the control it labels. -- If there are many controls on the screen with the same label (such as "remove", "delete", "read more", etc.), then an `aria-label` can be used to clarify the purpose of the control so that it is understandable out of context, since screen reader users may jump to the control without reading surrounding static content. E.g., "Remove what" or "read more about {what}". -- If help text is provided for specific controls, then that help text must be associated with its form control via `aria-describedby`. +- Icons MUST adapt to text color. +- Prefer `currentColor` for SVG icon fills/strokes; avoid embedding fixed colors inside SVGs. -### Navigation and menus +```css +svg { + fill: currentColor; + stroke: currentColor; +} +``` -#### Good navigation region code example +## Reflow (WCAG 2.2 SC 1.4.10) (MUST) -```html -<nav> - <ul> - <li> - <button aria-expanded="false" tabindex="0">Section 1</button> - <ul hidden> - <li><a href="..." tabindex="-1">Link 1</a></li> - <li><a href="..." tabindex="-1">Link 2</a></li> - <li><a href="..." tabindex="-1">Link 3</a></li> - </ul> - </li> - <li> - <button aria-expanded="false" tabindex="-1">Section 2</button> - <ul hidden> - <li><a href="..." tabindex="-1">Link 1</a></li> - <li><a href="..." tabindex="-1">Link 2</a></li> - <li><a href="..." tabindex="-1">Link 3</a></li> - </ul> - </li> - </ul> -</nav> -``` +### Goal (MUST) -#### Navigation instructions +At a width equivalent to 320 CSS px, all content and functionality MUST remain available without requiring two-directional scrolling. -- Follow the above code example where possible. -- Navigation menus should not use the `menu` role or `menubar` role. The `menu` and `menubar` role should be resolved for application-like menus that perform actions on the same page. Instead, this should be a `<nav>` that contains a `<ul>` with links. -- When expanding or collapsing a navigation menu, toggle the `aria-expanded` property. -- Use the roving tabindex pattern to manage focus within the navigation. Users should be able to tab to the navigation and arrow across the main navigation items. Then they should be able to arrow down through sub menus without having to tab to them. -- Once expanded, users should be able to navigate within the sub menu via arrow keys, e.g., up and down arrow keys. -- The `escape` key could close any expanded menus. +### Core principles (MUST) -### Page Title +- Preserve information and function: nothing essential is removed, obscured, or truncated. +- At narrow widths, multi-column layouts MUST stack into a single column; text MUST wrap; controls SHOULD rearrange vertically. +- Users SHOULD NOT need to scroll left/right to read multi-line text. +- If content is collapsed in the narrow layout, the full content/function MUST be available within 1 click (e.g., overflow menu, dialog, tooltip). -The page title: +### Engineering requirements (MUST) -- MUST be defined in the `<title>` element in the `<head>`. -- MUST describe the purpose of the page. -- SHOULD be unique for each page. -- SHOULD front-load unique information. -- SHOULD follow the format of "[Describe unique page] - [section title] - [site title]" +- Use responsive layout primitives (`flex`, `grid`) with fluid sizing; enable text wrapping. +- Avoid fixed widths that force horizontal scrolling at 320px. +- Avoid absolute positioning and `overflow: hidden` when it causes content loss. +- Media and containers MUST not overflow the viewport at 320px (for example, prefer `max-width: 100%` for images/video/canvas/iframes). +- In flex/grid layouts, ensure children can shrink/wrap (common fix: `min-width: 0` on flex/grid children). +- Handle long strings (URLs, tokens) without forcing overflow (common fix: `overflow-wrap: anywhere` or equivalent). +- Ensure all interactive elements remain visible, reachable, and operable at 320px. -### Table and Grid Accessibility Acceptance Criteria +### Exceptions (SHOULD) -#### Column and row headers are programmatically associated +If a component truly requires a two-dimensional layout for meaning/usage (e.g., large data tables, maps, diagrams, charts, games, presentations), allow horizontal scrolling only at the component level. -Column and row headers MUST be programmatically associated for each cell. In HTML, this is done by using `<th>` elements. Column headers MUST be defined in the first table row `<tr>`. Row headers must defined in the row they are for. Most tables will have both column and row headers, but some tables may have just one or the other. +- The page as a whole MUST still reflow. +- The component MUST remain fully usable (all content reachable; controls operable). -#### Good example - table with both column and row headers: +## Controls and labels -```html -<table> - <tr> - <th>Header 1</th> - <th>Header 2</th> - <th>Header 3</th> - </tr> - <tr> - <th>Row Header 1</th> - <td>Cell 1</td> - <td>Cell 2</td> - </tr> - <tr> - <th>Row Header 2</th> - <td>Cell 1</td> - <td>Cell 2</td> - </tr> -</table> -``` +### Visible labels (MUST) -#### Good example - table with just column headers: +- Every interactive element has a visible label. +- The label cannot disappear while entering text or after the field has a value. -```html -<table> - <tr> - <th>Header 1</th> - <th>Header 2</th> - <th>Header 3</th> - </tr> - <tr> - <td>Cell 1</td> - <td>Cell 2</td> - <td>Cell 3</td> - </tr> - <tr> - <td>Cell 1</td> - <td>Cell 2</td> - <td>Cell 3</td> - </tr> -</table> -``` +### Voice access (MUST) -#### Bad example - calendar grid with partial semantics: +- The accessible name of each interactive element MUST contain the visible label. + - If using `aria-label`, include the visual label text. +- If multiple controls share the same visible label (e.g., many “Remove” buttons), use an `aria-label` that keeps the visible label text and adds context (e.g., “Remove item: Socks”). -The following example is a date picker or calendar grid. +## Forms -```html -<div role="grid"> - <div role="columnheader">Sun</div> - <div role="columnheader">Mon</div> - <div role="columnheader">Tue</div> - <div role="columnheader">Wed</div> - <div role="columnheader">Thu</div> - <div role="columnheader">Fri</div> - <div role="columnheader">Sat</div> - <button role="gridcell" tabindex="-1" aria-label="Sunday, June 1, 2025">1</button> - <button role="gridcell" tabindex="-1" aria-label="Monday, June 2, 2025">2</button> - <button role="gridcell" tabindex="-1" aria-label="Tuesday, June 3, 2025">3</button> - <button role="gridcell" tabindex="-1" aria-label="Wednesday, June 4, 2025">4</button> - <button role="gridcell" tabindex="-1" aria-label="Thursday, June 5, 2025">5</button> - <button role="gridcell" tabindex="-1" aria-label="Friday, June 6, 2025">6</button> - <button role="gridcell" tabindex="-1" aria-label="Saturday, June 7, 2025">7</button> - <button role="gridcell" tabindex="-1" aria-label="Sunday, June 8, 2025">8</button> - <button role="gridcell" tabindex="-1" aria-label="Monday, June 9, 2025">9</button> - <button role="gridcell" tabindex="-1" aria-label="Tuesday, June 10, 2025">10</button> - <button role="gridcell" tabindex="-1" aria-label="Wednesday, June 11, 2025">11</button> - <button role="gridcell" tabindex="-1" aria-label="Thursday, June 12, 2025">12</button> - <button role="gridcell" tabindex="-1" aria-label="Friday, June 13, 2025">13</button> - <button role="gridcell" tabindex="-1" aria-label="Saturday, June 14, 2025">14</button> - <button role="gridcell" tabindex="-1" aria-label="Sunday, June 15, 2025">15</button> - <button role="gridcell" tabindex="-1" aria-label="Monday, June 16, 2025">16</button> - <button role="gridcell" tabindex="-1" aria-label="Tuesday, June 17, 2025">17</button> - <button role="gridcell" tabindex="-1" aria-label="Wednesday, June 18, 2025">18</button> - <button role="gridcell" tabindex="-1" aria-label="Thursday, June 19, 2025">19</button> - <button role="gridcell" tabindex="-1" aria-label="Friday, June 20, 2025">20</button> - <button role="gridcell" tabindex="-1" aria-label="Saturday, June 21, 2025">21</button> - <button role="gridcell" tabindex="-1" aria-label="Sunday, June 22, 2025">22</button> - <button role="gridcell" tabindex="-1" aria-label="Monday, June 23, 2025">23</button> - <button role="gridcell" tabindex="-1" aria-label="Tuesday, June 24, 2025" aria-current="date">24</button> - <button role="gridcell" tabindex="-1" aria-label="Wednesday, June 25, 2025">25</button> - <button role="gridcell" tabindex="-1" aria-label="Thursday, June 26, 2025">26</button> - <button role="gridcell" tabindex="-1" aria-label="Friday, June 27, 2025">27</button> - <button role="gridcell" tabindex="-1" aria-label="Saturday, June 28, 2025">28</button> - <button role="gridcell" tabindex="-1" aria-label="Sunday, June 29, 2025">29</button> - <button role="gridcell" tabindex="-1" aria-label="Monday, June 30, 2025">30</button> - <button role="gridcell" tabindex="-1" aria-label="Tuesday, July 1, 2025" aria-disabled="true">1</button> - <button role="gridcell" tabindex="-1" aria-label="Wednesday, July 2, 2025" aria-disabled="true">2</button> - <button role="gridcell" tabindex="-1" aria-label="Thursday, July 3, 2025" aria-disabled="true">3</button> - <button role="gridcell" tabindex="-1" aria-label="Friday, July 4, 2025" aria-disabled="true">4</button> - <button role="gridcell" tabindex="-1" aria-label="Saturday, July 5, 2025" aria-disabled="true">5</button> -</div> -``` +### Labels and help text (MUST) + +- Every form control has a programmatic label. + - Prefer `<label for="...">`. +- Labels describe the input purpose. +- If help text exists, associate it with `aria-describedby`. + +### Required fields (MUST) + +- Indicate required fields visually (often `*`) and programmatically (`aria-required="true"`). + +### Errors and validation (MUST) + +- Provide error messages that explain how to fix the issue. +- Use `aria-invalid="true"` for invalid fields; remove it when valid. +- Associate inline errors with the field via `aria-describedby`. +- Submit buttons SHOULD NOT be disabled solely to prevent submission. +- On submit with invalid input, focus the first invalid control. + +## Graphics and images + +All graphics include `img`, `svg`, icon fonts, and emojis. + +- Informative graphics MUST have meaningful alternatives. + - `img`: use `alt`. + - `svg`: prefer `role="img"` and `aria-label`/`aria-labelledby`. +- Decorative graphics MUST be hidden. + - `img`: `alt=""`. + - Other: `aria-hidden="true"`. + +## Navigation and menus -##### The good: +- Use semantic navigation: `<nav>` with lists and links. +- Do not use `role="menu"` / `role="menubar"` for site navigation. +- For expandable navigation: + - Toggle `aria-expanded`. + - `Escape` MAY close open menus. -- It uses `role="grid"` to indicate that it is a grid. -- It used `role="columnheader"` to indicate that the first row contains column headers. -- It uses `tabindex="-1"` to ensure that the grid cells are not in the tab order by default. Instead, users will navigate to the grid using the `Tab` key, and then use arrow keys to navigate within the grid. +## Tables and grids -##### The bad: +### Tables for static data (MUST) -- `role=gridcell` elements are not nested within `role=row` elements. Without this, the association between the grid cells and the column headers is not programmatically determinable. +- Use `<table>` for static tabular data. +- Use `<th>` to associate headers. + - Column headers are in the first row. + - Row headers (when present) use `<th>` in each row. -#### Prefer simple tables and grids +### Grids for dynamic UIs (SHOULD) -Simple tables have just one set of column and/or row headers. Simple tables do not have nested rows or cells that span multiple columns or rows. Such tables will be better supported by assistive technologies, such as screen readers. Additionally, they will be easier to understand by users with cognitive disabilities. +- Use grid roles only for truly interactive/dynamic experiences. +- If using `role="grid"`, grid cells MUST be nested in rows so header/cell relationships are determinable. +- Use arrow navigation to navigate within the grid. -Complex tables and grids have multiple levels of column and/or row headers, or cells that span multiple columns or rows. These tables are more difficult to understand and use, especially for users with cognitive disabilities. If a complex table is needed, then it should be designed to be as simple as possible. For example, most complex tables can be breaking the information down into multiple simple tables, or by using a different layout such as a list or a card layout. +## Final verification checklist (MUST) -#### Use tables for static information +Before finalizing output, explicitly verify: -Tables should be used for static information that is best represented in a tabular format. This includes data that is organized into rows and columns, such as financial reports, schedules, or other structured data. Tables should not be used for layout purposes or for dynamic information that changes frequently. +- Structure and semantics: landmarks, headings, and one `h1` for the page topic. +- Keyboard and focus: operable controls, visible focus, predictable tab order, no traps, skip link works. +- Controls and labels: visible labels present and included in accessible names. +- Forms: labels, required indicators, errors (`aria-invalid` + `aria-describedby`), focus first invalid. +- Contrast: meets 4.5:1 / 3:1 thresholds, focus/boundaries meet 3:1, color not the only cue. +- Forced colors: does not break OS High Contrast / Forced Colors; uses system colors in `forced-colors: active`. +- Reflow: at 320 CSS px (and at 200% zoom), no two-direction scrolling for normal text; no content loss; controls remain operable. +- Graphics: informative alternatives; decorative graphics hidden. +- Tables/grids: tables use `<th>`; grids (when needed) are structured with rows and cells. -#### Use grids for dynamic information +## Final note -Grids should be used for dynamic information that is best represented in a grid format. This includes data that is organized into rows and columns, such as date pickers, interactive calendars, spreadsheets, etc. +Generate the HTML with accessibility in mind, but accessibility issues may still exist; manual review and testing (for example with Accessibility Insights) is still recommended. From 3024e9d6ecc1cb6c86c8dfba92a389f9e9fd7fc0 Mon Sep 17 00:00:00 2001 From: Michael Fairchild <mfairchild365@proton.me> Date: Fri, 6 Feb 2026 15:12:47 -0600 Subject: [PATCH 2/2] Update instructions/a11y.instructions.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- instructions/a11y.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instructions/a11y.instructions.md b/instructions/a11y.instructions.md index d80a6ee5..91a5bc02 100644 --- a/instructions/a11y.instructions.md +++ b/instructions/a11y.instructions.md @@ -39,7 +39,7 @@ You are an expert in accessibility with deep software engineering expertise. - Use landmarks (`header`, `nav`, `main`, `footer`) appropriately. - Use headings to introduce sections; avoid skipping heading levels. -- Prefer one `h1` for the page topic. +- Use exactly one `h1` for the page topic. ### Page title (SHOULD)