diff --git a/ARIA/apg/patterns/accordion/accordion-pattern.md b/ARIA/apg/patterns/accordion/accordion-pattern.md
index cdb175baf..303d44b7b 100644
--- a/ARIA/apg/patterns/accordion/accordion-pattern.md
+++ b/ARIA/apg/patterns/accordion/accordion-pattern.md
@@ -102,7 +102,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Tab: Moves focus to the next focusable element; all focusable elements in the accordion are included in the page Tab sequence.
-
Shift + Tab: Moves focus to the previous focusable element; all focusable elements in the accordion are included in the page Tab sequence.
+
Shift + Tab: Moves focus to the previous focusable element; all focusable elements in the accordion are included in the page Tab sequence.
Down Arrow (Optional): If focus is on an accordion header, moves focus to the next accordion header.
If focus is on the last accordion header, either does nothing or moves focus to the first accordion header.
diff --git a/ARIA/apg/patterns/accordion/examples/accordion.md b/ARIA/apg/patterns/accordion/examples/accordion.md
index e780440d6..69b75b267 100644
--- a/ARIA/apg/patterns/accordion/examples/accordion.md
+++ b/ARIA/apg/patterns/accordion/examples/accordion.md
@@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/accordion/examples/accordion/
sidebar: true
-footer: " "
+footer: " "
# Context here: https://github.com/w3c/wai-aria-practices/issues/31
type_of_guidance: APG
@@ -238,7 +238,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Moves focus to previous focusable element inside the dialog.
@@ -210,11 +210,11 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Closes the dialog.
-
Command + S
+
Command + S
(Mac only) Save the contents of the notes textarea when focused.
-
Control + S
+
Control + S
(Windows only) Save the contents of the notes textarea when focused.
diff --git a/ARIA/apg/patterns/button/button-pattern.md b/ARIA/apg/patterns/button/button-pattern.md
index 636d0c970..2b6478e59 100644
--- a/ARIA/apg/patterns/button/button-pattern.md
+++ b/ARIA/apg/patterns/button/button-pattern.md
@@ -123,7 +123,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
If the button action indicates a context change, such as move to next step in a wizard or add another search criteria, then it is often appropriate to move focus to the starting point for that action.
If the button is activated with a shortcut key, the focus usually remains in the context from which the shortcut key was activated.
- For example, if Alt + U were assigned to an "Up" button that moves the currently focused item in a list one position higher in the list, pressing Alt + U when the focus is in the list would not move the focus from the list.
+ For example, if Alt + U were assigned to an "Up" button that moves the currently focused item in a list one position higher in the list, pressing Alt + U when the focus is in the list would not move the focus from the list.
diff --git a/ARIA/apg/patterns/carousel/carousel-pattern.md b/ARIA/apg/patterns/carousel/carousel-pattern.md
index bfd7a4d8c..59660148f 100644
--- a/ARIA/apg/patterns/carousel/carousel-pattern.md
+++ b/ARIA/apg/patterns/carousel/carousel-pattern.md
@@ -129,7 +129,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
If the carousel has an auto-rotate feature, automatic slide rotation stops when any element in the carousel receives keyboard focus.
It does not resume unless the user activates the rotation control.
-
Tab and Shift + Tab: Move focus through the interactive elements of the carousel as specified by the page tab sequence -- scripting for Tab is not necessary.
+
Tab and Shift + Tab: Move focus through the interactive elements of the carousel as specified by the page tab sequence -- scripting for Tab is not necessary.
Button elements implement the keyboard interaction defined in the button pattern.
Note: Activating the rotation control, next slide, and previous slide do not move focus, so users may easily repetitively activate them as many times as desired.
diff --git a/ARIA/apg/patterns/combobox/combobox-pattern.md b/ARIA/apg/patterns/combobox/combobox-pattern.md
index f58680ebd..6a6fbd4ed 100644
--- a/ARIA/apg/patterns/combobox/combobox-pattern.md
+++ b/ARIA/apg/patterns/combobox/combobox-pattern.md
@@ -191,9 +191,9 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
If the combobox is editable, it supports standard single line text editing keys appropriate for the device platform (see note below).
-
Alt + Down Arrow (Optional): If the popup is available but not displayed, displays the popup without moving focus.
+
Alt + Down Arrow (Optional): If the popup is available but not displayed, displays the popup without moving focus.
- Alt + Up Arrow (Optional): If the popup is displayed:
+ Alt + Up Arrow (Optional): If the popup is displayed:
If the popup contains focus, returns focus to the combobox.
Closes the popup.
@@ -313,8 +313,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
If the combobox is editable, returns focus to the combobox and places the cursor after the last character.
-
Control + Home (optional): moves focus to the first row.
-
Control + End (Optional): moves focus to the last row.
+
Control + Home (optional): moves focus to the first row.
+
Control + End (Optional): moves focus to the last row.
Any printable character: If the combobox is editable, returns the focus to the combobox without closing the popup and types the character.
Backspace (Optional): If the combobox is editable, returns focus to the combobox and deletes the character prior to the cursor.
Delete (Optional): If the combobox is editable, returns focus to the combobox, removes the selected state if a suggestion was selected, and removes the inline autocomplete string if present.
The below date picker demonstrates an implementation of the Combobox Pattern that opens a dialog.
- The date picker dialog is opened by activating the choose date button or by moving keyboard focus to the combobox and pressing Down Arrow or Alt + Down Arrow.
+ The date picker dialog is opened by activating the choose date button or by moving keyboard focus to the combobox and pressing Down Arrow or Alt + Down Arrow.
The dialog contains an implementation of the grid pattern for displaying a calendar and enabling selection of a date.
Additional buttons in the dialog are available for changing the month and year shown in the grid.
@@ -288,7 +288,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
-
Down Arrow, ALT + Down Arrow
+
Down Arrow, ALT + Down Arrow
Open the date picker dialog.
@@ -328,7 +328,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
-
Shift + TAB
+
Shift + TAB
Moves focus to previous element in the dialog Tab sequence.
Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
+
Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
An HTML input with type=text is used for the textbox so the browser will provide platform-specific editing keys.
diff --git a/ARIA/apg/patterns/dialog-modal/dialog-modal-pattern.md b/ARIA/apg/patterns/dialog-modal/dialog-modal-pattern.md
index fb027ca6b..af519b3e4 100644
--- a/ARIA/apg/patterns/dialog-modal/dialog-modal-pattern.md
+++ b/ARIA/apg/patterns/dialog-modal/dialog-modal-pattern.md
@@ -69,7 +69,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Like non-modal dialogs, modal dialogs contain their tab sequence.
- That is, Tab and Shift + Tab do not move focus outside the dialog.
+ That is, Tab and Shift + Tab do not move focus outside the dialog.
However, unlike most non-modal dialogs, modal dialogs do not provide means for moving keyboard focus outside the dialog window without closing the dialog.
@@ -106,7 +106,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- Shift + Tab:
+ Shift + Tab:
Moves focus to the previous tabbable element inside the dialog.
If focus is on the first tabbable element inside the dialog, moves focus to the last tabbable element inside the dialog.
Move focus to the first focusable element after the feed.
-
Control + Home
+
Control + Home
Move focus to the first focusable element in the feed.
diff --git a/ARIA/apg/patterns/feed/feed-pattern.md b/ARIA/apg/patterns/feed/feed-pattern.md
index eb3fa264b..c0c9f39db 100644
--- a/ARIA/apg/patterns/feed/feed-pattern.md
+++ b/ARIA/apg/patterns/feed/feed-pattern.md
@@ -122,8 +122,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Page Down: Move focus to next article.
Page Up: Move focus to previous article.
-
Control + End: Move focus to the first focusable element after the feed.
-
Control + Home: Move focus to the first focusable element before the feed.
+
Control + End: Move focus to the first focusable element after the feed.
+
Control + Home: Move focus to the first focusable element before the feed.
Note
@@ -139,8 +139,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Users move focus into the nested feed from the content of the containing article with Tab.
This may be slow if the article contains a significant number of links, buttons, or other widgets.
-
Provide a key for moving focus from the elements in the containing article to the first item in the nested feed, e.g., Alt + Page Down.
-
To continue reading the outer feed, Control + End moves focus to the next article in the outer feed.
+
Provide a key for moving focus from the elements in the containing article to the first item in the nested feed, e.g., Alt + Page Down.
+
To continue reading the outer feed, Control + End moves focus to the next article in the outer feed.
In the rare circumstance that a feed article contains a widget that uses the above suggested keys, the feed navigation key will operate the contained widget, and the user needs to move focus to an element that does not utilize the feed navigation keys in order to navigate the feed.
diff --git a/ARIA/apg/patterns/grid/grid-pattern.md b/ARIA/apg/patterns/grid/grid-pattern.md
index afd99e993..abafc7fa6 100644
--- a/ARIA/apg/patterns/grid/grid-pattern.md
+++ b/ARIA/apg/patterns/grid/grid-pattern.md
@@ -152,8 +152,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Home: moves focus to the first cell in the row that contains focus.
End: moves focus to the last cell in the row that contains focus.
-
Control + Home: moves focus to the first cell in the first row.
-
Control + End: moves focus to the last cell in the last row.
+
Control + Home: moves focus to the first cell in the first row.
+
Control + End: moves focus to the last cell in the last row.
Note
@@ -166,21 +166,21 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
While navigation keys, such as arrow keys, are moving focus from cell to cell, they are not available to do something like operate a combobox or move an editing caret inside of a cell.
If this functionality is needed, see Editing and Navigating Inside a Cell.
-
If navigation functions can dynamically add more rows or columns to the DOM, key events that move focus to the beginning or end of the grid, such as Control + End, may move focus to the last row in the DOM rather than the last available row in the back-end data.
+
If navigation functions can dynamically add more rows or columns to the DOM, key events that move focus to the beginning or end of the grid, such as Control + End, may move focus to the last row in the DOM rather than the last available row in the back-end data.
If a grid supports selection of cells, rows, or columns, the following keys are commonly used for these functions.
-
Control + Space: selects the column that contains the focus.
+
Control + Space: selects the column that contains the focus.
- Shift + Space: Selects the row that contains the focus.
+ Shift + Space: Selects the row that contains the focus.
If the grid includes a column with checkboxes for selecting rows, this key can serve as a shortcut for checking the box when focus is not on the checkbox.
-
Control + A: Selects all cells.
-
Shift + Right Arrow: Extends selection one cell to the right.
-
Shift + Left Arrow: Extends selection one cell to the left.
-
Shift + Down Arrow: Extends selection one cell down.
-
Shift + Up Arrow: Extends selection one cell up.
+
Control + A: Selects all cells.
+
Shift + Right Arrow: Extends selection one cell to the right.
+
Shift + Left Arrow: Extends selection one cell to the left.
+
Shift + Down Arrow: Extends selection one cell down.
+
Shift + Up Arrow: Extends selection one cell up.
Note
@@ -258,8 +258,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
End: moves focus to the last cell in the row that contains focus.
Optionally, if the grid has a single column or fewer than three cells per row, focus may instead move to the last cell in the grid.
-
Control + Home (optional): moves focus to the first cell in the first row.
-
Control + End (Optional): moves focus to the last cell in the last row.
+
Control + Home (optional): moves focus to the first cell in the first row.
+
Control + End (Optional): moves focus to the last cell in the last row.
Note
@@ -272,7 +272,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
While navigation keys, such as arrow keys, are moving focus from cell to cell, they are not available to do something like operate a combobox or move an editing caret inside of a cell.
If this functionality is needed, see Editing and Navigating Inside a Cell.
-
If navigation functions can dynamically add more rows or columns to the DOM, key events that move focus to the beginning or end of the grid, such as control + End, may move focus to the last row in the DOM rather than the last available row in the back-end data.
+
If navigation functions can dynamically add more rows or columns to the DOM, key events that move focus to the beginning or end of the grid, such as control + End, may move focus to the last row in the DOM rather than the last available row in the back-end data.
@@ -281,16 +281,16 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
-
Control + Space: selects the column that contains the focus.
+
Control + Space: selects the column that contains the focus.
- Shift + Space: Selects the row that contains the focus.
+ Shift + Space: Selects the row that contains the focus.
If the grid includes a column with checkboxes for selecting rows, this key can serve as a shortcut for checking the box when focus is not on the checkbox.
-
Control + A: Selects all cells.
-
Shift + Right Arrow: Extends selection one cell to the right.
-
Shift + Left Arrow: Extends selection one cell to the left.
-
Shift + Down Arrow: Extends selection one cell down.
-
Shift + Up Arrow: Extends selection one cell up.
+
Control + A: Selects all cells.
+
Shift + Right Arrow: Extends selection one cell to the right.
+
Shift + Left Arrow: Extends selection one cell to the left.
+
Shift + Down Arrow: Extends selection one cell down.
+
Shift + Up Arrow: Extends selection one cell up.
Note
@@ -397,7 +397,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Optionally, the focus movement may wrap inside a single cell or within the grid itself.
- Shift + Tab: moves focus to the previous widget in the grid.
+ Shift + Tab: moves focus to the previous widget in the grid.
Optionally, the focus movement may wrap inside a single cell or within the grid itself.
The SkipTo Landmarks & Headings browser extension implements the User Agent Accessibility Guidelines 1.0 requirement 9.9 Allow Structured Navigation. It does this by providing keyboard navigation to landmarks and headings on any web page. The keyboard shortcut to open the SkipTo menu is alt+2 on Windows/Linux and option+2 on Mac keyboards.
+
The SkipTo Landmarks & Headings browser extension implements the User Agent Accessibility Guidelines 1.0 requirement 9.9 Allow Structured Navigation. It does this by providing keyboard navigation to landmarks and headings on any web page. The keyboard shortcut to open the SkipTo menu is alt + 2 on Windows/Linux and option + 2 on Mac keyboards.
"Not Important", "Important", and "Remove": Delete
@@ -319,7 +319,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Using a shortcut key intentionally places focus to optimize both screen reader and keyboard usability.
- For example, pressing Alt + Up Arrow in the "Important Features" list keeps focus on the option that is moved up, enabling all keyboard users to easily perform consecutive move operations for an option and screen reader users to hear the position of an option after it is moved.
+ For example, pressing Alt + Up Arrow in the "Important Features" list keeps focus on the option that is moved up, enabling all keyboard users to easily perform consecutive move operations for an option and screen reader users to hear the position of an option after it is moved.
Similarly, pressing Enter in the available options list leaves focus in the available options list.
If the option that had focus before the add operation is no longer present in the list, focus lands on the first of the subsequent options that is still present.
@@ -439,24 +439,24 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
changes the selection state of the focused option .
-
Shift + Down Arrow
+
Shift + Down Arrow
Moves focus to and selects the next option.
-
Shift + Up Arrow
+
Shift + Up Arrow
Moves focus to and selects the previous option.
-
Control + Shift + Home
+
Control + Shift + Home
Selects from the focused option to the beginning of the list.
-
Control + Shift + End
+
Control + Shift + End
Selects from the focused option to the end of the list.
- Control + A (All Platforms)
+ Control + A (All Platforms) Command-A (macOS)
diff --git a/ARIA/apg/patterns/listbox/listbox-pattern.md b/ARIA/apg/patterns/listbox/listbox-pattern.md
index 649a1423a..f2f2383ef 100644
--- a/ARIA/apg/patterns/listbox/listbox-pattern.md
+++ b/ARIA/apg/patterns/listbox/listbox-pattern.md
@@ -152,19 +152,19 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Recommended selection model -- holding modifier keys is not necessary:
Space: changes the selection state of the focused option.
-
Shift + Down Arrow (Optional): Moves focus to and toggles the selected state of the next option.
-
Shift + Up Arrow (Optional): Moves focus to and toggles the selected state of the previous option.
-
Shift + Space (Optional): Selects contiguous items from the most recently selected item to the focused item.
+
Shift + Down Arrow (Optional): Moves focus to and toggles the selected state of the next option.
+
Shift + Up Arrow (Optional): Moves focus to and toggles the selected state of the previous option.
+
Shift + Space (Optional): Selects contiguous items from the most recently selected item to the focused item.
- Control + Shift + Home (Optional): Selects the focused option and all options up to the first option.
+ Control + Shift + Home (Optional): Selects the focused option and all options up to the first option.
Optionally, moves focus to the first option.
- Control + Shift + End (Optional): Selects the focused option and all options down to the last option.
+ Control + Shift + End (Optional): Selects the focused option and all options down to the last option.
Optionally, moves focus to the last option.
- Control + A (Optional): Selects all options in the list.
+ Control + A (Optional): Selects all options in the list.
Optionally, if all options are selected, it may also unselect all options.
@@ -172,22 +172,22 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Alternative selection model -- moving focus without holding a Shift or Control modifier unselects all selected options except the focused option:
-
Shift + Down Arrow: Moves focus to and toggles the selection state of the next option.
-
Shift + Up Arrow: Moves focus to and toggles the selection state of the previous option.
-
Control + Down Arrow: Moves focus to the next option without changing its selection state.
-
Control + Up Arrow: Moves focus to the previous option without changing its selection state.
-
Control + Space Changes the selection state of the focused option.
-
Shift + Space (Optional): Selects contiguous items from the most recently selected item to the focused item.
+
Shift + Down Arrow: Moves focus to and toggles the selection state of the next option.
+
Shift + Up Arrow: Moves focus to and toggles the selection state of the previous option.
+
Control + Down Arrow: Moves focus to the next option without changing its selection state.
+
Control + Up Arrow: Moves focus to the previous option without changing its selection state.
+
Control + Space Changes the selection state of the focused option.
+
Shift + Space (Optional): Selects contiguous items from the most recently selected item to the focused item.
- Control + Shift + Home (Optional): Selects the focused option and all options up to the first option.
+ Control + Shift + Home (Optional): Selects the focused option and all options up to the first option.
Optionally, moves focus to the first option.
- Control + Shift + End (Optional): Selects the focused option and all options down to the last option.
+ Control + Shift + End (Optional): Selects the focused option and all options down to the last option.
Optionally, moves focus to the last option.
- Control + A (Optional): Selects all options in the list.
+ Control + A (Optional): Selects all options in the list.
Optionally, if all options are selected, it may also unselect all options.
A menu is a widget that offers a list of choices to the user, such as a set of actions or functions.
Menu widgets behave like native operating system menus, such as the menus that pull down from the menubars commonly found at the top of many desktop application windows.
- A menu is usually opened, or made visible, by activating a menu button, choosing an item in a menu that opens a sub menu, or by invoking a command, such as Shift + F10 in Windows, that opens a context specific menu.
+ A menu is usually opened, or made visible, by activating a menu button, choosing an item in a menu that opens a sub menu, or by invoking a command, such as Shift + F10 in Windows, that opens a context specific menu.
When a user activates a choice in a menu, the menu usually closes unless the choice opened a submenu.
@@ -104,12 +104,12 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
When a menu opens, or when a menubar receives focus, keyboard focus is placed on the first item.
Because menubar and menu elements are composite widgets as described in the practice for
Keyboard Navigation Inside Components,
- Tab and Shift + Tab do not move focus among the items in the menu.
+ Tab and Shift + Tab do not move focus among the items in the menu.
Instead, the keyboard commands described in this section enable users to move focus among the elements in a menubar or menu.
- Tab and Shift + Tab:
+ Tab and Shift + Tab:
Move focus into a menubar:
@@ -123,7 +123,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
When focus is on a menuitem in a menu or menubar, move focus out of the menu or menubar, and close all menus and submenus.
- Note that Tab and Shift + Tab do not move focus into a menu.
+ Note that Tab and Shift + Tab do not move focus into a menu.
Unlike a menubar, a menu is not visually persistent, and authors are responsible for ensuring focus moves to an item inside of a menu when the menu opens.
@@ -201,7 +201,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
A separator in a menu is not focusable or interactive.
If a menu is opened or a menubar receives focus as a result of a context action, Escape or Enter may return focus to the invoking context.
- For example, a rich text editor may have a menubar that receives focus when a shortcut key, e.g., alt + F10, is pressed while editing.
+ For example, a rich text editor may have a menubar that receives focus when a shortcut key, e.g., alt + F10, is pressed while editing.
In this case, pressing Escape or activating a command from the menu may return focus to the editor.
diff --git a/ARIA/apg/patterns/radio/radio-group-pattern.md b/ARIA/apg/patterns/radio/radio-group-pattern.md
index c36fa8348..5497d2ab5 100644
--- a/ARIA/apg/patterns/radio/radio-group-pattern.md
+++ b/ARIA/apg/patterns/radio/radio-group-pattern.md
@@ -87,7 +87,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- Tab and Shift + Tab: Move focus into and out of the radio group.
+ Tab and Shift + Tab: Move focus into and out of the radio group.
When focus moves into a radio group:
If a radio button is checked, focus is set on the checked button.
@@ -107,7 +107,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Note
The initial focus behavior described above differs slightly from the behavior provided by some browsers for native HTML radio groups.
- In some browsers, if none of the radio buttons are selected, moving focus into the radio group with Shift+Tab will place focus on the last radio button instead of the first radio button.
+ In some browsers, if none of the radio buttons are selected, moving focus into the radio group with Shift + Tab will place focus on the last radio button instead of the first radio button.
Students currently enrolled in WAI-ARIA 101
- , column headers with buttons are sortable.
+  (column headers with buttons are sortable).
diff --git a/ARIA/apg/patterns/tabs/examples/tabs-actions.md b/ARIA/apg/patterns/tabs/examples/tabs-actions.md
new file mode 100644
index 000000000..bd1c2fc13
--- /dev/null
+++ b/ARIA/apg/patterns/tabs/examples/tabs-actions.md
@@ -0,0 +1,572 @@
+---
+# This file was generated by scripts/pre-build/library/formatForJekyll.js
+title: "Experimental Example of Tabs with Action Buttons"
+ref: /ARIA/apg/patterns/tabs/examples/tabs-actions/
+
+github:
+ repository: w3c/aria-practices
+ branch: main
+ path: content/patterns/tabs/examples/tabs-actions.html
+feedbackmail: public-aria-practices@w3.org
+permalink: /ARIA/apg/patterns/tabs/examples/tabs-actions/
+
+sidebar: true
+
+footer: " "
+
+# Context here: https://github.com/w3c/wai-aria-practices/issues/31
+type_of_guidance: APG
+
+lang: en
+---
+
+
+Experimental Example of Tabs with Action Buttons
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Read This First
+
+
+ Experimental content! Do not use except for new standards development purposes.
+ Please read below to understand why.
+
+
This is an experimental implementation of potential future techniques that may not yet be supported by web standards.
+
+
This example may include ARIA, HTML, CSS, or other code that is not yet included in a final web standard specification.
+
Experimental content is published in the APG only to facilitate discussion, gather feedback, and support testing of new features in browsers and assistive technologies.
+ This is an experimental implementation of the draft specification of the aria-actions attribute.
+ The aria-actions property enables an element to reference another interactive element that can be activated to perform an action on the referencing element.
+ In this example, each tab element references a menu button that opens a menu of actions that apply to the referencing tab.
+ The relationship provided by aria-actions enables an assistive technology to both communicate the availability of the action button and provide a command for activating the button while focus is on the tab.
+
+
+ The below example demonstrates a tabs widget that implements the Tabs Pattern.
+ In this example, a panel is displayed when users activate its tab with either Space, Enter, or a mouse click.
+ So, for keyboard users, activating a tab requires two steps: 1) focus the tab, and 2) activate the tab.
+ This two-step process is referred to as manual activation.
+ Manual activation of tabs is recommended unless panels can be displayed instantly, i.e., all the panel content is present in the DOM.
+ For additional guidance, see Deciding When to Make Selection Automatically Follow Focus.
+
+
+ This example also demonstrates how to provide buttons that provide contextual actions for the tab.
+ Each tab has an associated context actions menu button.
+ The menu button is referenced by aria-actions, which enables it to be activated by an assistive technology user while focus is on the tab.
+
+ The nurse
+ shark (Ginglymostoma cirratum) is an
+ elasmobranch fish in the family Ginglymostomatidae. The conservation status of
+ the nurse shark is globally assessed as Vulnerable in the IUCN
+ List of Threatened Species. They are considered to be a species
+ of least concern in the United States and in The Bahamas, but
+ considered to be near threatened in the western Atlantic Ocean
+ because of their vulnerable status in South America and reported
+ threats throughout many areas of Central America and the
+ Caribbean. They are directly targeted in some fisheries and
+ considered by-catch in others.
+
+
+
+
+ The basking
+ shark (Cetorhinus maximus) is the
+ second-largest living shark and fish, after the whale shark. It
+ is one of three plankton-eating shark species, along with the
+ whale shark and megamouth shark. Typically, basking sharks reach
+ 7.9 m (26 ft) in length. It is usually greyish-brown, with
+ mottled skin, with the inside of the mouth being white in
+ colour. The caudal fin has a strong lateral keel and a crescent
+ shape. Other common names include bone shark, elephant shark,
+ sailfish, and sunfish. In Orkney, it is called hoe-mother
+ (contracted homer), meaning "the mother of the picked dogfish".
+
+
+
+
+ The whale
+ shark (Rhincodon typus) is a
+ slow-moving, filter-feeding carpet shark and the largest known
+ extant fish species. The largest confirmed individual had a
+ length of 18.8 m (61.7 ft). The whale shark holds many records
+ for size in the animal kingdom, most notably being by far the
+ most massive living non-cetacean animal. It is the sole member
+ of the genus Rhincodon and the only
+ extant member of the family
+ Rhincodontidae, which belongs to the
+ subclass Elasmobranchii in the class
+ Chondrichthyes. Before 1984 it was
+ classified as Rhiniodon into Rhinodontidae.
+
+
+
+
+ The goblin
+ shark (Mitsukurina owstoni) is a rare
+ species of deep-sea shark. Sometimes called a "living fossil",
+ it is the only extant representative of the family
+ Mitsukurinidae, a lineage some 125
+ million years old. This pink-skinned animal has a distinctive
+ profile with an elongated, flat snout, and highly protrusible
+ jaws containing prominent nail-like teeth. It is usually between
+ 3 and 4 m (10 and 13 ft) long when mature, though it can grow
+ considerably larger such as one captured in 2000 that is thought
+ to have measured 6 m (20 ft). Goblin sharks are benthopelagic
+ creatures that inhabit upper continental slopes, submarine
+ canyons, and seamounts throughout the world at depths greater
+ than 100 m (330 ft), with adults found deeper than juveniles.
+ Some researchers believe that these sharks could also dive to
+ depths of up to 1,300 m (4,270 ft), for short periods of time.
+
+
+
+
+
+
+
+
+
+
Accessibility Features
+
+
+ To ensure people who rely on browser or operating system high contrast settings can both distinguish the active (selected) tab from other tabs and perceive keyboard focus:
+
+
+ The active tab has a 2 pixel border on its left and right sides and a 4 pixel border on top, while the names of inactive tabs have 1 pixel borders.
+ The active tab is also 4 pixels higher than the inactive tabs.
+
+
+ The focus ring is drawn with a CSS border on a child span element of the tab element.
+ This focus span is separated from the tab border by 2 pixels of space to ensure focus and selection are separately perceivable.
+ Note that when a tab element is focused, the outline of the tab element itself is set to 0 so that only one focus ring is displayed.
+
+
+ Because transparent borders are visible on some systems when high contrast settings are enabled, only the focused span element has a visible border.
+ When span elements are not indicating focus, they have a 0-width border and additional padding equal in width to the border that is used to indicate focus.
+
+
+
+
+ Note that since the first element in every tabpanel is a focusable element (i.e., a link), the tabpanel is not included in the page Tab sequence.
+ To make it easy for screen reader users to navigate from a tab to the beginning of content in the active tabpanel, it is recommended that all tabpanel elements in a tab set are focusable if there are any panels in the set that contain content where the first element in the panel is not focusable.
+
+
+ To ensure the tab content remains visible when the screen is magnified, the width of the tabs and tab panels are defined using a percentage of the screen width. As the page is magnified the height of the tab increases and the tab content re-flows to the new dimensions of the tab.
+
+
+
+
+
+
Keyboard Support
+
Tabs
+
+
+
+
Key
+
Function
+
+
+
+
+
Tab
+
+
+
When focus moves into the tablist, places focus on the active tab.
+
When focus is on a tab, places focus on its associated menu button.
+
When focus is on the menu button associated with a tab, moves focus to one of the following depending on which is nearest in the forward tab sequence:
+
+
The active tab.
+
The first focusable element following the tablist. In this example, that is the a element in tabpanel.
+
+
+
+
+
+
+
Shift + Tab
+
+
+
When focus moves into the tablist, places focus on the menu button associated with the active tab.
+
When focus is on the menu button associated with a tab, places focus on that tab.
+
When focus is on a tab, moves focus to one of the following depending on which is nearest in the backward tab sequence:
+
+
The active tab.
+
The first focusable element preceding the tablist.
+
+
+
+
+
+
+
Enter Space
+
When a tab has focus, activates the tab, causing its associated panel to be displayed.
+
+
+
Right Arrow
+
+ When a tab or its associated menu button have focus:
+
+
Moves focus to the next tab.
+
If focus is on the last tab, moves focus to the first tab.
+
+
+
+
+
Left Arrow
+
+ When a tab or its associated menu button have focus:
+
+
Moves focus to the previous tab.
+
If focus is on the first tab, moves focus to the last tab.
+
+
+
+
+
Home
+
When a tab has focus, moves focus to the first tab.
+
+
+
End
+
When a tab has focus, moves focus to the last tab.
+
+
+
+
Menu Button
+
+ Keyboard support for each tab’s associated menu button is documented in Menu Button Pattern.
+
+
+
+
+
Role, Property, State, and Tabindex Attributes
+
+
+
+
Role
+
Attribute
+
Element
+
Usage
+
+
+
+
+
+ tablist
+
+
+
+ div
+
+
Indicates that the element serves as a container for a set of tabs.
+
+
+
+
+ aria-labelledby="ID_REFERENCE"
+
+
+ div
+
+
Provides a label that describes the purpose of the set of tabs.
+
+
+
+ tab
+
+
+
+ button
+
+
+
+
Indicates the element serves as a tab control.
+
Provides a title for its associated tabpanel.
+
+
+
+
+
+
+ aria-selected="true"
+
+
+ button
+
+
+
+
Indicates the tab control is activated and its associated panel is displayed.
+
Set when a user activates a tab.
+
Does not change when focus moves in the tablist.
+
+
+
+
+
+
+ aria-selected="false"
+
+
+ button
+
+
+
+
Indicates the tab control is not active and its associated panel is NOT displayed.
+
Set for all tab elements in the tab set except the active tab; the one associated with the currently displayed panel.
+
+
+
+
+
+
+ tabindex="-1"
+
+
+ button
+
+
+
+
Removes the element from the page Tab sequence.
+
Set when a tab is not selected so that only the selected (active) tab is in the page Tab sequence.
+
Since an HTML button element is used for the tab, it is not necessary to set tabindex="0" on the selected (active) tab element.
To copy the following HTML code, please open it in CodePen.
+
+
+
+
+
+
+
+
+
+
diff --git a/ARIA/apg/patterns/tabs/tabs-pattern.md b/ARIA/apg/patterns/tabs/tabs-pattern.md
index 8441f941c..962d38f25 100644
--- a/ARIA/apg/patterns/tabs/tabs-pattern.md
+++ b/ARIA/apg/patterns/tabs/tabs-pattern.md
@@ -130,7 +130,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
End (Optional): Moves focus to the last tab.
Optionally, activates the newly focused tab (See note below).
-
Shift + F10: If the tab has an associated popup menu, opens the menu.
+
Shift + F10: If the tab has an associated popup menu, opens the menu.
Delete (Optional): If deletion is allowed, deletes (closes) the current tab element and its associated tab panel, sets focus on the tab following the tab that was closed, and optionally activates the newly focused tab.
If there is not a tab that followed the tab that was deleted, e.g., the deleted tab was the right-most tab in a left-to-right horizontal tab list, sets focus on and optionally activates the tab that preceded the deleted tab.
diff --git a/ARIA/apg/patterns/toolbar/toolbar-pattern.md b/ARIA/apg/patterns/toolbar/toolbar-pattern.md
index 99650ef7c..049ed45d0 100644
--- a/ARIA/apg/patterns/toolbar/toolbar-pattern.md
+++ b/ARIA/apg/patterns/toolbar/toolbar-pattern.md
@@ -101,7 +101,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Keyboard Interaction
- Tab and Shift + Tab: Move focus into and out of the toolbar.
+ Tab and Shift + Tab: Move focus into and out of the toolbar.
When focus moves into a toolbar:
If focus is moving into the toolbar for the first time, focus is set on the first control that is not disabled.
@@ -334,7 +334,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- Control + Home
+ Control + Home
@@ -345,7 +345,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- Control + End
+ Control + End
diff --git a/ARIA/apg/patterns/treegrid/treegrid-pattern.md b/ARIA/apg/patterns/treegrid/treegrid-pattern.md
index dd2646c92..1387e74fb 100644
--- a/ARIA/apg/patterns/treegrid/treegrid-pattern.md
+++ b/ARIA/apg/patterns/treegrid/treegrid-pattern.md
@@ -202,7 +202,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- Control + Home:
+ Control + Home:
If focus is on a row, moves focus to the first row.
@@ -215,7 +215,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- Control + End:
+ Control + End:
If focus is on a row, moves focus to the last row.
@@ -239,20 +239,20 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
While navigation keys, such as arrow keys, are moving focus from cell to cell, they are not available to do something like operate a combobox or move an editing caret inside of a cell.
If this functionality is needed, see Editing and Navigating Inside a Cell.
-
If navigation functions can dynamically add more rows or columns to the DOM, key events that move focus to the beginning or end of the grid, such as control + End, may move focus to the last row in the DOM rather than the last available row in the back-end data.
+
If navigation functions can dynamically add more rows or columns to the DOM, key events that move focus to the beginning or end of the grid, such as control + End, may move focus to the last row in the DOM rather than the last available row in the back-end data.
If a treegrid supports selection of cells, rows, or columns, the following keys are commonly used for these functions.
- Control + Space:
+ Control + Space:
If focus is on a row, selects all cells.
If focus is on a cell, selects the column that contains the focus.
- Shift + Space:
+ Shift + Space:
If focus is on a row, selects the row.
@@ -261,30 +261,30 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
-
Control + A: Selects all cells.
+
Control + A: Selects all cells.
- Shift + Right Arrow:
+ Shift + Right Arrow:
If focus is on a row, does not change selection.
if focus is on a cell, extends selection one cell to the right.
- Shift + Left Arrow:
+ Shift + Left Arrow:
If focus is on a row, does not change selection.
if focus is on a cell, extends selection one cell to the left.
- Shift + Down Arrow:
+ Shift + Down Arrow:
If focus is on a row, extends selection to all the cells in the next row.
If focus is on a cell, extends selection one cell down.
- Shift + Up Arrow:
+ Shift + Up Arrow:
If focus is on a row, extends selection to all the cells in the previous row.
If focus is on a cell, extends selection one cell up.
diff --git a/ARIA/apg/patterns/treeview/treeview-pattern.md b/ARIA/apg/patterns/treeview/treeview-pattern.md
index 34496f4b2..3e72a85a7 100644
--- a/ARIA/apg/patterns/treeview/treeview-pattern.md
+++ b/ARIA/apg/patterns/treeview/treeview-pattern.md
@@ -170,19 +170,19 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Recommended selection model -- holding a modifier key while moving focus is not necessary:
Space: Toggles the selection state of the focused node.
-
Shift + Down Arrow (Optional): Moves focus to and toggles the selection state of the next node.
-
Shift + Up Arrow (Optional): Moves focus to and toggles the selection state of the previous node.
-
Shift + Space (Optional): Selects contiguous nodes from the most recently selected node to the current node.
+
Shift + Down Arrow (Optional): Moves focus to and toggles the selection state of the next node.
+
Shift + Up Arrow (Optional): Moves focus to and toggles the selection state of the previous node.
+
Shift + Space (Optional): Selects contiguous nodes from the most recently selected node to the current node.
- Control + Shift + Home (Optional): Selects the node with focus and all nodes up to the first node.
+ Control + Shift + Home (Optional): Selects the node with focus and all nodes up to the first node.
Optionally, moves focus to the first node.
- Control + Shift + End (Optional): Selects the node with focus and all nodes down to the last node.
+ Control + Shift + End (Optional): Selects the node with focus and all nodes down to the last node.
Optionally, moves focus to the last node.
- Control + A (Optional): Selects all nodes in the tree.
+ Control + A (Optional): Selects all nodes in the tree.
Optionally, if all nodes are selected, it can also unselect all nodes.
@@ -190,22 +190,22 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Alternative selection model -- Moving focus without holding the Shift or Control modifier unselects all selected nodes except for the focused node:
-
Shift + Down Arrow: Moves focus to and toggles the selection state of the next node.
-
Shift + Up Arrow: Moves focus to and toggles the selection state of the previous node.
-
Control + Down Arrow: Without changing the selection state, moves focus to the next node.
-
Control + Up Arrow: Without changing the selection state, moves focus to the previous node.
-
Control + Space: Toggles the selection state of the focused node.
-
Shift + Space (Optional): Selects contiguous nodes from the most recently selected node to the current node.
+
Shift + Down Arrow: Moves focus to and toggles the selection state of the next node.
+
Shift + Up Arrow: Moves focus to and toggles the selection state of the previous node.
+
Control + Down Arrow: Without changing the selection state, moves focus to the next node.
+
Control + Up Arrow: Without changing the selection state, moves focus to the previous node.
+
Control + Space: Toggles the selection state of the focused node.
+
Shift + Space (Optional): Selects contiguous nodes from the most recently selected node to the current node.
- Control + Shift + Home (Optional): Selects the node with focus and all nodes up to the first node.
+ Control + Shift + Home (Optional): Selects the node with focus and all nodes up to the first node.
Optionally, moves focus to the first node.
- Control + Shift + End (Optional): Selects the node with focus and all nodes down to the last node.
+ Control + Shift + End (Optional): Selects the node with focus and all nodes down to the last node.
Optionally, moves focus to the last node.
- Control + A (Optional): Selects all nodes in the tree.
+ Control + A (Optional): Selects all nodes in the tree.
Optionally, if all nodes are selected, it can also unselect all nodes.
diff --git a/ARIA/apg/practices/keyboard-interface/keyboard-interface-practice.md b/ARIA/apg/practices/keyboard-interface/keyboard-interface-practice.md
index 64bcd3be5..9f56f807d 100644
--- a/ARIA/apg/practices/keyboard-interface/keyboard-interface-practice.md
+++ b/ARIA/apg/practices/keyboard-interface/keyboard-interface-practice.md
@@ -70,7 +70,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Understanding fundamental principles of focus movement conventions used in ARIA design patterns.
Maintaining visible focus, predictable focus movement, and distinguishing between keyboard focus and the selected state.
-
Managing movement of keyboard focus between components, e.g., how the focus moves when the Tab and Shift+Tab keys are pressed.
+
Managing movement of keyboard focus between components, e.g., how the focus moves when the Tab and Shift + Tab keys are pressed.
Managing movement of keyboard focus inside components that contain multiple focusable elements, e.g., two different methods for programmatically exposing focus inside widgets like radio groups, menus, listboxes, trees, and grids.
Determining when to make disabled interactive elements focusable.
Assigning and revealing keyboard shortcuts, including guidance on how to avoid problematic conflicts with keyboard commands of assistive technologies, browsers, and operating systems.
@@ -97,7 +97,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- A primary keyboard navigation convention common across all platforms is that the tab and shift+tab keys move focus from one UI component to another while other keys, primarily the arrow keys, move focus inside of components that include multiple focusable elements.
+ A primary keyboard navigation convention common across all platforms is that the tab and shift + tab keys move focus from one UI component to another while other keys, primarily the arrow keys, move focus inside of components that include multiple focusable elements.
The path that the focus follows when pressing the tab key is known as the tab sequence or tab ring.
@@ -305,7 +305,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
As described in section Fundamental Keyboard Navigation Conventions, the tab sequence should include only one focusable element of a composite UI component.
- Once a composite contains focus, keys other than Tab and Shift + Tab enable the user to move focus among its focusable elements.
+ Once a composite contains focus, keys other than Tab and Shift + Tab enable the user to move focus among its focusable elements.
Authors are free to choose which keys move focus inside of a composite, but they are strongly advised to use the same key bindings as similar components in common GUI operating systems as demonstrated in APG Patterns.
@@ -357,7 +357,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- If the design calls for a specific element to be focused the next time the user moves focus into the composite with Tab or Shift+Tab, check if that target element has tabindex="0" when the composite loses focus.
+ If the design calls for a specific element to be focused the next time the user moves focus into the composite with Tab or Shift + Tab, check if that target element has tabindex="0" when the composite loses focus.
If it does not, set tabindex="0" on the target element and set tabindex="-1" on the element that previously had tabindex="0".
@@ -398,7 +398,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
- If the design calls for a specific element to be focused the next time a user moves focus into the composite with Tab or Shift+Tab, check if aria-activedescendant is referring to that target element when the container loses focus.
+ If the design calls for a specific element to be focused the next time a user moves focus into the composite with Tab or Shift + Tab, check if aria-activedescendant is referring to that target element when the container loses focus.
If it is not, set aria-activedescendant to refer to the target element.
@@ -497,33 +497,33 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
open context menu
-
Shift + F10
+
Shift + F10
Copy to clipboard
-
Control + C
-
Command + C
+
Control + C
+
Command + C
Paste from clipboard
-
Control + V
-
Command + V
+
Control + V
+
Command + V
Cut to clipboard
-
Control + X
-
Command + X
+
Control + X
+
Command + X
undo last action
-
Control + Z
-
Command + Z
+
Control + Z
+
Command + Z
Redo action
-
Control + Y
-
Command + Shift + Z
+
Control + Y
+
Command + Shift + Z
@@ -635,7 +635,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Assigning Keyboard Shortcuts
When choosing the keys to assign to a shortcut, there are many factors to consider.
-
Making the shortcut easy to learn and remember by using a mnemonic (e.g., Control + S for "Save") or following a logical or spacial pattern.
+
Making the shortcut easy to learn and remember by using a mnemonic (e.g., Control + S for "Save") or following a logical or spacial pattern.
Localizing the interface, including for differences in which keys are available and how they behave and for language considerations that could impact mnemonics.
Avoiding and managing conflicts with key assignments used by an assistive technology, the browser, or the operating system.
@@ -700,7 +700,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
Caps Lock + any other combination of keys.
Insert + any combination of other keys.
Scroll Lock + any combination of other keys.
-
macOS only: Control+Option + any combination of other keys.
+
macOS only: Control + Option + any combination of other keys.
diff --git a/_external/aria-practices b/_external/aria-practices
index 2d60c413e..b606e347d 160000
--- a/_external/aria-practices
+++ b/_external/aria-practices
@@ -1 +1 @@
-Subproject commit 2d60c413ec23875a30162364a7e715b26875d9db
+Subproject commit b606e347d1dd1a70d35f80655c232725173314ba
diff --git a/_external/data b/_external/data
index bb1336392..42f334254 160000
--- a/_external/data
+++ b/_external/data
@@ -1 +1 @@
-Subproject commit bb1336392685c5dec9dbf30c5fb5c9e0c0c9260d
+Subproject commit 42f334254a860a697bda48f695354d505726415f
diff --git a/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv b/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv
index 7effc0b04..f0f597ea1 100644
--- a/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv
+++ b/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv
@@ -7,7 +7,7 @@
"aria-colcount","1","1","Guidance: Using aria-colcount and aria-colindex","Example: Data Grid"
"aria-colindex","3","1","Guidance: Using aria-colcount and aria-colindex","Guidance: Using aria-colindex When Column Indices Are Contiguous","Guidance: Using aria-colindex When Column Indices Are Not Contiguous","Example: Data Grid"
"aria-colspan","1","0","Guidance: Defining cell spans using aria-colspan and aria-rowspan"
-"aria-controls","0","20","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Toolbar"
+"aria-controls","0","21","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Toolbar"
"aria-current","0","5","Example: Breadcrumb","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Navigation Menubar","Example: Navigation Treeview"
"aria-describedby","1","6","Guidance: Describing by referencing content with aria-describedby","Example: Alert Dialog","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Table"
"aria-details","0","0"
@@ -22,7 +22,7 @@
"aria-invalid","0","0"
"aria-keyshortcuts","0","0"
"aria-label","1","18","Guidance: Naming with a String Attribute Via aria-label","Example: Breadcrumb","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Link","Example: Editor Menubar","Example: Navigation Menubar","Example: Rating Radio Group","Example: Horizontal Multi-Thumb Slider","Example: Date Picker Spin Button","Example: Table","Example: Toolbar","Example: Treegrid Email Inbox","Example: Navigation Treeview"
-"aria-labelledby","1","40","Guidance: Naming with Referenced Content Via aria-labelledby","Example: Accordion","Example: Alert Dialog","Example: Checkbox (Two State)","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Data Grid","Example: Layout Grid","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Navigation Menubar","Example: Meter","Example: Radio Group Using aria-activedescendant","Example: Rating Radio Group","Example: Radio Group Using Roving tabindex","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Switch Using HTML Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview","Example: Complementary Landmark","Example: Form Landmark","Example: Main Landmark","Example: Navigation Landmark","Example: Region Landmark","Example: Search Landmark"
+"aria-labelledby","1","41","Guidance: Naming with Referenced Content Via aria-labelledby","Example: Accordion","Example: Alert Dialog","Example: Checkbox (Two State)","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Data Grid","Example: Layout Grid","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Navigation Menubar","Example: Meter","Example: Radio Group Using aria-activedescendant","Example: Rating Radio Group","Example: Radio Group Using Roving tabindex","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Switch Using HTML Button","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview","Example: Complementary Landmark","Example: Form Landmark","Example: Main Landmark","Example: Navigation Landmark","Example: Region Landmark","Example: Search Landmark"
"aria-level","0","2","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Declared Properties"
"aria-live","0","5","Example: Alert","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Date Picker Combobox","Example: Date Picker Dialog"
"aria-modal","0","4","Example: Alert Dialog","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Modal Dialog"
@@ -40,7 +40,7 @@
"aria-rowcount","1","2","Guidance: Using aria-rowcount and aria-rowindex","Example: Data Grid","Example: Layout Grid"
"aria-rowindex","1","2","Guidance: Using aria-rowcount and aria-rowindex","Example: Data Grid","Example: Layout Grid"
"aria-rowspan","1","0","Guidance: Defining cell spans using aria-colspan and aria-rowspan"
-"aria-selected","0","16","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties"
+"aria-selected","0","17","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties"
"aria-setsize","0","3","Example: Infinite Scrolling Feed","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Declared Properties"
"aria-sort","1","2","Guidance: Indicating sort order with aria-sort","Example: Data Grid","Example: Sortable Table"
"aria-valuemax","1","8","Guidance: Using aria-valuemin, aria-valuemax and aria-valuenow","Example: Meter","Example: Horizontal Multi-Thumb Slider","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Toolbar"
diff --git a/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv b/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv
index cbebe3fb3..d3d229644 100644
--- a/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv
+++ b/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv
@@ -65,10 +65,10 @@
"spinbutton","1","2","Guidance: Spinbutton Pattern","Example: Date Picker Spin Button","Example: Toolbar"
"status","0","0"
"switch","1","3","Guidance: Switch Pattern","Example: Switch Using HTML Button","Example: Switch Using HTML Checkbox Input","Example: Switch"
-"tab","1","3","Guidance: Keyboard Navigation Between Components (The Tab Sequence)","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation"
+"tab","1","4","Guidance: Keyboard Navigation Between Components (The Tab Sequence)","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation"
"table","2","1","Guidance: Table Pattern","Guidance: Grid and Table Properties","Example: Table"
-"tablist","0","3","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation"
-"tabpanel","0","3","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation"
+"tablist","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation"
+"tabpanel","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation"
"term","0","0"
"textbox","0","0"
"timer","0","0"
diff --git a/content-assets/wai-aria-practices/patterns/dialog-modal/examples/js/dialog.js b/content-assets/wai-aria-practices/patterns/dialog-modal/examples/js/dialog.js
index 1fa43809c..5788e6500 100644
--- a/content-assets/wai-aria-practices/patterns/dialog-modal/examples/js/dialog.js
+++ b/content-assets/wai-aria-practices/patterns/dialog-modal/examples/js/dialog.js
@@ -85,7 +85,7 @@ aria.Utils = aria.Utils || {};
aria.OpenDialogList = aria.OpenDialogList || new Array(0);
/**
- * @returns {object} the last opened dialog (the current dialog)
+ * @returns {object|void} the last opened dialog (the current dialog)
*/
aria.getCurrentDialog = function () {
if (aria.OpenDialogList && aria.OpenDialogList.length) {
diff --git a/content-assets/wai-aria-practices/patterns/menu-button/examples/js/menu-button-actions.js b/content-assets/wai-aria-practices/patterns/menu-button/examples/js/menu-button-actions.js
index cd69d5621..bde2c8a25 100644
--- a/content-assets/wai-aria-practices/patterns/menu-button/examples/js/menu-button-actions.js
+++ b/content-assets/wai-aria-practices/patterns/menu-button/examples/js/menu-button-actions.js
@@ -333,10 +333,15 @@ class MenuButtonActions {
// Initialize menu buttons
window.addEventListener('load', function () {
- document.getElementById('action_output').value = 'none';
+ const output = document.getElementById('action_output');
+ if (output) {
+ output.value = 'none';
+ }
function performMenuAction(node) {
- document.getElementById('action_output').value = node.textContent.trim();
+ if (output) {
+ output.value = node.textContent.trim();
+ }
}
var menuButtons = document.querySelectorAll('.menu-button-actions');
diff --git a/content-assets/wai-aria-practices/patterns/tabs/examples/css/tabs-actions.css b/content-assets/wai-aria-practices/patterns/tabs/examples/css/tabs-actions.css
new file mode 100644
index 000000000..cb9436c72
--- /dev/null
+++ b/content-assets/wai-aria-practices/patterns/tabs/examples/css/tabs-actions.css
@@ -0,0 +1,215 @@
+.tabs {
+ --color-1-hue: 200;
+ --color-1-sat: 0%;
+ --light-max: 100%;
+ --light-inactive: 92%;
+ --light-interest: 94%;
+ --light-diminish: 60%;
+ --light-dark: 35%;
+ --light-min: 0%;
+ --border-radius: 0.25rem;
+ --selected-tab-accent-color: rgb(36 116 214);
+ --selected-tab-accent-block-size: 0.4rem;
+
+ font-family: system-ui, sans-serif;
+ margin-block-start: 2rem;
+}
+
+.tabs * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ background-color: transparent;
+ fill: currentcolor;
+}
+
+.tabs svg {
+ display: block;
+}
+
+.tabs output {
+ display: block;
+}
+
+.tabs output span {
+ display: inline-block;
+ padding: 0.25rem 1ch;
+ font-size: 1rem;
+ font-weight: bold;
+ background-color: darkgreen;
+ color: white;
+ border-radius: 0.25rem;
+}
+
+.tabs output span.error {
+ background-color: darkred;
+}
+
+.tabs button[data-operation] {
+ line-height: 1;
+}
+
+[role="tablist"] {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5ch;
+ z-index: 1;
+ margin-block-start: 1rem;
+ padding-block-start: var(--selected-tab-accent-block-size);
+}
+
+[role="tablist"] > * {
+ position: relative;
+ border: 1px solid
+ hsl(var(--color-1-hue) var(--color-1-sat) var(--light-diminish));
+ background-color: hsl(
+ var(--color-1-hue) var(--color-1-sat) var(--light-inactive)
+ );
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+}
+
+[role="tablist"] > *:hover {
+ background-color: hsl(
+ var(--color-1-hue) var(--color-1-sat) var(--light-interest)
+ );
+}
+
+[role="tablist"] > .tab-wrapper {
+ display: flex;
+}
+
+[role="tab"],
+[role="tab"] ~ button,
+[role="tab"] ~ .menu-button-actions > button {
+ appearance: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ min-inline-size: 6ch;
+ padding: 2ch;
+ color: hsl(var(--color-1-hue) var(--color-1-sat) var(--light-dark));
+ outline: 3px solid transparent;
+}
+
+[role="tab"] {
+ font-weight: bold;
+ text-align: start;
+}
+
+[role="tab"]::before,
+[role="tab"] ~ button::before,
+[role="tab"] ~ .menu-button-actions > button::before {
+ content: "";
+ display: block;
+ position: absolute;
+ inset-block: 0.3rem;
+ inset-inline: 0.3rem;
+ border: 0.2rem solid transparent;
+ outline: 0.2rem solid transparent;
+ outline-offset: -0.4rem;
+}
+
+[role="tab"]:focus::before,
+[role="tab"] ~ button:focus::before,
+[role="tab"] ~ .menu-button-actions > button:focus::before {
+ border-color: blue;
+ outline-color: white;
+}
+
+[role="tab"]:hover,
+[role="tab"]:focus,
+[role="tab"] ~ button:hover,
+[role="tab"] ~ button:focus,
+[role="tab"] ~ .menu-button-actions > button:hover,
+[role="tab"] ~ .menu-button-actions > button:focus {
+ color: hsl(var(--color-1-hue) var(--color-1-sat) var(--light-min));
+ outline-color: transparent;
+}
+
+[role="tab"][aria-selected="true"],
+.tab-wrapper:has([role="tab"][aria-selected="true"]) {
+ color: hsl(var(--color-1-hue) var(--color-1-sat) var(--light-min));
+ background-color: hsl(var(--color-1-hue) var(--color-1-sat) var(--light-max));
+}
+
+[role="tablist"] > [role="tab"][aria-selected="true"],
+[role="tablist"] > .tab-wrapper:has([role="tab"][aria-selected="true"]) {
+ border-radius: 0;
+ border-block-end-color: hsl(
+ var(--color-1-hue) var(--color-1-sat) var(--light-max)
+ );
+}
+
+[role="tablist"] > [role="tab"][aria-selected="true"]::after,
+[role="tablist"] > .tab-wrapper:has([role="tab"][aria-selected="true"])::after {
+ content: "";
+ position: absolute;
+ inset-block-start: calc(-1 * var(--selected-tab-accent-block-size));
+ inset-inline-start: -1px;
+ inset-inline-end: -1px;
+ block-size: var(--selected-tab-accent-block-size);
+ background-color: var(--selected-tab-accent-color);
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+}
+
+[role="tabpanel"] {
+ position: relative;
+ z-index: 0;
+ top: -1px;
+ overflow: auto;
+ padding: 1em 2ch;
+ min-block-size: 10em;
+ margin-block-end: 1rem;
+ background-color: hsl(var(--color-1-hue) var(--color-1-sat) var(--light-max));
+ border: 1px solid
+ hsl(var(--color-1-hue) var(--color-1-sat) var(--light-diminish));
+ border-radius: 0 var(--border-radius) var(--border-radius);
+}
+
+[role="tabpanel"].is-hidden {
+ display: none;
+}
+
+/* -------------------------------------------------------------------------- */
+
+.menu-button-actions {
+ position: relative;
+ display: flex;
+ z-index: 1;
+}
+
+.menu-button-actions [role="menu"] {
+ position: absolute;
+ inset-block-start: calc(100% - 1em);
+ inset-inline-start: 0%;
+ list-style: none;
+ font-size: 0.85rem;
+ min-inline-size: 28ch;
+ padding: 1ch;
+ background-color: white;
+ border: 1px solid
+ hsl(var(--color-1-hue) var(--color-1-sat) var(--light-diminish));
+ box-shadow: 0 0.25ch 0.5ch rgb(0 0 0 / 25%);
+ color: hsl(var(--color-1-hue) var(--color-1-sat) var(--light-min));
+}
+
+.menu-button-actions [aria-expanded="true"] svg {
+ rotate: 180deg;
+}
+
+.menu-button-actions [aria-expanded="false"] + [role="menu"] {
+ display: none;
+}
+
+.menu-button-actions [role="menuitem"] {
+ cursor: default;
+ padding: 0.5ch;
+}
+
+.menu-button-actions [role="menuitem"]:hover,
+.menu-button-actions [role="menuitem"]:focus {
+ color: blue;
+}
diff --git a/content-assets/wai-aria-practices/patterns/tabs/examples/js/tabs-actions.js b/content-assets/wai-aria-practices/patterns/tabs/examples/js/tabs-actions.js
new file mode 100644
index 000000000..aed6e5152
--- /dev/null
+++ b/content-assets/wai-aria-practices/patterns/tabs/examples/js/tabs-actions.js
@@ -0,0 +1,365 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: tabs-actions.js
+ *
+ * Desc: Tablist widget that implements ARIA Authoring Practices
+ */
+
+'use strict';
+
+class TabsManual {
+ constructor(groupNode) {
+ this.tablistNode = groupNode;
+
+ this.tabs = [];
+
+ this.firstTab = null;
+ this.lastTab = null;
+
+ this.tabs = Array.from(this.tablistNode.querySelectorAll('[role=tab]'));
+ this.tabpanels = [];
+
+ for (var i = 0; i < this.tabs.length; i += 1) {
+ var tab = this.tabs[i];
+ var tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
+
+ tab.tabIndex = -1;
+ tab.setAttribute('aria-selected', 'false');
+ this.tabpanels.push(tabpanel);
+
+ tab.addEventListener('keydown', this.onKeydown.bind(this));
+ tab.addEventListener('click', this.onClick.bind(this));
+ tab.addEventListener('focusout', this.onFocusout.bind(this));
+
+ this.getTabAriaActions(tab).forEach((action) => {
+ action.addEventListener('keydown', this.onKeydown.bind(this));
+ action.addEventListener('focusout', this.onFocusout.bind(this));
+
+ this.getTabAriaActionOperations(action).forEach((operation) => {
+ operation.addEventListener(
+ 'click',
+ this.performTabOperation.bind(this)
+ );
+ operation.addEventListener(
+ 'keydown',
+ this.performTabOperation.bind(this)
+ );
+ });
+ });
+
+ if (!this.firstTab) {
+ this.firstTab = tab;
+ }
+ this.lastTab = tab;
+ }
+
+ this.setSelectedTab(this.firstTab);
+ }
+
+ getTabAriaActions(tab) {
+ var actions = tab.getAttribute('aria-actions');
+ if (actions) {
+ return actions.split(' ').map((id) => document.getElementById(id));
+ } else {
+ return [];
+ }
+ }
+
+ getTabAriaActionOperations(action) {
+ const operationCode = action.getAttribute('data-operation');
+ const idrefControls = action.getAttribute('aria-controls');
+ let operations = [];
+ if (operationCode) {
+ operations.push(action);
+ }
+ if (idrefControls) {
+ const context = document.getElementById(idrefControls);
+ const nodes = Array.from(context.querySelectorAll('[data-operation]'));
+ operations = operations.concat(nodes);
+ }
+ return operations;
+ }
+
+ getTabAssociatedWithAction(action) {
+ return document.querySelector(`[aria-actions~="${action.id}"]`);
+ }
+
+ getClosestTabWrapper(el) {
+ return el ? el.closest('.tab-wrapper') : null;
+ }
+
+ getTabpanelAssociatedWithTab(tab) {
+ return document.getElementById(tab.getAttribute('aria-controls'));
+ }
+
+ getActionAssociatedWithOperation(operation) {
+ const idrefAction = operation.getAttribute('data-action');
+ if (idrefAction) {
+ return document.getElementById(idrefAction);
+ } else {
+ return operation;
+ }
+ }
+
+ makeTabAndActionsFocusable(tab) {
+ tab.removeAttribute('tabindex');
+ this.getTabAriaActions(tab).forEach((action) => {
+ action.removeAttribute('tabindex');
+ });
+ }
+
+ makeTabAndActionsUnfocusable(tab) {
+ const selectedTab = this.getSelectedTab();
+ if (tab !== selectedTab) {
+ tab.tabIndex = -1;
+ this.getTabAriaActions(tab).forEach((action) => {
+ action.tabIndex = -1;
+ });
+ }
+ }
+
+ setSelectedTab(currentTab) {
+ for (var i = 0; i < this.tabs.length; i += 1) {
+ var tab = this.tabs[i];
+ if (currentTab === tab) {
+ tab.setAttribute('aria-selected', 'true');
+ this.makeTabAndActionsFocusable(tab);
+ this.tabpanels[i].classList.remove('is-hidden');
+ } else {
+ tab.setAttribute('aria-selected', 'false');
+ this.makeTabAndActionsUnfocusable(tab);
+ this.tabpanels[i].classList.add('is-hidden');
+ }
+ }
+ }
+
+ getSelectedTab() {
+ return this.tablistNode.querySelector('[aria-selected="true"]');
+ }
+
+ moveFocusToTab(newTab) {
+ const selectedTab = this.getSelectedTab();
+ newTab.focus();
+ this.tabs.forEach((tab) => {
+ if (newTab === tab) {
+ this.makeTabAndActionsFocusable(tab);
+ } else if (tab !== selectedTab) {
+ this.makeTabAndActionsUnfocusable(tab);
+ }
+ });
+ return newTab;
+ }
+
+ moveFocusToPreviousTab(currentTab) {
+ var newTab;
+ if (currentTab === this.firstTab) {
+ newTab = this.lastTab;
+ } else {
+ newTab = this.tabs[this.tabs.indexOf(currentTab) - 1];
+ }
+ return this.moveFocusToTab(newTab);
+ }
+
+ moveFocusToNextTab(currentTab) {
+ var newTab;
+ if (currentTab === this.lastTab) {
+ newTab = this.firstTab;
+ } else {
+ newTab = this.tabs[this.tabs.indexOf(currentTab) + 1];
+ }
+ return this.moveFocusToTab(newTab);
+ }
+
+ copyTabpanelToClipboard(tab, output) {
+ const tabPanel = document.getElementById(tab.getAttribute('aria-controls'));
+ const tabText = tabPanel.textContent.replace(/\s+/g, ' ').trim();
+ navigator.clipboard.writeText(tabText).then(
+ () => {
+ this.showSuccess(
+ output,
+ `Copied ${tab.textContent} tab contents to clipboard`
+ );
+ },
+ (err) => {
+ this.showError(
+ output,
+ `Failed to copy ${tab.textContent} tab contents to clipboard`,
+ err
+ );
+ }
+ );
+ }
+
+ relocateTab(tab, dir) {
+ const index = this.tabs.indexOf(tab);
+ const tabWrapper = this.getClosestTabWrapper(tab);
+ const tabPanel = this.getTabpanelAssociatedWithTab(tab);
+ const movementFunction = dir === 'forward' ? 'after' : 'before';
+ const dest =
+ dir === 'forward' ? 'nextElementSibling' : 'previousElementSibling';
+ const destIndex = dir === 'forward' ? index + 1 : index - 1;
+ const destTabWrapper = tabWrapper[dest];
+ const destTabPanel = tabPanel[dest];
+
+ if (destTabWrapper && destTabPanel) {
+ destTabWrapper[movementFunction](tabWrapper);
+ destTabPanel[movementFunction](tabPanel);
+ this.tabs.splice(destIndex, 0, this.tabs.splice(index, 1)[0]);
+ this.tabpanels.splice(destIndex, 0, this.tabpanels.splice(index, 1)[0]);
+ this.firstTab = this.tabs[0];
+ this.lastTab = this.tabs[this.tabs.length - 1];
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ deleteTab(tab) {
+ if (this.tabs.length === 1) {
+ return false;
+ }
+
+ const tabWrapper = this.getClosestTabWrapper(tab);
+ const tabPanel = document.getElementById(tab.getAttribute('aria-controls'));
+ const selectedTab = this.getSelectedTab();
+
+ if (tab === selectedTab) {
+ const newTab =
+ tab === this.lastTab
+ ? this.moveFocusToPreviousTab(selectedTab)
+ : this.moveFocusToNextTab(selectedTab);
+ this.setSelectedTab(newTab);
+ } else {
+ this.moveFocusToTab(selectedTab);
+ }
+
+ this.tabs = this.tabs.filter((t) => t !== tab);
+ this.tabpanels = this.tabpanels.filter((tp) => tp !== tabPanel);
+ this.firstTab = this.tabs[0];
+ this.lastTab = this.tabs[this.tabs.length - 1];
+ tabWrapper.remove();
+ tabPanel.remove();
+
+ return true;
+ }
+
+ /* EVENT HANDLERS */
+
+ onKeydown(event) {
+ var tgt = event.currentTarget,
+ flag = false;
+
+ if (tgt.getAttribute('role') !== 'tab') {
+ tgt = this.getTabAssociatedWithAction(tgt);
+ }
+
+ switch (event.key) {
+ case 'ArrowLeft':
+ this.moveFocusToPreviousTab(tgt);
+ flag = true;
+ break;
+
+ case 'ArrowRight':
+ this.moveFocusToNextTab(tgt);
+ flag = true;
+ break;
+
+ case 'Home':
+ this.moveFocusToTab(this.firstTab);
+ flag = true;
+ break;
+
+ case 'End':
+ this.moveFocusToTab(this.lastTab);
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ }
+
+ // Since this example uses buttons for the tabs, the click onr also is activated
+ // with the space and enter keys
+ onClick(event) {
+ this.setSelectedTab(event.currentTarget);
+ }
+
+ performTabOperation(event) {
+ if (event.type === 'keydown' && event.code !== 'Enter') {
+ return;
+ }
+
+ const operation = event.currentTarget;
+ const action = this.getActionAssociatedWithOperation(operation);
+ const tab = this.getTabAssociatedWithAction(action);
+ const operationCode = operation.getAttribute('data-operation');
+ const output = operation.closest('.tabs').querySelector('output');
+ const tabName = tab.textContent.trim();
+
+ switch (operationCode) {
+ case 'clipboard-copy': {
+ this.copyTabpanelToClipboard(tab, output);
+ break;
+ }
+ case 'move-backward':
+ case 'move-forward': {
+ const dir = operationCode === 'move-forward' ? 'forward' : 'backward';
+ if (this.relocateTab(tab, dir)) {
+ this.showSuccess(output, `Moved ${tabName} ${dir}.`);
+ } else {
+ this.showError(output, `Can’t move ${tabName} ${dir} any further.`);
+ }
+ break;
+ }
+ case 'close': {
+ if (this.deleteTab(tab)) {
+ this.showSuccess(output, `Closed ${tabName}.`);
+ } else {
+ this.showError(output, 'Can’t delete the last tab.');
+ }
+ break;
+ }
+ default: {
+ this.showError(output, `Sorry, ${operationCode} isn’t built yet.`);
+ }
+ }
+ }
+
+ showSuccess(output, msg) {
+ if (output) {
+ output.innerHTML = `${msg}`;
+ }
+ }
+
+ showError(output, msg) {
+ if (output) {
+ output.innerHTML = `${msg}`;
+ }
+ }
+
+ onFocusout(event) {
+ const tabWrapperOld = this.getClosestTabWrapper(event.currentTarget);
+ const tabWrapperNew = this.getClosestTabWrapper(event.relatedTarget);
+ if (tabWrapperOld !== tabWrapperNew) {
+ const tab = tabWrapperOld.querySelector('[role="tab"]');
+ this.makeTabAndActionsUnfocusable(tab);
+ }
+ }
+}
+
+// Initialize tablist
+
+window.addEventListener('load', function () {
+ var tablists = document.querySelectorAll('[role=tablist].manual');
+ for (var i = 0; i < tablists.length; i++) {
+ new TabsManual(tablists[i]);
+ }
+});