@afixt/apg-react
v1.2.0
Published
Accessible React components implementing every pattern in the WAI-ARIA Authoring Practices Guide (APG)
Downloads
384
Readme
apg-react
Accessible React components implementing every pattern in the W3C ARIA Authoring Practices Guide (APG).
Each component ships with the full APG keyboard-interaction model, correct ARIA roles and state, focus management, and a Bootstrap-flavored visual default you can restyle via CSS custom properties. Written in TypeScript with full type declarations. All user-facing strings are translatable via an optional labels prop.
Why
Most component libraries treat accessibility as a checklist. This library treats the APG as the specification. Every component is tested against the APG's keyboard model, ARIA contract, and focus-management requirements — with 295 unit tests, 37 dedicated accessibility-contract tests, and E2E tests driving a real browser. Every assertion is implemented from first principles against the DOM.
Install
npm install @afixt/apg-reactPeer dependencies:
react≥ 18react-dom≥ 18react-router-dom≥ 6 (optional — only required if you use<Link>or<Breadcrumb>)
Quick start
import { Button, Accordion, ModalDialog } from '@afixt/apg-react';
import '@afixt/@afixt/apg-react/styles.css'; // full baseline styles
// or, to cherry-pick tokens only:
// import '@afixt/@afixt/apg-react/variables.css';
function App() {
return (
<Button label="Save" action={() => save()} />
);
}Components are tree-shakeable; only what you import will land in your bundle.
Components
Components are organized by the APG pattern they implement. Follow each link for the official APG documentation.
Widgets
| Component | APG pattern |
| --- | --- |
| Accordion | Accordion |
| Alert | Alert |
| AlertDialog | Alert Dialog |
| Breadcrumb | Breadcrumb |
| Button | Button |
| Carousel | Carousel |
| Checkbox | Checkbox (dual & tri-state) |
| CheckboxGroup | Checkbox — parent/child mixed state |
| Combobox | Combobox — supports none, list, both |
| Disclosure | Disclosure |
| Feed | Feed |
| Grid | Grid |
| Link | Link pattern (requires react-router-dom) |
| Listbox | Listbox — single & multi-select |
| MenuButton | Menu Button |
| Menubar | Menu / Menubar |
| Meter | role=meter |
| ModalDialog | Dialog (Modal) |
| Progressbar | role=progressbar |
| RadioGroup | Radio Group |
| Slider | Slider |
| SliderMultiThumb | Slider (Multi-Thumb) |
| Spinbutton | Spinbutton |
| Switch | Switch |
| Tabs | Tabs — automatic or manual activation, horizontal or vertical |
| Textbox | role=textbox — single- and multi-line |
| Toolbar | Toolbar |
| Tooltip | Tooltip |
| TreeGrid | Tree Grid |
| TreeView | Tree View |
Structural
| Component | Purpose |
| --- | --- |
| Article | Semantic <article> with heading + posinset/setsize for use inside Feed. |
Keyboard reference
Every component implements the full keyboard model specified by its APG pattern. Highlights:
- Roving tabindex where the pattern calls for it:
RadioGroup,Toolbar,Tabs,Grid,TreeView,TreeGrid,Menubar,Listbox. aria-activedescendantfor virtual focus:Combobox.- Focus return on dialog dismiss:
ModalDialog,AlertDialog,MenuButton,Menubar. - Escape closes popups:
ModalDialog,AlertDialog,MenuButton,Menubar,Combobox,Tooltip.
Explore the live Storybook demo or run npm run storybook locally to see every keyboard path with step-by-step play interactions.
Styling
The package ships two CSS files:
@afixt/apg-react/styles.css— full baseline styles for every component.@afixt/apg-react/variables.css— only the design tokens, if you want to write your own styles.
All visual choices are driven by CSS custom properties defined in variables.css. Override them at :root or at a container scope:
:root {
--apg-color-primary: #7c3aed;
--apg-radius-md: 0.25rem;
--apg-font-family: "Inter", system-ui, sans-serif;
}Key token groups: colors (--apg-color-*), spacing (--apg-space-*), radii (--apg-radius-*), typography (--apg-font-*), focus ring (--apg-focus-ring-*), shadows (--apg-shadow-*), z-index (--apg-z-*).
Internationalization (i18n)
Components that render hardcoded user-facing strings (aria-labels, button text) accept an optional labels prop — an object whose keys map to English defaults. Pass your own translations without forking:
<Alert message="Saved" type="info" labels={{ dismiss: "Fermer" }} />
<Carousel slides={slides} labels={{ previousSlide: "Anterior", nextSlide: "Siguiente" }} />
<ModalDialog isOpen onClose={close} labels={{ closeDialog: "Cerrar" }}>…</ModalDialog>
<Spinbutton min={0} max={10} labels={{ increaseValue: "Erhöhen", decreaseValue: "Verringern" }} />
<Breadcrumb items={items} navLabel="Fil d'Ariane" />When labels is omitted, the English defaults are used. Only the keys you override are affected; the rest keep their defaults.
Implementer responsibilities
A handful of concerns must be satisfied by you — they cannot be handled at the library level:
- Visible focus indicators. The default focus ring uses a soft box-shadow; ensure your brand palette preserves ≥3:1 contrast against the background.
- Color contrast. If you override tokens, verify WCAG 1.4.3 (4.5:1 for text) and 1.4.11 (3:1 for UI).
- External labels. Where a component exposes
ariaLabelledby, make sure the referenced element exists and carries a meaningful name. - Focus restoration on unmount. Dialog components return focus to the invoking element when dismissed, but if you unmount them imperatively, re-establish focus yourself.
- Live regions for dynamic content. Components that produce their own
role=alert/role=statusfire announcements; adjacent dynamic content you write still needs its own live region.
Testing
npm test # unit + a11y contract suite (jsdom)
npm run test:e2e:build # builds Storybook, runs Puppeteer E2E tests
npm run test:all # both- 295 unit tests across 32 suites; 92%+ statement coverage.
- 37 accessibility-contract tests built on a hand-rolled ARIA-aware DOM assertion library — no external a11y libraries of any kind.
- E2E tests drive a real Chromium against a built Storybook: accessible-name presence,
aria-*id resolution, ARIA boolean grammar, Tab reachability.
Development
npm install
npm run storybook # http://localhost:6006
npm test
npm run build # produces dist/Contributing
Issues and PRs are welcome. See CONTRIBUTING.md.
Versioning
This library follows Semantic Versioning. Breaking changes land in major releases; new components and opt-in features in minor; fixes in patch. See CHANGELOG.md.
License
MIT © AFixt, Inc. — see LICENSE.
