Awesome Accordions website

Accessibility

We coded the accordion with accessibility in mind, in contrast, plain text is inherently accessible, by hiding content inside hidden panels we need to make an extra effort to make it perfectly accessible for screen-readers and people who use keyboars to navigate.

Below you can read about the different solutions we used while building the accordion.

Screen readers

In the accordion panels we are using javascript to change the state of the accordion panels. Changing states are usually visually apparent to users who can see the page but they may not be obvious to users of assistive technologies like screen readers. To fill this gap and provide a way to programmatically expose dynamic content changes in a way that can be announced by assistive technologies we can use Accessible Rich Internet Applications (ARIA) attributes.

Below is an example of the code with an explanation of the attributes


 

Making the buttons accessible

By adding role="button" we will explain to a screen reader that the accordion-heading has a button control. We also need to indicate whether the collapsible content below is in the extended or in the collapsed state. We can do this by using the aria-expanded attribute

Our panel can be in one of two states:

aria-expanded=false
The block is collapsed so the content is not visible for the user.

aria-expanded=true
The block is expanded and the content is visible for the user.

Creating the relationship between the button and the panel

We need to explain that the heading and the panel are related to each other. We can do this by using the aria-controls to explain that this element has control over and affects the panel. And in the panel we can use the aria-labeledby attribute to describe the relationship and explain that this element is revealed by the header

aria-controls="accordion_1" points to the ID of the panel which the header controls. In the panel you see the aria-labelledby="accordion_1" this attribute establishes relationships between the panel and the header.

Making the Panel accessible

For the panel we are using a div and to explain the role of this div as a content area that has relevant information related to the heading we use role="region".

Source:
https://www.w3.org/TR/wai-aria-practices/examples/accordion/accordion.html

Keyboard

Keyboard accessibility is one of the most important aspects of web accessibility. Nowadays, when most web interfaces are designed with mouse cursors and touch interaction in mind, keyboard navigation is neglected.

The keyboard is still the primary means for a large group of users to navigate websites so we want to make sure that users can navigate efficiently and unhindered using just their keyboard.

 

Space or Enter When focus is on the accordion header of a collapsed section, expands the section.
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.
Down Arrow
  • When focus is on an accordion header, moves focus to the next accordion header.
  • When focus is on last accordion header, moves focus to first accordion header.
Up Arrow
  • When focus is on an accordion header, moves focus to the previous accordion header.
  • When focus is on first accordion header, moves focus to last accordion header.
Home When focus is on an accordion header, moves focus to the first accordion header.
End When focus is on an accordion header, moves focus to the last accordion header.

 

Helping keyboard users to focus the panels

inside the Panels we have elements and text that users can interact with, examples are input fields or long texts that they need to scroll. Keyboard users who browse the accordion need to be able to to focus on the panel itself to get access to these elements.
To make sure the panel can get focus successfully in all browsers we need to add the special case tabindex="-1" attribute to the panel itself. This attribute value prevents the panel from being part of the tab order of the page, but allows focus to be placed on it by scripting. So this means we can only focus to the panel itself when it is opened.
When the focus is on the panel they can easily have access to the elements inside the panel, they can access form fields or scroll text.

Keydown script

To make this work we created a key-down script that checks which key is pressed and then we do one function or other.




$(document).keydown(function (e) {
       // Space    32
       // Enter    13
       // TAB      9
       // Shift    16
       // Down     40
       // Up       38
       // Home     36
       // End      35
       let current = $(":focus");
       //let li_elem = current.closest("li.awesome-accordion-item");
       switch (e.which) {
           case 38: // up
               move_up_down(-1);
               break;
           case 40: // down
               move_up_down(1);
               break;
           case 36: // Home
               go_home_end(-1);
               break;
           case 35: // End
               go_home_end(1);
               break;
           default:
               return; // exit this handler for other keys
       }
       if( current.closest("ul.awesome-accordion").length > 0 && current.closest(".awesome-accordion-panel").length < 1 )
       {
           e.preventDefault(); // prevent the default action (scroll / move caret)
       }
   });


Move focus using arrow keys

Here move_up_down and go_home_end. Depends if is up/down we change the focus to right element


function move_up_down(action)
   {
       let current = $(":focus");
       if( current.hasClass("js-open-close-acc") )
       {
           let ind = $(".js-open-close-acc").index(current);
               ind = ind + action;
           if( ind === $(".js-open-close-acc").length )
           {
               ind = 0;
           }
           else if( ind < 0 ) { ind = $(".js-open-close-acc").length - 1; } $(".js-open-close-acc:eq(" + ind + ")").focus(); } } function go_home_end(action) { console.log("go") let current = $(":focus"); if( current.hasClass("js-open-close-acc") ) { var ind = 0; if( action > 0 )
           {
               ind = $(".js-open-close-acc").length -1;
           }
           $(".js-open-close-acc:eq(" + ind + ")").focus();
       }
   }

Further reading

Below a list of online resources that are useful if you want to know more about accessibility:

https://www.w3.org/TR/wai-aria-practices/#accordion

http://web-accessibility.carnegiemuseums.org/code/accordions/

https://webaim.org/techniques/keyboard/tabindex

Progressive enhancement

When we start building something we try to work in the simples, most accessible environment (browser with javascript disabled) From there we progressively enhance the accordion with features that will improve the user experience in browsers that support them, or silently fail in browsers that don't.

What this means for our accordion is that the core function of the accordion the expand/collapse version should also work when javascript is disabled.  We solved this by adding a default 'no-js' class in the accordion that is removed when Javascript is available.

JS part


if( $(".awesome-accordion").length > 0 )
{
    $(".awesome-accordion.no-js").removeClass("no-js");
    prevent_focusable();
}

 
CSS part


.awesome-accordion.no-js{

    li.awesome-accordion-item{
        overflow: hidden;
    }
    
    & .awesome-accordion-panel {
        max-height: 400px;
        
    }

    input[type='checkbox'].no-js-action{
        display: block;

        position: relative;
        z-index: 1;
        
        float: left;
        
        width: 100%;
        height: 50px;
        
        margin-bottom: -50px;
        
        cursor: pointer;

        opacity: 0;
        
    }

    input[type='checkbox']:checked ~ .awesome-accordion-panel{
        position: relative;
        z-index: 0;
        margin-top: 0;
        max-height: 0;
        opacity: 0;
        transform: translate(0, 50%);
        transition: 0.25s linear;
        padding: 0;
    }

    .awesome-accordion-panel h2{
        margin-bottom: -65px;
    }

}