From 755329ee976475e0e95cfb4c2331bffc40793779 Mon Sep 17 00:00:00 2001 From: Keith Cirkel Date: Tue, 10 Sep 2024 14:17:58 -0700 Subject: [PATCH] rewrite accessibility section on invokers (#1078) * rewrite accessibility section on invokers * Apply suggestions from code review Co-authored-by: Scott O'Hara * improve a11y sections based on code review * Apply suggestions from code review Co-authored-by: Scott O'Hara --------- Co-authored-by: Scott O'Hara --- .../pages/components/invokers.explainer.mdx | 295 ++++++++++++++++-- 1 file changed, 270 insertions(+), 25 deletions(-) diff --git a/site/src/pages/components/invokers.explainer.mdx b/site/src/pages/components/invokers.explainer.mdx index 573cde5f..f2447f2f 100644 --- a/site/src/pages/components/invokers.explainer.mdx +++ b/site/src/pages/components/invokers.explainer.mdx @@ -255,30 +255,6 @@ manual event handlers to the Invokers. ``` -### Accessibility - -> Warning: This section is incomplete PRs welcome. - -The _Invoker_ implicitly receives `aria-controls=IDREF` or `aria-details=IDREF` -(tbd) to expose the _Invoker_ controls another element (the _Invokee_) for -instances where the invokee is not a sibling to the invoker element. - -If the _Invokee_ has the `popover` attribute, the _Invoker_ implicitly receives -an `aria-expanded` state, as well as an `aria-details` association (for -instances where the elements are not DOM siblings) which will match the state of -the popover's openness. It will be `aria-expanded=true` when the `popover` is -`:popover-open` and `aria-expanded=false` otherwise. - -If the _Invokee_ is a `
` element the _Invoker_ implicitly receives an -`aria-expanded` state which will match the state of the _Invokee_'s openness. It -will be `aria-expanded=true` when the _Invokee_ is open and -`aria-expanded=false` otherwise. - -If the _Invokee_ is a `` element the _Invoker_ implicitly receives an -`aria-expanded` state which will match the state of the _Invokee_'s openness. It -will be `aria-expanded=true` when the _Invokee_ is open and -`aria-expanded=false` otherwise. - ### Defaults Depending on the target set by `commandfor`, invoking the button will trigger @@ -329,6 +305,274 @@ interactivity, and how the button may need to respond to such actions. Further to the initial ship we're also exploring implicit `command` or implicit `commandfor` values where the value can easily be inferred. +### Accessibility + +For built-in behaviours `command` attribute maps to specific accessibility +mappings which are placed on the button. The button may also use the +`commandfor` referenced element to gather other details (for example the state +of the element) to reflect that state on the button. + +These mappings will happen implicitly on the browsers Accessible Nodes, and so +while (for simplicity) this secion refers to various `aria-` attributes, +buttons will not sprout these attributes in the DOM, but the effective +equivalent will be exposed to Assistive Technologies. + +#### Buttons with `command=toggle-popover`, `command=hide-popover`, or `command=show-popover` + +Buttons with these popover commands will implicitly receive +`aria-details=IDREF`, where `IDREF` matches that of the `commandfor` attribute, +under certain conditions while the popover is in the showing state. + +
+ +Why? + +Some Assistive Technologes allow users to press a keyboard shortcut to navigate +to the element pointed to by `aria-details`. This is useful in scenarios where +the popover is _not_ the invoking element's next accessibility sibling, such as if there are buttons +or other controls between the invoking button and the popover. + +
+ +

+ +Browsers are expected to omit `aria-details` based on heuristics where it is redundant, +such as the contents of the popover being the next accessibility sibling to the invoking element, or while +the popover's contents are not traversable, such as when it is hidden, or not +in the DOM. + +Browsers should also omit `aria-details` when the invoking button is a descendant +of the popover it refers to, for example a close button inside the popover. + +Buttons will also implicitly receive an `aria-expanded` value. The state of the +`aria-expanded` value will map to the state of the popover. When the popover is +open the button will have `aria-expanded="true"`, when closed, +`aria-expanded="false"`. If the button is an descendant of the popover then it +will be explictly set to `aria-expanded="undefined"`, indicating to assistive +technology that the expanded state is not applicable to this button. +This attribute will be recomputed whenever the popover changes state, such that +the button always reflects the state of expansion. + +
+Why? + +Some Assistive Technologies will announce the state of expansion while the +button is focussed, for example a `` +may be announced by a screen reader as "File, button, collapsed", while +`` may be announced by a screen reader +as `File, button, expanded`. This is helpful information for users who require +an alternative of the visual confirmation of the popover being rendered on +screen. + +If the popover element is not in the DOM, browsers should ensure the button has +the `aria-expanded=false` state. Some websites may chose to render such +elements only when they are open, often referred to as "conditional rendering". +Browsers should aim to do their best to cater for this, and as such provide the +state under the assumpsion that a popover element may appear in the DOM on +press. + +
+ +

+ +Buttons with these popover commands will also have focus restored to the +button when the popover closes. This will require the popover keeping a +reference to the opening button so that it can return the focus to that button. + +
+Why? + +It is important for keyboard users (or other users who rely on focus) to not +have their focus state lost when performing actions on the page. If the page +shows a new piece of navigable UI, and shifts the user's focus to that UI, +then it is important to restore focus when that UI closes, so that the user's +journey is maintained. + +
+ +

+ +
+Other considerations not explicitly proposed. + +##### aria-pressed + +Given elements will have `aria-expanded`, adding `aria-pressed` would be +confusing or redundant, and as such won't be proposed for these buttons. + +##### aria-controls + +While `aria-controls` attempts to establish a similar style of relationship to +`aria-details`, `aria-details` sees broader support among various assistive +technologies, and it would be redundant to add both. + +##### aria-haspopup=dialog + +When the `commandfor=` element is a ``, adding aria-haspopup=dialog is +a potentially viable attribute to add. Some Assistive Technologies will announce +that the button will open a dialog, for example +`` may be announced by a screen +reader as "Delete, button, opens dialog". However, buttons that open dialogs +rarely come with additional visual treatment, and so the lack of a visual +analog means that using this attribute likely provides surplus information +to users of assistive technologies that isn't otherwise provided. Additionally, +the _intent_ for `aria-haspopup` is more focused towards combobox popovers, +where the type of popover may be ambigious. As command buttons can be used for +more than just combobox controls, `aria-haspopup` is not present by default. + +
+ +

+ +#### Buttons with `command=show-modal` + +Buttons with this dialog command will also have focus restored to the button +when the dialog closes. This will require the dialog keeping a reference to the +opening button so that it can return the focus to that button. + +
+Why? + +It is important for keyboard users (or other users who rely on focus) to not +have their focus state lost when performing actions on the page. If the page +shows a new piece of navigable UI, and shifts the user's focus to that UI, +then it is important to restore focus when that UI closes, so that the user's +journey is maintained. + +
+ +

+ +
+Other considerations not explicitly proposed. + +##### aria-details + +Exposing an `aria-details` relationship between the button and the dialog is +redundant. These relationships are useful only when the related element is +open and present on the page and the invoking button is still navigable. A +modal dialog renders the rest of the page inert meaning that it won't be +possible to navigate to the invoking button. + +##### aria-controls + +Similar to `aria-details`, `aria-controls` is not useful for a dialog that +is closed, and non-navigable for a dialog that is open as modal, so it would +provide no benefit to add. + +##### aria-pressed + +The `aria-pressed` state is the incorrect type of association for elements +which conditionally appear on the page. + +##### aria-expanded + +Similar to `aria-details`, the `aria-expanded` state redundant, given the +dialog is opened as modal. For the user to have navigated to a button that +opens a modal dialog, the dialog must not be open. If the dialog is open then +the button will be inert, therefore non-navigable, therefore it is redundant +to provide it an implicit `aria-expanded=true` state also. + +##### aria-haspopup=dialog + +When the `commandfor=` element is a `` it might seem `aria-haspopup=dialog` +would be a potentially viable attribute to add. Some Assistive Technologies will announce +that the button will open a dialog, for example +`` may be announced by a screen +reader as "Delete, button, opens dialog". However, buttons that open dialogs +rarely come with additional visual treatment, and so the [lack of a visual +analog means that using this attribute likely provides surplus information](https://w3c.github.io/aria/#h-note-50) +to users of assistive technologies that isn't otherwise provided. Additionally, +the _intent_ for `aria-haspopup` is more focused towards combobox popovers, +where the type of popover may be ambigious. As command buttons can be used for +more than just combobox controls, `aria-haspopup` is not present by default. + +
+ +

+ +#### Buttons with `command=close` + +Buttons with this dialog command will implicitly receive `aria-details=IDREF`, +where `IDREF` matches that of the `commandfor` attribute, while the dialog is +in the showing state, and the button is not a descendant of the dialog. + +
+Why? + +A button that closes a dialog is very typically found inside of the dialog, but +in some cases it may be found outside, perhaps as a "toggle" style button which +opens and closes a dialog as non-modal. This button may be used to close an open +dialog that is shown as non-modal. It may be useful for a user to traverse into +the dialog before closing it, for example to check if they have unsaved changes +within the dialog. + +
+ +

+ +Buttons will also implicitly receive an `aria-expanded` value, if they are not a +descendant of the `commandfor=` referenced element. The state of the +`aria-expanded` value will map to the state of the dialog's openness. When the +dialog is open the button will have `aria-expanded="true"`, when closed, +`aria-expanded="false"`. This will be recomputed whenever the dialog changes +state, such that the button always reflects the state of openness. + +
+Why? + +A button that closes a dialog is very typically found inside of the dialog, but +in some cases it may be found outside, perhaps as a "toggle" style button which +opens and closes a dialog as non-modal. + +Buttons outside of the dialog may be used to close an open dialog that is shown +as non-modal. It may be useful for a user to traverse into the dialog before +closing it, for example to check if they have unsaved changes within the dialog. +It may also be useful to know if the dialog is already closed (as in its +`aria-expanded` state is false), as this may help the user make a decision to +whether or not they action the close button. + +
+ +

+ +
+Other considerations not explicitly proposed. + +##### aria-pressed + +Given elements will have `aria-expanded`, adding `aria-pressed` would be +confusing or redundant, and as such won't be proposed for these buttons. + +##### aria-controls + +While `aria-controls` attempts to establish a similar style of relationship to +`aria-details`, `aria-details` sees broader support among various assistive +technologies, and it would be redundant to add both. + +
+ +

+ +#### Other built-in `command=` types + +The above commands are the core built-in command types which will be initially +shipping. Further built-in commands will be proposed on a case-per-case basis, +and additional aria or other logic will be considered with those at the time. + +#### Custom `command=` types + +Custom command types have accessibility requirements which will be hard to +determine based on the presence of the button and invokee alone. Consequently, +it is left to the implementer or user of the custom command to assign +appropriate aria markup to these buttons. + +#### Elements with `commandfor=` but no `command=` + +In this current proposal, elements without a `command=` attribute are +considered "author errors"; they will do nothing when invoked. These buttons +will recieve no additional implicit aria states. + ### Invokers and Custom Elements As the underlying system for invoke elements uses event dispatch, Custom Elements @@ -341,7 +585,8 @@ can make use of `CommandEvent` for their own behaviours. Consider the following: