Skip to content

Instantly share code, notes, and snippets.

@nextab
Last active December 10, 2024 13:11
Show Gist options
  • Save nextab/ac43f22db7f6f02822e84a01dfc98fcb to your computer and use it in GitHub Desktop.
Save nextab/ac43f22db7f6f02822e84a01dfc98fcb to your computer and use it in GitHub Desktop.
Moved incorrect cursor: pointer to the correct element
// Variables
:root {
--toggle-bg-color: var(--color-bg2, #333);
--toggle-active-bg-color: var(--primary, #0a0);
--toggle-switch-color: var(--color-text, #fff);
--toggle-border-radius: 1rem;
--toggle-height: 1.5rem;
--toggle-width: 2.5rem;
--toggle-switch-width: 1.5rem;
--toggle-transition: 0.5s;
--toggle-box-shadow: inset 0 0.125rem 1rem rgba(0, 0, 0, 0.1), inset 0 0.125rem 0.5rem rgba(0, 0, 0, 0.1), inset 0 -0.25rem 0.25rem rgba(0, 0, 0, 0.05);
}
html:not(.dark-mode) {
--toggle-bg-color: var(--color-text, #ddd);
--toggle-switch-color: var(--color-bg, #000);
}
%toggle {
position: relative;
summary {
cursor: pointer;
&::before, &::after {
border-radius: var(--toggle-border-radius);
box-sizing: border-box;
content: "";
height: var(--toggle-height);
top: 50%;
transform: translatey(-50%);
position: absolute;
}
&::before {
background-color: var(--toggle-bg-color);
box-shadow: var(--toggle-box-shadow);
outline: none;
pointer-events: none;
right: 0;
transition: var(--toggle-transition);
width: var(--toggle-width);
}
&::after {
background-color: var(--toggle-switch-color);
height: var(--toggle-switch-width);
right: calc(var(--toggle-width) - var(--toggle-switch-width));
transition: all 500ms ease;
transform: translatey(-50%) scale(0.9);
width: var(--toggle-switch-width);
}
}
&[open] {
summary {
&::before {
background-color: var(--toggle-active-bg-color);
}
&::after {
right: 0;
}
}
}
}
class Accordion {
constructor(el) {
// Store the <details> element
this.el = el;
// Store the <summary> element
this.summary = el.querySelector('summary');
// Store the <div class="content"> element
this.content = el.querySelector('.sub-menu, .wp-block-group');
// Store the animation object (so we can cancel it if needed)
this.animation = null;
// Store if the element is closing
this.isClosing = false;
// Store if the element is expanding
this.isExpanding = false;
// Detect user clicks on the summary element
this.summary.addEventListener('click', (e) => this.onClick(e));
// Add padding to account for border spacing
this.padding = 36;
this.paddingOpen = 55;
}
onClick(e) {
// Stop default behaviour from the browser
e.preventDefault();
// Add an overflow on the <details> to avoid content overflowing
this.el.style.overflow = 'hidden';
// Check if the element is being closed or is already closed
if (this.isClosing || !this.el.open) {
this.open();
// Check if the element is being openned or is already open
} else if (this.isExpanding || this.el.open) {
this.shrink();
}
}
shrink() {
// Set the element as "being closed"
this.isClosing = true;
const innerContent = this.el.querySelector('.wp-block-group');
if (innerContent) {
innerContent.style.opacity = '0';
innerContent.style.transition = 'opacity 0.1s';
}
const startHeight = `${this.el.offsetHeight + this.padding}px`;
const endHeight = `${this.summary.offsetHeight + this.padding}px`;
// If there is already an animation running
if (this.animation) {
// Cancel the current animation
this.animation.cancel();
}
// Start a WAAPI animation
this.animation = this.el.animate({
// Set the keyframes from the startHeight to endHeight
height: [startHeight, endHeight]
}, {
duration: 400,
easing: 'ease-out'
});
// When the animation is complete, call onAnimationFinish()
this.animation.onfinish = () => this.onAnimationFinish(false);
// If the animation is cancelled, isClosing variable is set to false
this.animation.oncancel = () => this.isClosing = false;
}
open() {
// Apply a fixed height on the element
this.el.style.height = `${this.el.offsetHeight}px`;
// Force the [open] attribute on the details element
this.el.open = true;
// Wait for the next frame to call the expand function
window.requestAnimationFrame(() => this.expand());
}
expand() {
// Set the element as "being expanding"
this.isExpanding = true;
const innerContent = this.el.querySelector('.wp-block-group');
if (innerContent) {
innerContent.style.opacity = '1';
// innerContent.style.transition = 'opacity 0.1s';
}
// Get the current fixed height of the element
const startHeight = `${this.el.offsetHeight}px`;
// Calculate the open height of the element (summary height + content height)
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight + this.paddingOpen}px`;
// If there is already an animation running
if (this.animation) {
// Cancel the current animation
this.animation.cancel();
}
// Start a WAAPI animation
this.animation = this.el.animate({
// Set the keyframes from the startHeight to endHeight
height: [startHeight, endHeight]
}, {
duration: 400,
easing: 'ease-out'
});
// When the animation is complete, call onAnimationFinish()
this.animation.onfinish = () => this.onAnimationFinish(true);
// If the animation is cancelled, isExpanding variable is set to false
this.animation.oncancel = () => this.isExpanding = false;
}
onAnimationFinish(open) {
// Set the open attribute based on the parameter
this.el.open = open;
// Clear the stored animation
this.animation = null;
// Reset isClosing & isExpanding
this.isClosing = false;
this.isExpanding = false;
// Remove the overflow hidden and the fixed height
this.el.style.height = this.el.style.overflow = '';
}
}
document.querySelectorAll('details').forEach((el) => {
new Accordion(el);
});
@import 'toggle';
details {
@extend %toggle;
border: 1px solid var(--primary);
border-radius: $input-border-radius;
padding: 1rem;
overflow: visible !important;
+ details {
margin-block-start: 4rem !important;
}
summary {
font-family: $header-font;
font-size: $larger;
font-weight: 700;
list-style: none;
padding-right: 2.25rem;
position: relative;
&::-webkit-details-marker {
display: none;
}
~ * {
animation: fadeInWithoutOpacity 700ms ease-in-out;
}
}
&[open] {
summary {
~ * {
animation: fadeIn 700ms ease-in-out;
}
}
}
/* #region width <= 550px */
@media only screen and (width <= 550px) {
.wp-block-group {
padding-top: 0.75rem;
}
}
/* #endregion width <= 550px */
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(0px);
}
to {
opacity: 1;
transform: translateY(10);
}
}
@keyframes fadeInWithoutOpacity {
from {
opacity: 0;
transform: translateY(0px);
}
50% {
opacity: 1;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(10px);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment