@use 'sprucecss/scss/spruce' as *;
.accordion-list {
@include layout-stack('s');
}
.accordion-card {
$this: &;
background-color: color('background');
border: 1px solid color('border');
border-radius: config('border-radius-lg', $display);
&__title {
font-size: font-size('h4');
margin-block: 0;
padding: spacer('m');
&:has(#{$this}__toggle) {
padding: 0;
}
}
&__toggle {
@include clear-btn;
align-items: center;
display: flex;
gap: spacer('m');
inline-size: 100%;
justify-content: space-between;
padding: spacer('m');
text-align: start;
&:focus-visible {
#{$this}__icon {
@include short-ring('button', 'primary');
}
}
&[aria-expanded='true'] {
svg {
rotate: 90deg;
}
.horizontal-line {
opacity: 0;
}
}
}
&__icon {
--size: 1.75rem;
align-items: center;
background-color: color('primary-background', 'btn');
block-size: var(--size);
border-radius: config('border-radius-sm', $display);
color: color('primary-foreground', 'btn');
display: flex;
flex-shrink: 0;
inline-size: var(--size);
justify-content: center;
svg {
--size: 1.25rem;
@include transition;
block-size: var(--size);
inline-size: var(--size);
}
.horizontal-line {
@include transition;
}
}
&__content {
@include transition($property: grid-template-rows);
display: grid;
padding-inline: spacer('m');
&[aria-hidden="true"] {
grid-template-rows: 0fr;
}
&[aria-hidden="false"] {
grid-template-rows: 1fr;
padding-block-end: spacer('m');
}
> div {
@include layout-stack('xs');
overflow: hidden;
* + h2,
* + h3,
* + h4,
* + h5 {
margin-block-start: spacer('s');
}
}
a:hover {
font-weight: 700;
}
}
}
<div class="accordion-list">
<div class="accordion-card">
<h3 class="accordion-card__title">
Why Make Another CSS Framework?
</h3>
<div class="accordion-card__content" aria-hidden="true">
<div>
As you may know, there are many CSS frameworks (hundreds of them, and a lot of them are not maintained today). Everybody can choose one that suits their work style or project requirements. So why make another one? It is certainly not because we can do it better but because we want to do it our way. We want to be in control and make decisions.
</div>
</div>
</div>
<div class="accordion-card">
<h3 class="accordion-card__title">
It Is Opinionated
</h3>
<div class="accordion-card__content" aria-hidden="true">
<div>
Each system is opinionated but on a different level; this is valid for Spruce too. We don’t want to vote for (strictly) any particular solution (because there is always more than one), but we will show you what we think is the best for us (and maybe for you too). We don’t believe there is a good or bad solution, but we can learn from any of them.
</div>
</div>
</div>
<div class="accordion-card">
<h3 class="accordion-card__title">
We Left the Grid Out
</h3>
<div class="accordion-card__content" aria-hidden="true">
<div>
One controversial decision we made with Spruce is to leave a classical grid system out. Because of the late CSS layout model developments like Flexbox and Grid, we think it can be eliminated; this doesn’t mean that we won’t show you how to make layouts with ease, but we try to make it the modern way.
</div>
</div>
</div>
<div class="accordion-card">
<h3 class="accordion-card__title">
Coding Style Guide and Practices
</h3>
<div class="accordion-card__content" aria-hidden="true">
<div>
Where it is possible, we use elements and/or attributes to style elements, but it is still a class-based framework.
</div>
</div>
</div>
</div>
(() => {
document.querySelectorAll('.accordion-card').forEach((accordion) => {
const heading = accordion.querySelector('.accordion-card__title');
const button = document.createElement('button');
const icon = document.createElement('span');
button.classList.add('accordion-card__toggle');
button.setAttribute('aria-expanded', 'false');
icon.classList.add('accordion-card__icon');
icon.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line class="vertical-line" x1="12" y1="5" x2="12" y2="19"></line>
<line class="horizontal-line" x1="5" y1="12" x2="19" y2="12"></line>
</svg>
`;
button.innerHTML = `${heading.textContent} ${icon.outerHTML}`;
heading.innerHTML = '';
heading.appendChild(button);
button.addEventListener('click', () => {
document.querySelectorAll('.accordion-card').forEach((otherAccordion) => {
const otherButton = otherAccordion.querySelector('button');
const otherContent = otherAccordion.querySelector('.accordion-card__content');
if (otherButton !== button) {
otherButton.setAttribute('aria-expanded', 'false');
otherContent.setAttribute('aria-hidden', 'true');
}
});
const expanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', !expanded);
heading.nextElementSibling.setAttribute('aria-hidden', expanded);
});
});
})();