diff --git a/.github/instructions/a11y.instructions.md b/.github/instructions/a11y.instructions.md
new file mode 100644
index 00000000000..aa3a8ef2a87
--- /dev/null
+++ b/.github/instructions/a11y.instructions.md
@@ -0,0 +1,297 @@
+---
+description: "Guidance for creating more accessible code"
+applyTo: "resources/js/**,resources/css/**,resources/templates/**,packages/craftcms-cp/src/**"
+---
+
+# Accessibility instructions
+
+You are an expert in accessibility with deep software engineering expertise.
+
+## Non-negotiables (MUST)
+
+- Conform to [WCAG 2.2 Level AA](https://www.w3.org/TR/WCAG22/).
+- Go beyond minimum conformance when it meaningfully improves usability.
+- UI components are defined as Lit Web Components in the component library (`@craftcms/cp`) or as Vue components in `./resources/js/`. You SHOULD use the component patterns as defined. Do not recreate patterns.
+ - 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, visible labels and meets at least minimum contrast requirements.
+- If a needed component does not exist, prefer native HTML elements/attributes over ARIA.
+ - The `@craftcms/cp` component library should include components that can be reused by plugin developers, or outside the context of this application. Vue components should be used for application-specific UI.
+- 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”.
+
+## Inclusive language (MUST)
+
+- Use respectful, inclusive, people-first language in any user-facing text.
+- Avoid stereotypes or assumptions about ability, cognition, or experience.
+
+## Cognitive load (SHOULD)
+
+- Prefer plain language.
+- Use consistent page structure (landmarks).
+- Keep navigation order consistent.
+- Keep the interface clean and simple (avoid unnecessary distractions).
+
+## Structure and semantics
+
+### Page structure (MUST)
+
+- Use landmarks (`header`, `nav`, `main`, `footer`) appropriately.
+- Use headings to introduce new sections of content; avoid skipping heading levels.
+- Prefer one `h1` for the page topic. Generally, the first heading within the `main` element / landmark.
+
+### Page title (SHOULD)
+
+- Set a descriptive `
`.
+- Prefer: “Unique page - section - site”.
+
+## Keyboard and focus
+
+### Core rules (MUST)
+
+- 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`).
+- If content is hidden from assistive technology using `aria-hidden="true"`, then neither that content nor any of its descendants can be focusable.
+- Static content MUST NOT be tabbable.
+ - Exception: if an element needs programmatic focus, use `tabindex="-1"`.
+
+### Skip link / bypass blocks (MUST)
+
+- Provide a skip link as the first focusable element.
+- When implementing a section with a large number of tab stops, consider adding a skip link to jump to the next section.
+
+```html
+
+ Skip to main content
+
+
+
+
+
+
+
+```
+
+### Composite widgets (SHOULD)
+
+If a component uses arrow-key navigation within itself (tabs, listbox, menu-like UI, grid/date picker):
+
+- Provide one tab stop for the composite container or one child.
+- Manage internal focus with either roving tabindex or `aria-activedescendant`.
+
+Roving tabindex (SHOULD):
+
+- Exactly one focusable item has `tabindex="0"`; all others are `-1`.
+- Arrow keys move focus by swapping tabindex and calling `.focus()`.
+
+`aria-activedescendant` (SHOULD):
+
+- Container is implicitly focusable or has `tabindex="0"` and `aria-activedescendant="IDREF"`.
+- Arrow keys update `aria-activedescendant`.
+
+## Low vision and contrast (MUST)
+
+### Contrast requirements (MUST)
+
+- 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.
+
+### Color generation rules (MUST)
+
+- Do not invent arbitrary colors.
+ - Use project-approved design tokens (CSS variables).
+ - If no relevant colors exist in the palette, 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.
+
+### Safe defaults when unsure (SHOULD)
+
+- 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.
+
+### Tokenized palette contract (SHOULD)
+
+- Define and use tokens like: `--c-bg-body`, `--c-bg-default`, `--c-color-danger-bg-normal`, `--c-color-danger-bg-emphasis`, `--c-color-danger-bg-subtle`.
+- Only assign UI colors via these tokens (avoid scattered inline hex values).
+
+### Verification (MUST)
+
+Contrast verification is covered by the Final verification checklist.
+
+## High contrast / forced colors mode (MUST)
+
+### Support OS-level accessibility features (MUST)
+
+- 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.
+
+### Use the `forced-colors` media query when needed (SHOULD)
+
+Use `@media (forced-colors: active)` only when system defaults are not sufficient.
+
+```css
+@media (forced-colors: active) {
+ /* Example: Replace box-shadow (suppressed in forced-colors) with a border */
+ .button {
+ border: 2px solid ButtonBorder;
+ }
+}
+
+/* if using box-shadow for a focus style, also use a transparent outline
+ so that the outline will render when the high contrast setting is enabled */
+.button:focus {
+ box-shadow: 0 0 4px 3px rgba(90, 50, 200, .7);
+ outline: 2px solid transparent;
+}
+```
+
+In Forced Colors mode, avoid relying on:
+
+- Box shadows
+- Decorative gradients
+
+### Respect user color schemes in forced colors (MUST)
+
+- Use system color keywords (e.g., `ButtonText`, `ButtonBorder`, `CanvasText`, `Canvas`).
+- Do not use fixed hex/RGB colors inside `@media (forced-colors: active)`.
+
+### Do not disable forced colors (MUST)
+
+- 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.
+
+### Icons (MUST)
+
+- Icons MUST adapt to text color.
+- Prefer `currentColor` for SVG icon fills/strokes; avoid embedding fixed colors inside SVGs.
+
+```css
+svg {
+ fill: currentColor;
+ stroke: currentColor;
+}
+```
+
+## Reflow (WCAG 2.2 SC 1.4.10) (MUST)
+
+### Goal (MUST)
+
+Multi-line text must be able to fit within 320px wide containers or viewports, so that users do not need to scroll in two-dimensions to read sections of content.
+
+### Core principles (MUST)
+
+- 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 MUST 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).
+
+### Engineering requirements (MUST)
+
+- Use responsive layout primitives (`flex`, `grid`) with fluid sizing; enable text wrapping.
+- Avoid fixed widths that force two-dimensional scrolling at 320px.
+- Avoid absolute positioning and `overflow: hidden` when it causes content loss, or would result in the obscuring of content at smaller viewport sizes.
+- Media and containers SHOULD 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.
+
+### Exceptions (SHOULD)
+
+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.
+
+- The page as a whole MUST still reflow (unless the page layout truly requires two-dimensional layout for usage).
+- The component MUST remain fully usable (all content reachable; controls operable).
+
+## Controls and labels
+
+### Visible labels (MUST)
+
+- Every interactive element has a visible label.
+- The label cannot disappear while entering text or after the field has a value.
+
+### Voice access (MUST)
+
+- 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”). If text that adds additional context exists on the page, prefer using a `aria-labelledby` with an ID that references the text node.
+
+## Forms
+
+### Labels and help text (MUST)
+
+- Every form control has a programmatic label.
+ - Prefer `