From 6cc0d6c04add82939706e177e85286007a1aca7a Mon Sep 17 00:00:00 2001 From: Matt King Date: Mon, 24 Sep 2018 11:37:05 -0700 Subject: [PATCH] Accordion Example: Restructure to use `div` instead of `dl` and improve focus styling To resolve issue #815: 1. Replace `dl` with `div` 2. Replace `dt` with `h3` 3. Replace `dd` with `div` 4. Update documentation tables. Also: 1. Improve visual focus styling so it is more apparent that arrow keys move focus from header to header. 2. Add accessibility features section that describes the focus styling. --- examples/accordion/accordion.html | 144 +++++++++++++-------------- examples/accordion/css/accordion.css | 52 ++++++++-- examples/accordion/js/accordion.js | 32 +++--- 3 files changed, 130 insertions(+), 98 deletions(-) diff --git a/examples/accordion/accordion.html b/examples/accordion/accordion.html index 4d0224b5e2..890613e068 100644 --- a/examples/accordion/accordion.html +++ b/examples/accordion/accordion.html @@ -41,27 +41,28 @@

Example

- + + +
+

Accessibility Features

+

+ The visual design includes features intended to help users understand that the accordion provides enhanced keyboard navigation functions. + When an accordion header button has keyboard focus, the styling of the accordion container and all its header buttons is changed. +

+

When any accordion header button receives focus:

+ +

The focused accordion header button:

+ +

Keyboard Support

@@ -232,8 +254,7 @@

Keyboard Support

Role, Property, State, and Tabindex Attributes

+ Update this table to describe how roles, properties, states, and tabindex are used in this example -->
@@ -244,37 +265,21 @@

Role, Property, State, and Tabindex Attributes

- - + - - - - - - - - - - - - + - + - + - + - + - + - - + + @@ -320,22 +326,8 @@

Role, Property, State, and Tabindex Attributes

Javascript and CSS Source Code

diff --git a/examples/accordion/css/accordion.css b/examples/accordion/css/accordion.css index e1ad412f38..f5e0291775 100644 --- a/examples/accordion/css/accordion.css +++ b/examples/accordion/css/accordion.css @@ -1,16 +1,26 @@ .Accordion { - border: 1px solid hsl(0, 0%, 82%); - border-radius: .3em; - box-shadow: 0 1px 2px hsl(0, 0%, 82%); + margin: 0; + padding: 0; + border: 2px solid hsl(0, 0%, 82%); + border-radius: 7px; + width: 20em; +} + +.Accordion.focus { + border-color: hsl(216, 94%, 73%); } +.Accordion.focus h3 { + background-color: hsl(0, 0%, 97%); +} + + .Accordion > * + * { border-top: 1px solid hsl(0, 0%, 82%); } .Accordion-trigger { background: none; - border: 0; color: hsl(0, 0%, 13%); display: block; font-size: 1rem; @@ -20,20 +30,38 @@ position: relative; text-align: left; width: 100%; + outline: none; +} + +.Accordion h3 { + margin: 0; + padding: 0; } -.Accordion dt:first-child .Accordion-trigger { - border-radius: .3em .3em 0 0; +.Accordion *:first-child .Accordion-trigger { + border-radius: 5px 5px 0 0; } .Accordion-trigger:focus, .Accordion-trigger:hover { - background: hsl(0, 0%, 93%); + background: hsl(216, 94%, 94%); +} + +.Accordion button::-moz-focus-inner { + border: 0; } .Accordion-title { - display: block; /* For Edge bug https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8295099/ */ + display: block; pointer-events: none; + border: transparent 2px solid; + border-radius: 5px; + padding: 0.25em; + outline: none; +} + +.Accordion-trigger:focus .Accordion-title { + border-color: hsl(216, 94%, 73%); } .Accordion-icon { @@ -42,7 +70,7 @@ height: .5rem; pointer-events: none; position: absolute; - right: 1.5em; + right: 2em; top: 50%; transform: translateY(-60%) rotate(45deg); width: .5rem; @@ -50,7 +78,7 @@ .Accordion-trigger:focus .Accordion-icon, .Accordion-trigger:hover .Accordion-icon { - border-color: hsl(0, 0%, 13%); + border-color: hsl(216, 94%, 73%); } .Accordion-trigger[aria-expanded="true"] .Accordion-icon { @@ -67,6 +95,10 @@ display: none; } +button { + border-style: none; +} + fieldset { border: 0; margin: 0; diff --git a/examples/accordion/js/accordion.js b/examples/accordion/js/accordion.js index bea53b9384..8d0b354ed0 100644 --- a/examples/accordion/js/accordion.js +++ b/examples/accordion/js/accordion.js @@ -18,6 +18,7 @@ Array.prototype.slice.call(document.querySelectorAll('.Accordion')).forEach(func var triggers = Array.prototype.slice.call(accordion.querySelectorAll('.Accordion-trigger')); var panels = Array.prototype.slice.call(accordion.querySelectorAll('.Accordion-panel')); + accordion.addEventListener('click', function (event) { var target = event.target; @@ -65,6 +66,10 @@ Array.prototype.slice.call(document.querySelectorAll('.Accordion')).forEach(func accordion.addEventListener('keydown', function (event) { var target = event.target; var key = event.which.toString(); + + var isExpanded = target.getAttribute('aria-expanded') == 'true'; + var allowToggle = (allowMultiple) ? allowMultiple : accordion.hasAttribute('data-allow-toggle'); + // 33 = Page Up, 34 = Page Down var ctrlModifier = (event.ctrlKey && key.match(/33|34/)); @@ -94,21 +99,24 @@ Array.prototype.slice.call(document.querySelectorAll('.Accordion')).forEach(func triggers[triggers.length - 1].focus(); break; } - event.preventDefault(); + } + } - else if (ctrlModifier) { - // Control + Page Up/ Page Down keyboard operations - // Catches events that happen inside of panels - panels.forEach(function (panel, index) { - if (panel.contains(target)) { - triggers[index].focus(); - - event.preventDefault(); - } - }); - } + }); + + // These are used to style the accordion when one of the buttons has focus + accordion.querySelectorAll('.Accordion-trigger').forEach(function (trigger) { + + trigger.addEventListener('focus', function (event) { + accordion.classList.add('focus'); + }); + + trigger.addEventListener('blur', function (event) { + accordion.classList.remove('focus'); + }); + }); // Minor setup: will set disabled state, via aria-disabled, to an
presentation
dl -
    -
  • Indicates that the dl element is being used to control presentation and does not represent a definition list.
  • -
  • This implementation uses an HTML definition list where each term is recast as a header and each definition is recast as a region that contains panel content.
  • -
-
headingdtIdentifies the element as a heading that serves as an accordion header.
aria-level=3dth3
    -
  • Specifies heading level for the accordion header.
  • -
  • Level 3 is chosen because the accordion is contained in a section with a level 2 heading.
  • +
  • Element that serves as an accordion header.
  • +
  • Each accordion header element contains a button that controls the visibility of its content panel.
  • +
  • The example uses heading level 3 so it fits correctly within the outline of the page; the example is contained in a section titled with a level 2 heading.
aria-expanded=truearia-expanded="true" button Set to true when the Accordion panel is expanded, otherwise set to false. @@ -282,7 +287,7 @@

Role, Property, State, and Tabindex Attributes

aria-controls=IDaria-controls="ID" button Points to the ID of the panel which the header controls. @@ -290,26 +295,27 @@

Role, Property, State, and Tabindex Attributes

aria-disabled=truearia-disabled="true" button If the accordion panel is expanded and is not allowed to be collapsed, then set to true.
regionregion dddiv Creates a landmark region that contains the currently expanded accordion panel.
aria-labelledby="ID_REF"ddaria-labelledby="IDREF"div
    -
  • Points to the accordion header.
  • -
  • Labels the landmark region with the accordion header.
  • +
  • Defines the accessible name for the region element.
  • +
  • References the accordion header button that expands and collapses the region.
  • +
  • region elements are required to have an accessible name to be identified as a landmark.