Facets

<fieldset class="rtds-facets">
    <div class="rtds-grid rtds-py-6 rtds-gap-4 md:rtds-gap-6">
        <legend class="rtds-facets__legend">
            <button type="button" class="rtds-facets__toggle rtds-group/facet" aria-expanded="true" aria-controls="facetsSection-1" id="facetsToggle1id">
                Nome faccetta
                <svg class="rtds-icon rtds-fill-current rtds-w-6 rtds-h-6 rtds-transition-transform rtds-duration-300 group-aria-expanded/facet:-rtds-rotate-180" aria-hidden="true" focusable="false" role="img">
                    <use href="../../icons.svg#solid--chevron-down" />
                </svg>

            </button>
        </legend>
        <div class="rtds-facets__content rtds-space-y-2 has-show-more" id="facetsSection-1" role="region" aria-labelledby="facetsToggle1id">
            <ul class="rtds-facets__list rtds-space-y-2">

                <li class="rtds-facets__item">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-1" name="checkbox-facetsSection-1-1" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-1">
                                <span class="rtds-facets__item-name">Filtro 1</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-2" name="checkbox-facetsSection-1-2" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-2">
                                <span class="rtds-facets__item-name">Filtro 2</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-3" name="checkbox-facetsSection-1-3" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-3">
                                <span class="rtds-facets__item-name">Filtro 3</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-4" name="checkbox-facetsSection-1-4" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-4">
                                <span class="rtds-facets__item-name">Filtro 4</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-5" name="checkbox-facetsSection-1-5" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-5">
                                <span class="rtds-facets__item-name">Filtro 5</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-6" name="checkbox-facetsSection-1-6" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-6">
                                <span class="rtds-facets__item-name">Filtro 6</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item is-hideable rtds-hidden">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-7" name="checkbox-facetsSection-1-7" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-7">
                                <span class="rtds-facets__item-name">Filtro 7</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item is-hideable rtds-hidden">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-8" name="checkbox-facetsSection-1-8" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-8">
                                <span class="rtds-facets__item-name">Filtro 8</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

                <li class="rtds-facets__item is-hideable rtds-hidden">

                    <div class="rtds-input rtds-input-field rtds-input-checkbox">

                        <div class="rtds-flex rtds-gap-2 rtds-items-center">
                            <input id="checkbox-facetsSection-1-9" name="checkbox-facetsSection-1-9" class="   " type="checkbox">
                            <label class="rtds-input-checkbox__label rtds-facets__label" for="checkbox-facetsSection-1-9">
                                <span class="rtds-facets__item-name">Filtro 9</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>
                            </label>
                        </div>

                    </div>
                </li>

            </ul>
            <button type="button" class="rtds-w-full rtds-btn rtds-btn--s rtds-btn--inverted rtds-btn--icon-right rtds-group/button rtds-justify-center rtds-btn--show-more rtds-facets__show-more" aria-expanded="false">
                <span class="rtds-sr-only">Nome faccetta</span>
                <span class="rtds-btn__label-show">
                    Mostra altri
                </span>
                <span class="rtds-btn__label-hide rtds-hidden">Mostra meno</span>
                <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4 group-aria-expanded/button:-rtds-rotate-180 rtds-icon rtds-fill-current rtds-transition-all rtds-duration-200 rtds-ease-out rtds-transform group-hover/button:rtds-scale-200" aria-hidden="true" focusable="false" role="img">
                    <use href="../../icons.svg#outline--chevron-down" />
                </svg>

            </button>
        </div>
    </div>
</fieldset>
<fieldset class="rtds-facets">
   <div class="rtds-grid rtds-py-6 rtds-gap-4 md:rtds-gap-6">
      <legend class="rtds-facets__legend">
         <button type="button" class="rtds-facets__toggle rtds-group/facet" aria-expanded="true" aria-controls="{{ sectionId }}" id="{{ toggleId }}">
               {{ legend | safe }}
               {% render '@icon', { id: 'solid--chevron-down', size: 'rtds-w-6 rtds-h-6 rtds-transition-transform rtds-duration-300', classes: 'group-aria-expanded/facet:-rtds-rotate-180' }, true %}
         </button>
      </legend>
      <div class="rtds-facets__content rtds-space-y-2 has-show-more" id="{{ sectionId }}" role="region" aria-labelledby="{{ toggleId }}">
         <ul class="rtds-facets__list rtds-space-y-2">
            {% for i in range(1, itemsCount) %}
            <li class="rtds-facets__item{% if i > 6 %} is-hideable rtds-hidden{% endif %}">
               {% render '@input-checkbox', 
               { 
                  label: '<span class="rtds-facets__item-name">Filtro ' + i + '</span> <span class="rtds-facets__item-count">44<span class="rtds-sr-only"> risultati</span></span>',
                  id: 'checkbox-' + sectionId + '-' + i,
                  labelClasses: 'rtds-facets__label'
               }, 
               true 
               %}
            </li>
            {% endfor %}
         </ul>
         <button type="button" class="rtds-w-full rtds-btn rtds-btn--s rtds-btn--inverted rtds-btn--icon-right rtds-group/button rtds-justify-center rtds-btn--show-more rtds-facets__show-more" aria-expanded="false">
            <span class="rtds-sr-only">{{ legend }}</span>
            <span class="rtds-btn__label-show">
               Mostra altri
            </span>
            <span class="rtds-btn__label-hide rtds-hidden">Mostra meno</span>
            {% render '@icon', { id: 'outline--chevron-down', size: 'rtds-w-4 rtds-h-4', classes: 'group-aria-expanded/button:-rtds-rotate-180 rtds-icon rtds-fill-current rtds-transition-all rtds-duration-200 rtds-ease-out rtds-transform group-hover/button:rtds-scale-200' }, true %}
         </button>
      </div>
   </div>
</fieldset>
{
  "itemsCount": 10,
  "legend": "Nome faccetta",
  "toggleId": "facetsToggle1id",
  "sectionId": "facetsSection-1"
}
  • Content:
    .rtds-facets {
        border-bottom-width: 1px;
        border-style: solid;
            border-color: var(--color-border-gray-01);
    }
    .rtds-facets:last-child {
        border-bottom-width: 0px;
    }
    legend.rtds-facets__legend {
        width: 100%;
    }
    .rtds-facets__toggle {
        width: 100%;
        justify-content: space-between;
        font-size: var(--fontsize-text-sm);
            color: var(--color-content-03);
        display: flex;
        align-items: center;
        gap: var(--spacing-2);
        font-weight: 700;
    }
    @media (min-width: 768px) {
        .rtds-facets__toggle {
            font-size: var(--fontsize-text-base);
        }
    }
    .rtds-facets__item {
        padding-top: var(--spacing-1);
        padding-bottom: var(--spacing-1);
    }
    .rtds-facets__label {
        flex: 1 1 0%;
        display: flex;
        gap: var(--spacing-2);
    }
    .rtds-facets__item-count {
        margin-left: auto;
        align-self: flex-start;
        border-radius: 9999px;
        padding-left: var(--spacing-2);
        padding-right: var(--spacing-2);
        font-size: var(--fontsize-text-sm);
            color: var(--color-content-02);
            background-color: var(--color-background-02);
            border-color: var(--color-border-secondary);
        display: inline-flex;
        justify-content: center;
        align-items: center;
        font-weight: 700;
        border-width: 1px;
    }
    
  • URL: /components/raw/facets/facets.built.css
  • Filesystem Path: components/04-organisms/facets/facets.built.css
  • Size: 1.3 KB
  • Content:
    @layer components {
        .rtds-facets {
            @apply rtds-border-solid rtds-border-b rtds-border-gray-01 last:rtds-border-b-0;
        }
    
        legend.rtds-facets__legend {
            @apply rtds-w-full;
        }
    
        .rtds-facets__toggle {
            @apply rtds-flex rtds-items-center rtds-justify-between rtds-w-full rtds-text-sm md:rtds-text-base rtds-font-bold rtds-content-03 rtds-gap-2;
        }
    
        .rtds-facets__item {
            @apply rtds-py-1;
        }
        
        .rtds-facets__label {
            @apply rtds-flex rtds-gap-2 rtds-flex-1;
        }
    
        .rtds-facets__item-count {
            @apply rtds-content-02 rtds-ml-auto rtds-self-start rtds-inline-flex rtds-px-2 rtds-items-center rtds-justify-center rtds-rounded-full rtds-border rtds-border-secondary rtds-background-02 rtds-font-bold rtds-text-sm;
        }
    
        
    }
  • URL: /components/raw/facets/facets.css
  • Filesystem Path: components/04-organisms/facets/facets.css
  • Size: 800 Bytes
  • Content:
    /*
     *   This content is licensed according to the W3C Software License at
     *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
     *
     *  From accordion pattern
     */
    
    'use strict';
    
    class FacetToggle {
      constructor(domNode) {
        this.rootEl = domNode;
        this.buttonEl = this.rootEl;
    
        const controlsId = this.buttonEl.getAttribute('aria-controls');
        this.contentEl = document.getElementById(controlsId);
    
        this.open = this.buttonEl.getAttribute('aria-expanded') === 'true';
    
        // add event listeners
        this.buttonEl.addEventListener('click', this.onButtonClick.bind(this));
      }
    
      onButtonClick() {
        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);
      }
    }
    
    window.addEventListener('load', function() {
      // init facet toggles
      document.querySelectorAll('button.rtds-facets__toggle').forEach((facetToggleEl) => {
        if (facetToggleEl) {
          new FacetToggle(facetToggleEl);
        }
      });
    
      /* SHOW MORE BTN */
      document.querySelectorAll('.article__show-btn').forEach(btn => {
        btn.addEventListener('click', () => {
          if (btn.getAttribute('data-shown') === 'false') {
            btn.closest('.article').setAttribute('data-expanded', 'true');
            btn.setAttribute('data-shown', 'true');
            btn.textContent = 'Show less';
            btn.closest('.article').querySelector('.article__content').focus();
          } else {
            btn.closest('.article').setAttribute('data-expanded', 'false');
            btn.setAttribute('data-shown', 'false');
            btn.textContent = 'Show more';
          }
        })
      });
    
      /* SHOW MORE FACETS */
      document.querySelectorAll('.has-show-more').forEach(container => {
        const showMoreBtn = container.querySelector('.rtds-btn--show-more');
        if (!showMoreBtn) return;
    
        const labelShow = showMoreBtn.querySelector('.rtds-btn__label-show');
        const labelHide = showMoreBtn.querySelector('.rtds-btn__label-hide');
        const hiddenItems = container.querySelectorAll('.rtds-facets__item.is-hideable.rtds-hidden');
    
        if (!labelShow || !labelHide) return;
    
        showMoreBtn.setAttribute('aria-expanded', 'false');
    
        showMoreBtn.addEventListener('click', () => {
          const isExpanded = showMoreBtn.getAttribute('data-expanded') === 'true';
    
          if (!isExpanded) {
            hiddenItems.forEach(item => item.classList.remove('rtds-hidden'));
            labelShow.classList.add('rtds-hidden');
            labelHide.classList.remove('rtds-hidden');
            if (hiddenItems.length > 0) {
              const firstInput = hiddenItems[0].querySelector('input[type="checkbox"]');
              if (firstInput) firstInput.focus();
            }
            showMoreBtn.setAttribute('data-expanded', 'true');
            showMoreBtn.setAttribute('aria-expanded', 'true');
          } else {
            hiddenItems.forEach(item => item.classList.add('rtds-hidden'));
            labelShow.classList.remove('rtds-hidden');
            labelHide.classList.add('rtds-hidden');
            showMoreBtn.setAttribute('data-expanded', 'false');
            showMoreBtn.setAttribute('aria-expanded', 'false');
          }
        });
      });
    });
  • URL: /components/raw/facets/facets.js
  • Filesystem Path: components/04-organisms/facets/facets.js
  • Size: 3.6 KB

Facets

Il componente Facets è un organismo che implementa un sistema di filtri espandibile con funzionalità “mostra più/meno” per gestire liste di opzioni di filtro.

Comportamento

Il componente offre due funzionalità principali:

  1. Toggle Espandibile: Un pulsante che permette di espandere/collassare la sezione dei filtri
  2. Mostra Più/Meno: Un pulsante che permette di visualizzare più o meno opzioni di filtro, con un limite iniziale di 6 elementi

Accessibilità

WCAG 2.1

Livello AA — criteri pertinenti:

  • 1.3.1 Informazioni e correlazioni — struttura con <fieldset> e <legend> per raggruppare semanticamente i filtri correlati
  • 4.1.2 Nome, Ruolo, Valore — il pulsante toggle usa aria-expanded e aria-controls; la regione espandibile ha role="region" e aria-labelledby
  • 2.1.1 Tastiera — tutti i controlli sono raggiungibili e attivabili da tastiera

Struttura e semantica

  • Il componente è renderizzato come <fieldset class="rtds-facets"> — raggruppa semanticamente i filtri correlati
  • La <legend> contiene il pulsante di toggle — il testo della legenda descrive il gruppo di filtri
  • La regione espandibile ha role="region" e aria-labelledby="" — identifica la sezione e la collega al toggle

Attributi ARIA

Attributo Valori Condizione
aria-expanded sul .rtds-facets__toggle "true" / "false" gestito da JavaScript — riflette lo stato espanso/collassato
aria-controls sul .rtds-facets__toggle valore di sectionId sempre presente — riferimento alla regione controllata
role="region" sul contenitore espandibile sempre presente
aria-labelledby sul contenitore espandibile valore di toggleId sempre presente — collega la regione al toggle
aria-expanded sul .rtds-facets__show-more "false" / "true" gestito da JavaScript — riflette lo stato del pannello “mostra più”
Tasto Azione
Tab / Shift+Tab Sposta il focus tra i controlli
Enter / Space Attiva il pulsante toggle per espandere/collassare; seleziona i checkbox

Gestione del focus

  • Focus visibile: lo stile :focus-visible è sempre visibile e non viene rimosso via CSS

Screen reader

  • Lo screen reader annuncia il gruppo di filtri con il testo della <legend> prima di enumerare i checkbox
  • Il testo <span class="rtds-sr-only"></span> nel pulsante “Mostra altri” fornisce il contesto del gruppo a cui appartiene
  • Il contatore dei risultati <span class="rtds-sr-only"> risultati</span> aggiunge testo leggibile accanto al numero

Struttura

Il componente è strutturato come un fieldset che contiene:

  • Una legenda con pulsante di toggle
  • Una lista di checkbox per i filtri
  • Un pulsante “mostra più/meno” per visualizzare ulteriori opzioni

Configurazione

Il componente può essere configurato attraverso il file facets.config.yml. Ecco una spiegazione dettagliata dei parametri disponibili:

name: Facets
context:
  itemsCount: 10      # Numero totale di elementi da generare
  legend: Nome faccetta  # Testo del pulsante di toggle
  toggleId: facetsToggle1id  # ID univoco del pulsante di toggle
  sectionId: facetsSection-1  # ID univoco della sezione contenente i filtri

Parametri

itemsCount

  • Tipo: Numero intero
  • Descrizione: Definisce il numero totale di elementi di filtro da generare
  • Esempio:
    itemsCount: 15  # Genererà 15 checkbox di filtro

legend

  • Tipo: Stringa
  • Descrizione: Testo visualizzato nel pulsante di toggle e utilizzato per l’accessibilità
  • Esempio:
    legend: "Filtri per categoria"  # Testo descrittivo per la sezione

toggleId

  • Tipo: Stringa
  • Descrizione: Identificatore univoco per il pulsante di toggle
  • Requisiti: Deve essere unico nella pagina
  • Esempio:
    toggleId: "category-filters-toggle"  # ID semantico per il toggle

sectionId

  • Tipo: Stringa
  • Descrizione: Identificatore univoco per la sezione contenente i filtri
  • Requisiti: Deve essere unico nella pagina e corrispondere all’aria-controls del toggle
  • Esempio:
    sectionId: "category-filters-section"  # ID semantico per la sezione

Esempio Completo

name: Facets
context:
  itemsCount: 20
  legend: "Filtri per categoria"
  toggleId: "category-filters-toggle"
  sectionId: "category-filters-section"

Classi CSS

  • rtds-facets: Contenitore principale del componente
  • rtds-facets__legend: Contenitore della legenda
  • rtds-facets__toggle: Pulsante per espandere/collassare la sezione
  • rtds-facets__content: Contenitore del contenuto espandibile
  • rtds-facets__list: Lista degli elementi di filtro
  • rtds-facets__item: Singolo elemento di filtro
  • rtds-facets__item-name: Nome dell’elemento di filtro
  • rtds-facets__item-count: Contatore dei risultati
  • rtds-facets__label: Etichetta del checkbox
  • rtds-facets__show-more: Pulsante “mostra più/meno”

JavaScript

Il componente include due funzionalità JavaScript:

  1. FacetToggle: Gestisce l’espansione/collasso della sezione
  2. Show More: Gestisce la visualizzazione di elementi aggiuntivi

Il JavaScript viene inizializzato automaticamente al caricamento della pagina e gestisce:

  • Eventi di click sui pulsanti
  • Stati aria-expanded
  • Gestione del focus
  • Transizioni visive