<div class="rtds-space-y-6 md:rtds-space-y-8">
    <div class="rtds-space-y-4">

        <div class="rtds-grid rtds-gap-6 lg:rtds-flex lg:rtds-justify-between">
            <div class="rtds-loading-status" aria-live="polite" aria-busy="false" role="status">

                <h2 class="rtds-heading-3">Nessun risultato trovato</h2>

            </div>

            <div>

                <div class="rtds-grid md:rtds-flex md:rtds-items-center rtds-gap-2 md:rtds-gap-6 md:rtds-justify-end">
                    <div class="rtds-flex-1 md:rtds-flex-none">
                        <div class="rtds-input-field rtds-select-field rtds-field-inline rtds-select-field--sm">
                            <label for="select-field-1" class="rtds-input-field__label">
                                Rivolto a

                            </label>

                            <div class="rtds-select rtds-select--light">

                                <select id="select-field-1" class="">
                                    <option value="1">Text</option>
                                    <option value="2">Option 1</option>
                                    <option value="3">Option 2</option>
                                    <option value="4">Option 3</option>
                                    <option value="5">Option 4</option>
                                    <option value="6">Option 5</option>

                                </select>
                            </div>

                        </div>
                    </div>
                    <div class="rtds-flex-1 md:rtds-flex-none">
                        <div class="rtds-input-field rtds-select-field rtds-field-inline rtds-select-field--sm">
                            <label for="select-field-2" class="rtds-input-field__label">
                                Ordina per

                            </label>

                            <div class="rtds-select rtds-select--light">

                                <select id="select-field-2" class="">
                                    <option value="1">Text</option>
                                    <option value="2">Option 1</option>
                                    <option value="3">Option 2</option>
                                    <option value="4">Option 3</option>
                                    <option value="5">Option 4</option>
                                    <option value="6">Option 5</option>

                                </select>
                            </div>

                        </div>
                    </div>
                </div>

            </div>

        </div>

        <!-- CONTAINER PER EMPTY STATE - HA CONTENUTO SOLO SE NON CI SONO RISULTATI - DEVE ESSERE SEMPRE PRESENTE - CON I RISULTATI HA CLASSE -> rtds-sr-only -->
        <div class="rtds-empty-state rtds-grid rtds-gap-4" aria-live="assertive" aria-atomic="true">
            <!-- CONTENUTO EMPTY STATE - PRESENTE SOLO SE NON CI SONO RISULTATI -->
            <div class="rtds-font-bold rtds-text-center rtds-grid rtds-gap-8 rtds-justify-center rtds-justify-items-center rtds-py-20">
                <svg class="rtds-icon rtds-fill-current rtds-w-16 rtds-h-16 rtds-content-03" aria-hidden="true" focusable="false" role="img">
                    <use href="../../icons.svg#outline--exclamation-circle" />
                </svg>

                <h3 class="rtds-heading-4">Non ci sono risultati per questi criteri di ricerca</h3>
            </div>
        </div>

    </div>

</div>
{% extends '@search-results' %}

{% block title %}
    <h2 class="rtds-heading-3">Nessun risultato trovato</h2>
{% endblock title %}

{% block emptyState %}
    <!-- CONTENUTO EMPTY STATE - PRESENTE SOLO SE NON CI SONO RISULTATI -->
    <div class="rtds-font-bold rtds-text-center rtds-grid rtds-gap-8 rtds-justify-center rtds-justify-items-center rtds-py-20">
        {% render '@icon',{id: 'outline--exclamation-circle', size: 'rtds-w-16 rtds-h-16', classes: 'rtds-content-03'},true %}
        <h3 class="rtds-heading-4">Non ci sono risultati per questi criteri di ricerca</h3>
    </div>
{% endblock emptyState %}

{% block results %}
{% endblock results %}

{% block pagination %}
{% endblock pagination %}
{
  "ariaBusy": false,
  "srOnlyTitle": true,
  "emptyState": true,
  "sorting": true,
  "resultsCount": 6,
  "title": "<strong>230</strong> Servizi",
  "resultsGridClasses": "md:rtds-grid-cols-2 lg:rtds-grid-cols-3",
  "resultsCard": {
    "variant": "--information-with-gradient"
  }
}
  • Content:
    @layer components {
        .rtds-loading-status[aria-busy="true"] {
            @apply rtds-w-full;
        }
    }
  • URL: /components/raw/search-results/search-results.css
  • Filesystem Path: components/04-organisms/sections/search-results/search-results.css
  • Size: 100 Bytes

Search Results

Area dei risultati di ricerca con intestazione contatore, griglia di card, paginazione e gestione degli stati di caricamento, vuoto e filtri selezionati.


Comportamento

Il componente è renderizzato come <div class="rtds-space-y-6 md:rtds-space-y-8">. Può essere avvolto in <div class="rtds-container"> se hasContainer: true.

Contiene, nell’ordine:

  1. Barra filtri attivi (@filters--v2-selected) — visibile solo se filtersSelected: true
  2. Riga contatore risultati + controlli (<div class="rtds-loading-status">) con aria-live, aria-busy e role="status"
  3. Titolo risultati — <h2> visibile (o rtds-sr-only se srOnlyTitle: true) + <span> con contatore visuale
  4. Area ordinamento (@select-field--sm) e/o pulsante filtri mobile + dialog — visibili se sorting / mobileFilters: true
  5. Contenitore empty state — sempre presente; visibile solo se emptyState: true, altrimenti rtds-sr-only
  6. Griglia card (@card + variante) — ripetuta resultsCount volte
  7. Paginazione (@pagination--centered)

Varianti template (--loading, --empty-state)

Le varianti loading e empty-state estendono @search-results sovrascrivendo blocchi specifici:

  • search-results--loading.njk — sostituisce il blocco title con spinner + heading “Caricamento risultati…”; svuota i blocchi results e pagination
  • search-results--empty-state.njk — sostituisce il blocco title con “Nessun risultato trovato”; popola il blocco emptyState con icona e messaggio; svuota results e pagination

Varianti

  • default: Griglia con sorting, senza filtri mobili
  • has-container: Aggiunge rtds-container attorno all’intera area
  • has-padding: Aggiunge padding verticale rtds-py-6
  • has-mobile-filters: Mostra il pulsante “Filtra la ricerca” su mobile con dialog modale
  • has-filters-selected: Come has-mobile-filters con la barra @filters--v2-selected visibile
  • loading: Spinner di caricamento, nessuna card né paginazione
  • empty-state: Messaggio “Nessun risultato trovato”, nessuna card né paginazione

Parametri di configurazione

Contesto di sviluppo Fractal/Nunjucks — parametri del context .config.yml. Non descrivono attributi HTML da usare nei CMS o ambienti di implementazione.

Parametri base

  • ariaBusy (booleano, opzionale): Valore di aria-busy sul contenitore .rtds-loading-status. Default: false
  • srOnlyTitle (booleano, opzionale): Se true, l’<h2> del contatore è nascosto visivamente (rtds-sr-only) e il contatore è mostrato in uno <span>. Default: true
  • emptyState (booleano, opzionale): Se true, mostra il contenitore .rtds-empty-state (rimuove rtds-sr-only). Default: false
  • title (stringa, opzionale): HTML del titolo risultati, accetta markup (filtro safe). Default: '<strong>230</strong> Servizi'
  • resultsCount (numero, opzionale): Numero di card da iterare nella griglia. Default: 6

Parametri di layout

  • hasContainer (booleano, opzionale): Avvolge l’intera area in <div class="rtds-container">. Default: non impostato
  • padding (booleano, opzionale): Aggiunge rtds-py-6 alla sezione. Default: non impostato
  • resultsGridClasses (stringa, opzionale): Classi Tailwind per la griglia risultati. Default: 'md:rtds-grid-cols-2 lg:rtds-grid-cols-3'

Parametri card

  • resultsCard.variant (stringa, opzionale): Suffisso variante della card da renderizzare (es. '--information-with-gradient', '--information-02', '--news-small'). Default: '--information-with-gradient'
  • resultsCard.context (oggetto, opzionale): Context aggiuntivo passato al componente @card

Parametri controlli

  • sorting (booleano, opzionale): Mostra i due @select-field--sm per ordinamento. Default: true
  • mobileFilters (booleano, opzionale): Mostra il pulsante “Filtra la ricerca” e il dialog @modal-dialog--filters. Default: non impostato
  • filtersSelected (booleano, opzionale): Mostra la barra @filters--v2-selected con i filtri attivi. Default: non impostato

Accessibilità

WCAG 2.1

Livello AA — criteri pertinenti:

  • 4.1.3 Messaggi di stato — aria-live="polite" sul contenitore .rtds-loading-status annuncia aggiornamenti risultati; aria-live="assertive" sul contenitore .rtds-empty-state annuncia immediatamente la mancanza di risultati
  • 4.1.2 Nome, Ruolo, Valore — aria-busy gestisce lo stato di caricamento; role="status" identifica l’area di stato; aria-controls, aria-haspopup, aria-expanded sul pulsante filtri mobile
  • 1.3.1 Informazioni e correlazioni — <h2> per il titolo della sezione risultati; gerarchia heading mantenuta in tutti gli stati

Struttura e semantica

  • .rtds-loading-status con aria-live="polite", aria-busy, role="status" — annuncia aggiornamenti risultati in modo non interruttivo
  • .rtds-empty-state con aria-live="assertive", aria-atomic="true" — annuncia immediatamente la transizione allo stato vuoto; è sempre presente nel DOM, nascosto con rtds-sr-only quando non attivo
  • <h2> con rtds-sr-only (se srOnlyTitle: true) — accessibile agli screen reader ma nascosto visivamente; affiancato da <span> con il contatore visuale
  • Dialog filtri mobile — @modal-dialog--filters gestisce internamente role="dialog", aria-modal, aria-labelledby e focus trap

Attributi ARIA

Attributo Valori Condizione
aria-live polite Su .rtds-loading-status — sempre presente
aria-busy true / false Su .rtds-loading-status — riflette il parametro ariaBusy
role status Su .rtds-loading-status — sempre presente
aria-live assertive Su .rtds-empty-state — sempre presente
aria-atomic true Su .rtds-empty-state — sempre presente
aria-controls "mobileFilters" Sul pulsante filtri mobile — quando mobileFilters: true
aria-haspopup dialog Sul pulsante filtri mobile — quando mobileFilters: true
aria-expanded true Sul pulsante filtri mobile — quando mobileFilters: true
Tasto Azione
Tab Raggiunge i select di ordinamento, il pulsante filtri mobile, le card, la paginazione
Enter / Space Apre il dialog filtri mobile (pulsante)
Escape Chiude il dialog filtri mobile (gestito da @modal-dialog--filters)

Gestione del focus

  • Dialog filtri mobile: il focus è intrappolato nel dialog all’apertura; ritorna al pulsante trigger alla chiusura — delegato a @modal-dialog--filters

Screen reader

  • Lo stato di caricamento: aria-busy="true" sull’elemento con role="status" — lo screen reader segnala che il contenuto è in aggiornamento
  • Transizione caricamento → risultati: aria-busy passa a false, aria-live="polite" annuncia il nuovo contatore risultati
  • Transizione verso empty state: .rtds-empty-state perde rtds-sr-only, aria-live="assertive" annuncia immediatamente il messaggio
  • Le card nella griglia delegano la propria accessibilità al componente @card usato

Stili CSS

Classi Base

  • .rtds-loading-status: Contenitore del titolo e contatore risultati; con [aria-busy="true"] si espande a rtds-w-full
  • .rtds-empty-state: Contenitore del messaggio stato vuoto; sempre nel DOM, nascosto con rtds-sr-only quando emptyState: false

Esempi di utilizzo

<!-- Stato predefinito: griglia 2–3 colonne con card information-with-gradient -->
<div class="rtds-space-y-6 md:rtds-space-y-8">
  <div class="rtds-space-y-4">
    <div class="rtds-loading-status" aria-live="polite" aria-busy="false" role="status">
      <h2 class="rtds-sr-only">Risultati di ricerca</h2>
      <span class="rtds-heading-3 rtds-font-normal"><strong>230</strong> Servizi</span>
    </div>
    <div class="rtds-empty-state rtds-grid rtds-gap-4 rtds-sr-only" aria-live="assertive" aria-atomic="true"></div>
    <div class="rtds-grid rtds-gap-4 rtds-grid-cols-1 md:rtds-grid-cols-2 lg:rtds-grid-cols-3">
      <!-- @card--information-with-gradient × resultsCount -->
    </div>
  </div>
  <!-- @pagination--centered -->
</div>

<!-- Stato di caricamento (ariaBusy: true) -->
<div class="rtds-loading-status" aria-live="polite" aria-busy="true" role="status">
  <div class="rtds-loading-results ...">
    <!-- @icon spinner -->
    <h2 class="rtds-heading-3">Caricamento risultati...</h2>
  </div>
</div>

<!-- Stato vuoto (emptyState: true) -->
<div class="rtds-empty-state rtds-grid rtds-gap-4" aria-live="assertive" aria-atomic="true">
  <!-- @icon outline--exclamation-circle -->
  <h3 class="rtds-heading-4">Non ci sono risultati per questi criteri di ricerca</h3>
</div>

Note

  • Il componente va inserito in un contesto di ricerca (tipicamente dopo un form con @search-box o @search-results-header)
  • I blocchi Nunjucks sovrascrivibili dai template figli (es. varianti loading, empty-state) sono: {% block filters %}, {% block title %}, {% block rightContent %}, {% block rightContentInner %}, {% block emptyState %}, {% block results %}, {% block pagination %}
  • Per gestire la transizione dinamica tra stati in produzione, aggiornare via JS aria-busy, la classe rtds-sr-only sull’elemento .rtds-empty-state e la visibilità della griglia risultati
  • Dipende da: @filters--v2-selected, @select-field--sm, @button--icon-left, @modal-dialog--filters, @card (+ variante), @pagination--centered