Awesome Accordions website

Codex

This section is the online technical manual for the plugin with explanations about the coding structure and examples of how to change it.

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;
    }

}

 

Making it responsive

Default Maximum Height Setting

Making the accordion responsive is important so that they can scale well across all screen sizes. By default, the accordion content has a max-height 0px and overflow hidden. This allows us to show the title only and exclude the content.

How it works Opening the Accordion

When a user clicks to open an accordion item, we calculate the height of the content. The max-height applies to only the container so the content added to the accordion has real height, even when it is hidden. This ensures the content adjusts appropriately and fits on the available screen area.

Resizing Event

In resizing event (landscape-portrait or other resize event) the script recalculates again the height of the elements that are open and adjust accordingly.

Code

To create the open or close effect the CSS rules are adjusted in jQuery for the specific element as shown in the code snippet below :

Default height : CSS("max-height", 0)

Updated height : CSS("max-height", nh + "px")

The height is updated by updating the default max- height with the new height of the content (nh). To make the process smooth we utilize the "transition" CSS rule and add the smooth effect instead hard open/close.

 

Semantic structure

When we write HTML we give content structure. We define things like paragraphs, lists, tables and headings. Writing semantic code means choosing the most appropriate element to define the required structure. This helps interoperability.

It is the best way to work with assistive technology. Semantic HTML gives context to screen readers or keyboard navigation to  enable people to identify different type of content and navigate to them.

HTML semantics are therefore important in two ways: We get a consistent understanding of content structure and native behaviour, and we get a common understanding of the content’s meaning and purpose. The best thing of all, is that we get those things for free whenever we use HTML as intended.

We use the most semantic element

There is a common understanding of what each element represents and does. For example the <p> tag represents a paragraph of static text, an <a> element is interactive and will fetch a new resource when activated. This common understanding enables browsers and other user agents to provide default functionality and styling for many elements.

In stead of reaching for a div or span,  we first check if there’s a better element to do the job. What is the role of that element? How should a person be interacting with the element? Based on that we pick the most implicit element of the  HTML5 elements


 

 

We separate structure from style

We don't pick HTML elements based on how they’re styled in your CSS but we add semantic classnames that are easily identifiable.
We use the following classnames in our accordion
- awesome-accordion
-- awesome-accordion-item
--- awesome-accordion-heading
---- awesome-accordion-btn
--- awesome-accordion-panel

We use progressive enhancement for enhanced functionality

After we created the structure using semantic elements and classes we can enhance the behaviour without breaking the underlying experience

We test our work

We Test cross-browser, test cross-device, and test with assistive technology. Testing with assistive technology is not as expensive as it used to be, you can even use your smartphone for testing on iOS and Android. Your visitors will thank you!

Usability

The accordion is an immensely useful pattern for progressive disclosure — highlighting important details of a section and revealing more details upon a tap or click, if necessary. As a result, the design stays focused and displays critical information first, while everything else is easily accessible. But for designing a well functioning accordion you have to consider a lot of things to make the pattern understandable for all users.

 

Expand / Collapse

To give the panel the needed expand/collapse effect we created a script. This will expand/collapse the panel and also uses tabindex for screen readers to skip the focus for hidden elements


function toggle_height(element) {
       let li_elem = element.closest("li.awesome-accordion-item");
       let ac_elem = li_elem.find(".awesome-accordion-panel");
       if (li_elem.hasClass("closed"))
       {
           ac_elem.css("max-height", "400px");
           li_elem.removeClass("closed");
           li_elem.find(".awesome-accordion-btn").attr("aria-expanded", "true");
           li_elem.find(".awesome-accordion-panel *").removeAttr("tabindex");
           $(".awesome-accordion-panel").attr("tabindex", 0);
       }
       else
       {
           ac_elem.css("max-height", 0);
           li_elem.addClass("closed");
           li_elem.removeClass("opened");
           li_elem.find(".awesome-accordion-btn").attr("aria-expanded", "false");
           li_elem.find(".awesome-accordion-panel *").attr("tabindex", -1);
           $(".awesome-accordion-panel").attr("tabindex",-1);
       }
   }

Scroll in panel

If the content in a panel is very long it will push the other panels down making you to lose the overview of the accordion. For this reason we think it is important that you should be able to scroll the text inside the div.
By using overflow: auto and setting a max-height we can solve this issue.


 

Toggle buttons

Since we are asking users to open/close panels manually we also need to give them an option to collapse / expand all panels at the same time. This helps users to reset the accordion to its initial state or helps them to open all panels for users. who don't want to open them one by one

For this we created two alternative controls to toggle between open all and close all, we use an unordered list to group them together.



$(document).on("click", "[data-accordion-action]", function () {
       if ($(this).data("accordion-action") === "openall")
       {
           $(".awesome-accordion-item").removeClass("closed");
           $(".awesome-accordion-panel").css("max-height", "none");
       }
       else if ($(this).data("accordion-action") === "closeall")
       {
           $(".awesome-accordion-item").addClass("closed");
           $(".awesome-accordion-panel").css("max-height", 0);
       }
   });

A big problem with accordions is that the content is hidden in a print screen. For this we created the following script. It will expand all panels when a user uses the print screen command.


var beforePrint = function () {
       $(".awesome-accordion-item").removeClass("closed");
       $(".awesome-accordion-panel").css("max-height", "none");
   };
   var afterPrint = function () {
       $(".awesome-accordion-item").addClass("closed");
       $(".awesome-accordion-panel").css("max-height", 0);
       $(".awesome-accordion-item").first().removeClass("closed").find(".awesome-accordion-panel").css("max-height", 400);
   };
   if (window.matchMedia) {
       var mediaQueryList = window.matchMedia('print');
       mediaQueryList.addListener(function (mql) {
           if (mql.matches) {
               beforePrint();
           }
           else {
               afterPrint();
           }
       });
   }
   window.onbeforeprint = beforePrint;
   window.onafterprint = afterPrint;

Hash Option

The hash option allows users to quickly skip from one accordion section to another.  The way it works is that the opened panel is the one with the hash.

Code

In the code, we are checking if hash # exists and if we are in the page that contains accordions. Then we check for the button that matches with the hash and simulates a click event and close the rest of accordions.


if( window.location.hash && $(".js-open-close-acc[data-hash]").length > 0 ) {

let key = window.location.hash;
key = key.substring(1);

$(".awesome-accordion-item").addClass("closed");
$(".awesome-accordion-panel").css("max-height", 0);

$(".js-open-close-acc[data-hash='"+key+"']").click();

}

Example

As you can see in the accordion demo, the first item has #item-1 and the second item #item-2 and so on. Each time the click event occurs the opened accordion replaced the hash item in the URL. These are the respective URLs with the hash options :

All accordions closed: https://awesome-accordions.awesome-plugins.com/demo-collapse/

The first item opened: https://awesome-accordions.awesome-plugins.com/demo-collapse/#item-1

The second item opened: https://awesome-accordions.awesome-plugins.com/demo-collapse/#item-2

The third item opened: https://awesome-accordions.awesome-plugins.com/demo-collapse/#item-3

 

Example

  • Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed sed risus pretium quam vulputate dignissim. Ipsum consequat nisl vel pretium lectus. Sodales ut etiam sit amet nisl purus in. Et malesuada fames ac turpis. Pretium lectus quam id leo in vitae. Mauris pharetra et ultrices neque ornare aenean. Consectetur adipiscing elit duis tristique. Fermentum posuere urna nec tincidunt praesent semper feugiat nibh. Blandit massa enim nec dui nunc mattis enim ut tellus. Lacus viverra vitae congue eu consequat ac felis donec. Commodo elit at imperdiet dui accumsan sit amet nulla facilisi. Nam aliquam sem et tortor consequat id. Lacus sed turpis tincidunt id aliquet risus feugiat in ante. Imperdiet dui accumsan sit amet.

  • Aliquam ultrices sagittis orci a scelerisque purus semper eget. Egestas sed sed risus pretium quam vulputate dignissim suspendisse. In tellus integer feugiat scelerisque varius. Non pulvinar neque laoreet suspendisse interdum consectetur libero id. Egestas dui id ornare arcu odio ut sem. Sit amet venenatis urna cursus eget nunc scelerisque viverra. Libero justo laoreet sit amet cursus sit. Dolor morbi non arcu risus quis varius quam quisque id. Vel risus commodo viverra maecenas accumsan lacus vel facilisis volutpat. Lorem sed risus ultricies tristique nulla aliquet enim. Viverra mauris in aliquam sem. Aliquet risus feugiat in ante. Sit amet consectetur adipiscing elit pellentesque. Dui vivamus arcu felis bibendum ut tristique et. Feugiat vivamus at augue eget arcu. Sapien eget mi proin sed.

  • Egestas sed sed risus pretium quam vulputate. Tellus integer feugiat scelerisque varius morbi enim nunc faucibus a. Viverra nibh cras pulvinar mattis nunc. Sem et tortor consequat id porta nibh venenatis. Vulputate ut pharetra sit amet aliquam id. Tortor condimentum lacinia quis vel eros. Malesuada proin libero nunc consequat interdum varius sit. Nisl nunc mi ipsum faucibus vitae aliquet. Lectus nulla at volutpat diam ut venenatis tellus. Ut tellus elementum sagittis vitae et. Sit amet mattis vulputate enim nulla aliquet porttitor lacus. Nisi lacus sed viverra tellus in hac. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt lobortis. Hendrerit dolor magna eget est lorem ipsum dolor sit. Sed lectus vestibulum mattis ullamcorper velit.