@access-kit/react
v0.2.0
Published
React accessibility toolkit with provider, devtools, and user widget.
Maintainers
Readme
@access-kit/react
React accessibility toolkit: provider for user preferences, accessibility widget, and DevTools for in-browser audits.
Install
npm install @access-kit/react
# or
yarn add @access-kit/react
# or
pnpm add @access-kit/reactPeer dependencies: react >= 18 and react-dom >= 18.
Quick start
- Wrap your app with
AccessKitProvider. - (Optional) Add
AccessibilityWidgetso users can adjust motion, contrast, focus, text spacing, font size, dyslexia-friendly font, and color vision mode. - (Optional) In development, add
AccessKitDevToolsto run accessibility audits and view findings.
// Next.js App Router example — adapt the router hook for your framework.
import { usePathname } from "next/navigation"
import { AccessKitProvider, AccessibilityWidget, AccessKitDevTools } from "@access-kit/react"
export default function RootLayout({ children }) {
const pathname = usePathname()
return (
<html lang="en">
<body>
<AccessKitProvider>
{children}
<AccessibilityWidget style={{ primary: "#0ea5e9" }} />
{process.env.NODE_ENV === "development" && (
<AccessKitDevTools routeKey={pathname} />
)}
</AccessKitProvider>
</body>
</html>
)
}AccessKitProvider
Stores user preferences (reduced motion, high contrast, enhanced focus, text spacing, font size, dyslexia-friendly font, color vision mode) and applies them via data-accesskit-* attributes and global styles on <html>.
| Prop | Type | Description |
|------|------|-------------|
| storageKey | string | Key for persisting settings in localStorage. Default: "accesskit:settings". |
| defaultSettings | Partial<AccessKitSettings> | Initial settings. |
| persist | boolean | Whether to persist to localStorage. Default: true. |
| onPersistError | (error: unknown) => void | Called when writing settings to localStorage fails (e.g. quota exceeded, private browsing). |
| focusColor | string | CSS color for focus ring when "Focus indicators" is on. |
AccessibilityWidget
Floating button that opens a panel where users can toggle reduced motion, high contrast, focus indicators, dyslexia-friendly font, font size, letter spacing, word spacing, line height, and color vision mode (protanopia, deuteranopia, tritanopia). Place it once inside AccessKitProvider (e.g. in a layout or root component).
| Prop | Type | Description |
|------|------|-------------|
| style | AccessibilityWidgetStyle | Optional theme overrides (primary, tabBackground, tabIcon, panelBackground, panelText, border, mutedText, icon). |
| initialPosition | AccessKitPosition | Position of the floating tab. Default: "center-right". |
| defaultOpen | boolean | Whether the panel is open on first render. Default: false. |
| zIndex | number | z-index of the widget. Default: 2147483643. |
AccessKitDevTools
Runs accessibility audits on the page and shows results in a floating panel with three tabs: Issues, Keyboard, and Media. Render it whenever you need it (e.g. in development or staging); use a conditional in your app if you want it tree-shaken from production builds.
Issues tab
Scans the live DOM using axe-core plus built-in AccessKit custom rules for WCAG 2.0, 2.1, and 2.2 violations. Features:
- Filters: severity (Error / Warning), WCAG level (A / AA / AAA / Other), category (Color, Forms, ARIA, Keyboard, etc.), and free-text search.
- Element highlighting: hovering a finding highlights the element on the page.
- Fix with AI: generates a prompt with the finding details and page context for use with an AI assistant.
- Scan phases: the status bar shows the current phase (DOM → Contrast → Custom).
- Stale detection: a MutationObserver watches the DOM after each scan and shows a "Stale" badge when the page has changed.
Default rules
By default, the Issues tab runs only WCAG 2.0, 2.1, and 2.2 Level A, AA, and AAA rules. All other rule sets are opt-in via additionalRules.
Opt-in rules: additionalRules
To run extra rule tags, pass additionalRules with an array of AccessKitAdditionalRuleTag values. This gives you autocomplete and type safety.
import { AccessKitDevTools, type AccessKitAdditionalRuleTag } from "@access-kit/react"
<AccessKitDevTools
routeKey={pathname}
additionalRules={["best-practice", "cat.color", "section508"]}
/>Standards & regulations
| Tag | Description |
|-----|-------------|
| "best-practice" | Industry best-practice recommendations. |
| "section508" | US Section 508 rules. |
| "ACT" | W3C Accessibility Conformance Testing (ACT) rules. |
| "EN-301-549" | European EN 301 549. |
| "TTv5" | Trusted Tester v5 (Section 508). |
| "RGAAv4" | French RGAA v4. |
| "experimental" | Experimental rules (may change). |
Category tags (by topic)
| Tag | Description |
|-----|-------------|
| "cat.color" | Color and contrast. |
| "cat.forms" | Forms and form controls. |
| "cat.aria" | ARIA roles and attributes. |
| "cat.keyboard" | Keyboard and focus. |
| "cat.name-role-value" | Accessible name, role, value. |
| "cat.structure" | Headings, landmarks, lists. |
| "cat.tables" | Tables. |
| "cat.text-alternatives" | Text alternatives. |
| "cat.sensory-and-visual" | Presentation and layout. |
| "cat.time-and-media" | Time and media. |
| "cat.language" | Language. |
| "cat.parsing" | Valid markup. |
| "cat.other" | Other. |
Example: run WCAG + best-practice + form-related rules:
<AccessKitDevTools routeKey={pathname} additionalRules={["best-practice", "cat.forms"]} />additionalRules only affects the Issues tab (axe-core and custom rule scans). The Keyboard and Media tabs run their own dedicated scanners.
Keyboard tab
Scans all focusable elements on the page and reports:
- Focus visibility: whether each element has a visible
:focus/:focus-visibleindicator or if the outline is suppressed. - Keyboard traps: elements inside containers with no visible escape (e.g. a dialog without a close button).
- Positive tabindex: elements with
tabindex > 0, which disrupts natural tab order. - Focus order walk: step through elements in tab order to verify the sequence makes sense.
Media tab
Scans <video>, <audio>, and embedded media iframes on the page, plus ARIA live regions:
- Captions & descriptions: checks for
<track kind="captions">and<track kind="descriptions">. - Autoplay & controls: flags media that autoplays without user controls.
- Iframe embeds: checks for missing
titleattributes and warns that captions can't be verified from the host page. - Live regions: lists elements with
role="alert",role="status", oraria-live, and flags mismatches (e.g.role="alert"witharia-live="polite").
DevTools props
| Prop | Type | Description |
|------|------|-------------|
| routeKey | string | Required. Current pathname from your router (e.g. Next.js usePathname(), React Router useLocation().pathname). Resets findings when the route changes. |
| defaultOpen | boolean | Whether the panel is open on first render. Default: false. |
| position | AccessKitPosition | Position of the tab (e.g. "bottom-left"). Default: "bottom-left". |
| zIndex | number | z-index of the panel. Default: 2147483641. |
| additionalRules | AccessKitAdditionalRuleTag[] | Opt-in rule tags (see above). |
useAccessKit
Hook to read and update settings from any component under AccessKitProvider:
import { useAccessKit } from "@access-kit/react"
function MyComponent() {
const {
settings,
setSetting,
toggleSetting,
setFontSize,
setLetterSpacing,
setWordSpacing,
setLineHeight,
resetSettings,
} = useAccessKit()
return (
<button onClick={() => toggleSetting("reducedMotion")}>
Reduced motion: {settings.reducedMotion ? "on" : "off"}
</button>
)
}Custom rules
AccessKit includes ~30 built-in custom rules that catch WCAG violations not covered by axe-core (e.g. meaningless alt text, missing skip links, non-descriptive link text). These run automatically as part of every audit.
You can also register your own rules:
import { registerRules, getRegisteredRules, type AccessKitRule } from "@access-kit/react"
const myRule: AccessKitRule = {
id: "my-app-heading-order",
wcagCriteria: "1.3.1",
wcagLevel: "A",
category: "structure",
description: "Headings must not skip levels",
helpUrl: "https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships",
run(doc) {
const findings = []
// ... your detection logic ...
return findings
},
}
registerRules(myRule)
// Inspect all registered rules (built-in + custom)
console.log(getRegisteredRules())Custom rules are executed in the third audit phase ("custom") after axe-core scans. Each rule's run function receives the Document and returns an array of AccessKitRuleFinding objects. The engine fills in the remaining AccessKitAuditFinding fields (id, ruleId, wcagLevel, category, source, helpUrl) from the rule definition.
Programmatic audits
You can run an audit without the DevTools UI. The result is an array of findings you can assert on, log, or send to CI.
import { runAccessibilityAudit, formatAuditFindingsForConsole, type RunAccessibilityAuditOptions } from "@access-kit/react"
const findings = await runAccessibilityAudit(document, {
additionalTags: ["best-practice", "cat.forms"],
logToConsole: true, // optional: print a readable summary to the console
})
// Or format findings yourself (e.g. for CI logs or custom reporting)
console.log(formatAuditFindingsForConsole(findings))additionalTags uses the same AccessKitAdditionalRuleTag values as AccessKitDevTools's additionalRules.
Every audit runs in three phases:
- DOM — full axe-core rule scan at the top of the page.
- Contrast — a viewport sweep that scrolls the document in viewport-sized steps, running only viewport-sensitive rules (color-contrast, link-in-text-block) at each position, then merges deduped findings and restores scroll.
- Custom — runs all registered AccessKit custom rules against the document.
Audit options
| Option | Type | Description |
|--------|------|-------------|
| additionalTags | AccessKitAdditionalRuleTag[] | Opt-in rule tags to run alongside default WCAG A/AA/AAA. |
| logToConsole | boolean | Print a readable summary to the console. |
| onPhase | (phase: AuditScanPhase) => void | Called when the audit transitions between scan phases ("dom" → "contrast" → "custom"). |
| includeAllCategories | boolean | When true, includes findings from all categories. By default, keyboard and time-and-media findings from custom rules are filtered out (those categories have dedicated DevTools tabs). |
When to use it
- Tests: After rendering a component or page, run an audit and assert on
findings(e.g. expect no errors, or expect a specific rule to be reported). - Browser scripts: Call from a button click or effect to audit the current page and show results in the UI or console (
logToConsole: true). - CI / headless: In a Node script or test runner with a DOM (e.g. jsdom, Puppeteer), run the audit and fail the build or pipeline if critical findings exist.
- Custom tooling: Use the returned array to build your own report format, integrate with issue trackers, or drive automated fixes.
Verifying color vision modes
If you are not color blind, you can still check that the protanopia / deuteranopia / tritanopia options behave correctly:
Compare with a reference
Open a color-blindness simulator or a test image (e.g. an Ishihara-style plate) in another tab. Enable the same mode (e.g. protanopia) in the widget and in the simulator; the page and the reference should look similar (same colors shifted in the same way).Visual change
When you switch from "None" to any mode, the whole page should change color (e.g. reds and greens shift for protanopia/deuteranopia, blues and yellows for tritanopia). If nothing changes, the filter is not applied.High contrast + color vision
Turn on "High contrast" and a color vision mode together; both effects should be visible (brighter contrast and CVD simulation).Automated / screenshots
Take a screenshot with "None" and with e.g. "Protanopia", then compare pixel colors or use an image-diff tool to confirm the filter alters the image.
The filters are based on Viénot 1999 (protanopia, deuteranopia) and Brettel 1997 (tritanopia) and are injected as SVG feColorMatrix filters.
Types
Data types
AccessKitSettings– user preference state: reduced motion, high contrast, enhanced focus, letter spacing (0–100), word spacing (0–100), line height (0–100), fontSize, dyslexia font, color vision mode.AccessKitSettingKey–keyof AccessKitSettings— union of all setting names.AccessKitContextValue– the shape returned byuseAccessKit(): settings object and mutation helpers (setSetting,toggleSetting,setFontSize,setLetterSpacing,setWordSpacing,setLineHeight,resetSettings).AccessKitAuditFinding– a single audit result: id, ruleId, axeIssueKind?, wcagLevel?, category?, severity, message, suggestion, helpUrl?, selector, standards?, source?.AccessKitAdditionalRuleTag– union of all opt-in rule tags foradditionalRules/additionalTags.AccessKitSeverity–"error" | "warning" | "pass".AccessKitWCAGLevel–"A" | "AA" | "AAA".AccessKitPosition–"bottom-right" | "center-right" | "top-right" | "bottom-left" | "center-left" | "top-left".AccessibilityWidgetStyle– optional style config for the widget: primary, tabBackground, tabIcon, panelBackground, panelText, border, mutedText, icon.RunAccessibilityAuditOptions– options forrunAccessibilityAudit(): additionalTags, logToConsole, onPhase, includeAllCategories.AuditScanPhase–"dom" | "contrast" | "custom".AccessKitFindingSource–"axe-core" | "accesskit".
Custom rule types
AccessKitRule– a custom rule definition: id, wcagCriteria, wcagLevel, category, description, helpUrl?, run.AccessKitRuleFinding– the subset of finding data a custom rule returns: selector, message, suggestion, severity.
Component prop types
AccessKitProviderProps– props forAccessKitProvider: children, defaultSettings, storageKey, persist, focusColor, onPersistError.AccessKitDevToolsProps– props forAccessKitDevTools: routeKey (required), defaultOpen, position, zIndex, additionalRules.AccessibilityWidgetProps– props forAccessibilityWidget: initialPosition, style, defaultOpen, zIndex.
License
MIT
