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.
Components
Section titled “Components”- 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") handleEnterandSpacekeyboard activation explicitly and havetabindex="0". - No interactive
<div>s. If a<div>is interactive, it should be a<button>or have aroleplus full keyboard handling. - Every form control has a label — via
<label for>,<label>wrapping,aria-label, oraria-labelledby. Visible labels are preferred. - Every error is announced to assistive technology, with
role="alert"for first-class errors andaria-describedbylinking 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>’sshowModal()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).
Keyboard
Section titled “Keyboard”- All interactive elements are reachable via Tab in a logical order.
- Custom widgets follow ARIA APG patterns (combobox, listbox, menu, tabs, etc.) when applicable.
-
Escapecloses dialogs, popovers, and dropdowns. (Native primitives do this for free.) - Cmd/Ctrl shortcut keys are documented and don’t conflict with screen reader shortcuts.
Screen readers
Section titled “Screen readers”-
aria-liveregions announce state changes that the user should know about (toasts, status messages). -
aria-live="polite"for low-priority updates;aria-live="assertive"(orrole="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>.
Color and contrast
Section titled “Color and contrast”- 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 darkis set on the root so form controls and scrollbars adapt.
Motion
Section titled “Motion”-
prefers-reduced-motionis 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
requiredattribute. - 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.
Modals and dialogs
Section titled “Modals and dialogs”- Use native
<dialog>(or<kit-dialog>). - Open with
showModal()— nevershow()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.
Live regions and notifications
Section titled “Live regions and notifications”- Toasts use
<kit-toast-region>(which setsrole="region" aria-live="polite"automatically). - Errors use
tone="error"so the toast renders withrole="alert". - Toasts auto-dismiss reasonably (4–6 seconds for short, longer for important).
- Critical errors don’t auto-dismiss; require user action.
Boundary and routing
Section titled “Boundary and routing”- 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.
Testing
Section titled “Testing”- 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.
Per-element notes
Section titled “Per-element notes”| Element | Native primitive | Notes |
|---|---|---|
<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>. |