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.
This commit is contained in:
Michael Fairchild
2026-02-06 14:44:13 -06:00
committed by GitHub
parent 4f9e20b13a
commit 892b265ef3

View File

@@ -3,75 +3,77 @@ description: "Guidance for creating more accessible code"
applyTo: "**" 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/). - 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. - Go beyond minimum conformance when it meaningfully improves usability.
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. - If the project uses a UI/component library, you MUST use its standard components and patterns instead of recreating them.
4. After generating code, review it against WCAG 2.2 and these instructions. Iterate on the code until it is accessible. - Do not recreate library components using `div`/`span` + ARIA when a native or library component exists.
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. - 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** ## Cognitive load (SHOULD)
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.
- **Bias-Aware and Error-Resistant** - Prefer plain language.
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. - Use consistent page structure (landmarks).
- Keep navigation order consistent.
- Keep the interface clean and simple (avoid unnecessary distractions).
- **Verification-Oriented Responses** ## Structure and semantics
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.
- **Clarity Without Oversimplification** ### Page structure (MUST)
Provide concise but accurate explanations—avoid fluff, empty reassurance, or overconfidence when accessibility nuances are present.
- **Tone Matters** - Use landmarks (`header`, `nav`, `main`, `footer`) appropriately.
Copilot output must be neutral, helpful, and respectful. Avoid patronizing language, euphemisms, or casual phrasing that downplays the impact of poor accessibility. - 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 `<title>`.
- Prefer: “Unique page - section - site”.
- Prefer plain language whenever possible. ## Keyboard and focus
- 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 instructions ### Core rules (MUST)
- All interactive elements need to be keyboard navigable and receive focus in a predictable order (usually following the reading order). - All interactive elements are keyboard operable.
- Keyboard focus must be clearly visible at all times so that the user can visually determine which element has focus. - Tab order follows reading order and is predictable.
- 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. - Focus is always visible.
- Static (non-interactive) elements, should not be in the tab order. These elements should not have a `tabindex` attribute. - Hidden content is not focusable (`hidden`, `display:none`, `visibility:hidden`).
- 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. - Static content MUST NOT be tabbable.
- Hidden elements must not be keyboard focusable. - Exception: if an element needs programmatic focus, use `tabindex="-1"`.
- 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: - Focus MUST NOT be trapped.
- 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).
#### 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 ```html
<header> <header>
<a href="#maincontent" class="sr-only">Skip to main</a> <a href="#maincontent" class="sr-only">Skip to main content</a>
<!-- logo and other header elements here --> <!-- header content -->
</header> </header>
<nav> <nav>
<!-- main nav here --> <!-- navigation -->
</nav> </nav>
<main id="maincontent"></main> <main id="maincontent" tabindex="-1">
<h1><!-- page title --></h1>
<!-- content -->
</main>
``` ```
```css ```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. If a component uses arrow-key navigation within itself (tabs, listbox, menu-like UI, grid/date picker):
- `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.
#### 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. - Exactly one focusable item has `tabindex="0"`; all others are `-1`.
- When the component contains focus and the user presses an arrow key that moves focus within the component: - Arrow keys move focus by swapping tabindex and calling `.focus()`.
- 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"`.
#### 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. - Container has `tabindex="0"` and `aria-activedescendant="IDREF"`.
- Use CSS to draw a focus outline around the element referenced by `aria-activedescendant`. - Arrow keys update `aria-activedescendant`.
- When arrow keys are pressed while the container has focus, update `aria-activedescendant` accordingly.
### Low vision instructions ## Low vision and contrast (MUST)
- Prefer dark text on light backgrounds, or light text on dark backgrounds. ### Contrast requirements (MUST)
- 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.
### 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. ### Color generation rules (MUST)
- 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.
### 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. ### Safe defaults when unsure (SHOULD)
- Interactive elements must have appropriate roles and keyboard behaviors.
## 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. - Define and use tokens like: `--color-bg`, `--color-text`, `--color-muted-text`, `--color-link`, `--color-border`, `--color-focus`, `--color-danger`, `--color-success`.
- Headings must accurately describe the topic that they introduce. - Only assign UI colors via these tokens (avoid scattered inline hex values).
- 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()`.
### 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. ### Support OS-level accessibility features (MUST)
- `<svg>` elements.
- Font icons
- Emojis
#### 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. Use `@media (forced-colors: active)` only when system defaults are not sufficient.
- 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.
#### All graphics MUST have appropriate alternative text ```css
@media (forced-colors: active) {
First, determine if the graphic is informative or decorative. /* Example: Replace box-shadow (suppressed in forced-colors) with a border */
.button {
- Informative graphics convey important information not found in elsewhere on the page. border: 2px solid ButtonBorder;
- Decorative graphics do not convey important information, or they contain information found elsewhere on the page. }
}
#### Informative graphics MUST have alternative text that conveys the purpose of the graphic
- 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.
#### Decorative graphics MUST be hidden from assistive technologies
- 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`.
### Input and control labels
- 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`.
### Navigation and menus
#### Good navigation region code example
```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>
``` ```
#### Navigation instructions In Forced Colors mode, avoid relying on:
- Follow the above code example where possible. - Box shadows
- 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. - Background images
- When expanding or collapsing a navigation menu, toggle the `aria-expanded` property. - Decorative gradients
- 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.
### Page Title ### Respect user color schemes in forced colors (MUST)
The page title: - Use system color keywords (e.g., `ButtonText`, `ButtonBorder`, `CanvasText`, `Canvas`).
- Do not use fixed hex/RGB colors inside `@media (forced-colors: active)`.
- MUST be defined in the `<title>` element in the `<head>`. ### Do not disable forced colors (MUST)
- 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]"
### Table and Grid Accessibility Acceptance Criteria - 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.
#### Column and row headers are programmatically associated ### Icons (MUST)
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. - Icons MUST adapt to text color.
- Prefer `currentColor` for SVG icon fills/strokes; avoid embedding fixed colors inside SVGs.
#### Good example - table with both column and row headers: ```css
svg {
```html fill: currentColor;
<table> stroke: currentColor;
<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>
``` ```
#### Good example - table with just column headers: ## Reflow (WCAG 2.2 SC 1.4.10) (MUST)
```html ### Goal (MUST)
<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>
```
#### Bad example - calendar grid with partial semantics: At a width equivalent to 320 CSS px, all content and functionality MUST remain available without requiring two-directional scrolling.
The following example is a date picker or calendar grid. ### Core principles (MUST)
```html - Preserve information and function: nothing essential is removed, obscured, or truncated.
<div role="grid"> - At narrow widths, multi-column layouts MUST stack into a single column; text MUST wrap; controls SHOULD rearrange vertically.
<div role="columnheader">Sun</div> - Users SHOULD NOT need to scroll left/right to read multi-line text.
<div role="columnheader">Mon</div> - If content is collapsed in the narrow layout, the full content/function MUST be available within 1 click (e.g., overflow menu, dialog, tooltip).
<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>
```
##### The good: ### Engineering requirements (MUST)
- It uses `role="grid"` to indicate that it is a grid. - Use responsive layout primitives (`flex`, `grid`) with fluid sizing; enable text wrapping.
- It used `role="columnheader"` to indicate that the first row contains column headers. - Avoid fixed widths that force horizontal scrolling at 320px.
- 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. - 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.
##### The bad: ### Exceptions (SHOULD)
- `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. 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.
#### Prefer simple tables and grids - The page as a whole MUST still reflow.
- The component MUST remain fully usable (all content reachable; controls operable).
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. ## Controls and labels
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. ### Visible labels (MUST)
#### Use tables for static information - Every interactive element has a visible label.
- The label cannot disappear while entering text or after the field has a value.
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. ### Voice access (MUST)
#### Use grids for dynamic information - 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”).
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. ## Forms
### 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
- 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.
## Tables and grids
### Tables for static data (MUST)
- 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.
### Grids for dynamic UIs (SHOULD)
- 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.
## Final verification checklist (MUST)
Before finalizing output, explicitly verify:
- 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.
## Final note
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.