<div class="rtds-accordion rtds-accordion--default">

    <div class="rtds-accordion__item">
        <h3 class="rtds-accordion__title rtds-flex rtds-font-bold rtds-heading-3">
            <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">
                    Panel 1 Nunc aliquam phasellus molestie blandit?
                </span>

                <span class="rtds-accordion__icon rtds-flex rtds-p-2 rtds-ml-auto rtds-h-auto rtds-transition-all rtds-background-02 rtds-rounded group-aria-expanded:rtds-background-primary group-aria-expanded:rtds-text-white ">
                    <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4 md:rtds-w-8 md:rtds-h-8 lg:rtds-w-9 lg:rtds-h-9 is-show-more group-aria-expanded:rtds-hidden" aria-hidden="true" focusable="false" role="img">
                        <use href="../../icons.svg#mini--plus" />
                    </svg>

                    <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4 md:rtds-w-8 md:rtds-h-8 lg:rtds-w-9 lg:rtds-h-9 is-show-less rtds-hidden group-aria-expanded:rtds-inline-block" aria-hidden="true" focusable="false" role="img">
                        <use href="../../icons.svg#mini--minus" />
                    </svg>

                </span>

            </button>
        </h3>
        <div id="accordionSection-1" role="region" aria-labelledby="accordion1id" class="rtds-accordion__pane rtds-transition-all" hidden>

            <p>Neque blandit pellentesque nunc sed amet. Nisl, semper sed aliquam amet proin purus augue et.</p>

        </div>
    </div>

    <div class="rtds-accordion__item">
        <h3 class="rtds-accordion__title rtds-flex rtds-font-bold rtds-heading-3">
            <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">
                    Panel 2 Nunc aliquam phasellus molestie blandit?
                </span>

                <span class="rtds-accordion__icon rtds-flex rtds-p-2 rtds-ml-auto rtds-h-auto rtds-transition-all rtds-background-02 rtds-rounded group-aria-expanded:rtds-background-primary group-aria-expanded:rtds-text-white ">
                    <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4 md:rtds-w-8 md:rtds-h-8 lg:rtds-w-9 lg:rtds-h-9 is-show-more group-aria-expanded:rtds-hidden" aria-hidden="true" focusable="false" role="img">
                        <use href="../../icons.svg#mini--plus" />
                    </svg>

                    <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4 md:rtds-w-8 md:rtds-h-8 lg:rtds-w-9 lg:rtds-h-9 is-show-less rtds-hidden group-aria-expanded:rtds-inline-block" aria-hidden="true" focusable="false" role="img">
                        <use href="../../icons.svg#mini--minus" />
                    </svg>

                </span>

            </button>
        </h3>
        <div id="accordionSection-2" role="region" aria-labelledby="accordion2id" class="rtds-accordion__pane rtds-transition-all" hidden>

            <p>Neque blandit pellentesque nunc sed amet. Nisl, semper sed aliquam amet proin purus augue et.</p>

        </div>
    </div>

    <div class="rtds-accordion__item">
        <h3 class="rtds-accordion__title rtds-flex rtds-font-bold rtds-heading-3">
            <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">
                    Panel 3 Nunc aliquam phasellus molestie blandit?
                </span>

                <span class="rtds-accordion__icon rtds-flex rtds-p-2 rtds-ml-auto rtds-h-auto rtds-transition-all rtds-background-02 rtds-rounded group-aria-expanded:rtds-background-primary group-aria-expanded:rtds-text-white ">
                    <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4 md:rtds-w-8 md:rtds-h-8 lg:rtds-w-9 lg:rtds-h-9 is-show-more group-aria-expanded:rtds-hidden" aria-hidden="true" focusable="false" role="img">
                        <use href="../../icons.svg#mini--plus" />
                    </svg>

                    <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4 md:rtds-w-8 md:rtds-h-8 lg:rtds-w-9 lg:rtds-h-9 is-show-less rtds-hidden group-aria-expanded:rtds-inline-block" aria-hidden="true" focusable="false" role="img">
                        <use href="../../icons.svg#mini--minus" />
                    </svg>

                </span>

            </button>
        </h3>
        <div id="accordionSection-3" role="region" aria-labelledby="accordion3id" class="rtds-accordion__pane rtds-transition-all" hidden>

            <p>Neque blandit pellentesque nunc sed amet. Nisl, semper sed aliquam amet proin purus augue et.</p>

        </div>
    </div>

</div>
<div{% if idSection %} id="accordionSection{{ idSection }}"{% endif %} class="rtds-accordion{% if spacingClasses %} {{ spacingClasses }}{% endif %}{%- block classes -%}
    {%- if classes %} {{ classes }}{% endif -%}{%- endblock classes -%}">
    {% for panel in panels %}
    <div class="rtds-accordion__item">
        <h3 class="rtds-accordion__title rtds-flex rtds-font-bold{% if titleSpacingClasses %} {{ titleSpacingClasses }}{% endif %}{% if panelTitleFont %} {{ panelTitleFont }}{% endif %}{% if panelTitleClasses %} {{ panelTitleClasses }}{% endif %}">
            <button type="button" aria-expanded="{% if panel.isOpen %}true{% else %}false{% endif %}" class="rtds-accordion__trigger rtds-group rtds-flex rtds-items-center rtds-flex-1 rtds-transition-all{% if triggerClasses %} {{ triggerClasses }} {% endif %}" aria-controls="accordionSection-{{ loop.index }}" id="accordion{{ loop.index }}id">
                <span class="rtds-accordion__trigger-label">
                    {{ panel.label | safe }}
                </span>
                {% 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: 'is-show-more group-aria-expanded:rtds-hidden'}, true %}
                        {% render '@icon', {id: showLessIcon, size: iconSize, classes: 'is-show-less rtds-hidden group-aria-expanded:rtds-inline-block'}, true %}
                    </span>
                {% endblock icon %}
            </button>
        </h3>
        <div id="accordionSection-{{ loop.index }}" role="region" aria-labelledby="accordion{{ loop.index }}id" class="rtds-accordion__pane rtds-transition-all{% if panelContentSpacing %} {{ panelContentSpacing }}{% endif %}{% if panelContentClasses %} {{ panelContentClasses }}{% endif %}"{% if not panel.isOpen %} hidden{% endif %}>
            {% block panelContent %}
                {{ panel.text|safe }}
            {% endblock panelContent %}
        </div>
    </div>
    {% endfor %}
</div>
{
  "showMoreIcon": "mini--plus",
  "showLessIcon": "mini--minus",
  "iconSize": "rtds-w-4 rtds-h-4 md:rtds-w-8 md:rtds-h-8 lg:rtds-w-9 lg:rtds-h-9",
  "panelTitleFont": "rtds-heading-3",
  "panels": [
    {
      "label": "Panel 1 Nunc aliquam phasellus molestie blandit?",
      "text": "<p>Neque blandit pellentesque nunc sed amet. Nisl, semper sed aliquam amet proin purus augue et.</p>"
    },
    {
      "label": "Panel 2 Nunc aliquam phasellus molestie blandit?",
      "text": "<p>Neque blandit pellentesque nunc sed amet. Nisl, semper sed aliquam amet proin purus augue et.</p>"
    },
    {
      "label": "Panel 3 Nunc aliquam phasellus molestie blandit?",
      "text": "<p>Neque blandit pellentesque nunc sed amet. Nisl, semper sed aliquam amet proin purus augue et.</p>"
    }
  ]
}
  • Content:
    /**
     * 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);
    }
    
  • URL: /components/raw/accordion/accordion.built.css
  • Filesystem Path: components/03-molecules/accordion/accordion.built.css
  • Size: 1.4 KB
  • Content:
    /**
     * 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;
        }
    }
  • URL: /components/raw/accordion/accordion.css
  • Filesystem Path: components/03-molecules/accordion/accordion.css
  • Size: 891 Bytes
  • Content:
    /*
     *   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));
      });
    });
    
  • URL: /components/raw/accordion/accordion.js
  • Filesystem Path: components/03-molecules/accordion/accordion.js
  • Size: 2.4 KB

Accordion

Componente per pannelli espandibili basato sulle linee guida WAI Authoring Practices Guide.


Comportamento

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-labelledby

Il comportamento — pannelli indipendenti o in toggle esclusivo (un solo pannello aperto alla volta) — è determinato dalla classe rtds-accordion--toggle sul wrapper.


Varianti

Varianti di Stile

  • default: Accordion con sfondo bianco, bordi arrotondati e ombra
  • minimal: Accordion con bordo inferiore
  • rounded: Accordion con icone circolari e ombreggiatura
  • widget: Accordion compatto per widget

Varianti di Stato

  • default: Pannelli indipendenti (ogni pannello si apre/chiude indipendentemente)
  • toggle: Un solo pannello aperto alla volta (l’apertura di un pannello chiude gli altri)

Parametri di configurazione

Contesto di sviluppo Fractal/Nunjucks — Questa sezione riguarda esclusivamente i parametri del context .config.yml usati 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.

Parametri Base

  • idSection (numero, opzionale): ID numerico per sezioni multiple
  • classes (stringa, opzionale): Classi CSS aggiuntive
  • spacingClasses (stringa, opzionale): Classi per lo spaziamento generale
  • titleSpacingClasses (stringa, opzionale): Classi per lo spaziamento dei titoli

Parametri per Pannelli

  • panels (array, obbligatorio): Lista dei pannelli
    • label (stringa, obbligatorio): Testo del titolo
    • text (stringa, obbligatorio): Contenuto del pannello
    • isOpen (booleano, opzionale): Stato iniziale del pannello

Parametri per Icone

  • showMoreIcon (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 icone
  • iconClasses (stringa, opzionale): Classi aggiuntive per le icone

Parametri per Stili

  • panelTitleClasses (stringa, opzionale): Classi per i titoli dei pannelli
  • panelTitleFont (stringa, opzionale): Classi per il font dei titoli
  • triggerClasses (stringa, opzionale): Classi per i pulsanti trigger
  • panelContentClasses (stringa, opzionale): Classi per il contenuto dei pannelli
  • panelContentSpacing (stringa, opzionale): Classi per lo spaziamento del contenuto

Accessibilità

WCAG 2.1

Livello AA — criteri pertinenti:

  • 1.3.1 Informazioni e correlazioni — la struttura è comunicata semanticamente tramite <h3> e role="region"
  • 4.1.2 Nome, ruolo, valore — aria-expanded riflette lo stato aperto/chiuso del pannello; aria-controls collega trigger e pannello

Struttura e semantica

  • Il componente è renderizzato come <div> contenitore con un elemento <div class="rtds-accordion__item"> per ogni pannello
  • Il trigger è un <button type="button"> annidato all’interno di <h3>, garantendo una struttura heading corretta nella gerarchia della pagina
  • Il pannello espandibile è un <div role="region"> collegato al trigger tramite aria-labelledby
  • Quando il pannello è chiuso è presente l’attributo hidden sul <div role="region">, rimuovendolo dal flusso di lettura degli screen reader

Attributi ARIA

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

Gestione del focus

  • Focus iniziale: nessuno; il focus segue l’ordine naturale del DOM
  • Focus trap: non presente — l’accordion non intrappola il focus
  • Focus restoration: non applicabile
  • Focus visibile: lo stile :focus-visible è sempre visibile e non viene rimosso via CSS

Screen reader

  • Alla pressione del trigger, lo screen reader annuncia il testo del pannello, lo stato (espanso / compresso) e il ruolo pulsante
  • Il contenuto del pannello è incluso nella regione nominata dal trigger tramite aria-labelledby, quindi viene identificato con il titolo del pannello
  • L’attributo hidden sul pannello chiuso impedisce agli screen reader di leggerne il contenuto

Stili CSS

Classi Base

  • .rtds-accordion:
    • Layout: grid
    • Gap: gap-6

Classi Modificatori

  • .rtds-accordion--default:
    • Sfondo: bg-white
    • Bordi: rounded
    • Ombra: shadow-md
  • .rtds-accordion--minimal:
    • Bordo: border-b, border-gray-01

Classi Elementi (BEM)

  • .rtds-accordion__title:
    • Layout: flex
    • Font: font-bold
  • .rtds-accordion__trigger:
    • Layout: group, flex, items-center, flex-1
    • Transizione: transition-all
  • .rtds-accordion__icon:
    • Layout: flex, p-2, ml-auto
    • Transizione: transition-all
  • .rtds-accordion__pane:
    • Transizione: transition-all

Esempi di utilizzo

<!-- 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>