From 6368b71f3312c75092b337004546f95e92ced8a9 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Tue, 31 Jan 2023 11:04:31 +0000 Subject: [PATCH 01/29] Create focus-returns-trigger-9au0ou.md First draft of the 2.4.3 focus returns to trigger rule/ --- _rules/focus-returns-trigger-9au0ou.md | 141 +++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 _rules/focus-returns-trigger-9au0ou.md diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md new file mode 100644 index 00000000000..654bda36dff --- /dev/null +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -0,0 +1,141 @@ +--- +id: 9au0ou +name: Focus returns to trigger +rule_type: atomic +description: | + This rule checks that when a modal closes the focus returns to the trigger that opened the modal. +accessibility_requirements: + wcag20:2.4.3: # Focus Order (A) + forConformance: true + failed: not satisfied + passed: further testing needed + inapplicable: further testing needed + wcag-technique:G59: # Placing the interactive elements in an order that follows sequences and relationships within the content + forConformance: false + failed: not satisfied + passed: further testing needed + inapplicable: further testing needed +input_aspects: + - Accessibility Tree + - DOM Tree +acknowledgments: + authors: + - Helen Burge + previous_authors: +--- + +## Applicability + +This rule applies to any [modal][] that when closed the [focus][] returns to the [trigger][]. + +## Expectation + +On closing the [modal][], the assistive technology [focus][] will return to the item in the owning document that triggered the [modal][]. + +## Assumptions + +There are no assumptions. + +## Accessibility Support + +There are no accessibility support issues known. + +## Background + +### Bibliography + +- [Understanding Success Criterion 2.4.3: Focus Order](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html) +- [G59: Placing the interactive elements in an order that follows sequences and relationships within the content](https://www.w3.org/WAI/WCAG21/Techniques/general/G59) + + +## Test Cases + +### Passed + +#### Passed Example 1 + +The button that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. + +```html +Code needed! +``` + +#### Passed Example 2 + +The button that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. + +```html +Code needed! +``` + +#### Passed Example 3 + +The button that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. + +```html +Code needed! +``` + +#### Passed Example 4 + +The link that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. + +```html +Code needed! +``` + +#### Passed Example 5 + +The link that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. + +```html +Code needed! +``` + +#### Passed Example 6 + +The link that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. + +```html +Code needed! +``` + +### Failed + +#### Failed Example 1 +The button that is activated to open the [modal][] gets the [focus][] returned to the item after it when the [modal][] is dismissed. + +```html +Code needed! +``` + +#### Failed Example 2 +The button that is activated to open the [modal][] gets the [focus][] returned to the start of the page when the modal is dismissed. + +```html +Code needed! +``` + +#### Failed Example 3 +The link that is activated to open the [modal][] gets the [focus][] returned to the item after it when the [modal][] is dismissed. + +```html +Code needed! +``` + +#### Failed Example 4 +The link that is activated to open the [modal][] gets the [focus][] returned to the start of the page when the modal is dismissed. + +```html +Code needed! +``` + +### Inapplicable + +#### Inapplicable Example 1 +TBC + +[trigger]: #trigger 'Definition of trigger' +[focus]: #focus 'Definition of focus' +[html]: #namespaced-element +[modal]: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/ From ef40a19bf7db0860f665a6b33d9b18d054e1559a Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Tue, 31 Jan 2023 11:16:02 +0000 Subject: [PATCH 02/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 654bda36dff..1768dcb1298 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -47,7 +47,6 @@ There are no accessibility support issues known. - [Understanding Success Criterion 2.4.3: Focus Order](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html) - [G59: Placing the interactive elements in an order that follows sequences and relationships within the content](https://www.w3.org/WAI/WCAG21/Techniques/general/G59) - ## Test Cases ### Passed @@ -135,6 +134,10 @@ Code needed! #### Inapplicable Example 1 TBC +```html +Code needed! +``` + [trigger]: #trigger 'Definition of trigger' [focus]: #focus 'Definition of focus' [html]: #namespaced-element From 12d4cacc22a63a1628dc8f3fefaaf9fc5d69a928 Mon Sep 17 00:00:00 2001 From: "U-CPHDOMAIN\\dtr" Date: Fri, 17 Feb 2023 18:05:42 -0500 Subject: [PATCH 03/29] For new rule 9au0ou "Focus returns to trigger" - adding passed example 1. Rough work. --- _rules/focus-returns-trigger-9au0ou.md | 31 ++- test-assets/9au0ou/9au0ou.css | 21 ++ test-assets/9au0ou/aria-apg-dialog.css | 135 ++++++++++ test-assets/9au0ou/aria-apg-dialog.js | 333 +++++++++++++++++++++++++ test-assets/9au0ou/aria-apg-utils.js | 131 ++++++++++ 5 files changed, 650 insertions(+), 1 deletion(-) create mode 100755 test-assets/9au0ou/9au0ou.css create mode 100755 test-assets/9au0ou/aria-apg-dialog.css create mode 100755 test-assets/9au0ou/aria-apg-dialog.js create mode 100755 test-assets/9au0ou/aria-apg-utils.js diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 1768dcb1298..8634b8dd317 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -56,7 +56,36 @@ There are no accessibility support issues known. The button that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. ```html -Code needed! + + + + Passed Example + + + + + + + + + +
+ +
+ + ``` #### Passed Example 2 diff --git a/test-assets/9au0ou/9au0ou.css b/test-assets/9au0ou/9au0ou.css new file mode 100755 index 00000000000..ca04dd9bd17 --- /dev/null +++ b/test-assets/9au0ou/9au0ou.css @@ -0,0 +1,21 @@ +.close-button { + padding: 0.3rem; + margin: 0.3rem; + /* + display: inline-block; + vertical-align: middle; + overflow: hidden; + text-decoration: none; + color: inherit; + background-color: inherit; + text-align: center; + cursor: pointer; + white-space: nowrap + */ +} + +.topright { + position: absolute; + right: 0; + top: 0 +} diff --git a/test-assets/9au0ou/aria-apg-dialog.css b/test-assets/9au0ou/aria-apg-dialog.css new file mode 100755 index 00000000000..0bf4286f807 --- /dev/null +++ b/test-assets/9au0ou/aria-apg-dialog.css @@ -0,0 +1,135 @@ +.hidden { + display: none; +} + +[role="dialog"] { + box-sizing: border-box; + padding: 15px; + border: 1px solid #000; + background-color: #fff; + min-height: 100vh; +} + +@media screen and (min-width: 640px) { + [role="dialog"] { + position: absolute; + top: 2rem; + left: 50vw; /* move to the middle of the screen (assumes relative parent is the body/viewport) */ + transform: translateX( + -50% + ); /* move backwards 50% of this element's width */ + + min-width: calc(640px - (15px * 2)); /* == breakpoint - left+right margin */ + min-height: auto; + box-shadow: 0 19px 38px rgb(0 0 0 / 12%), 0 15px 12px rgb(0 0 0 / 22%); + } +} + +.dialog_label { + text-align: center; +} + +.dialog_form { + margin: 15px; +} + +.dialog_form .label_text { + box-sizing: border-box; + padding-right: 0.5em; + display: inline-block; + font-size: 16px; + font-weight: bold; + width: 30%; + text-align: right; +} + +.dialog_form .label_info { + box-sizing: border-box; + padding-right: 0.5em; + font-size: 12px; + width: 30%; + text-align: right; + display: inline-block; +} + +.dialog_form_item { + margin: 10px 0; + font-size: 0; +} + +.dialog_form_item .wide_input { + box-sizing: border-box; + max-width: 70%; + width: 27em; +} + +.dialog_form_item .city_input { + box-sizing: border-box; + max-width: 70%; + width: 17em; +} + +.dialog_form_item .state_input { + box-sizing: border-box; + max-width: 70%; + width: 15em; +} + +.dialog_form_item .zip_input { + box-sizing: border-box; + max-width: 70%; + width: 9em; +} + +.dialog_form_actions { + text-align: right; + padding: 0 20px 20px; +} + +.dialog_close_button { + float: right; + position: absolute; + top: 10px; + left: 92%; + height: 25px; +} + +.dialog_close_button img { + border: 0; +} + +.dialog_desc { + padding: 10px 20px; +} + +/* native element uses the ::backdrop pseudo-element */ + +/* dialog::backdrop, */ +.dialog-backdrop { + display: none; + position: fixed; + overflow-y: auto; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +@media screen and (min-width: 640px) { + .dialog-backdrop { + background: rgb(0 0 0 / 30%); + } +} + +.dialog-backdrop.active { + display: block; +} + +.no-scroll { + overflow-y: auto !important; +} + +/* this is added to the body when a dialog is open */ +.has-dialog { + overflow: hidden; +} diff --git a/test-assets/9au0ou/aria-apg-dialog.js b/test-assets/9au0ou/aria-apg-dialog.js new file mode 100755 index 00000000000..1fa43809c61 --- /dev/null +++ b/test-assets/9au0ou/aria-apg-dialog.js @@ -0,0 +1,333 @@ +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + */ + +'use strict'; + +var aria = aria || {}; + +aria.Utils = aria.Utils || {}; + +(function () { + /* + * When util functions move focus around, set this true so the focus listener + * can ignore the events. + */ + aria.Utils.IgnoreUtilFocusChanges = false; + + aria.Utils.dialogOpenClass = 'has-dialog'; + + /** + * @description Set focus on descendant nodes until the first focusable element is + * found. + * @param element + * DOM node for which to find the first focusable descendant. + * @returns {boolean} + * true if a focusable element is found and focus is set. + */ + aria.Utils.focusFirstDescendant = function (element) { + for (var i = 0; i < element.childNodes.length; i++) { + var child = element.childNodes[i]; + if ( + aria.Utils.attemptFocus(child) || + aria.Utils.focusFirstDescendant(child) + ) { + return true; + } + } + return false; + }; // end focusFirstDescendant + + /** + * @description Find the last descendant node that is focusable. + * @param element + * DOM node for which to find the last focusable descendant. + * @returns {boolean} + * true if a focusable element is found and focus is set. + */ + aria.Utils.focusLastDescendant = function (element) { + for (var i = element.childNodes.length - 1; i >= 0; i--) { + var child = element.childNodes[i]; + if ( + aria.Utils.attemptFocus(child) || + aria.Utils.focusLastDescendant(child) + ) { + return true; + } + } + return false; + }; // end focusLastDescendant + + /** + * @description Set Attempt to set focus on the current node. + * @param element + * The node to attempt to focus on. + * @returns {boolean} + * true if element is focused. + */ + aria.Utils.attemptFocus = function (element) { + if (!aria.Utils.isFocusable(element)) { + return false; + } + + aria.Utils.IgnoreUtilFocusChanges = true; + try { + element.focus(); + } catch (e) { + // continue regardless of error + } + aria.Utils.IgnoreUtilFocusChanges = false; + return document.activeElement === element; + }; // end attemptFocus + + /* Modals can open modals. Keep track of them with this array. */ + aria.OpenDialogList = aria.OpenDialogList || new Array(0); + + /** + * @returns {object} the last opened dialog (the current dialog) + */ + aria.getCurrentDialog = function () { + if (aria.OpenDialogList && aria.OpenDialogList.length) { + return aria.OpenDialogList[aria.OpenDialogList.length - 1]; + } + }; + + aria.closeCurrentDialog = function () { + var currentDialog = aria.getCurrentDialog(); + if (currentDialog) { + currentDialog.close(); + return true; + } + + return false; + }; + + aria.handleEscape = function (event) { + var key = event.which || event.keyCode; + + if (key === aria.KeyCode.ESC && aria.closeCurrentDialog()) { + event.stopPropagation(); + } + }; + + document.addEventListener('keyup', aria.handleEscape); + + /** + * @class + * @description Dialog object providing modal focus management. + * + * Assumptions: The element serving as the dialog container is present in the + * DOM and hidden. The dialog container has role='dialog'. + * @param dialogId + * The ID of the element serving as the dialog container. + * @param focusAfterClosed + * Either the DOM node or the ID of the DOM node to focus when the + * dialog closes. + * @param focusFirst + * Optional parameter containing either the DOM node or the ID of the + * DOM node to focus when the dialog opens. If not specified, the + * first focusable element in the dialog will receive focus. + */ + aria.Dialog = function (dialogId, focusAfterClosed, focusFirst) { + this.dialogNode = document.getElementById(dialogId); + if (this.dialogNode === null) { + throw new Error('No element found with id="' + dialogId + '".'); + } + + var validRoles = ['dialog', 'alertdialog']; + var isDialog = (this.dialogNode.getAttribute('role') || '') + .trim() + .split(/\s+/g) + .some(function (token) { + return validRoles.some(function (role) { + return token === role; + }); + }); + if (!isDialog) { + throw new Error( + 'Dialog() requires a DOM element with ARIA role of dialog or alertdialog.' + ); + } + + // Wrap in an individual backdrop element if one doesn't exist + // Native elements use the ::backdrop pseudo-element, which + // works similarly. + var backdropClass = 'dialog-backdrop'; + if (this.dialogNode.parentNode.classList.contains(backdropClass)) { + this.backdropNode = this.dialogNode.parentNode; + } else { + this.backdropNode = document.createElement('div'); + this.backdropNode.className = backdropClass; + this.dialogNode.parentNode.insertBefore( + this.backdropNode, + this.dialogNode + ); + this.backdropNode.appendChild(this.dialogNode); + } + this.backdropNode.classList.add('active'); + + // Disable scroll on the body element + document.body.classList.add(aria.Utils.dialogOpenClass); + + if (typeof focusAfterClosed === 'string') { + this.focusAfterClosed = document.getElementById(focusAfterClosed); + } else if (typeof focusAfterClosed === 'object') { + this.focusAfterClosed = focusAfterClosed; + } else { + throw new Error( + 'the focusAfterClosed parameter is required for the aria.Dialog constructor.' + ); + } + + if (typeof focusFirst === 'string') { + this.focusFirst = document.getElementById(focusFirst); + } else if (typeof focusFirst === 'object') { + this.focusFirst = focusFirst; + } else { + this.focusFirst = null; + } + + // Bracket the dialog node with two invisible, focusable nodes. + // While this dialog is open, we use these to make sure that focus never + // leaves the document even if dialogNode is the first or last node. + var preDiv = document.createElement('div'); + this.preNode = this.dialogNode.parentNode.insertBefore( + preDiv, + this.dialogNode + ); + this.preNode.tabIndex = 0; + var postDiv = document.createElement('div'); + this.postNode = this.dialogNode.parentNode.insertBefore( + postDiv, + this.dialogNode.nextSibling + ); + this.postNode.tabIndex = 0; + + // If this modal is opening on top of one that is already open, + // get rid of the document focus listener of the open dialog. + if (aria.OpenDialogList.length > 0) { + aria.getCurrentDialog().removeListeners(); + } + + this.addListeners(); + aria.OpenDialogList.push(this); + this.clearDialog(); + this.dialogNode.className = 'default_dialog'; // make visible + + if (this.focusFirst) { + this.focusFirst.focus(); + } else { + aria.Utils.focusFirstDescendant(this.dialogNode); + } + + this.lastFocus = document.activeElement; + }; // end Dialog constructor + + aria.Dialog.prototype.clearDialog = function () { + Array.prototype.map.call( + this.dialogNode.querySelectorAll('input'), + function (input) { + input.value = ''; + } + ); + }; + + /** + * @description + * Hides the current top dialog, + * removes listeners of the top dialog, + * restore listeners of a parent dialog if one was open under the one that just closed, + * and sets focus on the element specified for focusAfterClosed. + */ + aria.Dialog.prototype.close = function () { + aria.OpenDialogList.pop(); + this.removeListeners(); + aria.Utils.remove(this.preNode); + aria.Utils.remove(this.postNode); + this.dialogNode.className = 'hidden'; + this.backdropNode.classList.remove('active'); + this.focusAfterClosed.focus(); + + // If a dialog was open underneath this one, restore its listeners. + if (aria.OpenDialogList.length > 0) { + aria.getCurrentDialog().addListeners(); + } else { + document.body.classList.remove(aria.Utils.dialogOpenClass); + } + }; // end close + + /** + * @description + * Hides the current dialog and replaces it with another. + * @param newDialogId + * ID of the dialog that will replace the currently open top dialog. + * @param newFocusAfterClosed + * Optional ID or DOM node specifying where to place focus when the new dialog closes. + * If not specified, focus will be placed on the element specified by the dialog being replaced. + * @param newFocusFirst + * Optional ID or DOM node specifying where to place focus in the new dialog when it opens. + * If not specified, the first focusable element will receive focus. + */ + aria.Dialog.prototype.replace = function ( + newDialogId, + newFocusAfterClosed, + newFocusFirst + ) { + aria.OpenDialogList.pop(); + this.removeListeners(); + aria.Utils.remove(this.preNode); + aria.Utils.remove(this.postNode); + this.dialogNode.className = 'hidden'; + this.backdropNode.classList.remove('active'); + + var focusAfterClosed = newFocusAfterClosed || this.focusAfterClosed; + new aria.Dialog(newDialogId, focusAfterClosed, newFocusFirst); + }; // end replace + + aria.Dialog.prototype.addListeners = function () { + document.addEventListener('focus', this.trapFocus, true); + }; // end addListeners + + aria.Dialog.prototype.removeListeners = function () { + document.removeEventListener('focus', this.trapFocus, true); + }; // end removeListeners + + aria.Dialog.prototype.trapFocus = function (event) { + if (aria.Utils.IgnoreUtilFocusChanges) { + return; + } + var currentDialog = aria.getCurrentDialog(); + if (currentDialog.dialogNode.contains(event.target)) { + currentDialog.lastFocus = event.target; + } else { + aria.Utils.focusFirstDescendant(currentDialog.dialogNode); + if (currentDialog.lastFocus == document.activeElement) { + aria.Utils.focusLastDescendant(currentDialog.dialogNode); + } + currentDialog.lastFocus = document.activeElement; + } + }; // end trapFocus + + window.openDialog = function (dialogId, focusAfterClosed, focusFirst) { + new aria.Dialog(dialogId, focusAfterClosed, focusFirst); + }; + + window.closeDialog = function (closeButton) { + var topDialog = aria.getCurrentDialog(); + if (topDialog.dialogNode.contains(closeButton)) { + topDialog.close(); + } + }; // end closeDialog + + window.replaceDialog = function ( + newDialogId, + newFocusAfterClosed, + newFocusFirst + ) { + var topDialog = aria.getCurrentDialog(); + if (topDialog.dialogNode.contains(document.activeElement)) { + topDialog.replace(newDialogId, newFocusAfterClosed, newFocusFirst); + } + }; // end replaceDialog +})(); diff --git a/test-assets/9au0ou/aria-apg-utils.js b/test-assets/9au0ou/aria-apg-utils.js new file mode 100755 index 00000000000..116414b8f5f --- /dev/null +++ b/test-assets/9au0ou/aria-apg-utils.js @@ -0,0 +1,131 @@ +'use strict'; +/** + * @namespace aria + */ + +var aria = aria || {}; + +/** + * @description + * Key code constants + */ +aria.KeyCode = { + BACKSPACE: 8, + TAB: 9, + RETURN: 13, + SHIFT: 16, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46, +}; + +aria.Utils = aria.Utils || {}; + +// Polyfill src https://developer.mozilla.org/en-US/docs/Web/API/Element/matches +aria.Utils.matches = function (element, selector) { + if (!Element.prototype.matches) { + Element.prototype.matches = + Element.prototype.matchesSelector || + Element.prototype.mozMatchesSelector || + Element.prototype.msMatchesSelector || + Element.prototype.oMatchesSelector || + Element.prototype.webkitMatchesSelector || + function (s) { + var matches = element.parentNode.querySelectorAll(s); + var i = matches.length; + while (--i >= 0 && matches.item(i) !== this) { + // empty + } + return i > -1; + }; + } + + return element.matches(selector); +}; + +aria.Utils.remove = function (item) { + if (item.remove && typeof item.remove === 'function') { + return item.remove(); + } + if ( + item.parentNode && + item.parentNode.removeChild && + typeof item.parentNode.removeChild === 'function' + ) { + return item.parentNode.removeChild(item); + } + return false; +}; + +aria.Utils.isFocusable = function (element) { + if (element.tabIndex < 0) { + return false; + } + + if (element.disabled) { + return false; + } + + switch (element.nodeName) { + case 'A': + return !!element.href && element.rel != 'ignore'; + case 'INPUT': + return element.type != 'hidden'; + case 'BUTTON': + case 'SELECT': + case 'TEXTAREA': + return true; + default: + return false; + } +}; + +aria.Utils.getAncestorBySelector = function (element, selector) { + if (!aria.Utils.matches(element, selector + ' ' + element.tagName)) { + // Element is not inside an element that matches selector + return null; + } + + // Move up the DOM tree until a parent matching the selector is found + var currentNode = element; + var ancestor = null; + while (ancestor === null) { + if (aria.Utils.matches(currentNode.parentNode, selector)) { + ancestor = currentNode.parentNode; + } else { + currentNode = currentNode.parentNode; + } + } + + return ancestor; +}; + +aria.Utils.hasClass = function (element, className) { + return new RegExp('(\\s|^)' + className + '(\\s|$)').test(element.className); +}; + +aria.Utils.addClass = function (element, className) { + if (!aria.Utils.hasClass(element, className)) { + element.className += ' ' + className; + } +}; + +aria.Utils.removeClass = function (element, className) { + var classRegex = new RegExp('(\\s|^)' + className + '(\\s|$)'); + element.className = element.className.replace(classRegex, ' ').trim(); +}; + +aria.Utils.bindMethods = function (object /* , ...methodNames */) { + var methodNames = Array.prototype.slice.call(arguments, 1); + methodNames.forEach(function (method) { + object[method] = object[method].bind(object); + }); +}; From 28dc461fc8e47f1cfff1e135df2fa477f4afb9b3 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Thu, 4 May 2023 12:21:24 +0100 Subject: [PATCH 04/29] Update focus-returns-trigger-9au0ou.md Added updates from @Jym77 feedback --- _rules/focus-returns-trigger-9au0ou.md | 39 ++++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 8634b8dd317..8ac094aef9d 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -26,11 +26,11 @@ acknowledgments: ## Applicability -This rule applies to any [modal][] that when closed the [focus][] returns to the [trigger][]. +This rule applies to any [modal][] or item with the role of `dialog` + `aria-modal="true"` that when no longer visible, the [focus][] returns to the [instrument][] or [trigger][] that made the [modal][] visible. ## Expectation -On closing the [modal][], the assistive technology [focus][] will return to the item in the owning document that triggered the [modal][]. +On exiting the [modal][] making it no longer visible, the assistive technology [focus][] will return to the item in the owning document that was used to make the [modal][] visible. ## Assumptions @@ -53,7 +53,7 @@ There are no accessibility support issues known. #### Passed Example 1 -The button that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. +The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. ```html @@ -90,7 +90,7 @@ The button that is activated to open the [modal][] gets the [focus][] returned t #### Passed Example 2 -The button that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. +The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. ```html Code needed! @@ -98,7 +98,7 @@ Code needed! #### Passed Example 3 -The button that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. +The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. ```html Code needed! @@ -106,7 +106,7 @@ Code needed! #### Passed Example 4 -The link that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. +The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. ```html Code needed! @@ -114,7 +114,7 @@ Code needed! #### Passed Example 5 -The link that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. +The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. ```html Code needed! @@ -122,7 +122,14 @@ Code needed! #### Passed Example 6 -The link that is activated to open the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. +The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. + +```html +Code needed! +``` + +#### Passed Example 7 +The page has a list of buttons that is activated to display a [modal][], the [focus][] is returned to the correct button on dismissing the modal. ```html Code needed! @@ -131,28 +138,35 @@ Code needed! ### Failed #### Failed Example 1 -The button that is activated to open the [modal][] gets the [focus][] returned to the item after it when the [modal][] is dismissed. +The button that is activated to display the [modal][] gets the [focus][] returned to the item after it when the [modal][] is dismissed. ```html Code needed! ``` #### Failed Example 2 -The button that is activated to open the [modal][] gets the [focus][] returned to the start of the page when the modal is dismissed. +The button that is activated to display the [modal][] gets the [focus][] returned to the start of the page when the modal is dismissed. ```html Code needed! ``` #### Failed Example 3 -The link that is activated to open the [modal][] gets the [focus][] returned to the item after it when the [modal][] is dismissed. +The link that is activated to display the [modal][] gets the [focus][] returned to the item after it when the [modal][] is dismissed. ```html Code needed! ``` #### Failed Example 4 -The link that is activated to open the [modal][] gets the [focus][] returned to the start of the page when the modal is dismissed. +The link that is activated to display the [modal][] gets the [focus][] returned to the start of the page when the modal is dismissed. + +```html +Code needed! +``` + +#### Failed Example 5 +The page has a list of buttons that is activated to display a [modal][], the [focus][] is returned to the incorrect button on dismissing the modal. ```html Code needed! @@ -171,3 +185,4 @@ Code needed! [focus]: #focus 'Definition of focus' [html]: #namespaced-element [modal]: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/ +[instrument]: #instrument-to-achieve-an-objective 'Definition of instrument' From 16eb30cad1c74cccb5a3072563d57084c98410dc Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Thu, 8 Jun 2023 14:27:29 +0100 Subject: [PATCH 05/29] Update _rules/focus-returns-trigger-9au0ou.md Co-authored-by: Jean-Yves Moyen --- _rules/focus-returns-trigger-9au0ou.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 8ac094aef9d..7bfb9366b1e 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -30,7 +30,7 @@ This rule applies to any [modal][] or item with the role of `dialog` + `aria-mod ## Expectation -On exiting the [modal][] making it no longer visible, the assistive technology [focus][] will return to the item in the owning document that was used to make the [modal][] visible. +After the [modal][] is closed, the [instrument][] that was used to open it becomes [focused][]. ## Assumptions From 83ece03c55985103ce993b634aa9d48ec44c6a74 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:13:08 +0000 Subject: [PATCH 06/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 7bfb9366b1e..9c5ee86f502 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -26,11 +26,11 @@ acknowledgments: ## Applicability -This rule applies to any [modal][] or item with the role of `dialog` + `aria-modal="true"` that when no longer visible, the [focus][] returns to the [instrument][] or [trigger][] that made the [modal][] visible. +This rule applies to any element with a semantic role of dialog or alertdialog that is not visible. ## Expectation -After the [modal][] is closed, the [instrument][] that was used to open it becomes [focused][]. +The element that has the focus when the target element becomes visible is the same element that receives focus when the target element stops being visible. ## Assumptions From 302ffdccc91ba276db8afdf713514160904a8dff Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:50:48 +0000 Subject: [PATCH 07/29] Update spelling-ignore.yml Adding TBC to Notes and acronyms --- __tests__/spelling-ignore.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/__tests__/spelling-ignore.yml b/__tests__/spelling-ignore.yml index 3a0101090eb..0d83ee36d2f 100644 --- a/__tests__/spelling-ignore.yml +++ b/__tests__/spelling-ignore.yml @@ -127,6 +127,7 @@ - TODO - TBD - CJK +- TBC # Tool and library names - axe-core From 8f7ce2a3edae672a71e495260b2b7a485cfb4636 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:56:53 +0000 Subject: [PATCH 08/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 9c5ee86f502..87df3750c2e 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -185,4 +185,3 @@ Code needed! [focus]: #focus 'Definition of focus' [html]: #namespaced-element [modal]: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/ -[instrument]: #instrument-to-achieve-an-objective 'Definition of instrument' From 24f8a6a30baf56106d9144eab2a5a4307ca8f264 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:00:28 +0000 Subject: [PATCH 09/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 87df3750c2e..fc292f5db3c 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -183,5 +183,4 @@ Code needed! [trigger]: #trigger 'Definition of trigger' [focus]: #focus 'Definition of focus' -[html]: #namespaced-element [modal]: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/ From 969770f0b8a1ce5edd4d9090bb47fd25b5d2057e Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:03:35 +0000 Subject: [PATCH 10/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index fc292f5db3c..a679e05bc69 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -181,6 +181,5 @@ TBC Code needed! ``` -[trigger]: #trigger 'Definition of trigger' [focus]: #focus 'Definition of focus' [modal]: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/ From eb1cfe4e8da270d75e54ede94782c43401bdcd73 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 09:29:56 +0000 Subject: [PATCH 11/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index a679e05bc69..8e265a054d5 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -44,8 +44,8 @@ There are no accessibility support issues known. ### Bibliography -- [Understanding Success Criterion 2.4.3: Focus Order](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html) -- [G59: Placing the interactive elements in an order that follows sequences and relationships within the content](https://www.w3.org/WAI/WCAG21/Techniques/general/G59) +- [Understanding Success Criterion 2.4.3: Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) +- [G59: Placing the interactive elements in an order that follows sequences and relationships within the content](https://www.w3.org/WAI/WCAG22/Techniques/general/G59) ## Test Cases From a154fc355048997d559e76a2e090db0b6905a935 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 09:45:16 +0000 Subject: [PATCH 12/29] Create focus.md As focused is not great context with a test for current focus --- pages/glossary/focus.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 pages/glossary/focus.md diff --git a/pages/glossary/focus.md b/pages/glossary/focus.md new file mode 100644 index 00000000000..22bca2de933 --- /dev/null +++ b/pages/glossary/focus.md @@ -0,0 +1,12 @@ +--- +title: Focus +key: focus +unambiguous: true +objective: true +input_aspects: + - CSS styling +--- + +An element is said to have _focus_ when the element matches the [`:focus` pseudo-class](https://drafts.csswg.org/selectors-4/#focus-pseudo) uninterruptedly for a period of 1 second after a user stopped interacting with the page. + +The 1 second time span is an arbitrary limit which is not included in WCAG. Given the possibility of the focus state of elements being managed through scripts, testing the focused state of an element consistently would be impractical without a time limit. From 9dc8b424d7dcc0370f670b51980b7105127cef35 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 09:52:00 +0000 Subject: [PATCH 13/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 8e265a054d5..4c9ddd423ad 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -32,16 +32,16 @@ This rule applies to any element with a semantic role of dialog or alertdialog t The element that has the focus when the target element becomes visible is the same element that receives focus when the target element stops being visible. -## Assumptions +## Background + +### Assumptions There are no assumptions. -## Accessibility Support +### Accessibility Support There are no accessibility support issues known. -## Background - ### Bibliography - [Understanding Success Criterion 2.4.3: Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) From dc189e1108c1d1f1a3c416d9fa2f088f19a03ef0 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 09:55:10 +0000 Subject: [PATCH 14/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 4c9ddd423ad..800e863636c 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -22,6 +22,7 @@ acknowledgments: authors: - Helen Burge previous_authors: + - Helen Burge --- ## Applicability From d05143d48b670726c99d43af8ba7188988b39d7f Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 10:22:28 +0000 Subject: [PATCH 15/29] Delete pages/glossary/focus.md --- pages/glossary/focus.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 pages/glossary/focus.md diff --git a/pages/glossary/focus.md b/pages/glossary/focus.md deleted file mode 100644 index 22bca2de933..00000000000 --- a/pages/glossary/focus.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Focus -key: focus -unambiguous: true -objective: true -input_aspects: - - CSS styling ---- - -An element is said to have _focus_ when the element matches the [`:focus` pseudo-class](https://drafts.csswg.org/selectors-4/#focus-pseudo) uninterruptedly for a period of 1 second after a user stopped interacting with the page. - -The 1 second time span is an arbitrary limit which is not included in WCAG. Given the possibility of the focus state of elements being managed through scripts, testing the focused state of an element consistently would be impractical without a time limit. From d593f433a8c7eee0b4396583cbcf504599e147ef Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 10:23:12 +0000 Subject: [PATCH 16/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 800e863636c..636b886242a 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -182,5 +182,5 @@ TBC Code needed! ``` -[focus]: #focus 'Definition of focus' +[focus]: #focused 'Definition of focused' [modal]: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/ From d0c16223be7c381fba3e553e78b7c6d80d86cff6 Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:06:45 +0000 Subject: [PATCH 17/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 150 +++++++++++++++++-------- 1 file changed, 105 insertions(+), 45 deletions(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 636b886242a..74137475f28 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -54,13 +54,13 @@ There are no accessibility support issues known. #### Passed Example 1 -The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. +The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key or activating close, Ok or cancel button in the modal. ```html - Passed Example + Passed Example 1 @@ -91,49 +91,39 @@ The button that is activated to display the [modal][] gets the [focus][] returne #### Passed Example 2 -The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. +The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key or activating close, Ok or cancel button in the modal. ```html -Code needed! -``` - -#### Passed Example 3 - -The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. - -```html -Code needed! -``` - -#### Passed Example 4 - -The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the close button in the modal. - -```html -Code needed! -``` - -#### Passed Example 5 - -The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key. - -```html -Code needed! -``` - -#### Passed Example 6 - -The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the cancel button. - -```html -Code needed! -``` - -#### Passed Example 7 -The page has a list of buttons that is activated to display a [modal][], the [focus][] is returned to the correct button on dismissing the modal. - -```html -Code needed! + + + + Passed Example 2 + + + + + + + Do nothing + Open modal dialog + Do nothing +
+ +
+ + ``` ### Failed @@ -142,14 +132,84 @@ Code needed! The button that is activated to display the [modal][] gets the [focus][] returned to the item after it when the [modal][] is dismissed. ```html -Code needed! + + + + Failed Example 1 + + + + + + + +

+

+

+
+ +
+ + ``` #### Failed Example 2 The button that is activated to display the [modal][] gets the [focus][] returned to the start of the page when the modal is dismissed. ```html -Code needed! + + + + Failed Example 2 + + + + + + + +

+

+

+
+ +
+ + ``` #### Failed Example 3 From c760002ebd9eedb825d91f2a262b937a483eaafe Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:11:51 +0000 Subject: [PATCH 18/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 74137475f28..64887d3ee76 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -54,7 +54,7 @@ There are no accessibility support issues known. #### Passed Example 1 -The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key or activating close, Ok or cancel button in the modal. +The button that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key or activating close, OK or cancel button in the modal. ```html @@ -80,7 +80,7 @@ The button that is activated to display the [modal][] gets the [focus][] returne ×
- +
@@ -91,7 +91,7 @@ The button that is activated to display the [modal][] gets the [focus][] returne #### Passed Example 2 -The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key or activating close, Ok or cancel button in the modal. +The link that is activated to display the [modal][] gets the [focus][] returned to it when the [modal][] is dismissed using the escape key or activating close, OK or cancel button in the modal. ```html @@ -117,7 +117,7 @@ The link that is activated to display the [modal][] gets the [focus][] returned ×
- +
@@ -161,7 +161,7 @@ The button that is activated to display the [modal][] gets the [focus][] returne ×
- +
@@ -203,7 +203,7 @@ The button that is activated to display the [modal][] gets the [focus][] returne ×
- +
From 545599b740227e811c6870664c8b4a8d44f3bfad Mon Sep 17 00:00:00 2001 From: HelenBurge <41951878+HelenBurge@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:18:55 +0000 Subject: [PATCH 19/29] Update focus-returns-trigger-9au0ou.md --- _rules/focus-returns-trigger-9au0ou.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_rules/focus-returns-trigger-9au0ou.md b/_rules/focus-returns-trigger-9au0ou.md index 64887d3ee76..733092a95d5 100644 --- a/_rules/focus-returns-trigger-9au0ou.md +++ b/_rules/focus-returns-trigger-9au0ou.md @@ -104,9 +104,9 @@ The link that is activated to display the [modal][] gets the [focus][] returned - Do nothing + Do nothing Open modal dialog - Do nothing + Do nothing