<div class="rtds-accordion rtds-accordion--widget">
<div class="rtds-accordion__item">
<h3 class="rtds-accordion__title rtds-flex rtds-font-bold rtds-text-base rtds-background-03">
<button type="button" aria-expanded="false" class="rtds-accordion__trigger rtds-group rtds-flex rtds-items-center rtds-flex-1 rtds-transition-all" aria-controls="accordionSection-1" id="accordion1id">
<span class="rtds-accordion__trigger-label">
Titolo panel
</span>
<span class="rtds-accordion__icon rtds-flex rtds-p-2 rtds-ml-auto rtds-h-auto rtds-transition-all">
<svg class="rtds-icon rtds-fill-current rtds-w-6 rtds-h-6 rtds-transition-all is-expand-arrow group-aria-expanded:rtds-rotate-180" aria-hidden="true" focusable="false" role="img">
<use href="../../icons.svg#mini--chevron-up" />
</svg>
</span>
</button>
</h3>
<div id="accordionSection-1" role="region" aria-labelledby="accordion1id" class="rtds-accordion__pane rtds-transition-all" hidden>
Content
</div>
</div>
<div class="rtds-accordion__item">
<h3 class="rtds-accordion__title rtds-flex rtds-font-bold rtds-text-base rtds-background-03">
<button type="button" aria-expanded="false" class="rtds-accordion__trigger rtds-group rtds-flex rtds-items-center rtds-flex-1 rtds-transition-all" aria-controls="accordionSection-2" id="accordion2id">
<span class="rtds-accordion__trigger-label">
Titolo panel
</span>
<span class="rtds-accordion__icon rtds-flex rtds-p-2 rtds-ml-auto rtds-h-auto rtds-transition-all">
<svg class="rtds-icon rtds-fill-current rtds-w-6 rtds-h-6 rtds-transition-all is-expand-arrow group-aria-expanded:rtds-rotate-180" aria-hidden="true" focusable="false" role="img">
<use href="../../icons.svg#mini--chevron-up" />
</svg>
</span>
</button>
</h3>
<div id="accordionSection-2" role="region" aria-labelledby="accordion2id" class="rtds-accordion__pane rtds-transition-all" hidden>
Content
</div>
</div>
<div class="rtds-accordion__item">
<h3 class="rtds-accordion__title rtds-flex rtds-font-bold rtds-text-base rtds-background-03">
<button type="button" aria-expanded="false" class="rtds-accordion__trigger rtds-group rtds-flex rtds-items-center rtds-flex-1 rtds-transition-all" aria-controls="accordionSection-3" id="accordion3id">
<span class="rtds-accordion__trigger-label">
Titolo panel
</span>
<span class="rtds-accordion__icon rtds-flex rtds-p-2 rtds-ml-auto rtds-h-auto rtds-transition-all">
<svg class="rtds-icon rtds-fill-current rtds-w-6 rtds-h-6 rtds-transition-all is-expand-arrow group-aria-expanded:rtds-rotate-180" aria-hidden="true" focusable="false" role="img">
<use href="../../icons.svg#mini--chevron-up" />
</svg>
</span>
</button>
</h3>
<div id="accordionSection-3" role="region" aria-labelledby="accordion3id" class="rtds-accordion__pane rtds-transition-all" hidden>
Content
</div>
</div>
</div>
{% extends '@accordion' %}
{% block icon %}
<span class="rtds-accordion__icon rtds-flex rtds-p-2 rtds-ml-auto rtds-h-auto rtds-transition-all{% if iconClasses %} {{ iconClasses }} {% endif %}">
{% render '@icon', {id: showMoreIcon, size: iconSize, classes: 'rtds-transition-all is-expand-arrow group-aria-expanded:rtds-rotate-180'}, true %}
</span>
{% endblock icon %}
{
"showMoreIcon": "mini--chevron-up",
"showLessIcon": "mini--minus",
"iconSize": "rtds-w-6 rtds-h-6",
"panelTitleFont": "rtds-text-base",
"panels": [
{
"label": "Titolo panel",
"text": "Content"
},
{
"label": "Titolo panel",
"text": "Content"
},
{
"label": "Titolo panel",
"text": "Content"
}
],
"classes": "rtds-accordion--widget",
"panelTitleClasses": "rtds-background-03"
}
/**
* ACCORDION
*
*/
.rtds-accordion {
gap: var(--spacing-4);
display: grid;
}
.rtds-accordion--minimal {
gap: var(--spacing-0);
}
.rtds-accordion__item > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(var(--spacing-4) * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(var(--spacing-4) * var(--tw-space-y-reverse));
}
/*.rtds-accordion--default, .rtds-accordion--minimal {
}*/
:where(.rtds-accordion--default) .rtds-accordion__item,
:where(.rtds-accordion--minimal) .rtds-accordion__item {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
padding: var(--spacing-4);
}
:where(.rtds-accordion--default) .rtds-accordion__item {
border-radius: 0.25rem;
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
:where(.rtds-accordion--widget) .rtds-accordion__title {
padding: var(--spacing-2);
}
:where(.rtds-accordion--widget) .rtds-accordion__pane {
padding-left: var(--spacing-2);
padding-right: var(--spacing-2);
}
:where(.rtds-accordion--minimal) .rtds-accordion__item {
border-bottom-width: 1px;
border-color: var(--color-border-gray-01);
}
/**
* ACCORDION
*
*/
@layer components {
.rtds-accordion {
@apply rtds-grid rtds-gap-4;
}
.rtds-accordion--minimal {
@apply rtds-gap-0;
}
.rtds-accordion__item {
@apply rtds-space-y-4;
}
/*.rtds-accordion--default, .rtds-accordion--minimal {
}*/
:where(.rtds-accordion--default) .rtds-accordion__item,
:where(.rtds-accordion--minimal) .rtds-accordion__item {
@apply rtds-p-4 rtds-bg-white;
}
:where(.rtds-accordion--default) .rtds-accordion__item {
@apply rtds-rounded rtds-shadow-md;
}
:where(.rtds-accordion--widget) .rtds-accordion__title {
@apply rtds-p-2;
}
:where(.rtds-accordion--widget) .rtds-accordion__pane {
@apply rtds-px-2;
}
:where(.rtds-accordion--minimal) .rtds-accordion__item {
@apply rtds-border-b rtds-border-gray-01;
}
}
/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*
* Simple accordion pattern example
*/
'use strict';
class Accordion {
constructor(domNode) {
this.rootEl = domNode;
this.buttonEl = this.rootEl.querySelector('button[aria-expanded]');
const controlsId = this.buttonEl.getAttribute('aria-controls');
this.contentEl = document.getElementById(controlsId);
this.open = this.buttonEl.getAttribute('aria-expanded') === 'true';
// Aggiungiamo una reference al container principale dell'accordion
this.accordionContainer = this.rootEl.closest('.rtds-accordion');
// add event listeners
this.buttonEl.addEventListener('click', this.onButtonClick.bind(this));
}
onButtonClick() {
// Se l'accordion ha la classe toggle, chiudiamo gli altri pannelli
if (this.accordionContainer.classList.contains('rtds-accordion--toggle')) {
// Troviamo tutti i pannelli aperti nello stesso accordion
const openButtons = this.accordionContainer.querySelectorAll('button[aria-expanded="true"]');
openButtons.forEach(button => {
if (button !== this.buttonEl) {
// Troviamo l'istanza dell'accordion per questo button e la chiudiamo
const accordionTitle = button.closest('.rtds-accordion__title');
const accordionInstance = accordions.get(accordionTitle);
if (accordionInstance) {
accordionInstance.close();
}
}
});
}
this.toggle(!this.open);
}
toggle(open) {
// don't do anything if the open state doesn't change
if (open === this.open) {
return;
}
// update the internal state
this.open = open;
// handle DOM updates
this.buttonEl.setAttribute('aria-expanded', `${open}`);
if (open) {
this.contentEl.removeAttribute('hidden');
} else {
this.contentEl.setAttribute('hidden', '');
}
}
// Add public open and close methods for convenience
open() {
this.toggle(true);
}
close() {
this.toggle(false);
}
}
// Modifichiamo l'inizializzazione per mantenere un riferimento alle istanze
const accordions = new Map();
window.addEventListener('load', function() {
document.querySelectorAll('.rtds-accordion__title').forEach((accordionEl) => {
accordions.set(accordionEl, new Accordion(accordionEl));
});
});
Componente per pannelli espandibili basato sulle linee guida WAI Authoring Practices Guide.
Il componente è renderizzato con la seguente struttura per ogni pannello:
<h3> + <button type="button"> — trigger del pannello; <button> garantisce ruolo button e attivabilità da tastiera nativa<div role="region"> — area del contenuto espandibile; associata al trigger tramite aria-labelledbyIl comportamento — pannelli indipendenti o in toggle esclusivo (un solo pannello aperto alla volta) — è determinato dalla classe rtds-accordion--toggle sul wrapper.
default: Accordion con sfondo bianco, bordi arrotondati e ombraminimal: Accordion con bordo inferiorerounded: Accordion con icone circolari e ombreggiaturawidget: Accordion compatto per widgetdefault: Pannelli indipendenti (ogni pannello si apre/chiude indipendentemente)toggle: Un solo pannello aperto alla volta (l’apertura di un pannello chiude gli altri)Contesto di sviluppo Fractal/Nunjucks — Questa sezione riguarda esclusivamente i parametri del context
.config.ymlusati durante lo sviluppo nella libreria. Non descrive attributi HTML o proprietà da usare nei temi, CMS o ambienti di implementazione: per quello, fare riferimento alla sezione Esempi di utilizzo.
idSection (numero, opzionale): ID numerico per sezioni multipleclasses (stringa, opzionale): Classi CSS aggiuntivespacingClasses (stringa, opzionale): Classi per lo spaziamento generaletitleSpacingClasses (stringa, opzionale): Classi per lo spaziamento dei titolipanels (array, obbligatorio): Lista dei pannellilabel (stringa, obbligatorio): Testo del titolotext (stringa, obbligatorio): Contenuto del pannelloisOpen (booleano, opzionale): Stato iniziale del pannelloshowMoreIcon (stringa, opzionale): ID dell’icona per “mostra più”showLessIcon (stringa, opzionale): ID dell’icona per “mostra meno”iconSize (stringa, opzionale): Classi per la dimensione delle iconeiconClasses (stringa, opzionale): Classi aggiuntive per le iconepanelTitleClasses (stringa, opzionale): Classi per i titoli dei pannellipanelTitleFont (stringa, opzionale): Classi per il font dei titolitriggerClasses (stringa, opzionale): Classi per i pulsanti triggerpanelContentClasses (stringa, opzionale): Classi per il contenuto dei pannellipanelContentSpacing (stringa, opzionale): Classi per lo spaziamento del contenutoLivello AA — criteri pertinenti:
<h3> e role="region"aria-expanded riflette lo stato aperto/chiuso del pannello; aria-controls collega trigger e pannello<div> contenitore con un elemento <div class="rtds-accordion__item"> per ogni pannello<button type="button"> annidato all’interno di <h3>, garantendo una struttura heading corretta nella gerarchia della pagina<div role="region"> collegato al trigger tramite aria-labelledbyhidden sul <div role="region">, rimuovendolo dal flusso di lettura degli screen reader| Attributo | Valori | Condizione |
|---|---|---|
aria-expanded |
"true" / "false" |
sempre presente sul <button> trigger |
aria-controls |
ID del pannello | sempre presente sul <button> trigger |
id |
stringa univoca (es. accordion1id) |
sempre presente sul <button> trigger |
role="region" |
— | sempre presente sul <div> pannello |
aria-labelledby |
ID del trigger corrispondente | sempre presente sul <div> pannello |
| Tasto | Azione |
|---|---|
Tab / Shift+Tab |
Sposta il focus tra i trigger dei pannelli |
Enter / Space |
Apre o chiude il pannello corrente |
:focus-visible è sempre visibile e non viene rimosso via CSSespanso / compresso) e il ruolo pulsantearia-labelledby, quindi viene identificato con il titolo del pannellohidden sul pannello chiuso impedisce agli screen reader di leggerne il contenuto.rtds-accordion:.rtds-accordion--default:.rtds-accordion--minimal:.rtds-accordion__title:.rtds-accordion__trigger:.rtds-accordion__icon:.rtds-accordion__pane:<!-- Accordion standard -->
<div class="rtds-accordion rtds-accordion--default">
<h3 class="rtds-accordion__title">
<button class="rtds-accordion__trigger" aria-expanded="false">
Titolo Pannello
<span class="rtds-accordion__icon">
<svg class="rtds-icon is-show-more">...</svg>
<svg class="rtds-icon is-show-less rtds-hidden">...</svg>
</span>
</button>
</h3>
<div class="rtds-accordion__pane" hidden>
Contenuto del pannello
</div>
</div>
<!-- Accordion con toggle -->
<div class="rtds-accordion rtds-accordion--widget rtds-accordion--toggle">
<!-- ... pannelli ... -->
</div>
<!-- Accordion minimal -->
<div class="rtds-accordion rtds-accordion--minimal">
<!-- ... pannelli ... -->
</div>