Skip to content

Accessibility Checklist

Kitsune doesn’t have an accessibility layer because the platform is the layer. This checklist is what we hold every Kitsune app and every Kitsune component to.

  • Every interactive element is a real native element when one exists (<button>, <a>, <input>, <select>, <textarea>, <dialog>, <details>).
  • Custom elements that wrap interactive natives delegate keyboard activation, focus, and form participation to the inner native element.
  • Custom elements that don’t wrap a native interactive element (e.g., a clickable card with role="button") handle Enter and Space keyboard activation explicitly and have tabindex="0".
  • No interactive <div>s. If a <div> is interactive, it should be a <button> or have a role plus full keyboard handling.
  • Every form control has a label — via <label for>, <label> wrapping, aria-label, or aria-labelledby. Visible labels are preferred.
  • Every error is announced to assistive technology, with role="alert" for first-class errors and aria-describedby linking the error to the input.
  • :focus-visible, not :focus, controls focus indicators.
  • Focus indicators are visible in every color scheme (light, dark, high contrast).
  • When a dialog opens, focus moves to a sensible place inside it (the first focusable element, or a designated input).
  • When a dialog closes, focus returns to the element that opened it.
  • Native <dialog>’s showModal() handles both of the above; prefer it over JS focus traps.
  • Inert content is marked with inert (or via <dialog>.showModal() which inerts the rest of the page).
  • Focus is never moved without user intent (no auto-scroll, no auto-jump).
  • All interactive elements are reachable via Tab in a logical order.
  • Custom widgets follow ARIA APG patterns (combobox, listbox, menu, tabs, etc.) when applicable.
  • Escape closes dialogs, popovers, and dropdowns. (Native primitives do this for free.)
  • Cmd/Ctrl shortcut keys are documented and don’t conflict with screen reader shortcuts.
  • aria-live regions announce state changes that the user should know about (toasts, status messages).
  • aria-live="polite" for low-priority updates; aria-live="assertive" (or role="alert") for errors.
  • Custom elements that hide content in shadow DOM still expose accessible names, descriptions, and roles to assistive tech.
  • Decorative elements (icons, separators) are marked aria-hidden="true".
  • Headings follow a logical hierarchy. No skipping from <h1> to <h3>.
  • Text contrast meets WCAG 2.2 AA at minimum (4.5:1 for body, 3:1 for large text).
  • Information is never conveyed by color alone (use icons, text, or shape too).
  • System colors (Canvas, CanvasText, Highlight) appear as fallbacks so forced-colors mode works.
  • color-scheme: light dark is set on the root so form controls and scrollbars adapt.
  • prefers-reduced-motion is respected for non-essential animations.
  • Auto-play, parallax, and decorative motion can be disabled by user preference.
  • Required fields are marked visually and with the required attribute.
  • Validation messages appear next to the field, not just at the top of the form.
  • Submission errors don’t strip the user’s input.
  • Long forms have a logical tab order and clear section headings.
  • Use native <dialog> (or <kit-dialog>).
  • Open with showModal() — never show() for confirmations or disruptive flows.
  • Have a clear close affordance (Escape, an explicit Cancel button, optional click-outside).
  • Don’t trap focus on the page with a custom mechanism — let the platform do it.
  • Toasts use <kit-toast-region> (which sets role="region" aria-live="polite" automatically).
  • Errors use tone="error" so the toast renders with role="alert".
  • Toasts auto-dismiss reasonably (4–6 seconds for short, longer for important).
  • Critical errors don’t auto-dismiss; require user action.
  • After SPA route changes, focus moves to the new page’s heading or an id="main" skip target.
  • Skip-to-main-content link present on every page.
  • Page titles update on navigation so screen readers announce the new page.
  • Test with a keyboard only (no mouse) and verify every interactive element is reachable and operable.
  • Test with a screen reader (VoiceOver on macOS/iOS, NVDA on Windows, TalkBack on Android).
  • Test in high-contrast mode (Windows Forced Colors / macOS Increase Contrast).
  • Test with reduced motion turned on.
  • Run automated audits (axe-core, Lighthouse) but don’t rely on them — manual testing finds the rest.
ElementNative primitiveNotes
<kit-button><button>Inherits all native button semantics.
<kit-dialog><dialog>Use showModal() for confirms; supports closeEvent attribute for emitting on close.
<kit-field><input>/<textarea>/<select>Wires aria-describedby for description and error. Slot the real control.
<kit-toast-region>div with role="region" aria-live="polite"Errors automatically use role="alert".
<kit-card interactive>div with role="button" (your responsibility)Add tabindex="0", role, and keyboard handlers — or wrap content in a real <button>.