Accordion
Accordions are used widely throughout our sites. Often, they are used as a ACF Flexible Section.
Advanced Custom Fields
If working with ACF, you will want to utilize the following fields:
Repeater Field: accordion
Fields:
- Text: accordion_header
- Text: accordion_label
- WYSIWYG: accordion_content
Markup
HTML
<div class="Accordion">
<button class="Accordion-header" type="button">Accordion Header</button>
<div class="Accordion-content">
<h2 class="Accordion-label">Accordion Heading</h2>
<p>Here is the content of 1st tab <a href="#">link</a></p>
</div> <!-- //.Accordion-content -->
<button class="Accordion-header" type="button">Accordion Header</button>
<div class="Accordion-content">
<h2 class="Accordion-label">Accordion Heading</h2>
<p>Here is the content of 2nd tab <a href="#">link</a></p>
</div> <!-- //.Accordion-content -->
<button class="Accordion-header" type="button">Accordion Header</button>
<div class="Accordion-content">
<h2 class="Accordion-label">Accordion Heading</h2>
<p>Here is the content of 3rd tab <a href="#">link</a></p>
</div> <!-- //.Accordion-content -->
<button class="Accordion-header" type="button">Accordion Header</button>
<div class="Accordion-content">
<h2 class="Accordion-label">Accordion Heading</h2>
<p>Here is the content of 4th tab <a href="#">link</a></p>
</div> <!-- //.Accordion-content -->
</div> <!-- //.accordion -->
PHP
<?php if( have_rows( 'accordion' ) ): ?>
<div class="Accordion">
<?php while( have_rows( 'accordion' ) ): the_row(); ?>
<button class="Accordion-header" type="button"><?php the_sub_field( 'accordion_header' ); ?></button>
<div class="Accordion-content">
<h2 class="Accordion-label"><?php the_sub_field( 'accordion_label' ); ?></h2>
<?php the_sub_field( 'accordion_content' ); ?>
</div> <!-- //.Accordion-content -->
<?php endwhile; ?>
</div> <!-- //.accordion -->
<?php endif; ?>
Twig
<div class="Accordion">
{% for item in post.get_field('accordion') %}
<button class="Accordion-header" type="button">{{item.accordion_header}}</button>
<div class="Accordion-content">
<h2 class="Accordion-label">{{item.accordion_label}}</h2>
{{item.accordion_content}}
</div> {# .Accordion-content #}
{% endfor %}
</div>{# .Accordion #}
Styles
CSS
.Accordion-header {
background: #eeeeee;
border: 1px solid #ccc;
cursor: pointer;
font-size: 1.5em;
margin-top: 10px;
padding: 10px 15px;
position: relative;
text-align: left;
width: 100%;
}
.Accordion-header:first-of-type {
margin-top: 0;
}
.Accordion-header.is-active {
border-bottom: none;
}
.Accordion-header:after {
content: "+";
position: absolute;
right: 15px;
transition: transform 0.2s ease-in-out;
top: 8px;
}
.Accordion-header.is-active:after {
transform: rotate(45deg);
}
.Accordion-content {
box-sizing: border-box;
border: 1px solid #ccc;
border-top: none;
display: none;
padding: 10px 15px;
}
.Accordion-content.is-active {
display: block;
}
Sass
.Accordion-header {
background: #eeeeee;
border: 1px solid #ccc;
cursor: pointer;
font-size: 1.5em;
margin-top: 10px;
padding: 10px 15px;
position: relative;
text-align: left;
width: 100%;
&:after {
content: "+";
position: absolute;
right: 15px;
transition: transform 0.2s ease-in-out;
top: 8px;
}
&:first-of-type {
margin-top: 0;
}
&.is-active {
border-bottom: none;
&:after {
transform: rotate(45deg);
}
}
}
.Accordion-content {
box-sizing: border-box;
border: 1px solid #ccc;
border-top: none;
display: none;
padding: 10px 15px;
&.is-active {
display: block;
}
}
Javascript
Plugin
/**
* Objectiv Accessible Accordion
*
* Loosely based off of 10up's Accordion: https://10up.github.io/wp-component-library/component/accordion/index.html
*/
( function() {
'use strict';
// Let's make sure that we have an Objectiv object
if ( 'object' !== typeof window.Objectiv) {
window.Objectiv = {};
}
// Start creating the accordionobject
window.Objectiv.accordion = function(options) {
// Make sure that a target is passed into the options object
if ('undefined' === typeof options.target)
return false;
// Let's set up our variables
var accordion = document.querySelector(options.target),
accordionContent = accordion.getElementsByClassName('Accordion-content'),
accordionHeader = accordion.getElementsByClassName('Accordion-header');
// If we don't have an accordion, jump out
if (!accordion) return;
// Loop through the headers
Array.prototype.map.call(accordionHeader, function(value, index) {
var index = index + 1,
header = value;
function accordionClickHandler() {
var content = value.nextElementSibling,
contentLabel = content.getElementsByClassName('Accordion-label')[0];
// Set our active classes
header.classList.toggle('is-active');
content.classList.toggle('is-active');
// Focus on the label in the accordion content
contentLabel.focus();
// Let's now set all of our attributes if the accordion is active
if (content.classList.contains('is-active')) {
header.setAttribute( 'aria-selected', 'true' );
header.setAttribute( 'aria-expanded', 'true' );
content.setAttribute( 'aria-hidden', 'false' );
} else {
header.setAttribute( 'aria-selected', 'false' );
header.setAttribute( 'aria-expanded', 'false' );
content.setAttribute( 'aria-hidden', 'true' );
}
}
header.addEventListener( 'click', accordionClickHandler );
});
// Set proper attributes for the content and content label
Array.prototype.map.call(accordionContent, function(value, index) {
var content = value;
content.setAttribute( 'aria-hidden', 'true' );
content.setAttribute( 'role', 'tabpanel' );
});
}
})();
Usage
Objectiv.accordion({
target: '.Accordion', // ID (or class) of accordion container
});