This code emulates the accordion layout here, and uses Bootstrap 5.
See the Pen
Accordion with categories – Bootstrap 5 by Laura Sage (@ThePixelPixie)
on CodePen.
<span style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" data-mce-type="bookmark" class="mce_SELRES_start"></span><div class="faq-container container content mb-3">
<div class="row gx-5 justify-content-center">
<div class="col-12 p-5 content">
<section class="cd-faq js-cd-faq">
<ul class="cd-faq__categories m-0 p-0">
<?php $terms = get_terms( array("taxonomy" => "category") );
foreach ($terms as $term) :
$posts = get_posts(["post_type" => "faq", "cat" => $term->term_id, "post_status" => "publish"]);
?>
<li><a class="cd-faq__category cd-faq__category-selected truncate" href="#<?php echo $term->slug; ?>"><?php echo $term->name; ?></a></li>
<?php wp_reset_postdata();
endforeach; ?>
</ul> <!-- cd-faq__categories -->
<div class="cd-faq__items">
<?php $terms2 = get_terms( array("taxonomy" => "category") );
foreach ($terms2 as $term2) :
$posts2 = get_posts(["post_type" => "faq", "cat" => $term2->term_id, "post_status" => "publish"]);
?>
<ul id="<?php echo $term2->slug; ?>" class="cd-faq__group m-0 p-0">
<li class="cd-faq__title">
<h2><?php echo $term2->name; ?></h2>
</li>
<?php foreach($posts2 as $post2) :
$content = apply_filters('the_content',$post2->post_content);
?>
<li class="cd-faq__item">
<a class="cd-faq__trigger" href="#0"><span><?php echo $post2->post_title; ?></span></a>
<div class="cd-faq__content">
<div class="text-component">
<?php echo $content; ?>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php wp_reset_postdata();
endforeach; ?>
</div>
<a href="#0" class="cd-faq__close-panel text-replace">Close</a>
<div class="cd-faq__overlay" aria-hidden="true"></div>
</section>
</div>
</div>
</div><span style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" data-mce-type="bookmark" class="mce_SELRES_end"></span>
Here’s the CSS you need to add:
@import "https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700";
body {
font-size: 15px;
background-color: #f2f2f2;
}
ul.cd-faq__categories,
ul.cd-faq__group {
list-style: none;
border: 0;
}
.cd-faq {
border: 0;
display: block;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.085), 0 1px 8px rgba(0, 0, 0, 0.1);
}
.cd-faq::before {
content: "mobile";
display: none;
}
.cd-faq a {
text-decoration: none;
font-weight: 300;
transition: all .15s ease-in;
}
.cd-faq a:hover {
color: rgba(255,255,255,.7);
font-weight: 600;
}
.cd-faq .cd-faq__trigger:hover {
color: #8f3985;
}
@media (min-width: 768px) {
.cd-faq {
position: relative;
box-shadow: none;
display: -ms-flexbox;
display: flex;
}
.cd-faq::before {
content: "desktop";
}
}
@media (min-width: 768px) {
.cd-faq__categories {
position: -webkit-sticky;
position: sticky;
-ms-flex-item-align: start;
align-self: flex-start;
-ms-flex-negative: 0;
flex-shrink: 0;
top: 20px;
width: 20%;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.085), 0 1px 8px rgba(0, 0, 0, 0.1);
margin-top: 1.25rem;
}
}
@media (min-width: 992px) {
.cd-faq__categories {
width: 200px;
}
}
.cd-faq__category {
position: relative;
display: block;
height: 50px;
line-height: 50px;
padding: 0 2rem 0 1.05rem;
color: #fff;
background-color: #4e545a;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
border-bottom: 1px solid #565c63;
}
.cd-faq__category::before,
.cd-faq__category::after {
content: "";
position: absolute;
top: 50%;
right: 16px;
display: inline-block;
height: 1px;
width: 10px;
background-color: #7e868f;
}
.cd-faq__category::after {
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
li:last-child .cd-faq__category {
border-bottom: none;
}
@media (min-width: 768px) {
.cd-faq__category {
font-weight: 600;
padding: 0 1.25rem;
transition: background 0.2s;
}
.cd-faq__category::before,
.cd-faq__category::after {
display: none;
}
.cd-faq__category:hover {
background: #565c63;
color: #fff;
}
}
@media (min-width: 992px) {
.cd-faq__category::before {
display: block;
top: 0;
right: auto;
left: 0;
height: 100%;
width: 3px;
background-color: #8f3985;
opacity: 0;
transition: opacity 0.2s;
}
}
@media (min-width: 992px) {
.cd-faq__category-selected {
background: #3f4348;
}
.cd-faq__category-selected:hover {
background: #3f4348;
}
.cd-faq__category-selected::before {
opacity: 1;
}
}
.cd-faq__items {
position: fixed;
z-index: 1;
height: 100%;
width: 90%;
top: 0;
right: 0;
background: #fff;
padding: 0 1.25rem 1.25rem;
overflow: auto;
-webkit-overflow-scrolling: touch;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transform: translateZ(0) translateX(100%);
transform: translateZ(0) translateX(100%);
transition: -webkit-transform 0.3s;
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
}
@media (min-width: 768px) {
.cd-faq__items {
position: static;
height: auto;
width: auto;
-ms-flex-positive: 1;
flex-grow: 1;
overflow: visible;
-webkit-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
padding: 0 0 0 0.75rem;
background: 0 0;
}
}
.cd-faq__items--slide-in {
-webkit-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
}
html:not(.js) .cd-faq__items {
position: static;
height: auto;
width: 100%;
-webkit-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
}
.cd-faq__group {
display: none;
}
@media (min-width: 768px) {
.cd-faq__group {
display: block;
padding-top: 1px;
}
}
html:not(.js) .cd-faq__group,
.cd-faq__group--selected {
display: block;
}
.cd-faq__title {
margin: 1.25rem 0;
}
.cd-faq__title h2 {
text-transform: uppercase;
font-weight: 700;
color: #3f4348;
}
@media (min-width: 768px) {
.cd-faq__title {
margin-bottom: 0.75rem;
}
}
@media (min-width: 768px) {
.cd-faq__item {
background: #fff;
margin-bottom: 0.25em;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
transition: box-shadow 0.2s;
}
.cd-faq__item:hover {
box-shadow: undefined;
}
@media (min-width: 768px) {
.cd-faq__item:hover {
box-shadow: 0 1px 10px #6b7d8e;
}
}
.cd-faq__item:last-of-type {
margin-bottom: 0;
}
}
.cd-faq__trigger {
display: block;
position: relative;
margin: 1.25rem 0 0.5rem;
color: #8f3985;
}
@media (min-width: 768px) {
.cd-faq__trigger {
font-weight: 300;
margin: 0;
padding: 0.75rem 2rem 0.75rem 0.75rem;
}
.cd-faq__trigger::before,
.cd-faq__trigger::after {
content: "";
position: absolute;
right: 24px;
top: 50%;
height: 2px;
width: 13px;
background: #8f3985;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
transition: -webkit-transform 0.2s;
transition: transform 0.2s;
transition: transform 0.2s, -webkit-transform 0.2s;
}
.cd-faq__trigger::before {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
right: 32px;
}
.cd-faq__trigger::after {
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.cd-faq__item-visible .cd-faq__trigger::before {
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.cd-faq__item-visible .cd-faq__trigger::after {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
}
.cd-faq__content p {
color: #6b7d8e;
}
@media (min-width: 768px) {
.cd-faq__content {
display: none;
padding: 0 0.75em;
overflow: hidden;
}
.cd-faq__content .text-component {
padding-bottom: 1.25rem;
}
}
.cd-faq__content--visible {
display: block;
}
@media (min-width: 768px) {
html:not(.js) .cd-faq__content {
display: block;
}
}
.cd-faq__close-panel {
position: fixed;
z-index: 2;
display: block;
top: 5px;
right: -40px;
height: 40px;
width: 40px;
-webkit-transform: translateZ(0);
transform: translateZ(0);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
transition: right 0.3s;
color: #fff;
}
.cd-faq__close-panel::before,
.cd-faq__close-panel::after {
content: '';
position: absolute;
top: 16px;
left: 12px;
display: inline-block;
height: 3px;
width: 18px;
background: #6b7d8e;
}
.cd-faq__close-panel::before {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.cd-faq__close-panel::after {
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
}
@media (min-width: 768px) {
.cd-faq__close-panel {
display: none;
}
}
.cd-faq__close-panel--move-left {
right: 1.25rem;
transition-delay: 0.3s;
}
.cd-faq__overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #4e545a;
visibility: hidden;
opacity: 0;
transition: opacity 0.3s, visibility 0.3s;
}
@media (min-width: 768px) {
.cd-faq__overlay {
display: none;
}
}
.cd-faq__overlay--is-visible {
visibility: visible;
opacity: 1;
}
.cd-header {
height: 180px;
background-color: #8f3985;
}
@media (min-width: 992px) {
.cd-header {
height: 240px;
}
}
.cd-header h1 {
font-size: 1.728rem;
font-weight: 300;
color: #fff;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@media (min-width: 992px) {
.cd-header {
height: 240px;
}
}
.cd-article-link {
color: #586626;
}
And finally, the js you need to add at the bottom of the file, before
// Utility function
function Util() {}
/*
class manipulation functions
*/
Util.hasClass = function (el, className) {
if (el.classList) return el.classList.contains(className);
else
return !!el.className.match(new RegExp("(\\s|^)" + className + "(\\s|$)"));
};
Util.addClass = function (el, className) {
var classList = className.split(" ");
if (el.classList) el.classList.add(classList[0]);
else if (!Util.hasClass(el, classList[0])) el.className += " " + classList[0];
if (classList.length > 1) Util.addClass(el, classList.slice(1).join(" "));
};
Util.removeClass = function (el, className) {
var classList = className.split(" ");
if (el.classList) el.classList.remove(classList[0]);
else if (Util.hasClass(el, classList[0])) {
var reg = new RegExp("(\\s|^)" + classList[0] + "(\\s|$)");
el.className = el.className.replace(reg, " ");
}
if (classList.length > 1) Util.removeClass(el, classList.slice(1).join(" "));
};
Util.toggleClass = function (el, className, bool) {
if (bool) Util.addClass(el, className);
else Util.removeClass(el, className);
};
Util.setAttributes = function (el, attrs) {
for (var key in attrs) {
el.setAttribute(key, attrs[key]);
}
};
/*
DOM manipulation
*/
Util.getChildrenByClassName = function (el, className) {
var children = el.children,
childrenByClass = [];
for (var i = 0; i < el.children.length; i++) {
if (Util.hasClass(el.children[i], className))
childrenByClass.push(el.children[i]);
}
return childrenByClass;
};
/*
Animate height of an element
*/
Util.setHeight = function (start, to, element, duration, cb) {
var change = to - start,
currentTime = null;
var animateHeight = function (timestamp) {
if (!currentTime) currentTime = timestamp;
var progress = timestamp - currentTime;
var val = parseInt((progress / duration) * change + start);
// console.log(val);
element.setAttribute("style", "height:" + val + "px;");
if (progress < duration) {
window.requestAnimationFrame(animateHeight);
} else {
cb();
}
};
//set the height of the element before starting animation -> fix bug on Safari
element.setAttribute("style", "height:" + start + "px;");
window.requestAnimationFrame(animateHeight);
};
/*
Smooth Scroll
*/
Util.scrollTo = function (final, duration, cb) {
var start = window.scrollY || document.documentElement.scrollTop,
currentTime = null;
var animateScroll = function (timestamp) {
if (!currentTime) currentTime = timestamp;
var progress = timestamp - currentTime;
if (progress > duration) progress = duration;
var val = Math.easeInOutQuad(progress, start, final - start, duration);
window.scrollTo(0, val);
if (progress < duration) {
window.requestAnimationFrame(animateScroll);
} else {
cb && cb();
}
};
window.requestAnimationFrame(animateScroll);
};
/*
Focus utility classes
*/
//Move focus to an element
Util.moveFocus = function (element) {
if (!element) element = document.getElementsByTagName("body")[0];
element.focus();
if (document.activeElement !== element) {
element.setAttribute("tabindex", "-1");
element.focus();
}
};
/*
Misc
*/
Util.getIndexInArray = function (array, el) {
return Array.prototype.indexOf.call(array, el);
};
Util.cssSupports = function (property, value) {
if ("CSS" in window) {
return CSS.supports(property, value);
} else {
var jsProperty = property.replace(/-([a-z])/g, function (g) {
return g[1].toUpperCase();
});
return jsProperty in document.body.style;
}
};
/*
Polyfills
*/
//Closest() method
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
}
if (!Element.prototype.closest) {
Element.prototype.closest = function (s) {
var el = this;
if (!document.documentElement.contains(el)) return null;
do {
if (el.matches(s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
//Custom Event() constructor
if (typeof window.CustomEvent !== "function") {
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(
event,
params.bubbles,
params.cancelable,
params.detail
);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
}
/*
Animation curves
*/
Math.easeInOutQuad = function (t, b, c, d) {
t /= d / 2;
if (t < 1) return (c / 2) * t * t + b;
t--;
return (-c / 2) * (t * (t - 2) - 1) + b;
};
(function () {
// FAQ Template - by CodyHouse.co
var FaqTemplate = function (element) {
this.element = element;
this.sections = this.element.getElementsByClassName("cd-faq__group");
this.triggers = this.element.getElementsByClassName("cd-faq__trigger");
this.faqContainer = this.element.getElementsByClassName("cd-faq__items")[0];
this.faqsCategoriesContainer = this.element.getElementsByClassName(
"cd-faq__categories"
)[0];
this.faqsCategories = this.faqsCategoriesContainer.getElementsByClassName(
"cd-faq__category"
);
this.faqOverlay = this.element.getElementsByClassName("cd-faq__overlay")[0];
this.faqClose = this.element.getElementsByClassName(
"cd-faq__close-panel"
)[0];
this.scrolling = false;
initFaqEvents(this);
};
function initFaqEvents(faqs) {
// click on a faq category
faqs.faqsCategoriesContainer.addEventListener("click", function (event) {
var category = event.target.closest(".cd-faq__category");
if (!category) return;
var mq = getMq(faqs),
selectedCategory = category.getAttribute("href").replace("#", "");
if (mq == "mobile") {
// on mobile, open faq panel
event.preventDefault();
faqs.faqContainer.scrollTop = 0;
Util.addClass(faqs.faqContainer, "cd-faq__items--slide-in");
Util.addClass(faqs.faqClose, "cd-faq__close-panel--move-left");
Util.addClass(faqs.faqOverlay, "cd-faq__overlay--is-visible");
var selectedSection = faqs.faqContainer.getElementsByClassName(
"cd-faq__group--selected"
);
if (selectedSection.length > 0) {
Util.removeClass(selectedSection[0], "cd-faq__group--selected");
}
Util.addClass(
document.getElementById(selectedCategory),
"cd-faq__group--selected"
);
} else {
// on desktop, scroll to section
if (!window.requestAnimationFrame) return;
event.preventDefault();
var windowScrollTop =
window.scrollY || document.documentElement.scrollTop;
Util.scrollTo(
document.getElementById(selectedCategory).getBoundingClientRect()
.top +
windowScrollTop +
2,
200
);
}
});
// on mobile -> close faq panel
faqs.faqOverlay.addEventListener("click", function (event) {
closeFaqPanel(faqs);
});
faqs.faqClose.addEventListener("click", function (event) {
event.preventDefault();
closeFaqPanel(faqs);
});
// on desktop -> toggle faq content visibility when clicking on the trigger element
faqs.faqContainer.addEventListener("click", function (event) {
if (getMq(faqs) != "desktop") return;
var trigger = event.target.closest(".cd-faq__trigger");
if (!trigger) return;
event.preventDefault();
var content = trigger.nextElementSibling,
parent = trigger.closest("li"),
bool = Util.hasClass(parent, "cd-faq__item-visible");
Util.toggleClass(parent, "cd-faq__item-visible", !bool);
//store initial and final height - animate faq content height
Util.addClass(content, "cd-faq__content--visible");
var initHeight = bool ? content.offsetHeight : 0,
finalHeight = bool ? 0 : content.offsetHeight;
if (window.requestAnimationFrame) {
Util.setHeight(initHeight, finalHeight, content, 200, function () {
heighAnimationCb(content, bool);
});
} else {
heighAnimationCb(content, bool);
}
});
if (window.requestAnimationFrame) {
// on scroll -> update selected category
window.addEventListener("scroll", function () {
if (getMq(faqs) != "desktop" || faqs.scrolling) return;
faqs.scrolling = true;
window.requestAnimationFrame(updateCategory.bind(faqs));
});
}
}
function closeFaqPanel(faqs) {
Util.removeClass(faqs.faqContainer, "cd-faq__items--slide-in");
Util.removeClass(faqs.faqClose, "cd-faq__close-panel--move-left");
Util.removeClass(faqs.faqOverlay, "cd-faq__overlay--is-visible");
}
function getMq(faqs) {
//get MQ value ('desktop' or 'mobile')
return window
.getComputedStyle(faqs.element, "::before")
.getPropertyValue("content")
.replace(/'|"/g, "");
}
function updateCategory() {
// update selected category -> show green rectangle to the left of the category
var selected = false;
for (var i = 0; i < this.sections.length; i++) {
var top = this.sections[i].getBoundingClientRect().top,
bool = top <= 0 && -1 * top < this.sections[i].offsetHeight;
Util.toggleClass(
this.faqsCategories[i],
"cd-faq__category-selected",
bool
);
if (bool) selected = true;
}
if (!selected)
Util.addClass(this.faqsCategories[0], "cd-faq__category-selected");
this.scrolling = false;
}
function heighAnimationCb(content, bool) {
content.removeAttribute("style");
if (bool) Util.removeClass(content, "cd-faq__content--visible");
}
var faqTemplate = document.getElementsByClassName("js-cd-faq"),
faqArray = [];
if (faqTemplate.length > 0) {
for (var i = 0; i < faqTemplate.length; i++) {
faqArray.push(new FaqTemplate(faqTemplate[i]));
}
}
})();