diff --git a/gulp/helpers/markdown.js b/gulp/helpers/markdown.js index d27814d3..99f1160b 100644 --- a/gulp/helpers/markdown.js +++ b/gulp/helpers/markdown.js @@ -169,12 +169,21 @@ module.exports = rootDir => filePath => { let example let insertToken let exampleLinkClass + const tokenTextContent = token.children + .filter(child => child.type === 'text') + .map(child => child.content) + .join(' ') + const hasLegacyMarker = /\blegacy\b/i.test(tokenTextContent) token.children.some((childToken, childIdx) => { exampleLink = examples.getLink(childToken) - // Add title + // Add example embedding (unless line contains "legacy" keyword) if (exampleLink) { + if (hasLegacyMarker) { + return false + } + examplePath = path.isAbsolute(exampleLink) ? path.join(rootDir, exampleLink) : path.resolve(path.dirname(filePath), exampleLink) diff --git a/pages/examples/widgets/accordion/README.md b/pages/examples/widgets/accordion/README.md index 08b2d378..e9989028 100644 --- a/pages/examples/widgets/accordion/README.md +++ b/pages/examples/widgets/accordion/README.md @@ -5,11 +5,11 @@ position: 8 # Accordions -**Accordions contain of a number of content panels, each of wich can be expanded or collapsed vertically by the user.** +**Accordions consist of a number of content panels, each of which can be expanded or collapsed vertically by the user.** [[_TOC_]] -Accordions help to save vertical space and prevent from visual noise. Some accordions allow only a single panel to be expanded at a time, others allow multiple. +Accordions help to save vertical space and reduce visual noise. Some accordions allow only a single panel to be expanded at a time, others allow multiple. ![Accordion](_media/accordion.png) @@ -17,11 +17,12 @@ Before you continue, please read [Tablist widgets (or: tab panels, tabs)](/examp ## General requirements -The following requirements are based on well established best practices and [WAI-WAI-ARIA Authoring Practices: Accordion (widget)](https://www.w3.org/TR/wai-aria-practices/#accordion). +The following requirements are based on well established best practices and the [WAI-ARIA Authoring Practices Guide (APG): Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/). -In addition to the tablists’ requirements, and besides many other requirements, we want to stress out explicitly the following: +Accordions and [Tablists](/examples/widgets/tablists) share the same underlying logic: a trigger (header/tab) controls the visibility of a content panel. While they are structurally similar, accordions have specific requirements: -- Multiple slides can be visible (optional). +- Multiple panels can be visible at the same time (optional). +- Keyboard support: Users can navigate between accordion headers using `Tab` and toggle them with `Enter` or `Space`. (Optional but recommended: Arrow key navigation). ## Proofs of concept @@ -35,42 +36,48 @@ It is relatively simple to create a custom accordion implementation with ARIA: #### Implementation details -A link with an `aria-expanded="true"` attribute is placed around each panel’s header; its value (`true`/`false`) and the visibility of the corresponding panel is toggled using JavaScript. See [Marking elements expandable using aria-expanded](/examples/sensible-aria-usage/expanded). +This implementation follows the current APG approach and uses a real `button` in each header. -While this may feel tempting in some circumstances, there are several drawbacks: +- The button toggles `aria-expanded` (`true`/`false`). +- The button uses `aria-controls` to reference the associated panel. +- The panel uses `role="region"` and `aria-labelledby` to expose a clear relationship back to the controlling header button. +- The panel visibility is synchronized with the semantic state using JavaScript. -- It needs more JavaScript (instead of relying on browser standard behaviour). - - The current implementation allows multiple elements to be open. If you wanted to restrict it to one element though, a lot of additional JavaScript would be needed to manage states - something that radio buttons would offer "for free". -- This solution is less intuitive: a screen reader announcement "link X collapsed" is less expressive than "show panel X checkbox not checked" or "show panel X radio button not checked 2 of 3". -- Missing backwards compatibility for older clients with incomplete/missing ARIA support. +### Native HTML Disclosure Elements -### Radio buttons implementation +For simple disclosure-like use cases, the native HTML `
` and `` elements are a solid, no-JavaScript option. You can find the technical specification and browser behavior in the [MDN Web Docs for the Details element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details). -This implementation is based on the [tablists’ proof of concept](/examples/widgets/tablists/#proof-of-concept), only the layout is different. +- The `summary` element works as the interactive header. +- The surrounding `details` element manages the expanded/collapsed state natively. +- This removes the need for JavaScript compared to custom ARIA widgets. -[Example](_examples/accordion-with-radio-buttons) +[Example](_examples/accordion-with-details-summary) -#### Implementation details +#### Implementation details -Some interesting peculiarities: +This implementation follows the current APG approach and uses a real `button` in each header. -- Using `.accordion:focus-within .control label`, a style can be applied to all radio button labels upon interacting with the accordion. - - This gives users a clue that they are interacting with a single control now (indicating to use the `Arrow` keys instead of `Tab` to navigate through accordion items). - - If you would rather like to make each control focusable on its own, you could use a group of checkboxes instead of radio buttons. - - Do not forget to make sure only one of them is checked at a time though (using some JavaScript). +- The button toggles `aria-expanded` (`true`/`false`). +- The button uses `aria-controls` to reference the associated panel. +- The panel uses `role="region"` and `aria-labelledby` to expose a clear relationship back to the controlling header button. +- **Keyboard Navigation:** Implementation should ideally support Arrow keys (`Up`/`Down`) to move focus between headers, and `Home`/`End` to jump to the first/last header. -### Checkboxes implementation -This implementation is based on the [tablists’ proof of concept](/examples/widgets/tablists/#proof-of-concept), with a slightly different layout: +### Comparison: ARIA vs. Native HTML -[Example](_examples/multi-accordion-with-checkboxes) +#### Comparison of Implementation Methods -#### Implementation details +| Implementation Method | Advantages | Limitations | +| :--- | :--- | :--- | +| **Custom ARIA Implementation** | | | +| **Native Disclosure Elements (`
`)**|
  • Works without JavaScript.
  • Native accessibility "out of the box".
  • Minimal code footprint.
|
  • Limited styling options.
  • No native support for "only one open" (requires JS).
  • Default keyboard support is limited to Tab/Space/Enter.
| + + +### Legacy implementations (Historical) + +**Note:** The following legacy variants are deprecated and provided for historical reference only. -Some interesting peculiarities: +For all new projects, use one of the recommended implementations above (ARIA or native `
`/``, depending on your requirements). -- Checkboxes replace the radio buttons to offer multiple selection. - - We waived using a `
`/`` structure, as this is no traditional group of checkboxes, and JAWS tends to be very wordy with focusable items nested within those, see [Grouping form controls with fieldset and legend](/examples/forms/grouping-with-fieldset-legend). -- By default, only the `Space` key is used to toggle a checkbox (while pressing `Enter` submits a form). - - To make it more intuitive for visual users (who do not know about any checkbox behind the scenes, and thinking they are interacting with a link or button), the `Enter` key was re-wired to also toggle the checkboxes. -- In contrast to the radio button solution above, we omitted a visual `.accordion:focus-within .control label` state for the accordion items, as checkboxes are individual controls and (thereby accessed by the `Tab` key, as most users would expect). +[Accordion with radio buttons](_examples/accordion-with-radio-buttons) *(Legacy — for reference only)** +[Multi accordion with checkboxes](_examples/multi-accordion-with-checkboxes) *(Legacy — for reference only)* diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-aria/README.md b/pages/examples/widgets/accordion/_examples/accordion-with-aria/README.md index 115a54ff..20a4044f 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-aria/README.md +++ b/pages/examples/widgets/accordion/_examples/accordion-with-aria/README.md @@ -1,5 +1,5 @@ --- -title: "Accordion with ARIA" +title: "Accordion with ARIA (APG pattern)" compatibility: Keyboard only: status: pass diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-aria/_example.png b/pages/examples/widgets/accordion/_examples/accordion-with-aria/_example.png index 331523ca..1d0b6bf4 100644 Binary files a/pages/examples/widgets/accordion/_examples/accordion-with-aria/_example.png and b/pages/examples/widgets/accordion/_examples/accordion-with-aria/_example.png differ diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.css b/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.css index 988b0eff..de1682e8 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.css +++ b/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.css @@ -1,60 +1,82 @@ -*:focus { - outline: 2px dotted black; +body { + padding: 20px; + font-family: system-ui, sans-serif; } -.visually-hidden { - position: absolute; - margin: -1px; - border: 0; - padding: 0; - width: 1px; - height: 1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; +.accordion-widget > h2 { + margin: 0 0 12px; } -.accordion button[aria-controls] { +.accordion-item { + margin: 0; +} + +.accordion-trigger { appearance: none; - all: inherit; - display: block; + background: none; + font: inherit; + color: inherit; + display: flex; + align-items: center; box-sizing: border-box; - margin: 0 0 -1px 0; + margin: 0; border: 1px solid black; - padding: 4px 10px; + padding: 6px 12px; width: 100%; - background-color: lightyellow; cursor: pointer; + text-align: left; } -.accordion button[aria-controls]:hover { - text-decoration: underline; +.accordion-trigger:hover { + background: #fff9c4; } -.accordion *:focus, -.accordion button[aria-controls]:focus { - outline: 2px dotted red; +.accordion-trigger:focus-visible { + outline: 2px dotted; outline-offset: 2px; } -.accordion button[aria-controls]::before { - content: "-"; - display: inline-block; - width: 0.75em; +.indicator { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + flex: 0 0 1rem; + margin-right: 8px; font-weight: normal; } -.accordion button[aria-controls][aria-expanded=false]::before { - content: "+"; +.indicator-closed { + display: none; } -.header { - margin-bottom: 0; +.accordion-trigger[aria-expanded='false'] .indicator-open { + display: none; } -.panel { +.accordion-trigger[aria-expanded='false'] .indicator-closed { + display: inline-block; +} + +.accordion-header { + margin: 0; + font-size: 1.25rem; + font-weight: 600; +} + +.accordion-panel { border: 1px solid black; - border-top-color: lightyellow; - padding: 0 0 0 10px; - background-color: lightyellow; + border-top: none; + padding: 1rem; + background: lightyellow; +} + +.accordion-trigger[aria-expanded='true'] { + background: lightyellow; +} + +a:focus-visible, +button:focus-visible { + outline: 2px dotted; + outline-offset: 2px; } diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.js b/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.js index 168df4e2..28c94307 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.js +++ b/pages/examples/widgets/accordion/_examples/accordion-with-aria/example.js @@ -1,58 +1,30 @@ class AdgAccordion { constructor(el) { this.element = el - this.triggers = this.element.querySelectorAll('[aria-controls]') + this.triggers = Array.from( + this.element.querySelectorAll('.accordion-trigger') + ) this.initTriggers() } initTriggers() { - this.triggers.forEach((trigger, triggerIndex, triggerArray) => { + this.triggers.forEach(trigger => { const panelId = trigger.getAttribute('aria-controls') const panel = document.getElementById(panelId) - this.hide(panel, trigger) + const isExpanded = trigger.getAttribute('aria-expanded') === 'true' + panel.hidden = !isExpanded trigger.addEventListener('click', () => { - if (panel.hidden) { - this.show(panel, trigger) - } else { - this.hide(panel, trigger) - } - }) - - trigger.addEventListener('keydown', event => { - if (trigger === document.activeElement) { - let focusTarget - switch (event.keyCode || event.key) { - case 38: - case 'Up': - case 'ArrowUp': - focusTarget = - triggerIndex > 0 ? triggerIndex - 1 : triggerArray.length - 1 - break - case 40: - case 'Down': - case 'ArrowDown': - focusTarget = - triggerIndex < triggerArray.length - 1 ? triggerIndex + 1 : 0 - break - } - if (triggerArray[focusTarget]) { - triggerArray[focusTarget].focus() - } - } + this.toggle(panel, trigger) }) }) } - show(panel, trigger) { - panel.hidden = false - trigger.setAttribute('aria-expanded', 'true') - } - - hide(panel, trigger) { - panel.hidden = true - trigger.setAttribute('aria-expanded', 'false') + toggle(panel, trigger) { + const isExpanded = trigger.getAttribute('aria-expanded') === 'true' + trigger.setAttribute('aria-expanded', String(!isExpanded)) + panel.hidden = isExpanded } } diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-aria/index.html b/pages/examples/widgets/accordion/_examples/accordion-with-aria/index.html index 14d411b0..b8417e47 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-aria/index.html +++ b/pages/examples/widgets/accordion/_examples/accordion-with-aria/index.html @@ -1,74 +1,58 @@

-
-

- -

-
-

- A rose is a woody perennial flowering plant of the genus Rosa, in the family Rosaceae, or the flower it bears. There are over three hundred species and tens of thousands of cultivars. -

- Rosa rubiginosa by Stan Shebs (wikimedia.org) -

- Botany + +
+

Flowers

+ +
+

+

-

- The flowers of most species have five petals, with the exception of Rosa sericea, which usually has only four. Each petal is divided into two distinct lobes and is usually white or pink, though in a few species yellow or red. Beneath the petals are five sepals (or in the case of some Rosa sericea, four). -

-

- Learn more: Rose (wikipedia.org) -

-

- - -

-
-

- -

-
-

- Tulips are a genus of spring-blooming perennial herbaceous bulbiferous geophytes. The flowers are usually large, showy and brightly colored, generally red, pink, yellow, or white. -

- Red and pink tulips by Inuyas (wikimedia.org) -

- Description +
+

A rose is a woody perennial flowering plant of the genus Rosa.

+

+ Learn more about roses +

+
+

+ +
+

+

-

- The tulip’s flowers are usually large and are actinomorphic (radially symmetric) and hermaphrodite (contain both male and female characteristics), generally erect, or more rarely pendulous, and are arranged more usually as a single terminal flower. In structure, the flower is generally cup or star shaped. -

-

- Learn more: Tulip (wikipedia.org) -

-

- - -

- -

- -

-
-

- Helianthus annuus, the common sunflower, is a large annual forb of the genus Helianthus grown as a crop for its edible oil and edible fruits. The name sunflower may derive from the flower’s head’s shape, which resembles the sun. -

- Sunflower against a blue sky by Fir0002 (wikimedia.org) -

- Description + +

+ +
+

+

-

- The plant has an erect rough-hairy stem, reaching typical heights of 3 metres. The tallest sunflower on record achieved 9.17 metres. Sunflower leaves are broad, coarsely toothed, rough and mostly alternate. What is often called the "flower" of the sunflower is actually a "flower head" or pseudanthium of numerous small individual five-petaled flowers. The outer flowers, which resemble petals, are called ray flowers. -

-

- Learn more: Sunflower (wikipedia.org) -

-

- - -

- -
+ + +

diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/README.md b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/README.md new file mode 100644 index 00000000..9a19b427 --- /dev/null +++ b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/README.md @@ -0,0 +1,4 @@ +--- +title: "Accordion with details and summary" +compatibility: +--- diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/_example.png b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/_example.png new file mode 100644 index 00000000..1d0b6bf4 Binary files /dev/null and b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/_example.png differ diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/example.css b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/example.css new file mode 100644 index 00000000..f8a64905 --- /dev/null +++ b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/example.css @@ -0,0 +1,93 @@ +body { + padding: 20px; + font-family: system-ui, sans-serif; +} + +.accordion-widget > h2 { + margin: 0 0 12px; +} + +.accordion-item { + margin: 0; + padding: 0; + border: 0; +} + +.accordion-trigger { + appearance: none; + background: none; + font: inherit; + color: inherit; + display: flex; + align-items: center; + box-sizing: border-box; + border: 1px solid #000; + padding: 6px 12px; + margin: 0; + width: 100%; + cursor: pointer; + text-align: left; + list-style: none; +} + +.accordion-header { + margin: 0; + font-size: 1.25rem; + font-weight: 600; +} + +.accordion-trigger::marker { + content: ''; +} + +.accordion-trigger::-webkit-details-marker { + display: none; +} + +.indicator { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + flex: 0 0 1rem; + margin-right: 8px; + font-weight: normal; +} + +.indicator-closed { + display: inline-block; +} + +.indicator-open { + display: none; +} + +.accordion-trigger:hover { + background: #fff9c4; +} + +.accordion-item[open] > .accordion-trigger { + background: lightyellow; +} + +.accordion-item[open] > .accordion-trigger .indicator-open { + display: inline-block; +} + +.accordion-item[open] > .accordion-trigger .indicator-closed { + display: none; +} + +.accordion-panel { + border: 1px solid #000; + border-top: none; + padding: 1rem; + background: lightyellow; +} + +.accordion-trigger:focus-visible, +a:focus-visible, +button:focus-visible { + outline: 2px dotted; + outline-offset: 2px; +} diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/example.js b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/example.js new file mode 100644 index 00000000..d8c12a89 --- /dev/null +++ b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/example.js @@ -0,0 +1 @@ +// Native details/summary behavior requires no JavaScript for basic toggle behavior. diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/index.html b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/index.html new file mode 100644 index 00000000..28584c92 --- /dev/null +++ b/pages/examples/widgets/accordion/_examples/accordion-with-details-summary/index.html @@ -0,0 +1,47 @@ +

+ +

+ +
+

Flowers

+ +
+ + + + Rose + +
+

A rose is a woody perennial flowering plant of the genus Rosa.

+

Learn more about roses

+
+
+ +
+ + + + Tulip + +
+

Tulips are a genus of spring-blooming perennial plants.

+

Learn more about tulips

+
+
+ +
+ + + + Sunflower + +
+

A sunflower is a large annual plant.

+

Learn more about sunflowers

+
+
+
+ +

+ +

diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/README.md b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/README.md index 8be61164..9c6cbff5 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/README.md +++ b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/README.md @@ -1,5 +1,5 @@ --- -title: "Accordion with radio buttons" +title: "Accordion with radio buttons (Legacy)" compatibility: Keyboard only: status: pass diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/_example.png b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/_example.png index b37abfe4..1d0b6bf4 100644 Binary files a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/_example.png and b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/_example.png differ diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.css b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.css index 064d9681..7b53a9e0 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.css +++ b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.css @@ -1,62 +1,107 @@ -*:focus { - outline: 2px dotted black; -} - .visually-hidden { position: absolute; - margin: -1px; - border: 0; - padding: 0; + left: -10000px; + top: auto; width: 1px; height: 1px; overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; } -.accordion:focus-within .header label { - background-color: #ffffb3; +body { + padding: 20px; + font-family: system-ui, sans-serif; } -.header h2 { - margin-bottom: 0; +.accordion-widget > h2 { + margin: 0 0 12px; } -.header label { - display: block; + +.accordion-item { + margin: 0; +} + +.notice { + background: #fff3cd; + border: 1px solid #ffc107; + padding: 8px; + margin: 0 0 16px 0; +} + +.accordion-header { + margin: 0; + font-size: 1.25rem; + font-weight: 600; +} + +.accordion-trigger { + appearance: none; + background: none; + font: inherit; + color: inherit; + display: flex; + align-items: center; + box-sizing: border-box; border: 1px solid black; - margin: 0 0 -1px 0; - padding: 4px 10px; - background-color: lightyellow; + margin: 0; + padding: 6px 12px; + width: 100%; + cursor: pointer; + text-align: left; } -.header label::before { - content: "+"; - display: inline-block; - width: 0.75em; + +.indicator { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + flex: 0 0 1rem; + margin-right: 8px; font-weight: normal; } -input[type="radio"]:checked + h2 label { - border-bottom-color: lightyellow; - position: relative; +.indicator-open { + display: none; +} + +input[type='radio']:checked + .accordion-header .accordion-trigger { + background: lightyellow; } -input[type="radio"]:checked + h2 label::before { - content: "-"; + +input[type='radio']:checked + .accordion-header .accordion-trigger .indicator-open { + display: inline-block; } -.accordion a:focus, -.accordion input:focus, -.accordion input[type="radio"]:focus + h2 label { - outline: 2px dotted red; +input[type='radio']:checked + .accordion-header .accordion-trigger .indicator-closed { + display: none; +} + +input[type='radio']:focus-visible + .accordion-header .accordion-trigger { + outline: 2px dotted; outline-offset: 2px; } -input[type="radio"] + h2 label:hover { - cursor: pointer; - text-decoration: underline; +.accordion-trigger:hover { + background: #fff9c4; +} + +input[type='radio']:checked + .accordion-header .accordion-trigger:hover { + background: lightyellow; +} + +.accordion-panel { + display: none; + border: 1px solid #000; + border-top: none; + padding: 1rem; + background: lightyellow; } -.panel { - border: 1px solid; - background-color: lightyellow; - padding: 0 0 0 10px; +.accordion-item > input[type='radio']:checked ~ .accordion-panel { + display: block; +} + +a:focus-visible, +button:focus-visible { + outline: 2px dotted; + outline-offset: 2px; } diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.js b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.js index ec56dd04..4c5a728b 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.js +++ b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/example.js @@ -1,27 +1 @@ -class AdgRadioAccordion { - constructor(el) { - this.element = el - this.triggers = this.element.querySelectorAll('.trigger') - this.initTriggers() - } - - initTriggers() { - this.triggers.forEach(trigger => { - const panelId = `${trigger.id}_panel` - const panel = document.getElementById(panelId) - panel.style.display = trigger.checked ? 'block' : 'none' - - trigger.addEventListener('change', () => { - this.element.querySelectorAll('.panel').forEach(p => { - p.style.display = p.id === panel.id ? 'block' : 'none' - }) - }) - }) - } -} - -const accordions = [] - -document - .querySelectorAll('[data-adg-radio-accordion]') - .forEach(element => accordions.push(new AdgRadioAccordion(element))) +// No JavaScript needed: panel visibility is handled by CSS `:checked` selectors. diff --git a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/index.html b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/index.html index 7bca3dd0..e5c36872 100644 --- a/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/index.html +++ b/pages/examples/widgets/accordion/_examples/accordion-with-radio-buttons/index.html @@ -1,89 +1,65 @@

-
-

- flowers (accordion) -

-

- Tablist help: use the accordion controls to toggle the visibility of their respective panels (below the controls). -

-
- -

- -

-
-
-

- A rose is a woody perennial flowering plant of the genus Rosa, in the family Rosaceae, or the flower it bears. There are over three hundred species and tens of thousands of cultivars. -

- Rosa rubiginosa by Stan Shebs (wikimedia.org) -

- Botany + +

+ Deprecated: Use an ARIA-based implementation instead. Radio buttons are not semantically appropriate for accordion behavior. +

+ +
+

Flowers

+ +
+ +

+

-

- The flowers of most species have five petals, with the exception of Rosa sericea, which usually has only four. Each petal is divided into two distinct lobes and is usually white or pink, though in a few species yellow or red. Beneath the petals are five sepals (or in the case of some Rosa sericea, four). -

-

- Learn more: Rose (wikipedia.org) -

-

- - -

+
+

A rose is a woody perennial flowering plant of the genus Rosa.

+

+ Learn more about roses +

+
-
+ +
-

- -

-
-
-

- Tulips are a genus of spring-blooming perennial herbaceous bulbiferous geophytes. The flowers are usually large, showy and brightly colored, generally red, pink, yellow, or white. -

- Red and pink tulips by Inuyas (wikimedia.org) -

- Description +

+

-

- The tulip’s flowers are usually large and are actinomorphic (radially symmetric) and hermaphrodite (contain both male and female characteristics), generally erect, or more rarely pendulous, and are arranged more usually as a single terminal flower. In structure, the flower is generally cup or star shaped. -

-

- Learn more: Tulip (wikipedia.org) -

-

- - -

+
+

Tulips are a genus of spring-blooming perennial plants.

+

+ Learn more about tulips +

+
-
+ +
-

- -

-
-
-

- Helianthus annuus, the common sunflower, is a large annual forb of the genus Helianthus grown as a crop for its edible oil and edible fruits. The name sunflower may derive from the flower’s head’s shape, which resembles the sun. -

- Sunflower against a blue sky by Fir0002 (wikimedia.org) -

- Description +

+

-

- The plant has an erect rough-hairy stem, reaching typical heights of 3 metres. The tallest sunflower on record achieved 9.17 metres. Sunflower leaves are broad, coarsely toothed, rough and mostly alternate. What is often called the "flower" of the sunflower is actually a "flower head" or pseudanthium of numerous small individual five-petaled flowers. The outer flowers, which resemble petals, are called ray flowers. -

-

- Learn more: Sunflower (wikipedia.org) -

-

- - -

+
+

A sunflower is a large annual plant.

+

+ Learn more about sunflowers +

+
-
+

diff --git a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/README.md b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/README.md index 1f63cc66..02704d5c 100644 --- a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/README.md +++ b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/README.md @@ -1,5 +1,5 @@ --- -title: "Multi accordion with checkboxes" +title: "Multi accordion with checkboxes (Legacy)" compatibility: Keyboard only: status: pass diff --git a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/_example.png b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/_example.png index 27b3206c..1d0b6bf4 100644 Binary files a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/_example.png and b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/_example.png differ diff --git a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.css b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.css index 13e78827..3886306d 100644 --- a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.css +++ b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.css @@ -1,58 +1,107 @@ -*:focus { - outline: 2px dotted black; -} - .visually-hidden { position: absolute; - margin: -1px; - border: 0; - padding: 0; + left: -10000px; + top: auto; width: 1px; height: 1px; overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; } -.header h2 { - margin-bottom: 0; +body { + padding: 20px; + font-family: system-ui, sans-serif; } -.header label { - display: block; + +.accordion-widget > h2 { + margin: 0 0 12px; +} + +.accordion-item { + margin: 0; +} + +.notice { + background: #fff3cd; + border: 1px solid #ffc107; + padding: 8px; + margin: 0 0 16px 0; +} + +.accordion-header { + margin: 0; + font-size: 1.25rem; + font-weight: 600; +} + +.accordion-trigger { + appearance: none; + background: none; + font: inherit; + color: inherit; + display: flex; + align-items: center; + box-sizing: border-box; border: 1px solid black; - margin: 0 0 -1px 0; - padding: 4px 10px; - background-color: lightyellow; + margin: 0; + padding: 6px 12px; + width: 100%; + cursor: pointer; + text-align: left; } -.header label::before { - content: "+"; - display: inline-block; - width: 0.75em; + +.indicator { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + flex: 0 0 1rem; + margin-right: 8px; font-weight: normal; } -input[type="checkbox"]:checked + h2 label { - border-bottom-color: lightyellow; - position: relative; +.indicator-open { + display: none; } -input[type="checkbox"]:checked + h2 label::before { - content: "-"; + +input[type='checkbox']:checked + .accordion-header .accordion-trigger { + background: lightyellow; +} + +input[type='checkbox']:checked + .accordion-header .accordion-trigger .indicator-open { + display: inline-block; } -.accordion a:focus, -.accordion input:focus, -.accordion input[type="checkbox"]:focus + h2 label { - outline: 2px dotted red; +input[type='checkbox']:checked + .accordion-header .accordion-trigger .indicator-closed { + display: none; +} + +input[type='checkbox']:focus-visible + .accordion-header .accordion-trigger { + outline: 2px dotted; outline-offset: 2px; } -input[type="checkbox"] + h2 label:hover { - cursor: pointer; - text-decoration: underline; +.accordion-trigger:hover { + background: #fff9c4; } -.panel { - border: 1px solid; - background-color: lightyellow; - padding: 0 0 0 10px; +input[type='checkbox']:checked + .accordion-header .accordion-trigger:hover { + background: lightyellow; +} + +.accordion-panel { + display: none; + border: 1px solid #000; + border-top: none; + padding: 1rem; + background: lightyellow; +} + +.accordion-item > input[type='checkbox']:checked ~ .accordion-panel { + display: block; +} + +a:focus-visible, +button:focus-visible { + outline: 2px dotted; + outline-offset: 2px; } diff --git a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.js b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.js index 62638889..a0c7ec50 100644 --- a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.js +++ b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/example.js @@ -1,37 +1,10 @@ -class AdgCheckboxAccordion { - constructor(el) { - this.element = el - this.triggers = this.element.querySelectorAll('.trigger') - this.initTriggers() - } - - initTriggers() { - this.triggers.forEach(trigger => { - const panelId = `${trigger.id}_panel` - const panel = document.getElementById(panelId) - this.updatePanelVisibility(panel, trigger) - - trigger.addEventListener('keydown', event => { - if (event.keyCode === 13 || event.key === 'Enter') { - event.preventDefault() - trigger.checked = !trigger.checked - this.updatePanelVisibility(panel, trigger) - } - }) - - trigger.addEventListener('change', () => { - this.updatePanelVisibility(panel, trigger) - }) - }) - } - - updatePanelVisibility(panel, trigger) { - panel.style.display = trigger.checked === true ? 'block' : 'none' - } -} - -const accordions = [] - document - .querySelectorAll('[data-adg-checkbox-accordion]') - .forEach(element => accordions.push(new AdgCheckboxAccordion(element))) + .querySelectorAll('[data-adg-checkbox-accordion] .trigger') + .forEach(trigger => { + trigger.addEventListener('keydown', event => { + if (event.keyCode === 13 || event.key === 'Enter') { + event.preventDefault() + trigger.checked = !trigger.checked + } + }) + }) diff --git a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/index.html b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/index.html index 07798916..cb82b6f4 100644 --- a/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/index.html +++ b/pages/examples/widgets/accordion/_examples/multi-accordion-with-checkboxes/index.html @@ -1,89 +1,64 @@

-
-

- flowers (accordion) -

-

- Tablist help: use the accordion controls to toggle the visibility of their respective panels (below the controls). -

-
- -

- -

-
-
-

- A rose is a woody perennial flowering plant of the genus Rosa, in the family Rosaceae, or the flower it bears. There are over three hundred species and tens of thousands of cultivars. -

- Rosa rubiginosa by Stan Shebs (wikimedia.org) -

- Botany +

+ Deprecated: Use an ARIA-based implementation instead. Checkboxes are not semantically appropriate for accordion behavior. +

+ +
+

Flowers

+ +
+ +

+

-

- The flowers of most species have five petals, with the exception of Rosa sericea, which usually has only four. Each petal is divided into two distinct lobes and is usually white or pink, though in a few species yellow or red. Beneath the petals are five sepals (or in the case of some Rosa sericea, four). -

-

- Learn more: Rose (wikipedia.org) -

-

- - -

+
+

A rose is a woody perennial flowering plant of the genus Rosa.

+

+ Learn more about roses +

+
-
+ +
-

- -

-
-
-

- Tulips are a genus of spring-blooming perennial herbaceous bulbiferous geophytes. The flowers are usually large, showy and brightly colored, generally red, pink, yellow, or white. -

- Red and pink tulips by Inuyas (wikimedia.org) -

- Description +

+

-

- The tulip’s flowers are usually large and are actinomorphic (radially symmetric) and hermaphrodite (contain both male and female characteristics), generally erect, or more rarely pendulous, and are arranged more usually as a single terminal flower. In structure, the flower is generally cup or star shaped. -

-

- Learn more: Tulip (wikipedia.org) -

-

- - -

+
+

Tulips are a genus of spring-blooming perennial plants.

+

+ Learn more about tulips +

+
-
+ +
-

- -

-
-
-

- Helianthus annuus, the common sunflower, is a large annual forb of the genus Helianthus grown as a crop for its edible oil and edible fruits. The name sunflower may derive from the flower’s head’s shape, which resembles the sun. -

- Sunflower against a blue sky by Fir0002 (wikimedia.org) -

- Description +

+

-

- The plant has an erect rough-hairy stem, reaching typical heights of 3 metres. The tallest sunflower on record achieved 9.17 metres. Sunflower leaves are broad, coarsely toothed, rough and mostly alternate. What is often called the "flower" of the sunflower is actually a "flower head" or pseudanthium of numerous small individual five-petaled flowers. The outer flowers, which resemble petals, are called ray flowers. -

-

- Learn more: Sunflower (wikipedia.org) -

-

- - -

+
+

A sunflower is a large annual plant.

+

+ Learn more about sunflowers +

+
-
+

diff --git a/pages/examples/widgets/accordion/_media/_example.png b/pages/examples/widgets/accordion/_media/_example.png new file mode 100644 index 00000000..1d0b6bf4 Binary files /dev/null and b/pages/examples/widgets/accordion/_media/_example.png differ diff --git a/pages/examples/widgets/accordion/_media/accordion.png b/pages/examples/widgets/accordion/_media/accordion.png index 4303eac6..1d0b6bf4 100644 Binary files a/pages/examples/widgets/accordion/_media/accordion.png and b/pages/examples/widgets/accordion/_media/accordion.png differ diff --git a/pages/examples/widgets/tablists/README.md b/pages/examples/widgets/tablists/README.md index 2f322a4d..910939ea 100644 --- a/pages/examples/widgets/tablists/README.md +++ b/pages/examples/widgets/tablists/README.md @@ -101,5 +101,5 @@ The ARIA-based implementation (POC #1) should be used for all new projects, as i The radio button approach was previously used as a simpler alternative based on native form controls. However, radio buttons represent form input choices rather than navigational relationships between tabs and panels. This semantic mismatch leads to poorer screen reader support and inconsistent interaction patterns, requiring significant workarounds to achieve comparable accessibility. -[Example](_examples/tablist-with-radio-buttons) +[Tablist with radio buttons](_examples/tablist-with-radio-buttons) *(Legacy — for reference only)*