<nav class="rtds-pagination rtds-flex-wrap rtds-justify-center" role="navigation" aria-label="Paginazione">
    <ul class="rtds-pagination__list rtds-justify-center">

        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button " href="#" aria-label="Prima pagina">
                <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5" aria-hidden="true" focusable="false" role="img">
                    <use href="../../icons.svg#mini--chevron-double-left" />
                </svg>

            </a>
        </li>
        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button" href="#" aria-label="Pagina precedente">
                <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5" aria-hidden="true" focusable="false" role="img">
                    <use href="../../icons.svg#mini--chevron-left" />
                </svg>

            </a>
        </li>

        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text " href="#" aria-label="pagina 1">1</a>
        </li>

        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text  rtds-background-primary rtds-border-primary rtds-text-white hover:rtds-content-01" href="#" aria-label="Pagina corrente, pagina 2" aria-current="page">2</a>
        </li>

        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text " href="#" aria-label="pagina 3">3</a>
        </li>

        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text " href="#" aria-label="pagina 4">4</a>
        </li>

        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button" href="#" aria-label="Pagina successiva">
                <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5" aria-hidden="true" focusable="false" role="img">
                    <use href="../../icons.svg#mini--chevron-right" />
                </svg>

            </a>
        </li>
        <li>
            <a class="rtds-pagination__link rtds-pagination__link-button " href="#" aria-label="Ultima pagina">
                <svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5" aria-hidden="true" focusable="false" role="img">
                    <use href="../../icons.svg#mini--chevron-double-right" />
                </svg>

            </a>
        </li>

    </ul>

</nav>
<nav class="rtds-pagination rtds-flex-wrap{% block classes %}{% if classes %} {{ classes }}{% endif %}{% endblock %}{% if padding %} {{ padding }}{% endif %}{% if centered %} rtds-justify-center{% endif %}" role="navigation" aria-label="Paginazione">
    <ul class="rtds-pagination__list{% if centered %} rtds-justify-center{% endif %}">
        {% if hasLabels %}
            <li>
                <a class="rtds-pagination__link rtds-pagination__link-text rtds-py-1 rtds-px-2"{% if isFirstPage %} aria-disabled="true"{% endif %} href="">Precedente</a>
            </li>
        {% else %}
            <li>
                <a class="rtds-pagination__link rtds-pagination__link-button {% if linkClasses %} {{ linkClasses }}{%- endif -%}" href="#" aria-label="Prima pagina"{% if isFirstPage %} aria-disabled="true"{% endif %}>
                    {% render '@icon', { id: 'mini--chevron-double-left', size:'rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5'}, true %}
                </a>
            </li>
            <li>
                <a class="rtds-pagination__link rtds-pagination__link-button{% if linkClasses %} {{ linkClasses }}{%- endif -%}" href="#" aria-label="Pagina precedente"{% if isFirstPage %} aria-disabled="true"{% endif %}>
                    {% render '@icon', { id: 'mini--chevron-left', size:'rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5'}, true %}
                </a>
            </li>
        {% endif %}
        {% for item in items %}
            <li>
                <a class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text {% if linkClasses %} {{ linkClasses }}{%- endif -%}{% if item.isCurrent %} rtds-background-primary rtds-border-primary rtds-text-white hover:rtds-content-01{% endif %}" href="#" aria-label="{% if item.isCurrent %}Pagina corrente, {% endif %}pagina {{ item.label }}"{% if item.isCurrent %} aria-current="page"{% endif %}>{{ item.label }}</a>
            </li>
        {% endfor %}
        {% if hasLabels %}
            <li>
                <a class="rtds-pagination__link rtds-pagination__link-text rtds-py-1 rtds-px-2"{% if isLastPage %} aria-disabled="true"{% endif %} href="">Successiva</a>
            </li>
        {% else %}
            <li>
                <a class="rtds-pagination__link rtds-pagination__link-button{% if linkClasses %} {{ linkClasses }}{%- endif -%}" href="#" aria-label="Pagina successiva"{% if isLastPage %} aria-disabled="true"{% endif %}>
                    {% render '@icon', { id: 'mini--chevron-right', size:'rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5'}, true %}
                </a>
            </li>
            <li>
                <a class="rtds-pagination__link rtds-pagination__link-button {% if linkClasses %} {{ linkClasses }}{%- endif -%}" href="#" aria-label="Ultima pagina"{% if isLastPage %} aria-disabled="true"{% endif %}>
                    {% render '@icon', { id: 'mini--chevron-double-right', size:'rtds-w-4 rtds-h-5 md:rtds-w-5 md:rtds-h-5'}, true %}
                </a>
            </li>
        {% endif %}
    </ul>
    {% if totalText %}
        <p class="rtds-pagination__total rtds-px-4">{{ totalText }}</p>
    {% endif %}
    {% if perPageId %}
    <div class="rtds-pagination-per-page rtds-px-2{% if not centered %} rtds-ml-auto{% endif %}">
        <label for="{{ perPageId }}">Risultati per pagina</label>
        <div class="rtds-select rtds-select--sm">
            <select id="{{ perPageId }}" name="{{ perPageId }}">
                {% for opt in perPageOptions %}
                    <option value="{{ opt.value }}"{% if opt.value == perPageSelected %} selected{% endif %}>{{ opt.label }}</option>
                {% endfor %}
            </select>
        </div>
    </div>
    {% endif %}
</nav>
{
  "items": [
    {
      "label": 1
    },
    {
      "label": 2,
      "isCurrent": true
    },
    {
      "label": 3
    },
    {
      "label": 4
    }
  ],
  "isFirstPage": false,
  "isLastPage": false,
  "centered": true
}
  • Content:
    /**
     * PAGINATION
     *
    */
    .rtds-pagination {
        padding: var(--spacing-2);
        display: flex;
    }
    .rtds-pagination__list {
        flex-wrap: wrap;
        display: flex;
        gap: var(--spacing-2);
    }
    .rtds-pagination__link {
        height: var(--spacing-7);
        display: flex;
        justify-content: center;
        align-items: center;
        transition-property: all;
        transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
        transition-duration: 150ms;
    }
    @media (min-width: 768px) {
        .rtds-pagination__link {
            height: var(--spacing-8);
        }
    }
    .rtds-pagination__link-button {
        width: var(--spacing-7);
        border-radius: 0.25rem;
        --tw-bg-opacity: 1;
        background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
            border-color: var(--color-border-gray-01);
        border-width: 1px;
    }
    .rtds-pagination__link-button:hover {
            background-color: var(--color-background-02);
        }
    @media (min-width: 768px) {
        .rtds-pagination__link-button {
            width: var(--spacing-8);
        }
    }
    .rtds-pagination__link-text {
        font-size: var(--fontsize-text-xs);
        font-weight: 500;
    }
    .rtds-pagination__link-text:hover {
        text-decoration-line: underline;
    }
    @media (min-width: 768px) {
        .rtds-pagination__link-text {
            font-size: var(--fontsize-text-sm);
        }
    }
    .rtds-pagination--rounded .rtds-pagination__link-button {
        border-radius: 9999px;
    }
    .rtds-pagination-wrapper {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            justify-content: space-between;
            gap: var(--spacing-4);
        }
    .rtds-pagination-wrapper--centered {
            justify-content: center;
        }
    :where(.rtds-pagination__link)[aria-disabled="true"] {
            color: var(--color-content-disabled);
            background-color: var(--color-background-02);
            border-color: var(--color-background-03);
            cursor: default;
            pointer-events: none;
        }
    .rtds-pagination__total {
            font-size: var(--fontsize-text-sm);
            color: var(--color-content-03);
            align-self: center;
            margin: 0;
            padding: var(--spacing-2);
        }
    .rtds-pagination-per-page {
            display: flex;
            align-items: center;
            gap: var(--spacing-2);
        }
    
  • URL: /components/raw/pagination/pagination.built.css
  • Filesystem Path: components/03-molecules/pagination/pagination.built.css
  • Size: 2.2 KB
  • Content:
     /**
     * PAGINATION
     *
    */
    @layer components {
        .rtds-pagination {
            @apply  rtds-flex rtds-p-2;
        }
    
        .rtds-pagination__list {
            @apply rtds-flex rtds-gap-2 rtds-flex-wrap;
        }
    
        .rtds-pagination__link {
            @apply rtds-flex rtds-items-center rtds-justify-center rtds-h-7 md:rtds-h-8 rtds-transition-all;
        }
    
        .rtds-pagination__link-button {
            @apply rtds-w-7 md:rtds-w-8 rtds-border rtds-rounded rtds-border-gray-01 rtds-bg-white hover:rtds-background-02;
        }
    
        .rtds-pagination__link-text {
            @apply rtds-text-xs md:rtds-text-sm rtds-font-medium hover:rtds-underline;
        }
    
        .rtds-pagination--rounded .rtds-pagination__link-button {
            @apply rtds-rounded-full;
        }
    
        .rtds-pagination-wrapper {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            justify-content: space-between;
            gap: var(--spacing-4);
        }
    
        .rtds-pagination-wrapper--centered {
            justify-content: center;
        }
    
        :where(.rtds-pagination__link)[aria-disabled="true"] {
            color: var(--color-content-disabled);
            background-color: var(--color-background-02);
            border-color: var(--color-background-03);
            cursor: default;
            pointer-events: none;
        }
    
        .rtds-pagination__total {
            font-size: var(--fontsize-text-sm);
            color: var(--color-content-03);
            align-self: center;
            margin: 0;
            padding: var(--spacing-2);
        }
    
        .rtds-pagination-per-page {
            display: flex;
            align-items: center;
            gap: var(--spacing-2);
        }
    }
    
  • URL: /components/raw/pagination/pagination.css
  • Filesystem Path: components/03-molecules/pagination/pagination.css
  • Size: 1.6 KB

Paginazione

Il componente Paginazione gestisce la navigazione tra le pagine di un contenuto. Supporta diverse varianti di visualizzazione e offre una navigazione accessibile.


Comportamento

Il componente è sempre renderizzato come <nav role="navigation" aria-label="Paginazione"> contenente un <ul> con un <li> per ogni elemento. I link con sola icona (prima/ultima/precedente/successiva pagina) hanno aria-label descrittivo; i link con testo visibile (“Precedente”, “Successiva”) non usano aria-label — il testo è già il nome accessibile. La pagina corrente ha aria-current="page". Quando il componente si trova alla prima o ultima pagina, i link di navigazione corrispondenti ricevono aria-disabled="true".


Varianti

Varianti di Stile

  • pagination--rounded: Versione con bordi arrotondati
  • pagination--text: Versione con etichette testuali (“Precedente”/“Successiva”) invece di icone

Varianti di Stato

  • first-page: Prima pagina attiva — link “Prima pagina” e “Pagina precedente” con aria-disabled="true"
  • last-page: Ultima pagina attiva — link “Pagina successiva” e “Ultima pagina” con aria-disabled="true"

Varianti di Layout

  • centered: Paginazione centrata orizzontalmente
  • con-totale: Testo totale elementi inline nella nav (es. “100-110 di 300 elementi”)
  • con-per-page: Selettore “Risultati per pagina” dentro il <nav>, allineato a destra tramite rtds-ml-auto
  • con-totale-e-per-page: Combinazione totale elementi + selettore per-page, entrambi dentro il <nav>
  • con-totale-e-per-page-centered: Come sopra, con paginazione centrata

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

  • classes (stringa, opzionale): Classi CSS aggiuntive per il componente
  • padding (stringa, opzionale): Classi per il padding
  • centered (booleano, opzionale): Se true, centra la paginazione orizzontalmente
  • hasLabels (booleano, opzionale): Se true, usa etichette testuali (“Precedente”/“Successiva”) invece di icone
  • linkClasses (stringa, opzionale): Classi CSS aggiuntive per i link
  • isFirstPage (booleano, opzionale, default false): Se true, aggiunge aria-disabled="true" sui link di navigazione verso le pagine precedenti
  • isLastPage (booleano, opzionale, default false): Se true, aggiunge aria-disabled="true" sui link di navigazione verso le pagine successive
  • totalText (stringa, opzionale): Testo totale elementi (es. "100-110 di 300 elementi") mostrato inline nella nav; se assente nessun elemento renderizzato
  • items (array, obbligatorio): Elementi della paginazione
    • isCurrent (booleano, opzionale): Indica se l’elemento è la pagina corrente
    • label (stringa, obbligatorio): Etichetta dell’elemento

Parametri varianti con-per-page e con-totale-e-per-page

Tutti i parametri base, più:

  • perPageId (stringa, obbligatorio): ID usato per <label for> e <select id> — deve essere univoco nella pagina
  • perPageOptions (array, obbligatorio): Opzioni del selettore; ogni elemento ha:
    • value (stringa/numero): valore dell’<option>
    • label (stringa): testo visibile dell’<option>
  • perPageSelected (stringa/numero, opzionale): Valore dell’opzione selezionata

Accessibilità

WCAG 2.1

Livello AA — criteri pertinenti:

  • 1.3.1 Informazioni e correlazioni — la struttura è comunicata tramite <nav> e <ul> semanticamente corretti per la navigazione paginata
  • 2.4.8 Posizione — aria-current="page" sulla pagina corrente comunica la posizione nella paginazione
  • 4.1.2 Nome, ruolo, valore — ogni link icona ha un aria-label descrittivo; aria-current identifica la pagina corrente; aria-disabled comunica lo stato disabilitato

Struttura e semantica

  • Il componente è renderizzato come <nav role="navigation" aria-label="Paginazione"> — identifica la regione di navigazione e la distingue da altri elementi <nav> nella pagina
  • La lista è un <ul> con <li> per ogni elemento di paginazione
  • I link icona-only (chevron) hanno aria-label descrittivi (“Prima pagina”, “Pagina precedente”, “Pagina successiva”, “Ultima pagina”)
  • I link con testo visibile (“Precedente”, “Successiva”) non usano aria-label — il testo è già il nome accessibile

Attributi ARIA

Attributo Valori Condizione
aria-label "Paginazione" sempre presente sul <nav>
aria-label "Prima pagina", "Pagina precedente", "Pagina successiva", "Ultima pagina" solo sui link con icona (senza testo visibile)
aria-current "page" presente sull’<a> della pagina corrente
aria-disabled "true" su prima/prev quando isFirstPage: true; su next/ultima quando isLastPage: true
Tasto Azione
Tab / Shift+Tab Sposta il focus tra i link di paginazione
Enter Attiva il link corrente

Stato disabilitato (aria-disabled)

aria-disabled="true" viene usato invece di disabled perché l’attributo disabled non è valido su elementi <a>. Il link rimane focusabile e annuncia lo stato “disabilitato” agli screen reader. Il consumer deve gestire lato JS il blocco della navigazione quando aria-disabled è presente (es. event.preventDefault() sul click).

Gestione del focus

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

Screen reader

  • Ogni link icona viene annunciato con il proprio aria-label (es. “Prima pagina”, “Pagina precedente”)
  • I link con testo visibile (“Precedente”, “Successiva”) non hanno aria-label — il testo è già il nome accessibile; aggiungere un aria-label ridondante potrebbe divergere dal testo visibile
  • La pagina corrente è identificata da aria-current="page", che gli screen reader annunciano come “pagina corrente”
  • I link con aria-disabled="true" vengono annunciati come disabilitati ma rimangono nel tab order; il consumer è responsabile di prevenire la navigazione via JS o event.preventDefault()

Esempi di utilizzo

<!-- Esempio base -->
<nav class="rtds-pagination rtds-flex-wrap" role="navigation" aria-label="Paginazione">
  <ul class="rtds-pagination__list">
    <li>
      <a href="#" aria-label="Prima pagina" class="rtds-pagination__link rtds-pagination__link-button">
        <svg class="rtds-icon" aria-hidden="true" focusable="false"><use href="#mini--chevron-double-left"></use></svg>
      </a>
    </li>
    <li>
      <a href="#" aria-label="Pagina precedente" class="rtds-pagination__link rtds-pagination__link-button">
        <svg class="rtds-icon" aria-hidden="true" focusable="false"><use href="#mini--chevron-left"></use></svg>
      </a>
    </li>
    <li>
      <a href="#" aria-label="pagina 1" class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text">1</a>
    </li>
    <li>
      <a href="#" aria-label="Pagina corrente, pagina 2" aria-current="page" class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text rtds-background-primary rtds-border-primary rtds-text-white">2</a>
    </li>
    <li>
      <a href="#" aria-label="pagina 3" class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text">3</a>
    </li>
    <li>
      <a href="#" aria-label="Pagina successiva" class="rtds-pagination__link rtds-pagination__link-button">
        <svg class="rtds-icon" aria-hidden="true" focusable="false"><use href="#mini--chevron-right"></use></svg>
      </a>
    </li>
    <li>
      <a href="#" aria-label="Ultima pagina" class="rtds-pagination__link rtds-pagination__link-button">
        <svg class="rtds-icon" aria-hidden="true" focusable="false"><use href="#mini--chevron-double-right"></use></svg>
      </a>
    </li>
  </ul>
</nav>

<!-- Esempio: prima pagina (link prev/first disabilitati) -->
<nav class="rtds-pagination rtds-flex-wrap" role="navigation" aria-label="Paginazione">
  <ul class="rtds-pagination__list">
    <li>
      <a href="#" aria-label="Prima pagina" aria-disabled="true" class="rtds-pagination__link rtds-pagination__link-button">
        <svg class="rtds-icon" aria-hidden="true" focusable="false"><use href="#mini--chevron-double-left"></use></svg>
      </a>
    </li>
    <li>
      <a href="#" aria-label="Pagina precedente" aria-disabled="true" class="rtds-pagination__link rtds-pagination__link-button">
        <svg class="rtds-icon" aria-hidden="true" focusable="false"><use href="#mini--chevron-left"></use></svg>
      </a>
    </li>
    <li>
      <a href="#" aria-label="Pagina corrente, pagina 1" aria-current="page" class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text rtds-background-primary rtds-border-primary rtds-text-white">1</a>
    </li>
    <li>
      <a href="#" aria-label="pagina 2" class="rtds-pagination__link rtds-pagination__link-button rtds-pagination__link-text">2</a>
    </li>
    <!-- ... -->
  </ul>
</nav>

<!-- Esempio: variante con totale elementi -->
<nav class="rtds-pagination rtds-flex-wrap" role="navigation" aria-label="Paginazione">
  <ul class="rtds-pagination__list">
    <!-- ... -->
  </ul>
  <p class="rtds-pagination__total rtds-px-4">100-110 di 300 elementi</p>
</nav>

<!-- Esempio: variante con selettore per-page (per-page dentro nav, allineato a destra) -->
<nav class="rtds-pagination rtds-flex-wrap" role="navigation" aria-label="Paginazione">
  <ul class="rtds-pagination__list">
    <!-- ... -->
  </ul>
  <div class="rtds-pagination-per-page rtds-px-2 rtds-ml-auto">
    <label for="per-page-select">Risultati per pagina</label>
    <div class="rtds-select rtds-select--sm">
      <select id="per-page-select" name="per-page-select">
        <option value="10" selected>10</option>
        <option value="25">25</option>
        <option value="50">50</option>
      </select>
    </div>
  </div>
</nav>

<!-- Esempio: variante con totale + per-page -->
<nav class="rtds-pagination rtds-flex-wrap" role="navigation" aria-label="Paginazione">
  <ul class="rtds-pagination__list">
    <!-- ... -->
  </ul>
  <p class="rtds-pagination__total rtds-px-4">100-110 di 300 elementi</p>
  <div class="rtds-pagination-per-page rtds-px-2 rtds-ml-auto">
    <label for="per-page-select-totale">Risultati per pagina</label>
    <div class="rtds-select rtds-select--sm">
      <select id="per-page-select-totale" name="per-page-select-totale">
        <option value="10" selected>10</option>
        <option value="25">25</option>
        <option value="50">50</option>
      </select>
    </div>
  </div>
</nav>