@cssints/apg-specs 0.0.0-stage → 0.5.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 (260) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -2
  3. package/dist/apg/accordion.bridge.d.mts +35 -0
  4. package/dist/apg/accordion.bridge.mjs +326 -0
  5. package/dist/apg/accordion.model.json +8397 -0
  6. package/dist/apg/alert.bridge.d.mts +22 -0
  7. package/dist/apg/alert.bridge.mjs +86 -0
  8. package/dist/apg/alert.model.json +871 -0
  9. package/dist/apg/alertdialog.bridge.d.mts +21 -0
  10. package/dist/apg/alertdialog.bridge.mjs +221 -0
  11. package/dist/apg/alertdialog.model.json +5232 -0
  12. package/dist/apg/breadcrumb.bridge.d.mts +26 -0
  13. package/dist/apg/breadcrumb.bridge.mjs +120 -0
  14. package/dist/apg/breadcrumb.model.json +574 -0
  15. package/dist/apg/button.bridge.d.mts +35 -0
  16. package/dist/apg/button.bridge.mjs +208 -0
  17. package/dist/apg/button.model.json +5814 -0
  18. package/dist/apg/carousel.bridge.d.mts +44 -0
  19. package/dist/apg/carousel.bridge.mjs +262 -0
  20. package/dist/apg/carousel.model.json +8963 -0
  21. package/dist/apg/checkbox-group.bridge.d.mts +25 -0
  22. package/dist/apg/checkbox-group.bridge.mjs +276 -0
  23. package/dist/apg/checkbox-group.model.json +3863 -0
  24. package/dist/apg/checkbox.bridge.d.mts +34 -0
  25. package/dist/apg/checkbox.bridge.mjs +253 -0
  26. package/dist/apg/checkbox.model.json +3158 -0
  27. package/dist/apg/combobox-autocomplete.bridge.d.mts +14 -0
  28. package/dist/apg/combobox-autocomplete.bridge.mjs +326 -0
  29. package/dist/apg/combobox-autocomplete.model.json +11586 -0
  30. package/dist/apg/combobox-dialog.bridge.d.mts +17 -0
  31. package/dist/apg/combobox-dialog.bridge.mjs +250 -0
  32. package/dist/apg/combobox-dialog.model.json +8981 -0
  33. package/dist/apg/combobox-grid.bridge.d.mts +17 -0
  34. package/dist/apg/combobox-grid.bridge.mjs +227 -0
  35. package/dist/apg/combobox-grid.model.json +8554 -0
  36. package/dist/apg/combobox-select-only.bridge.d.mts +24 -0
  37. package/dist/apg/combobox-select-only.bridge.mjs +304 -0
  38. package/dist/apg/combobox-select-only.model.json +7846 -0
  39. package/dist/apg/combobox-tree.bridge.d.mts +17 -0
  40. package/dist/apg/combobox-tree.bridge.mjs +211 -0
  41. package/dist/apg/combobox-tree.model.json +9345 -0
  42. package/dist/apg/dialog-modal.bridge.d.mts +35 -0
  43. package/dist/apg/dialog-modal.bridge.mjs +186 -0
  44. package/dist/apg/dialog-modal.model.json +7580 -0
  45. package/dist/apg/disclosure.bridge.d.mts +25 -0
  46. package/dist/apg/disclosure.bridge.mjs +202 -0
  47. package/dist/apg/disclosure.model.json +4703 -0
  48. package/dist/apg/feed.bridge.d.mts +31 -0
  49. package/dist/apg/feed.bridge.mjs +246 -0
  50. package/dist/apg/feed.model.json +4523 -0
  51. package/dist/apg/grid-selection.bridge.d.mts +13 -0
  52. package/dist/apg/grid-selection.bridge.mjs +160 -0
  53. package/dist/apg/grid-selection.model.json +6974 -0
  54. package/dist/apg/grid-sort.bridge.d.mts +12 -0
  55. package/dist/apg/grid-sort.bridge.mjs +138 -0
  56. package/dist/apg/grid-sort.model.json +3799 -0
  57. package/dist/apg/grid.bridge.d.mts +54 -0
  58. package/dist/apg/grid.bridge.mjs +441 -0
  59. package/dist/apg/grid.model.json +3802 -0
  60. package/dist/apg/link.bridge.d.mts +24 -0
  61. package/dist/apg/link.bridge.mjs +126 -0
  62. package/dist/apg/link.model.json +694 -0
  63. package/dist/apg/listbox-multi.bridge.d.mts +10 -0
  64. package/dist/apg/listbox-multi.bridge.mjs +261 -0
  65. package/dist/apg/listbox-multi.model.json +10893 -0
  66. package/dist/apg/listbox-rearrangeable.bridge.d.mts +20 -0
  67. package/dist/apg/listbox-rearrangeable.bridge.mjs +237 -0
  68. package/dist/apg/listbox-rearrangeable.model.json +17592 -0
  69. package/dist/apg/listbox-shared.d.mts +58 -0
  70. package/dist/apg/listbox-shared.mjs +171 -0
  71. package/dist/apg/listbox-single.bridge.d.mts +10 -0
  72. package/dist/apg/listbox-single.bridge.mjs +196 -0
  73. package/dist/apg/listbox-single.model.json +8024 -0
  74. package/dist/apg/listbox-typeahead.bridge.d.mts +10 -0
  75. package/dist/apg/listbox-typeahead.bridge.mjs +153 -0
  76. package/dist/apg/listbox-typeahead.model.json +5615 -0
  77. package/dist/apg/menu-button.bridge.d.mts +29 -0
  78. package/dist/apg/menu-button.bridge.mjs +259 -0
  79. package/dist/apg/menu-button.model.json +5706 -0
  80. package/dist/apg/menu-checkable.bridge.d.mts +25 -0
  81. package/dist/apg/menu-checkable.bridge.mjs +226 -0
  82. package/dist/apg/menu-checkable.model.json +4277 -0
  83. package/dist/apg/menu-menubar.bridge.d.mts +27 -0
  84. package/dist/apg/menu-menubar.bridge.mjs +273 -0
  85. package/dist/apg/menu-menubar.model.json +9540 -0
  86. package/dist/apg/meter.bridge.d.mts +28 -0
  87. package/dist/apg/meter.bridge.mjs +159 -0
  88. package/dist/apg/meter.model.json +1185 -0
  89. package/dist/apg/player.bridge.d.mts +24 -0
  90. package/dist/apg/player.bridge.mjs +342 -0
  91. package/dist/apg/player.model.json +11013 -0
  92. package/dist/apg/radio.bridge.d.mts +35 -0
  93. package/dist/apg/radio.bridge.mjs +371 -0
  94. package/dist/apg/radio.model.json +5011 -0
  95. package/dist/apg/slider-multithumb.bridge.d.mts +34 -0
  96. package/dist/apg/slider-multithumb.bridge.mjs +365 -0
  97. package/dist/apg/slider-multithumb.model.json +5774 -0
  98. package/dist/apg/slider.bridge.d.mts +54 -0
  99. package/dist/apg/slider.bridge.mjs +359 -0
  100. package/dist/apg/slider.model.json +4881 -0
  101. package/dist/apg/spinbutton.bridge.d.mts +41 -0
  102. package/dist/apg/spinbutton.bridge.mjs +327 -0
  103. package/dist/apg/spinbutton.model.json +7571 -0
  104. package/dist/apg/switch.bridge.d.mts +30 -0
  105. package/dist/apg/switch.bridge.mjs +243 -0
  106. package/dist/apg/switch.model.json +3159 -0
  107. package/dist/apg/tabs.bridge.d.mts +41 -0
  108. package/dist/apg/tabs.bridge.mjs +294 -0
  109. package/dist/apg/tabs.model.json +7659 -0
  110. package/dist/apg/timeline.bridge.d.mts +36 -0
  111. package/dist/apg/timeline.bridge.mjs +265 -0
  112. package/dist/apg/timeline.model.json +6189 -0
  113. package/dist/apg/toolbar.bridge.d.mts +25 -0
  114. package/dist/apg/toolbar.bridge.mjs +307 -0
  115. package/dist/apg/toolbar.model.json +6616 -0
  116. package/dist/apg/tooltip.bridge.d.mts +36 -0
  117. package/dist/apg/tooltip.bridge.mjs +208 -0
  118. package/dist/apg/tooltip.model.json +8295 -0
  119. package/dist/apg/treegrid.bridge.d.mts +28 -0
  120. package/dist/apg/treegrid.bridge.mjs +311 -0
  121. package/dist/apg/treegrid.model.json +17358 -0
  122. package/dist/apg/treeview.bridge.d.mts +26 -0
  123. package/dist/apg/treeview.bridge.mjs +396 -0
  124. package/dist/apg/treeview.model.json +13745 -0
  125. package/dist/apg/windowsplitter.bridge.d.mts +36 -0
  126. package/dist/apg/windowsplitter.bridge.mjs +242 -0
  127. package/dist/apg/windowsplitter.model.json +6253 -0
  128. package/dist/canvas/camera.model.json +6568 -0
  129. package/dist/canvas/canvas.bridge.d.mts +190 -0
  130. package/dist/canvas/canvas.bridge.mjs +729 -0
  131. package/dist/canvas/pointer.model.json +17095 -0
  132. package/dist/scenario.d.mts +233 -0
  133. package/dist/scenario.mjs +313 -0
  134. package/package.json +57 -4
  135. package/scenarios/apg/README.md +221 -0
  136. package/scenarios/apg/accordion.base-ui.json +1005 -0
  137. package/scenarios/apg/accordion.react-aria.json +929 -0
  138. package/scenarios/apg/alert.base-ui.json +81 -0
  139. package/scenarios/apg/alert.react-aria.json +98 -0
  140. package/scenarios/apg/alertdialog.base-ui.json +331 -0
  141. package/scenarios/apg/alertdialog.react-aria.json +225 -0
  142. package/scenarios/apg/breadcrumb.react-aria.json +312 -0
  143. package/scenarios/apg/button.ariakit.json +136 -0
  144. package/scenarios/apg/button.base-ui.json +747 -0
  145. package/scenarios/apg/button.react-aria.json +443 -0
  146. package/scenarios/apg/carousel.apg.json +909 -0
  147. package/scenarios/apg/checkbox.ariakit.json +299 -0
  148. package/scenarios/apg/checkbox.base-ui.json +1126 -0
  149. package/scenarios/apg/checkbox.react-aria.json +518 -0
  150. package/scenarios/apg/combobox.ariakit.json +945 -0
  151. package/scenarios/apg/combobox.base-ui.json +2444 -0
  152. package/scenarios/apg/combobox.react-aria.json +845 -0
  153. package/scenarios/apg/dialog-modal.ariakit.json +1102 -0
  154. package/scenarios/apg/dialog-modal.base-ui.json +775 -0
  155. package/scenarios/apg/dialog-modal.react-aria.json +638 -0
  156. package/scenarios/apg/disclosure.apg.json +349 -0
  157. package/scenarios/apg/disclosure.ariakit.json +163 -0
  158. package/scenarios/apg/disclosure.base-ui.json +451 -0
  159. package/scenarios/apg/disclosure.react-aria.json +643 -0
  160. package/scenarios/apg/feed.apg.json +442 -0
  161. package/scenarios/apg/grid.ariakit.json +197 -0
  162. package/scenarios/apg/grid.base-ui.json +720 -0
  163. package/scenarios/apg/grid.react-aria.json +969 -0
  164. package/scenarios/apg/landmarks.ariakit.json +93 -0
  165. package/scenarios/apg/landmarks.react-aria.json +330 -0
  166. package/scenarios/apg/link.ariakit.json +83 -0
  167. package/scenarios/apg/link.base-ui.json +88 -0
  168. package/scenarios/apg/link.react-aria.json +186 -0
  169. package/scenarios/apg/listbox.ariakit.json +78 -0
  170. package/scenarios/apg/listbox.base-ui.json +163 -0
  171. package/scenarios/apg/listbox.react-aria.json +1118 -0
  172. package/scenarios/apg/menu-button.ariakit.json +924 -0
  173. package/scenarios/apg/menu-button.base-ui.json +1155 -0
  174. package/scenarios/apg/menu-button.react-aria.json +1080 -0
  175. package/scenarios/apg/menubar.ariakit.json +758 -0
  176. package/scenarios/apg/menubar.base-ui.json +673 -0
  177. package/scenarios/apg/meter.base-ui.json +243 -0
  178. package/scenarios/apg/meter.react-aria.json +201 -0
  179. package/scenarios/apg/radio.ariakit.json +459 -0
  180. package/scenarios/apg/radio.base-ui.json +581 -0
  181. package/scenarios/apg/radio.react-aria.json +576 -0
  182. package/scenarios/apg/slider-multithumb.base-ui.json +316 -0
  183. package/scenarios/apg/slider-multithumb.react-aria.json +363 -0
  184. package/scenarios/apg/slider.base-ui.json +544 -0
  185. package/scenarios/apg/slider.react-aria.json +456 -0
  186. package/scenarios/apg/spinbutton.base-ui.json +1688 -0
  187. package/scenarios/apg/spinbutton.react-aria.json +426 -0
  188. package/scenarios/apg/switch.base-ui.json +456 -0
  189. package/scenarios/apg/switch.react-aria.json +303 -0
  190. package/scenarios/apg/table.ariakit.json +65 -0
  191. package/scenarios/apg/table.react-aria.json +221 -0
  192. package/scenarios/apg/tabs.ariakit.json +329 -0
  193. package/scenarios/apg/tabs.base-ui.json +875 -0
  194. package/scenarios/apg/tabs.react-aria.json +644 -0
  195. package/scenarios/apg/toolbar.ariakit.json +595 -0
  196. package/scenarios/apg/toolbar.base-ui.json +1189 -0
  197. package/scenarios/apg/toolbar.react-aria.json +383 -0
  198. package/scenarios/apg/tooltip.ariakit.json +441 -0
  199. package/scenarios/apg/tooltip.base-ui.json +411 -0
  200. package/scenarios/apg/tooltip.react-aria.json +454 -0
  201. package/scenarios/apg/treegrid.apg.json +2558 -0
  202. package/scenarios/apg/treegrid.react-aria.json +5094 -0
  203. package/scenarios/apg/treeview.apg.json +928 -0
  204. package/scenarios/apg/windowsplitter.apg.json +557 -0
  205. package/scenarios/canvas/README.md +69 -0
  206. package/scenarios/canvas/camera.contract.json +237 -0
  207. package/scenarios/canvas/camera.excalidraw.json +57 -0
  208. package/scenarios/canvas/marquee.excalidraw.json +122 -0
  209. package/scenarios/canvas/pan.excalidraw.json +46 -0
  210. package/scenarios/canvas/pointer.contract.json +528 -0
  211. package/scenarios/parts.json +291 -0
  212. package/shared/aria.qnt +186 -0
  213. package/shared/details-group.qnt +50 -0
  214. package/shared/focusgroup.qnt +152 -0
  215. package/shared/invokers.qnt +81 -0
  216. package/shared/popover.qnt +71 -0
  217. package/specs/apg/STATIC.md +103 -0
  218. package/specs/apg/accordion.qnt +578 -0
  219. package/specs/apg/alert.qnt +131 -0
  220. package/specs/apg/alertdialog.qnt +455 -0
  221. package/specs/apg/breadcrumb.qnt +88 -0
  222. package/specs/apg/button.qnt +445 -0
  223. package/specs/apg/carousel.qnt +732 -0
  224. package/specs/apg/checkbox-group.qnt +388 -0
  225. package/specs/apg/checkbox.qnt +394 -0
  226. package/specs/apg/combobox-autocomplete.qnt +764 -0
  227. package/specs/apg/combobox-dialog.qnt +627 -0
  228. package/specs/apg/combobox-grid.qnt +632 -0
  229. package/specs/apg/combobox-select-only.qnt +626 -0
  230. package/specs/apg/combobox-tree.qnt +633 -0
  231. package/specs/apg/dialog-modal.qnt +499 -0
  232. package/specs/apg/disclosure.qnt +309 -0
  233. package/specs/apg/feed.qnt +485 -0
  234. package/specs/apg/grid-selection.qnt +512 -0
  235. package/specs/apg/grid-sort.qnt +330 -0
  236. package/specs/apg/grid.qnt +360 -0
  237. package/specs/apg/link.qnt +119 -0
  238. package/specs/apg/listbox-multi.qnt +799 -0
  239. package/specs/apg/listbox-rearrangeable.qnt +801 -0
  240. package/specs/apg/listbox-single.qnt +511 -0
  241. package/specs/apg/listbox-typeahead.qnt +396 -0
  242. package/specs/apg/menu-button.qnt +363 -0
  243. package/specs/apg/menu-checkable.qnt +304 -0
  244. package/specs/apg/menu-menubar.qnt +531 -0
  245. package/specs/apg/meter.qnt +129 -0
  246. package/specs/apg/player.qnt +1081 -0
  247. package/specs/apg/radio.qnt +394 -0
  248. package/specs/apg/slider-multithumb.qnt +441 -0
  249. package/specs/apg/slider.qnt +396 -0
  250. package/specs/apg/spinbutton.qnt +494 -0
  251. package/specs/apg/switch.qnt +389 -0
  252. package/specs/apg/tabs.qnt +412 -0
  253. package/specs/apg/timeline.qnt +612 -0
  254. package/specs/apg/toolbar.qnt +364 -0
  255. package/specs/apg/tooltip.qnt +553 -0
  256. package/specs/apg/treegrid.qnt +983 -0
  257. package/specs/apg/treeview.qnt +1004 -0
  258. package/specs/apg/windowsplitter.qnt +487 -0
  259. package/specs/canvas/camera.qnt +407 -0
  260. package/specs/canvas/pointer.qnt +841 -0
@@ -0,0 +1,909 @@
1
+ {
2
+ "pattern": "carousel",
3
+ "source": "apg",
4
+ "commit": null,
5
+ "license": "W3C Software and Document License",
6
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/",
7
+ "notes": "No upstream test implementation of the carousel pattern exists in ariakit, base-ui or react-aria (none of them ships a carousel), so every scenario is derived from the WAI-ARIA APG Carousel Pattern itself: the About This Pattern, Keyboard Interaction and WAI-ARIA Roles, States, and Properties sections, plus the two APG examples (carousel-1-prev-next and carousel-2-tablist) and their carousel-prev-next.js / carousel-tablist.js, all licensed W3C Software and Document License. Targets: `carousel` (the container carrying region/group + aria-roledescription and, in this vocabulary, aria-live, since the APG target names given for this port have no separate slides wrapper), `rotation`, `prev`, `next`, `slide:<i>` and `tab:<i>`. A rotation tick is `{ \"event\": \"wait\", \"ms\": 5000 }` (the examples' timeInterval). config vocabulary: items = slide count (default 6); variant = \"basic\" (default) | \"tabbed\"; autoRotate = carousel has the auto-rotate feature (false = the example's \\\"Disable auto-rotation\\\" option, which removes the rotation control); playing = rotation active at start (false models the paused-on-load option; the user must activate the rotation control); reducedMotion = prefers-reduced-motion, which forces paused-on-load; focusInside = DOM focus starts inside the carousel; hovering = pointer starts over the carousel; initialIndex = initially shown slide / selected tab, 0-based (default 0); labelledBy = ID of the carousel's visible label; role = container role (region or group). Known APG ambiguity: the pattern page says a tabbed slide (tabpanel) does NOT have aria-roledescription, while the carousel-2 example's role/attribute table sets aria-roledescription=\"slide\" on it; the tabbed scenario below follows the example table and is flagged in its notes. The basic example resumes rotation on focusout, while the pattern text and the tabbed example say focus does not resume rotation until the user activates the rotation control; the scenarios follow the pattern text and flag the divergence.",
8
+ "scenarios": [
9
+ {
10
+ "id": "carousel.apg.about.auto-rotation-advances-on-interval",
11
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#aboutthispattern",
12
+ "upstreamTest": "About This Pattern: rotation automatically starts when the page loads and rotates the next slide into view",
13
+ "apg": "about",
14
+ "config": { "items": 6, "autoRotate": true, "playing": true },
15
+ "steps": [
16
+ { "expect": { "visible": ["slide:0"] } },
17
+ { "event": "wait", "ms": 5000 },
18
+ {
19
+ "expect": {
20
+ "visible": ["slide:1"],
21
+ "attrs": { "slide:1": { "aria-label": "2 of 6" } }
22
+ }
23
+ }
24
+ ],
25
+ "notes": "The examples' timeInterval is 5000 ms; one interval advances exactly one slide."
26
+ },
27
+ {
28
+ "id": "carousel.apg.about.rotation-stops-on-keyboard-focus",
29
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
30
+ "upstreamTest": "Keyboard Interaction: automatic slide rotation stops when any element in the carousel receives keyboard focus",
31
+ "apg": "about",
32
+ "config": { "items": 6, "autoRotate": true, "playing": true },
33
+ "steps": [
34
+ { "event": "focus", "target": "next" },
35
+ { "event": "wait", "ms": 5000 },
36
+ {
37
+ "expect": {
38
+ "visible": ["slide:0"],
39
+ "attrs": { "carousel": { "aria-live": "polite" } }
40
+ }
41
+ }
42
+ ],
43
+ "notes": "Focus entering any carousel element (here the next button) latches rotation off for the tick; the live region flips to polite while focus is inside."
44
+ },
45
+ {
46
+ "id": "carousel.apg.about.focus-stop-persists-after-blur",
47
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
48
+ "upstreamTest": "Keyboard Interaction: rotation does not resume unless the user activates the rotation control",
49
+ "apg": "about",
50
+ "config": { "items": 6, "autoRotate": true, "playing": true },
51
+ "steps": [
52
+ { "event": "focus", "target": "next" },
53
+ { "event": "blur", "target": "next" },
54
+ { "event": "wait", "ms": 5000 },
55
+ { "expect": { "visible": ["slide:0"] } }
56
+ ],
57
+ "notes": "Divergence: carousel-prev-next.js resumes on focusout, but the pattern text and the tabbed example keep rotation stopped until the user activates the rotation control; this scenario pins the pattern-text behaviour."
58
+ },
59
+ {
60
+ "id": "carousel.apg.about.rotation-resumes-only-on-rotation-control",
61
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
62
+ "upstreamTest": "Keyboard Interaction: a focus-stopped carousel resumes rotation only when the user activates the rotation control",
63
+ "apg": "about",
64
+ "config": {
65
+ "items": 6,
66
+ "autoRotate": true,
67
+ "playing": true,
68
+ "focusInside": true
69
+ },
70
+ "steps": [
71
+ { "event": "focus", "target": "next" },
72
+ { "event": "wait", "ms": 5000 },
73
+ { "expect": { "visible": ["slide:0"] } },
74
+ { "event": "click", "target": "rotation" },
75
+ { "event": "wait", "ms": 5000 },
76
+ { "expect": { "visible": ["slide:1"] } }
77
+ ],
78
+ "notes": "Once the user activates the rotation control, focus/hover states are ignored until it is activated again (APG Accessibility Features). In carousel-prev-next.js reaching this state after a focus pause takes a stop-then-start pair of activations; the scenario states the intended single-action resume."
79
+ },
80
+ {
81
+ "id": "carousel.apg.about.hover-pauses-rotation",
82
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
83
+ "upstreamTest": "Accessibility Features: hovering the mouse over any carousel content pauses automatic rotation",
84
+ "apg": "about",
85
+ "config": { "items": 6, "autoRotate": true, "playing": true },
86
+ "steps": [
87
+ { "event": "hover", "target": "carousel" },
88
+ { "event": "wait", "ms": 5000 },
89
+ { "expect": { "visible": ["slide:0"] } }
90
+ ]
91
+ },
92
+ {
93
+ "id": "carousel.apg.about.unhover-resumes-rotation",
94
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
95
+ "upstreamTest": "Accessibility Features: automatic rotation resumes when the mouse moves away unless another condition prevents rotation",
96
+ "apg": "about",
97
+ "config": { "items": 6, "autoRotate": true, "playing": true },
98
+ "steps": [
99
+ { "event": "hover", "target": "carousel" },
100
+ { "event": "wait", "ms": 5000 },
101
+ { "expect": { "visible": ["slide:0"] } },
102
+ { "event": "unhover", "target": "carousel" },
103
+ { "event": "wait", "ms": 5000 },
104
+ { "expect": { "visible": ["slide:1"] } }
105
+ ]
106
+ },
107
+ {
108
+ "id": "carousel.apg.about.hover-does-not-resume-when-stopped",
109
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
110
+ "upstreamTest": "Accessibility Features: rotation resumes on unhover only when it has not been stopped",
111
+ "apg": "about",
112
+ "config": { "items": 6, "autoRotate": true, "playing": true },
113
+ "steps": [
114
+ { "event": "click", "target": "rotation" },
115
+ { "event": "hover", "target": "carousel" },
116
+ { "event": "unhover", "target": "carousel" },
117
+ { "event": "wait", "ms": 5000 },
118
+ { "expect": { "visible": ["slide:0"] } }
119
+ ]
120
+ },
121
+ {
122
+ "id": "carousel.apg.about.user-started-rotation-ignores-hover",
123
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
124
+ "upstreamTest": "Accessibility Features: once the user activates the rotation control, hover states that pause rotation are ignored",
125
+ "apg": "about",
126
+ "config": {
127
+ "items": 6,
128
+ "autoRotate": true,
129
+ "playing": false,
130
+ "hovering": true
131
+ },
132
+ "steps": [
133
+ { "event": "click", "target": "rotation" },
134
+ { "event": "wait", "ms": 5000 },
135
+ { "expect": { "visible": ["slide:1"] } }
136
+ ],
137
+ "notes": "Mirrors carousel-prev-next.js rotateSlides: the hasUserActivatedPlay branch advances even while hasHover is set."
138
+ },
139
+ {
140
+ "id": "carousel.apg.example.reduced-motion-pauses-on-load",
141
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
142
+ "upstreamTest": "Accessibility Features: a prefers-reduced-motion setting always overrides autoplay and starts the carousel paused",
143
+ "apg": "example",
144
+ "config": {
145
+ "items": 6,
146
+ "autoRotate": true,
147
+ "reducedMotion": true,
148
+ "playing": false
149
+ },
150
+ "steps": [
151
+ {
152
+ "expect": {
153
+ "attrs": {
154
+ "rotation": { "aria-label": "Start automatic slide show" }
155
+ }
156
+ }
157
+ },
158
+ { "event": "wait", "ms": 5000 },
159
+ { "expect": { "visible": ["slide:0"] } }
160
+ ]
161
+ },
162
+ {
163
+ "id": "carousel.apg.example.disable-auto-rotation-hides-control",
164
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#exampleoptions",
165
+ "upstreamTest": "Example Options / Accessibility Features: disabling auto-rotation removes the rotation control from the interface",
166
+ "apg": "example",
167
+ "config": { "items": 6, "autoRotate": false },
168
+ "steps": [{ "expect": { "attrs": { "rotation": { "hidden": "" } } } }],
169
+ "notes": "carousel-prev-next.js sets pausePlayButtonNode.hidden = disable; the control is absent from the tab sequence in that state."
170
+ },
171
+ {
172
+ "id": "carousel.apg.example.manual-controls-work-without-auto-rotation",
173
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#exampleoptions",
174
+ "upstreamTest": "Accessibility Features: when auto-rotation is disabled the previous and next buttons still rotate slides",
175
+ "apg": "example",
176
+ "config": { "items": 6, "autoRotate": false },
177
+ "steps": [
178
+ { "event": "click", "target": "next" },
179
+ { "expect": { "visible": ["slide:1"] } },
180
+ { "event": "click", "target": "prev" },
181
+ { "expect": { "visible": ["slide:0"] } }
182
+ ]
183
+ },
184
+
185
+ {
186
+ "id": "carousel.apg.rotation.label-stop-when-playing",
187
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
188
+ "upstreamTest": "Roles/States: while the carousel is rotating the rotation control's label is Stop automatic slide show",
189
+ "apg": "rotation",
190
+ "config": { "items": 6, "autoRotate": true, "playing": true },
191
+ "steps": [
192
+ {
193
+ "expect": {
194
+ "attrs": {
195
+ "rotation": { "aria-label": "Stop automatic slide show" }
196
+ }
197
+ }
198
+ }
199
+ ]
200
+ },
201
+ {
202
+ "id": "carousel.apg.rotation.label-start-when-paused",
203
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
204
+ "upstreamTest": "Roles/States: while the carousel is not rotating the rotation control's label is Start automatic slide show",
205
+ "apg": "rotation",
206
+ "config": { "items": 6, "autoRotate": true, "playing": false },
207
+ "steps": [
208
+ {
209
+ "expect": {
210
+ "attrs": {
211
+ "rotation": { "aria-label": "Start automatic slide show" }
212
+ }
213
+ }
214
+ }
215
+ ]
216
+ },
217
+ {
218
+ "id": "carousel.apg.rotation.label-toggles-on-activation",
219
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
220
+ "upstreamTest": "Roles/States: the label changes to match the action the button will perform each time it is activated",
221
+ "apg": "rotation",
222
+ "config": { "items": 6, "autoRotate": true, "playing": true },
223
+ "steps": [
224
+ { "event": "click", "target": "rotation" },
225
+ {
226
+ "expect": {
227
+ "attrs": {
228
+ "rotation": { "aria-label": "Start automatic slide show" }
229
+ }
230
+ }
231
+ },
232
+ { "event": "click", "target": "rotation" },
233
+ {
234
+ "expect": {
235
+ "attrs": {
236
+ "rotation": { "aria-label": "Stop automatic slide show" }
237
+ }
238
+ }
239
+ }
240
+ ]
241
+ },
242
+ {
243
+ "id": "carousel.apg.rotation.no-aria-pressed-state",
244
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
245
+ "upstreamTest": "Roles/States: because the label changes, the rotation control has no states such as aria-pressed",
246
+ "apg": "rotation",
247
+ "config": { "items": 6, "autoRotate": true, "playing": true },
248
+ "steps": [
249
+ { "expect": { "attrs": { "rotation": { "aria-pressed": null } } } }
250
+ ]
251
+ },
252
+ {
253
+ "id": "carousel.apg.rotation.activation-does-not-move-focus",
254
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
255
+ "upstreamTest": "Keyboard Interaction: activating the rotation control does not move focus, so it can be activated repeatedly",
256
+ "apg": "rotation",
257
+ "config": { "items": 6, "autoRotate": true, "playing": true },
258
+ "steps": [
259
+ { "event": "focus", "target": "rotation" },
260
+ { "event": "click", "target": "rotation" },
261
+ { "expect": { "focus": "rotation" } }
262
+ ]
263
+ },
264
+ {
265
+ "id": "carousel.apg.rotation.enter-or-space-toggles",
266
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#kbd_label",
267
+ "upstreamTest": "Keyboard Support: Enter or Space toggles auto rotation of the slides",
268
+ "apg": "rotation",
269
+ "config": { "items": 6, "autoRotate": true, "playing": true },
270
+ "steps": [
271
+ { "event": "focus", "target": "rotation" },
272
+ { "event": "key", "key": "Enter", "target": "rotation" },
273
+ {
274
+ "expect": {
275
+ "attrs": {
276
+ "rotation": { "aria-label": "Start automatic slide show" }
277
+ }
278
+ }
279
+ },
280
+ { "event": "key", "key": " ", "target": "rotation" },
281
+ {
282
+ "expect": {
283
+ "attrs": {
284
+ "rotation": { "aria-label": "Stop automatic slide show" }
285
+ }
286
+ }
287
+ }
288
+ ]
289
+ },
290
+ {
291
+ "id": "carousel.apg.rotation.control-precedes-content-in-tab-order",
292
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
293
+ "upstreamTest": "Keyboard Interaction: the rotation control is the first element in the Tab sequence inside the carousel and precedes previous, next and the slide content",
294
+ "apg": "keyboard",
295
+ "config": { "items": 6, "autoRotate": true, "playing": true },
296
+ "steps": [
297
+ { "event": "focus", "target": "rotation" },
298
+ { "event": "key", "key": "Tab", "target": "rotation" },
299
+ { "expect": { "focus": "prev" } },
300
+ { "event": "key", "key": "Tab", "target": "prev" },
301
+ { "expect": { "focus": "next" } },
302
+ { "event": "key", "key": "Tab", "target": "next" },
303
+ { "expect": { "focus": "slide:0" } }
304
+ ],
305
+ "notes": "In the basic example the focusable content of the shown slide is the image link, so Tab lands on the slide container (target slide:0)."
306
+ },
307
+ {
308
+ "id": "carousel.apg.rotation.shift-tab-reverses-tab-order",
309
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
310
+ "upstreamTest": "Keyboard Interaction: Shift+Tab moves focus backwards through the interactive elements without scripting",
311
+ "apg": "keyboard",
312
+ "config": { "items": 6, "autoRotate": true, "playing": true },
313
+ "steps": [
314
+ { "event": "focus", "target": "next" },
315
+ { "event": "key", "key": "Shift+Tab", "target": "next" },
316
+ { "expect": { "focus": "prev" } },
317
+ { "event": "key", "key": "Shift+Tab", "target": "prev" },
318
+ { "expect": { "focus": "rotation" } }
319
+ ]
320
+ },
321
+
322
+ {
323
+ "id": "carousel.apg.next.advances-to-next-slide",
324
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#aboutthispattern",
325
+ "upstreamTest": "About This Pattern: the next slide control hides the current slide and rotates the next slide into view",
326
+ "apg": "next",
327
+ "config": { "items": 6, "autoRotate": true, "playing": true },
328
+ "steps": [
329
+ { "event": "click", "target": "next" },
330
+ {
331
+ "expect": {
332
+ "visible": ["slide:1"],
333
+ "attrs": { "slide:1": { "aria-label": "2 of 6" } }
334
+ }
335
+ }
336
+ ]
337
+ },
338
+ {
339
+ "id": "carousel.apg.next.wraps-from-last-to-first",
340
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#kbd_label",
341
+ "upstreamTest": "Keyboard Support: the next control at the last slide rotates to the first slide",
342
+ "apg": "next",
343
+ "config": {
344
+ "items": 6,
345
+ "autoRotate": true,
346
+ "playing": true,
347
+ "initialIndex": 5
348
+ },
349
+ "steps": [
350
+ { "event": "click", "target": "next" },
351
+ {
352
+ "expect": {
353
+ "visible": ["slide:0"],
354
+ "attrs": { "slide:0": { "aria-label": "1 of 6" } }
355
+ }
356
+ }
357
+ ],
358
+ "notes": "nextCarouselItem wraps nextIndex >= length back to 0."
359
+ },
360
+ {
361
+ "id": "carousel.apg.next.activation-does-not-move-focus",
362
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
363
+ "upstreamTest": "Keyboard Interaction: activating the next slide control does not move focus",
364
+ "apg": "next",
365
+ "config": { "items": 6, "autoRotate": true, "playing": true },
366
+ "steps": [
367
+ { "event": "focus", "target": "next" },
368
+ { "event": "click", "target": "next" },
369
+ { "expect": { "focus": "next", "visible": ["slide:1"] } }
370
+ ]
371
+ },
372
+ {
373
+ "id": "carousel.apg.next.enter-key-advances",
374
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#kbd_label",
375
+ "upstreamTest": "Keyboard Support: Enter or Space on the next button displays the next slide",
376
+ "apg": "next",
377
+ "config": { "items": 6, "autoRotate": true, "playing": true },
378
+ "steps": [
379
+ { "event": "focus", "target": "next" },
380
+ { "event": "key", "key": "Enter", "target": "next" },
381
+ { "expect": { "visible": ["slide:1"] } }
382
+ ]
383
+ },
384
+ {
385
+ "id": "carousel.apg.prev.goes-to-previous-slide",
386
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#aboutthispattern",
387
+ "upstreamTest": "About This Pattern: the previous slide control rotates the previous slide into view",
388
+ "apg": "prev",
389
+ "config": {
390
+ "items": 6,
391
+ "autoRotate": true,
392
+ "playing": true,
393
+ "initialIndex": 2
394
+ },
395
+ "steps": [
396
+ { "event": "click", "target": "prev" },
397
+ { "expect": { "visible": ["slide:1"] } }
398
+ ]
399
+ },
400
+ {
401
+ "id": "carousel.apg.prev.wraps-from-first-to-last",
402
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#kbd_label",
403
+ "upstreamTest": "Keyboard Support: the previous control at the first slide rotates to the last slide",
404
+ "apg": "prev",
405
+ "config": {
406
+ "items": 6,
407
+ "autoRotate": true,
408
+ "playing": true,
409
+ "initialIndex": 0
410
+ },
411
+ "steps": [
412
+ { "event": "click", "target": "prev" },
413
+ {
414
+ "expect": {
415
+ "visible": ["slide:5"],
416
+ "attrs": { "slide:5": { "aria-label": "6 of 6" } }
417
+ }
418
+ }
419
+ ],
420
+ "notes": "previousCarouselItem wraps nextIndex < 0 to length - 1."
421
+ },
422
+ {
423
+ "id": "carousel.apg.prev.activation-does-not-move-focus",
424
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#keyboardinteraction",
425
+ "upstreamTest": "Keyboard Interaction: activating the previous slide control does not move focus",
426
+ "apg": "prev",
427
+ "config": {
428
+ "items": 6,
429
+ "autoRotate": true,
430
+ "playing": true,
431
+ "initialIndex": 2
432
+ },
433
+ "steps": [
434
+ { "event": "focus", "target": "prev" },
435
+ { "event": "click", "target": "prev" },
436
+ { "expect": { "focus": "prev", "visible": ["slide:1"] } }
437
+ ]
438
+ },
439
+
440
+ {
441
+ "id": "carousel.apg.roles.container-roledescription-carousel",
442
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
443
+ "upstreamTest": "Roles/States: the carousel container has aria-roledescription set to carousel",
444
+ "apg": "roles",
445
+ "config": { "items": 6, "autoRotate": true, "playing": true },
446
+ "steps": [
447
+ {
448
+ "expect": {
449
+ "attrs": { "carousel": { "aria-roledescription": "carousel" } }
450
+ }
451
+ }
452
+ ]
453
+ },
454
+ {
455
+ "id": "carousel.apg.roles.container-has-visible-label",
456
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
457
+ "upstreamTest": "Roles/States: a visible carousel label is referenced by aria-labelledby on the carousel container",
458
+ "apg": "roles",
459
+ "config": { "items": 6, "labelledBy": "carousel-heading" },
460
+ "steps": [
461
+ {
462
+ "expect": {
463
+ "attrs": { "carousel": { "aria-labelledby": "carousel-heading" } }
464
+ }
465
+ }
466
+ ],
467
+ "notes": "Without a visible label the name comes from aria-label instead; since aria-roledescription is carousel, the label itself must not contain the word carousel."
468
+ },
469
+ {
470
+ "id": "carousel.apg.roles.container-role-region",
471
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
472
+ "upstreamTest": "Roles/States: the carousel container has role region or group",
473
+ "apg": "roles",
474
+ "config": { "items": 6, "role": "region" },
475
+ "steps": [
476
+ { "expect": { "attrs": { "carousel": { "role": "region" } } } }
477
+ ],
478
+ "notes": "The pattern allows region or group depending on the page's information architecture; this scenario pins the region branch."
479
+ },
480
+ {
481
+ "id": "carousel.apg.roles.slide-role-and-roledescription",
482
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
483
+ "upstreamTest": "Roles/States: each slide container has role group and aria-roledescription set to slide",
484
+ "apg": "roles",
485
+ "config": { "items": 6, "autoRotate": true, "playing": true },
486
+ "steps": [
487
+ {
488
+ "expect": {
489
+ "attrs": {
490
+ "slide:0": { "role": "group", "aria-roledescription": "slide" }
491
+ }
492
+ }
493
+ }
494
+ ]
495
+ },
496
+ {
497
+ "id": "carousel.apg.roles.slide-label-is-position-of-set",
498
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
499
+ "upstreamTest": "Roles/States: each slide has an accessible name giving its position, e.g. 1 of 6",
500
+ "apg": "roles",
501
+ "config": { "items": 6, "autoRotate": true, "playing": true },
502
+ "steps": [
503
+ {
504
+ "expect": {
505
+ "attrs": {
506
+ "slide:0": { "aria-label": "1 of 6" },
507
+ "slide:5": { "aria-label": "6 of 6" }
508
+ }
509
+ }
510
+ }
511
+ ],
512
+ "notes": "Normally position/size in an accessible name is discouraged; the carousel is an exception because group elements do not support aria-setsize / aria-posinset."
513
+ },
514
+ {
515
+ "id": "carousel.apg.roles.live-off-while-rotating",
516
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
517
+ "upstreamTest": "Roles/States: aria-live is off while the carousel is automatically rotating",
518
+ "apg": "roles",
519
+ "config": { "items": 6, "autoRotate": true, "playing": true },
520
+ "steps": [
521
+ { "expect": { "attrs": { "carousel": { "aria-live": "off" } } } }
522
+ ]
523
+ },
524
+ {
525
+ "id": "carousel.apg.roles.live-polite-when-not-rotating",
526
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
527
+ "upstreamTest": "Roles/States: aria-live is polite when the carousel is not automatically rotating",
528
+ "apg": "roles",
529
+ "config": { "items": 6, "autoRotate": true, "playing": false },
530
+ "steps": [
531
+ { "expect": { "attrs": { "carousel": { "aria-live": "polite" } } } }
532
+ ]
533
+ },
534
+ {
535
+ "id": "carousel.apg.roles.live-polite-when-focus-inside",
536
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
537
+ "upstreamTest": "Accessibility Features: moving focus into the carousel turns the live region on so slide changes are announced",
538
+ "apg": "roles",
539
+ "config": { "items": 6, "autoRotate": true, "playing": true },
540
+ "steps": [
541
+ { "event": "focus", "target": "next" },
542
+ { "expect": { "attrs": { "carousel": { "aria-live": "polite" } } } }
543
+ ]
544
+ },
545
+ {
546
+ "id": "carousel.apg.roles.live-off-after-user-restarts",
547
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
548
+ "upstreamTest": "Accessibility Features: when automatic rotation is on, the live region is disabled",
549
+ "apg": "roles",
550
+ "config": { "items": 6, "autoRotate": true, "playing": false },
551
+ "steps": [
552
+ { "event": "click", "target": "rotation" },
553
+ { "expect": { "attrs": { "carousel": { "aria-live": "off" } } } }
554
+ ]
555
+ },
556
+ {
557
+ "id": "carousel.apg.roles.controls-aria-controls-slides",
558
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#rps_label",
559
+ "upstreamTest": "Example attributes: each control button has aria-controls referring to the element containing the slides",
560
+ "apg": "roles",
561
+ "config": { "items": 6, "autoRotate": true, "playing": true },
562
+ "steps": [
563
+ {
564
+ "expect": {
565
+ "attrs": {
566
+ "rotation": { "aria-controls": "*" },
567
+ "prev": { "aria-controls": "*" },
568
+ "next": { "aria-controls": "*" }
569
+ }
570
+ }
571
+ }
572
+ ],
573
+ "notes": "Value \"*\" means any non-empty IDREF list (see feed.apg.json for the same convention)."
574
+ },
575
+
576
+ {
577
+ "id": "carousel.apg.tablist.selected-tab-shows-its-slide",
578
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
579
+ "upstreamTest": "Tabbed: activating a tab displays the slide associated with that tab",
580
+ "apg": "tablist",
581
+ "config": {
582
+ "items": 6,
583
+ "variant": "tabbed",
584
+ "autoRotate": true,
585
+ "playing": true,
586
+ "initialIndex": 0
587
+ },
588
+ "steps": [
589
+ {
590
+ "expect": {
591
+ "visible": ["slide:0"],
592
+ "attrs": {
593
+ "tab:0": { "aria-selected": "true" },
594
+ "tab:1": { "aria-selected": "false" }
595
+ }
596
+ }
597
+ }
598
+ ]
599
+ },
600
+ {
601
+ "id": "carousel.apg.tablist.roving-tabindex",
602
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#rps_label",
603
+ "upstreamTest": "Example attributes: only the selected tab is in the page Tab sequence; unselected tabs get tabindex=-1 and the selected tab's tabindex is removed",
604
+ "apg": "tablist",
605
+ "config": {
606
+ "items": 6,
607
+ "variant": "tabbed",
608
+ "autoRotate": true,
609
+ "playing": true,
610
+ "initialIndex": 0
611
+ },
612
+ "steps": [
613
+ {
614
+ "expect": {
615
+ "attrs": {
616
+ "tab:0": { "tabindex": null },
617
+ "tab:1": { "tabindex": "-1" },
618
+ "tab:5": { "tabindex": "-1" }
619
+ }
620
+ }
621
+ }
622
+ ]
623
+ },
624
+ {
625
+ "id": "carousel.apg.tablist.arrow-right-selects-next-tab",
626
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
627
+ "upstreamTest": "Keyboard Support: Right Arrow moves focus to the next tab and shows its slide",
628
+ "apg": "tablist",
629
+ "config": {
630
+ "items": 6,
631
+ "variant": "tabbed",
632
+ "autoRotate": true,
633
+ "playing": true,
634
+ "initialIndex": 0
635
+ },
636
+ "steps": [
637
+ { "event": "focus", "target": "tab:0" },
638
+ { "event": "key", "key": "ArrowRight", "target": "tab:0" },
639
+ {
640
+ "expect": {
641
+ "focus": "tab:1",
642
+ "visible": ["slide:1"],
643
+ "attrs": { "tab:1": { "aria-selected": "true" } }
644
+ }
645
+ }
646
+ ]
647
+ },
648
+ {
649
+ "id": "carousel.apg.tablist.arrow-right-wraps-last-to-first",
650
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
651
+ "upstreamTest": "Keyboard Support: Right Arrow on the last tab moves focus to the first tab and shows the first slide",
652
+ "apg": "tablist",
653
+ "config": {
654
+ "items": 6,
655
+ "variant": "tabbed",
656
+ "autoRotate": true,
657
+ "playing": true,
658
+ "initialIndex": 5
659
+ },
660
+ "steps": [
661
+ { "event": "focus", "target": "tab:5" },
662
+ { "event": "key", "key": "ArrowRight", "target": "tab:5" },
663
+ { "expect": { "focus": "tab:0", "visible": ["slide:0"] } }
664
+ ]
665
+ },
666
+ {
667
+ "id": "carousel.apg.tablist.arrow-left-selects-previous-tab",
668
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
669
+ "upstreamTest": "Keyboard Support: Left Arrow moves focus to the previous tab and shows its slide",
670
+ "apg": "tablist",
671
+ "config": {
672
+ "items": 6,
673
+ "variant": "tabbed",
674
+ "autoRotate": true,
675
+ "playing": true,
676
+ "initialIndex": 2
677
+ },
678
+ "steps": [
679
+ { "event": "focus", "target": "tab:2" },
680
+ { "event": "key", "key": "ArrowLeft", "target": "tab:2" },
681
+ { "expect": { "focus": "tab:1", "visible": ["slide:1"] } }
682
+ ]
683
+ },
684
+ {
685
+ "id": "carousel.apg.tablist.arrow-left-wraps-first-to-last",
686
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
687
+ "upstreamTest": "Keyboard Support: Left Arrow on the first tab moves focus to the last tab and shows the last slide",
688
+ "apg": "tablist",
689
+ "config": {
690
+ "items": 6,
691
+ "variant": "tabbed",
692
+ "autoRotate": true,
693
+ "playing": true,
694
+ "initialIndex": 0
695
+ },
696
+ "steps": [
697
+ { "event": "focus", "target": "tab:0" },
698
+ { "event": "key", "key": "ArrowLeft", "target": "tab:0" },
699
+ { "expect": { "focus": "tab:5", "visible": ["slide:5"] } }
700
+ ]
701
+ },
702
+ {
703
+ "id": "carousel.apg.tablist.home-selects-first-tab",
704
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
705
+ "upstreamTest": "Keyboard Support: Home moves focus to the first tab and shows the first slide",
706
+ "apg": "tablist",
707
+ "config": {
708
+ "items": 6,
709
+ "variant": "tabbed",
710
+ "autoRotate": true,
711
+ "playing": true,
712
+ "initialIndex": 3
713
+ },
714
+ "steps": [
715
+ { "event": "focus", "target": "tab:3" },
716
+ { "event": "key", "key": "Home", "target": "tab:3" },
717
+ { "expect": { "focus": "tab:0", "visible": ["slide:0"] } }
718
+ ]
719
+ },
720
+ {
721
+ "id": "carousel.apg.tablist.end-selects-last-tab",
722
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
723
+ "upstreamTest": "Keyboard Support: End moves focus to the last tab and shows the last slide",
724
+ "apg": "tablist",
725
+ "config": {
726
+ "items": 6,
727
+ "variant": "tabbed",
728
+ "autoRotate": true,
729
+ "playing": true,
730
+ "initialIndex": 0
731
+ },
732
+ "steps": [
733
+ { "event": "focus", "target": "tab:0" },
734
+ { "event": "key", "key": "End", "target": "tab:0" },
735
+ { "expect": { "focus": "tab:5", "visible": ["slide:5"] } }
736
+ ]
737
+ },
738
+ {
739
+ "id": "carousel.apg.tablist.click-selects-tab-and-moves-focus",
740
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
741
+ "upstreamTest": "Keyboard Support/Tabs: activating a tab (click) shows its slide and moves focus to the tab",
742
+ "apg": "tablist",
743
+ "config": {
744
+ "items": 6,
745
+ "variant": "tabbed",
746
+ "autoRotate": true,
747
+ "playing": true,
748
+ "initialIndex": 0
749
+ },
750
+ "steps": [
751
+ { "event": "click", "target": "tab:2" },
752
+ {
753
+ "expect": {
754
+ "focus": "tab:2",
755
+ "visible": ["slide:2"],
756
+ "attrs": {
757
+ "tab:2": { "aria-selected": "true" },
758
+ "tab:0": { "aria-selected": "false" }
759
+ }
760
+ }
761
+ }
762
+ ]
763
+ },
764
+ {
765
+ "id": "carousel.apg.tablist.tab-has-role-label-and-controls",
766
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#rps_label",
767
+ "upstreamTest": "Example attributes: each control is role tab, named Slide X, with aria-controls on its panel",
768
+ "apg": "tablist",
769
+ "config": {
770
+ "items": 6,
771
+ "variant": "tabbed",
772
+ "autoRotate": true,
773
+ "playing": true,
774
+ "initialIndex": 0
775
+ },
776
+ "steps": [
777
+ {
778
+ "expect": {
779
+ "attrs": {
780
+ "tab:0": {
781
+ "role": "tab",
782
+ "aria-label": "Slide 1",
783
+ "aria-controls": "*"
784
+ },
785
+ "tab:5": { "aria-label": "Slide 6" }
786
+ }
787
+ }
788
+ }
789
+ ]
790
+ },
791
+ {
792
+ "id": "carousel.apg.tablist.tabpanel-roledescription-slide",
793
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#rps_label",
794
+ "upstreamTest": "Example attributes: each tabpanel has aria-roledescription slide and aria-label X of Y",
795
+ "apg": "tablist",
796
+ "config": {
797
+ "items": 6,
798
+ "variant": "tabbed",
799
+ "autoRotate": true,
800
+ "playing": true,
801
+ "initialIndex": 0
802
+ },
803
+ "steps": [
804
+ {
805
+ "expect": {
806
+ "attrs": {
807
+ "slide:0": {
808
+ "role": "tabpanel",
809
+ "aria-roledescription": "slide",
810
+ "aria-label": "1 of 6"
811
+ }
812
+ }
813
+ }
814
+ }
815
+ ],
816
+ "notes": "Divergence: the Carousel Pattern page says a tabbed slide (tabpanel) does NOT carry aria-roledescription, but the carousel-2 example's role/attribute table sets it to slide; this scenario follows the example table."
817
+ },
818
+ {
819
+ "id": "carousel.apg.tablist.tab-order-rotation-then-tablist-then-slide",
820
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#kbd_label",
821
+ "upstreamTest": "Keyboard Support: the rotation control and the tab list precede the slide content in the Tab sequence; when the tablist holds focus Tab moves on to a link in the shown slide",
822
+ "apg": "tablist",
823
+ "config": {
824
+ "items": 6,
825
+ "variant": "tabbed",
826
+ "autoRotate": true,
827
+ "playing": true,
828
+ "initialIndex": 0
829
+ },
830
+ "steps": [
831
+ { "event": "focus", "target": "rotation" },
832
+ { "event": "key", "key": "Tab", "target": "rotation" },
833
+ { "expect": { "focus": "tab:0" } },
834
+ { "event": "key", "key": "Tab", "target": "tab:0" },
835
+ { "expect": { "focus": "slide:0" } }
836
+ ]
837
+ },
838
+ {
839
+ "id": "carousel.apg.tablist.auto-rotation-selects-next-tab",
840
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#aboutthispattern",
841
+ "upstreamTest": "About This Pattern: automatic rotation moves to the next slide, which in the tabbed variant activates the next tab",
842
+ "apg": "tablist",
843
+ "config": {
844
+ "items": 6,
845
+ "variant": "tabbed",
846
+ "autoRotate": true,
847
+ "playing": true,
848
+ "initialIndex": 0
849
+ },
850
+ "steps": [
851
+ { "event": "wait", "ms": 5000 },
852
+ {
853
+ "expect": {
854
+ "visible": ["slide:1"],
855
+ "attrs": { "tab:1": { "aria-selected": "true" } }
856
+ }
857
+ }
858
+ ]
859
+ },
860
+ {
861
+ "id": "carousel.apg.tablist.focus-stops-auto-rotation",
862
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#accessibilityfeatures",
863
+ "upstreamTest": "Accessibility Features: moving keyboard focus to any carousel content, including the tab elements, pauses automatic rotation",
864
+ "apg": "tablist",
865
+ "config": {
866
+ "items": 6,
867
+ "variant": "tabbed",
868
+ "autoRotate": true,
869
+ "playing": true,
870
+ "initialIndex": 0
871
+ },
872
+ "steps": [
873
+ { "event": "focus", "target": "tab:0" },
874
+ { "event": "wait", "ms": 5000 },
875
+ { "expect": { "visible": ["slide:0"] } }
876
+ ]
877
+ },
878
+ {
879
+ "id": "carousel.apg.tablist.hover-pauses-auto-rotation",
880
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-2-tablist/#accessibilityfeatures",
881
+ "upstreamTest": "Accessibility Features: hovering the mouse over carousel content pauses automatic rotation",
882
+ "apg": "tablist",
883
+ "config": {
884
+ "items": 6,
885
+ "variant": "tabbed",
886
+ "autoRotate": true,
887
+ "playing": true,
888
+ "initialIndex": 0
889
+ },
890
+ "steps": [
891
+ { "event": "hover", "target": "carousel" },
892
+ { "event": "wait", "ms": 5000 },
893
+ { "expect": { "visible": ["slide:0"] } }
894
+ ]
895
+ }
896
+ ],
897
+ "skipped": [
898
+ {
899
+ "upstreamTest": "Grouped carousel slide picker controls (buttons in a role=group, current picker aria-disabled=true)",
900
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/#wai-ariaroles,states,andproperties",
901
+ "reason": "third carousel style; the port targets only the basic and tabbed variants named in this task"
902
+ },
903
+ {
904
+ "upstreamTest": "Rendering controls/captions inside vs outside the image, focus styling, color contrast",
905
+ "upstream": "https://www.w3.org/WAI/ARIA/apg/patterns/carousel/examples/carousel-1-prev-next/#accessibilityfeatures",
906
+ "reason": "styling and view-option presentation, not interaction behaviour"
907
+ }
908
+ ]
909
+ }