aria-ease 7.5.0 → 7.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +66 -81
  2. package/dist/ToggleComponentStrategy-TMRNXFSL.js +1 -0
  3. package/dist/cli.cjs +63 -63
  4. package/dist/cli.js +1 -1
  5. package/dist/contractTestRunnerPlaywright-CHCVW7VO.js +46 -0
  6. package/dist/contractTestRunnerPlaywright-FECB4HSX.js +46 -0
  7. package/dist/index.cjs +36 -36
  8. package/dist/index.d.cts +18 -19
  9. package/dist/index.d.ts +18 -19
  10. package/dist/index.js +10 -10
  11. package/dist/src/{Types.d-D96FYkCN.d.cts → Types.d-BjBTlIzl.d.cts} +18 -8
  12. package/dist/src/{Types.d-D96FYkCN.d.ts → Types.d-BjBTlIzl.d.ts} +18 -8
  13. package/dist/src/accordion/index.cjs +1 -1
  14. package/dist/src/accordion/index.d.cts +1 -1
  15. package/dist/src/accordion/index.d.ts +1 -1
  16. package/dist/src/accordion/index.js +1 -1
  17. package/dist/src/block/index.d.cts +1 -1
  18. package/dist/src/block/index.d.ts +1 -1
  19. package/dist/src/checkbox/index.cjs +1 -1
  20. package/dist/src/checkbox/index.d.cts +1 -1
  21. package/dist/src/checkbox/index.d.ts +1 -1
  22. package/dist/src/checkbox/index.js +1 -1
  23. package/dist/src/combobox/index.cjs +1 -1
  24. package/dist/src/combobox/index.d.cts +1 -1
  25. package/dist/src/combobox/index.d.ts +1 -1
  26. package/dist/src/combobox/index.js +1 -1
  27. package/dist/src/menu/index.cjs +1 -1
  28. package/dist/src/menu/index.d.cts +1 -1
  29. package/dist/src/menu/index.d.ts +1 -1
  30. package/dist/src/menu/index.js +1 -1
  31. package/dist/src/radio/index.cjs +1 -1
  32. package/dist/src/radio/index.d.cts +1 -1
  33. package/dist/src/radio/index.d.ts +1 -1
  34. package/dist/src/radio/index.js +1 -1
  35. package/dist/src/tabs/index.cjs +1 -1
  36. package/dist/src/tabs/index.d.cts +1 -2
  37. package/dist/src/tabs/index.d.ts +1 -2
  38. package/dist/src/tabs/index.js +1 -1
  39. package/dist/src/toggle/index.cjs +1 -1
  40. package/dist/src/toggle/index.d.cts +2 -7
  41. package/dist/src/toggle/index.d.ts +2 -7
  42. package/dist/src/toggle/index.js +1 -1
  43. package/dist/src/utils/test/ToggleComponentStrategy-UOGYK2U4.js +1 -0
  44. package/dist/src/utils/test/contractTestRunnerPlaywright-7ERFIHCM.js +46 -0
  45. package/dist/src/utils/test/dsl/index.cjs +1 -1
  46. package/dist/src/utils/test/dsl/index.d.cts +0 -5
  47. package/dist/src/utils/test/dsl/index.d.ts +0 -5
  48. package/dist/src/utils/test/dsl/index.js +1 -1
  49. package/dist/src/utils/test/index.cjs +29 -29
  50. package/dist/src/utils/test/index.d.cts +4 -3
  51. package/dist/src/utils/test/index.d.ts +4 -3
  52. package/dist/src/utils/test/index.js +1 -1
  53. package/dist/{test-FURQN5KO.js → test-CMD6E5YF.js} +1 -1
  54. package/package.json +1 -1
  55. package/dist/contractTestRunnerPlaywright-75NI6SN7.js +0 -46
  56. package/dist/contractTestRunnerPlaywright-VLOD5IB3.js +0 -46
  57. package/dist/src/utils/test/contractTestRunnerPlaywright-FSZDW7IR.js +0 -46
package/README.md CHANGED
@@ -13,15 +13,12 @@ Stop treating accessibility as an afterthought. Aria-Ease engineers accessibilit
13
13
 
14
14
  Aria-Ease isn't a utility library. **It's an accessibility infrastructure** that integrates into every phase of your frontend engineering lifecycle:
15
15
 
16
- | Phase | Feature | Status | Impact |
17
- | ------------------ | --------------------------------------------- | ------------ | --------------------------------------------- |
18
- | **🔧 Development** | Component utilities for accessible patterns | ✅ Available | Build it right from the start |
19
- | **⚡ Linting** | ESLint rules to enforce accessible coding | 🚧 Roadmap | Catch mistakes as you type |
20
- | **🔍 Pre-Deploy** | Axe-core powered static accessibility audit | Available | Verify before it ships |
21
- | **🧪 Testing** | WAI-ARIA APG contract testing with Playwright | Available | Fast, determinic component accessibility test |
22
- | **🚀 CI/CD** | Accessibility as deployment gatekeeper | ✅ Available | Block inaccessible code from production |
23
- | **📊 Production** | Real user signal monitoring and replay | 🚧 Roadmap | Understand how users actually interact |
24
- | **📈 Insights** | Dashboard for reporting and analytics | 🚧 Roadmap | Visualize accessibility health |
16
+ | Phase | Feature | Impact |
17
+ | ------------------ | --------------------------------------------- | --------------------------------------------- |
18
+ | **🔧 Development** | Component utilities for accessible patterns | Build it right from the start |
19
+ | **🔍 Pre-Deploy** | Axe-core powered static accessibility audit | Verify before it ships |
20
+ | **🧪 Testing** | WAI-ARIA APG contract testing with Playwright | Fast, determinic component accessibility test |
21
+ | **🚀 CI/CD** | Accessibility as deployment gatekeeper | Block inaccessible code from production |
25
22
 
26
23
  ---
27
24
 
@@ -31,16 +28,16 @@ Aria-Ease isn't a utility library. **It's an accessibility infrastructure** that
31
28
 
32
29
  **Traditional approach:** Build features → Manual testing → Find accessibility issues → Fix them → Manual testing again → Ship (maybe)
33
30
 
34
- **Aria-Ease approach:** Build with accessible baseline utilities → Automated audits catch issues → Contract tests verify consistent baseline component behaviors → CI/CD gates deployment → Ship with confidence
31
+ **Aria-Ease approach:** Build with accessible baseline utilities → Automated audits catch issues → Contract tests verify consistent component behaviors → CI/CD gates deployment → Ship with confidence
35
32
 
36
33
  ### What Makes This Different?
37
34
 
38
- #### 1. **Component Utilities** (Available Now)
35
+ #### 1. **Component Utilities**
39
36
 
40
37
  Reusable accessible interaction patterns based on Aria-Ease's baseline interpretation of WAI-ARIA APG guidance. Not the only valid implementation, but a proven, consistent place to start. Tree-shakable, framework-agnostic, production-ready.
41
38
 
42
39
  ```javascript
43
- // Instead of 50+ lines managing ARIA attributes and keyboard events...
40
+ // Instead of 50+ lines managing ARIA accessibility states and interactions...
44
41
  import { makeMenuAccessible } from "aria-ease/menu";
45
42
 
46
43
  const menu = makeMenuAccessible({
@@ -50,19 +47,19 @@ const menu = makeMenuAccessible({
50
47
  }); // Arrow keys, Escape, focus management — all handled
51
48
  ```
52
49
 
53
- #### 2. **Static Audit** (Available Now)
50
+ #### 2. **Static Audit**
54
51
 
55
- Axe-core powered CLI that scans your entire site and generates comprehensive reports. Run it locally or in CI/CD.
52
+ Axe-core powered CLI that scans your entire site and generates multi-format reports in HTML, CSV or JSON. Run it locally or in CI/CD.
56
53
 
57
54
  ```bash
58
55
  npx aria-ease audit --url https://yoursite.com
59
56
  ```
60
57
 
61
- #### 3. **Contract Testing** (Available Now)
58
+ #### 3. **Contract Testing**
62
59
 
63
60
  This is the game-changer. Encode a deterministic, testable interpretation of WAI-ARIA APG guidance into JSON "contracts" using Aria-Ease DSL API, and validate your contract against your component using Aria-Ease's Playwright runner with isolated test-harness architecture. Run it locally or in CI/CD.
64
61
 
65
- Teams and experts can enforce their own standards and maintain reusability and consistency.
62
+ Teams and experts can enforce their own standards, maintain reusability and consistency, and prevent regression.
66
63
 
67
64
  **The result?** Component interaction testing that feels closer to unit testing than manual QA.
68
65
 
@@ -72,9 +69,9 @@ npx aria-ease test
72
69
  # ✓ 26 assertions in ~1 second in CI
73
70
  ```
74
71
 
75
- **Why this matters:** Before, verifying a combobox meant testing every interaction manually. Now, Aria-Ease automates the repeatable, deterministic aspects of testing a combobox: keyboard interaction, ARIA state updates, visibility, and semantic roles.
72
+ **Why this matters:** Before, testing for regression in combobox meant testing every interaction manually. Now, Aria-Ease automates the repeatable, deterministic aspects of testing a combobox: keyboard interaction, ARIA state updates, visibility, and semantic attributes.
76
73
 
77
- #### 4. **CI/CD Integration** (Available Now)
74
+ #### 4. **CI/CD Integration**
78
75
 
79
76
  Turn accessibility into a deployment invariant. Add audit and test commands to your pipeline — if they fail, deployment is blocked.
80
77
 
@@ -90,22 +87,10 @@ jobs:
90
87
  # Only deploy if both pass ☝️
91
88
  ```
92
89
 
93
- Real example from our docs site: Push to branch → Accessibility checks run → Green check mark → Deploys to Firebase. Red X → Deploy blocked.
90
+ Real example from our docs site: Push to branch → Accessibility checks run → Green check mark → Deploys to Prod. Red X → Deploy blocked.
94
91
 
95
92
  **No one has any excuse to ship inaccessible code anymore.**
96
93
 
97
- #### 5. **Linting** (Roadmap)
98
-
99
- ESLint rules that enforce accessible coding patterns as you type. Catch issues before they compile.
100
-
101
- #### 6. **Production Monitoring** (Roadmap)
102
-
103
- Real user signal monitoring, interaction replay, and analytics. Understand how assistive technology users actually experience your app.
104
-
105
- #### 7. **Insights Dashboard** (Roadmap)
106
-
107
- Visualize accessibility health across your entire application. Track progress, identify patterns, generate reports.
108
-
109
94
  ---
110
95
 
111
96
  ## ✨ Features
@@ -113,9 +98,9 @@ Visualize accessibility health across your entire application. Track progress, i
113
98
  - 🎯 **Tree-shakable** - Import only what you need (1.4KB - 3.7KB per component)
114
99
  - ♿ **WCAG Compliant** - Follows WAI-ARIA best practices
115
100
  - ⌨️ **Keyboard Interaction** - Full keyboard support out of the box
116
- - 🧪 **Contract Testing** - Built-in baseline accessibility testing framework
101
+ - 🧪 **Contract Testing** - Built-in accessibility interaction testing framework
117
102
  - 🎭 **Framework Agnostic** - Works with React, Vue, vanilla JS, etc.
118
- - 🔍 **CLI Audit Tool** - Automated accessibility testing for your sites
103
+ - 🔍 **CLI Audit Tool** - Automated static accessibility testing for your sites
119
104
  - 📦 **TypeScript Support** - Full type definitions included
120
105
 
121
106
  ## 📦 Installation
@@ -156,9 +141,7 @@ export default {
156
141
  out: "./accessibility-reports",
157
142
  },
158
143
  },
159
- test: {
160
- strictness: "balanced", // 'minimal' | 'balanced' | 'strict' | 'paranoid'
161
- },
144
+ test: {...},
162
145
  };
163
146
  ```
164
147
 
@@ -180,7 +163,7 @@ The CLI will automatically find and load your config file, with validation to ca
180
163
 
181
164
  ### Contract DSL Build Workflow
182
165
 
183
- You can author custom component contracts as readable DSL files and compile them with a built-in CLI command.
166
+ You can author custom component interaction contracts as readable DSL files and compile them with a built-in CLI command.
184
167
 
185
168
  1. Create one or more `*.contract.mjs` files that export a contract built with `contract("component.name", ...)`.
186
169
  2. Configure contract sources in `ariaease.config.js`.
@@ -191,16 +174,7 @@ Example config with multiple contract sources:
191
174
 
192
175
  ```javascript
193
176
  export default {
194
- test: {
195
- strictness: "balanced",
196
- components: [
197
- {
198
- name: "combobox",
199
- path: "./tests/external-contracts/combobox.listbox.contract.json",
200
- strategyPath: "./tests/external-strategies/CustomComboboxStrategy.js",
201
- },
202
- ],
203
- },
177
+ test: {...},
204
178
  contracts: [
205
179
  {
206
180
  src: "./tests/external-contracts/**/*.contract.mjs",
@@ -296,6 +270,11 @@ useEffect(() => {
296
270
  menuId: "menu-div",
297
271
  menuItemsClass: "profile-menu-items",
298
272
  triggerId: "display-button",
273
+ callback: {
274
+ onExpandedChange: (expanded) => {
275
+ // Side effect
276
+ },
277
+ },
299
278
  });
300
279
 
301
280
  return () => menuRef.current.cleanup(); // Clean up on unmount
@@ -311,6 +290,11 @@ const menu = Menu.makeMenuAccessible({
311
290
  menuId: "dropdown-menu",
312
291
  menuItemsClass: "menu-item",
313
292
  triggerId: "menu-button",
293
+ callback: {
294
+ onExpandedChange: (expanded) => {
295
+ // Side effect
296
+ },
297
+ },
314
298
  });
315
299
 
316
300
  // Programmatically control
@@ -358,6 +342,12 @@ useEffect(() => {
358
342
  comboboxInputId: "search-input",
359
343
  listBoxId: "suggestions-list",
360
344
  listBoxItemsClass: "suggestion-item",
345
+ callback: {
346
+ onSelect: (option) => {...} //when a listbox option is selected
347
+ onExpandedChange: (expanded) => {...} //when a combobox listbox opens or closes
348
+ onActiveDescendantChange: (optId, item) => {...} //when combobox aria-activedescendant changes
349
+ onClear: () => {...} //when combobox input (if present) is cleared by Escape key
350
+ }
361
351
  });
362
352
 
363
353
  return () => {
@@ -378,15 +368,11 @@ const combobox = Combobox.makeComboboxAccessible({
378
368
  listBoxId: "fruits-listbox",
379
369
  listBoxItemsClass: "list-option",
380
370
  callback: {
381
- onSelect: (option) => {
382
- input.value = option.textContent;
383
- // Show all options after selection
384
- options.forEach((opt) => (opt.hidden = false));
385
- },
386
- onOpenChange: (isOpen) => {
387
- console.log("Listbox is", isOpen ? "open" : "closed");
388
- },
389
- },
371
+ onSelect: (option) => {...} //when a listbox option is selected
372
+ onExpandedChange: (expanded) => {...} //when a combobox listbox opens or closes
373
+ onActiveDescendantChange: (optId, item) => {...} //when combobox aria-activedescendant changes
374
+ onClear: () => {...} //when combobox input (if present) is cleared by Escape key
375
+ }
390
376
  });
391
377
 
392
378
  // Programmatically control
@@ -444,6 +430,9 @@ useEffect(() => {
444
430
  triggersClass: "accordion-trigger",
445
431
  panelsClass: "accordion-panel",
446
432
  allowMultipleOpen: false, // Only one panel open at a time (default)
433
+ callback: {
434
+ onExpandedChange: (index, expanded) => {...}
435
+ }
447
436
  });
448
437
 
449
438
  return () => accordion.cleanup();
@@ -508,6 +497,9 @@ useEffect(() => {
508
497
  const checkboxGroup = makeCheckboxAccessible({
509
498
  checkboxGroupId: "checkbox-group",
510
499
  checkboxesClass: "custom-checkbox",
500
+ callback: {
501
+ onCheckedChange: (index, checked) => {...}
502
+ }
511
503
  });
512
504
 
513
505
  return () => checkboxGroup.cleanup();
@@ -566,6 +558,9 @@ useEffect(() => {
566
558
  radioGroupId: "radio-group",
567
559
  radiosClass: "custom-radio",
568
560
  defaultSelectedIndex: 0, // Initially selected (optional)
561
+ callback: {
562
+ onValueChange: (index, value) => {...}
563
+ }
569
564
  });
570
565
 
571
566
  return () => radioGroup.cleanup();
@@ -620,6 +615,11 @@ import { makeToggleAccessible } from "aria-ease/toggle";
620
615
  const toggle = makeToggleAccessible({
621
616
  toggleId: "mute-button",
622
617
  isSingleToggle: true,
618
+ callback: {
619
+ onPressedChange: (pressed) => {
620
+ console.log("Toggle has pressed of value pressed");
621
+ },
622
+ },
623
623
  });
624
624
 
625
625
  // Toggle button group
@@ -627,6 +627,11 @@ const toggleGroup = makeToggleAccessible({
627
627
  toggleId: "toolbar",
628
628
  togglesClass: "toggle-btn",
629
629
  isSingleToggle: false,
630
+ callback: {
631
+ onPressedChange: (index, pressed) => {
632
+ console.log("Toggle at index has pressed of value pressed");
633
+ },
634
+ },
630
635
  });
631
636
 
632
637
  // Programmatic control
@@ -701,8 +706,9 @@ afterAll(async () => {
701
706
  describe("Shopify User Menu Accessibility Test", () => {
702
707
  test("renders Shopify user menu without accessibility violation(s)", async () => {
703
708
  await testUiComponent(
704
- "menu", "http://localhost:5173/test-harness?component=menu",
705
- );
709
+ "menu",
710
+ "http://localhost:5173/test-harness?component=menu",
711
+ );
706
712
  });
707
713
  });
708
714
  ```
@@ -765,7 +771,6 @@ Or override per test call:
765
771
  ```javascript
766
772
  await testUiComponent(
767
773
  "menu",
768
- null,
769
774
  "http://localhost:5173/test-harness?component=menu",
770
775
  {
771
776
  strictness: "strict",
@@ -788,7 +793,7 @@ Aria-Ease is designed to be lightweight and tree-shakable:
788
793
  | `aria-ease/menu` | ~6.7KB |
789
794
  | `aria-ease/block` | ~1.7KB |
790
795
  | `aria-ease/combobox` | ~8.1KB |
791
- | Full bundle (all components) | ~459KB (uncompressed) |
796
+ | Full bundle (all components) | ~659KB (uncompressed) |
792
797
 
793
798
  **💡 Tip:** Always import individual components for optimal bundle size:
794
799
 
@@ -820,27 +825,6 @@ useEffect(() => {
820
825
 
821
826
  ---
822
827
 
823
- ## 🎨 Focus Styling
824
-
825
- Aria-Ease handles ARIA attributes and keyboard interaction, but **you must provide visible focus styles**:
826
-
827
- ```css
828
- :focus {
829
- outline: 2px solid rgba(0, 91, 211, 1);
830
- outline-offset: 2px;
831
- }
832
-
833
- /* Or custom styles */
834
- .menu-item:focus {
835
- background: #e3f2fd;
836
- box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.3);
837
- }
838
- ```
839
-
840
- Without visible focus indicators, keyboard users cannot tell which element is active.
841
-
842
- ---
843
-
844
828
  ## 🌐 Browser Support
845
829
 
846
830
  Aria-Ease supports all modern browsers:
@@ -997,6 +981,7 @@ export default {
997
981
  out: "./accessibility-reports",
998
982
  },
999
983
  },
984
+ test: {...}
1000
985
  contracts: [
1001
986
  {
1002
987
  src: "./tests/external-contracts/**/*.contract.mjs",
@@ -0,0 +1 @@
1
+ import{a as r}from"./chunk-SM6ZKEDR.js";import"./chunk-CNU4N4AY.js";var i=class{constructor(e,o,s=400,t=400){this.mainSelector=e;this.selectors=o;this.actionTimeoutMs=s;this.assertionTimeoutMs=t}async resetState(e){if(!this.selectors.toggle||!this.selectors.relative)return;let o=this.selectors.toggle;if(!o)return;let s=await e.locator(o).all();for(let t of s)if(await t.getAttribute("aria-pressed")==="true"){await t.click({timeout:this.actionTimeoutMs});let a=e.locator(`#${t}`);await(0,r.expect)(a).toHaveAttribute("aria-pressed","false",{timeout:this.assertionTimeoutMs}).catch(()=>{})}}async shouldSkipTest(){return!1}getMainSelector(){return this.mainSelector}};export{i as ToggleComponentStrategy};