<div class="rtds-upload rtds-w-full rtds-space-y-5 md:rtds-space-y-6">
    <!-- Pulsante principale per il caricamento -->
    <div class="rtds-upload__button-area rtds-flex rtds-flex-col rtds-p-4 rtds-items-center rtds-justify-center rtds-gap-4 md:rtds-gap-6 rtds-background-01 rtds-border-dashed rtds-border-gray-03 rtds-border-2 rtds-rounded-md">
        <div class="rtds-upload__description rtds-space-y-1.5">
            <!-- 
            note per accessibilità: il seguente paragrafo NON fornisce informazioni aggiuntive chi usa tecnologie assistive
            così come è formulato è riferito all'esperienza utente che usa un mouse ecc (non tastiera o screen reader).
            Inoltre fornisce indicazioni per la localizzazione del pulsante tramite posizionamento del pulsante stesso ("clicca sul pulsante qui sotto")
            violando così il criterio di successo WCAG 1.3.3 -->

            <p class="rtds-upload__help">Clicca il pulsante qui sotto per caricare i documenti dal tuo dispositivo.</p>
            <!-- note per accessibilità: il seguente paragrafo fornisce informazioni aggiuntive chi usa tecnologie assistive
             attraverso l'uso dell'id e dell'attributo aria-describedby="id-del-paragrafo" sull'input file-->
            <p id="upload-help-text" class="rtds-upload__help">Formati <strong>PDF, DOCX, JPG, PNG</strong> - Max: <strong>10 MB per file</strong></p>
        </div>
        <input type="file" id="upload-input-file-upload-default" class="rtds-upload__input rtds-sr-only" aria-describedby="upload-help-text" multiple accept="image/*,.pdf,.doc,.docx">
        <label for="upload-input-file-upload-default" class="rtds-upload__button rtds-btn rtds-btn--outlined rtds-content-01 rtds-border-gray-03 rtds-cursor-pointer">
            <svg class="rtds-icon rtds-fill-current rtds-w-5 rtds-h-5" aria-hidden="true" focusable="false" role="img">
                <use href="../../icons.svg#outline--arrow-up-on-square" />
            </svg>

            Carica file
        </label>
    </div>

    <!-- Lista dei file caricati/in caricamento -->

</div>
<div class="rtds-upload rtds-w-full rtds-space-y-5 md:rtds-space-y-6">
  <!-- Pulsante principale per il caricamento -->
  <div class="rtds-upload__button-area rtds-flex rtds-flex-col rtds-p-4 rtds-items-center rtds-justify-center rtds-gap-4 md:rtds-gap-6 rtds-background-01 rtds-border-dashed rtds-border-gray-03 rtds-border-2 rtds-rounded-md">
      <div class="rtds-upload__description rtds-space-y-1.5">
            <!-- 
            note per accessibilità: il seguente paragrafo NON fornisce informazioni aggiuntive chi usa tecnologie assistive
            così come è formulato è riferito all'esperienza utente che usa un mouse ecc (non tastiera o screen reader).
            Inoltre fornisce indicazioni per la localizzazione del pulsante tramite posizionamento del pulsante stesso ("clicca sul pulsante qui sotto")
            violando così il criterio di successo WCAG 1.3.3 -->

            <p class="rtds-upload__help">Clicca il pulsante qui sotto per caricare i documenti dal tuo dispositivo.</p>
            <!-- note per accessibilità: il seguente paragrafo fornisce informazioni aggiuntive chi usa tecnologie assistive
             attraverso l'uso dell'id e dell'attributo aria-describedby="id-del-paragrafo" sull'input file-->
            <p id="upload-help-text" class="rtds-upload__help">Formati <strong>PDF, DOCX, JPG, PNG</strong> - Max: <strong>10 MB per file</strong></p>
      </div>
      <input 
      type="file" 
      id="upload-input-{{ 'file-upload-' + (loop.index if loop.index is defined else 'default') }}"
      class="rtds-upload__input rtds-sr-only" 
      aria-describedby="upload-help-text"
      {% if multiple %}multiple{% endif %}
      {% if accept %}accept="{{ accept }}"{% endif %}
    >
    <label for="upload-input-{{ 'file-upload-' + (loop.index if loop.index is defined else 'default') }}" class="rtds-upload__button rtds-btn rtds-btn--outlined rtds-content-01 rtds-border-gray-03 rtds-cursor-pointer">
      {% render '@icon', {
        id: 'outline--arrow-up-on-square',
        size: 'rtds-w-5 rtds-h-5'
      }, true %}
      {{ label or 'Carica file' }}
    </label>
  </div>

  <!-- Lista dei file caricati/in caricamento -->
  {% if files and files.length > 0 %}
    <ul class="rtds-upload__file-list">
      {% for file in files %}
        <li class="rtds-upload__file">
          {% render '@file' + file.variant, file.context, true %}
        </li>
      {% endfor %}
    </ul>
  {% endif %}
</div>
{
  "label": "Carica file",
  "multiple": true,
  "accept": "image/*,.pdf,.doc,.docx",
  "helpText": "Formato supportati: JPG, PNG, PDF, DOC, DOCX. Dimensione massima: 10MB per file.",
  "maxFileSize": "10MB",
  "files": []
}
  • Content:
    /**
     * UPLOAD COMPONENT
     * Componente per il caricamento file
     */
    .rtds-upload__button:focus-visible {
        --tw-shadow: 0 0 0 7px rgba(255,255,255,1);
        --tw-shadow-colored: 0 0 0 7px var(--tw-shadow-color);
        box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
        outline: 2px solid transparent;
        outline-offset: 2px;
        --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
        --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
        box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
        --tw-ring-color: var(--color-focusring);
        --tw-ring-offset-width: 2px;
        --tw-ring-offset-color: #ffffff;
    }
    .rtds-upload__input:focus + .rtds-upload__button,
      .rtds-upload__input:focus-visible + .rtds-upload__button {
        --tw-shadow: 0 0 0 7px rgba(255,255,255,1);
        --tw-shadow-colored: 0 0 0 7px var(--tw-shadow-color);
        box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
        outline: 2px solid transparent;
        outline-offset: 2px;
        --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
        --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
        box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
        --tw-ring-color: var(--color-focusring);
        --tw-ring-offset-width: 2px;
        --tw-ring-offset-color: #ffffff;
    }
    .rtds-upload__description {
        text-align: center;
        font-size: var(--fontsize-text-sm);
            color: var(--color-content-03);
    }
    @media (min-width: 768px) {
        .rtds-upload__description {
            font-size: var(--fontsize-text-base);
        }
    }
    /* Lista file - Struttura ul/li */
    .rtds-upload__file-list {
        margin: var(--spacing-0);
        list-style-type: none;
    }
    .rtds-upload__file-list > :not([hidden]) ~ :not([hidden]) {
        --tw-space-y-reverse: 0;
        margin-top: calc(var(--spacing-3) * calc(1 - var(--tw-space-y-reverse)));
        margin-bottom: calc(var(--spacing-3) * var(--tw-space-y-reverse));
    }
    .rtds-upload__file-list {
        padding: var(--spacing-0);
    }
    
  • URL: /components/raw/upload/upload.built.css
  • Filesystem Path: components/08-servizi/upload/upload.built.css
  • Size: 2.3 KB
  • Content:
    /**
     * UPLOAD COMPONENT
     * Componente per il caricamento file
     */
    
    @layer components {
    
      .rtds-upload__button {
        @apply focus-visible:rtds-ring focus-visible:rtds-ring-offset-2 focus-visible:rtds-ring-offset-white focus-visible:rtds-ring-focusring focus-visible:rtds-shadow-[0_0_0_7px_rgba(255,255,255,1)] focus-visible:rtds-outline-none;
      }
    
      .rtds-upload__input:focus + .rtds-upload__button,
      .rtds-upload__input:focus-visible + .rtds-upload__button {
        @apply rtds-ring rtds-ring-offset-2 rtds-ring-offset-white rtds-ring-focusring rtds-shadow-[0_0_0_7px_rgba(255,255,255,1)] rtds-outline-none;
      }
    
      .rtds-upload__description {
        @apply rtds-text-sm md:rtds-text-base rtds-text-center rtds-content-03;
      }
    
    
      /* Lista file - Struttura ul/li */
      .rtds-upload__file-list {
        @apply rtds-list-none rtds-p-0 rtds-m-0 rtds-space-y-3;
      }
    }
  • URL: /components/raw/upload/upload.css
  • Filesystem Path: components/08-servizi/upload/upload.css
  • Size: 854 Bytes

Componente Upload File

Componente per il caricamento di file basato sul Design System di Designers Italia, variante “Lista file” con rappresentazione statica degli stati.

Descrizione

Il componente Upload è un organismo che permette la rappresentazione dell’interfaccia di caricamento file con una struttura semantica ul/li seguendo esattamente le specifiche di Designers Italia. Il componente mostra diversi stati dei file (successo, caricamento, errore) in modo puramente visuale.

Quando Usarlo

Utilizza questo componente quando è necessario mostrare l’interfaccia di caricamento file, come:

  • Form di registrazione o profili utente per foto del profilo
  • Richieste di servizi online che prevedono allegati
  • Piattaforme di condivisione contenuti
  • Form di supporto tecnico con screenshot
  • Qualsiasi contesto che richiede raccolta di documenti

Caratteristiche Principali

Struttura HTML

  • Lista semantica: Utilizza <ul class="upload-file-list"> e <li class="upload-file">
  • Accessibilità: Testo nascosto con rtds-sr-only per screen reader
  • Stati visivi: Classi per success, uploading, error
  • Icone SVG: Supporto per sprite SVG

Stati Supportati (tramite componente File)

  • loading: File in caricamento con progress bar e pulsante cancella
  • loading-error: Errore durante caricamento con icona errore e messaggio
  • loading-completed: Caricamento completato con icona check
  • download: File disponibile con pulsanti scarica ed elimina

Componente File

Il componente Upload utilizza il componente File separato (@file) per gestire la rappresentazione di ogni singolo file. Consulta la documentazione del componente File per maggiori dettagli sui parametri e comportamenti specifici.

Comportamento

Il componente è puramente rappresentativo e non include logica JavaScript. Mostra stati statici definiti nella configurazione. La lista dei file utilizza il componente separato @file per ogni elemento.

Ogni file viene renderizzato tramite il componente File che supporta i seguenti stati:

  • loading: File in caricamento
  • loading-error: Errore durante caricamento
  • loading-completed: Caricamento completato
  • download: File disponibile per download

Accessibilità

WCAG 2.1

Livello AA — criteri pertinenti:

  • 4.1.2 Nome, ruolo, valore — l’<input type="file"> è associato al suo <label> tramite for/id; aria-describedby collega l’input al testo di aiuto con le specifiche sui formati
  • 1.3.1 Informazioni e correlazioni — la lista dei file è un <ul> semantico; ogni file è un <li> che avvolge il componente @file
  • 1.3.3 Caratteristiche sensoriali — il testo “Clicca il pulsante qui sotto per caricare” contiene un riferimento al posizionamento (“qui sotto”), in violazione parziale del criterio; questo è documentato nel template come nota di sviluppo

Struttura e semantica

  • Il componente è renderizzato come <div class="rtds-upload"> — contenitore non semantico
  • L’<input type="file"> è nascosto visivamente con rtds-sr-only — è ancora raggiungibile da tastiera; il <label> funge da pulsante cliccabile visibile
  • Il <label> è associato all’input tramite for/id — attivare il label con Enter o Space apre il selettore file
  • La lista dei file caricati è un <ul class="rtds-upload__file-list"> — ogni elemento è un <li> che contiene il componente @file

Attributi ARIA

Attributo Valori Condizione
aria-describedby upload-help-text Sull’<input type="file"> — collega le specifiche sui formati e dimensioni accettati
id="upload-help-text" stringa Sul <p> con le specifiche — referenziato da aria-describedby

Per gli attributi ARIA dei componenti file nella lista (progress bar, stato), consultare la documentazione del componente @file.

Tasto Azione
Tab / Shift+Tab Raggiunge il pulsante/label di caricamento e i pulsanti azione di ogni file nella lista
Enter / Space Attiva il label di caricamento (apre il selettore file) o i pulsanti azione dei file

Gestione del focus

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

Screen reader

  • Lo screen reader annuncia il label di caricamento come il testo del pulsante (es. “Carica file”) e lo associa all’input di tipo file
  • Quando il focus è sull’<input>, lo screen reader legge anche il testo di aria-describedby (“Formati PDF, DOCX, JPG, PNG - Max: 10 MB per file”) — fornisce informazioni sui vincoli senza doverli cercare visivamente
  • La lista dei file usa elementi <li> semantici — lo screen reader annuncia il numero di file nella lista quando vi entra
  • Il testo “Clicca il pulsante qui sotto per caricare i documenti dal tuo dispositivo” non fornisce informazioni aggiuntive agli utenti di tecnologie assistive (non identifica il pulsante tramite nome, ruolo o posizione accessibile)

Configurazioni - Sviluppo in Nunjucks

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 da usare nei temi o ambienti di implementazione.

Parametri Principali

label (string)

  • Descrizione: Testo del pulsante di caricamento
  • Default: “Carica file”
  • Esempio: label: "Carica documenti"

multiple (boolean)

  • Descrizione: Permette caricamento multiplo
  • Default: true
  • Esempio: multiple: false

accept (string)

  • Descrizione: Tipi di file accettati
  • Default: "image/*,.pdf,.doc,.docx"
  • Esempi:
    • Solo immagini: "image/*"
    • Solo PDF: ".pdf"
    • Documenti: ".pdf,.doc,.docx,.txt"

helpText (string)

  • Descrizione: Testo di aiuto sotto il pulsante
  • Default: Informazioni sui formati supportati
  • Esempio: helpText: "Dimensione massima: 5MB per file"

maxFileSize (string)

  • Descrizione: Dimensione massima per file (solo informativo)
  • Default: "10MB"
  • Esempi: "5MB", "2GB", "500KB"

files (array)

  • Descrizione: Lista di file da mostrare (utilizza componente File)
  • Struttura:
    files:
      - name: "documento.pdf"
        size: "1.2MB"
        status: "loading-completed" # loading, loading-error, loading-completed, download
        progress: 100 # obbligatorio per stati loading
        errorMessage: "Messaggio di errore" # solo per loading-error

Varianti Disponibili

1. Default

context:
  label: 'Carica documenti'
  • Componente base senza file mostrati

2. Con File in Diversi Stati

context:
  files:
    - name: 'documento-caricamento.pdf'
      size: '2.1MB'
      status: 'loading'
      progress: 65
    - name: 'file-completato.jpg'
      size: '850KB'
      status: 'loading-completed'
      progress: 100
    - name: 'file-errore.pdf'
      size: '15MB'
      status: 'loading-error'
      progress: 30
      errorMessage: 'File troppo grande. Max 10MB'
    - name: 'documento-download.docx'
      size: '2.1MB'
      status: 'download'
  • Mostra tutti gli stati del componente File

Stili CSS

Classi Principali

  • .rtds-upload: Container principale
  • .rtds-upload__button-area: Area pulsante di caricamento
  • .rtds-upload__button: Pulsante di caricamento
  • .rtds-upload__file-list: Lista file (ul)

Le classi per i singoli file sono gestite dal componente File:

  • .rtds-upload__file: Singolo file (su elemento <li> che wrappa il componente rtds-file)
  • .rtds-file.is-loading: File in caricamento
  • .rtds-file.is-loading-error: File con errore
  • .rtds-file.is-loading-completed: File completato
  • .rtds-file.is-download: File per download

Responsive

Il componente è completamente responsive:

  • Desktop: Layout orizzontale con icona, testo e pulsante
  • Mobile: Layout verticale compatto

Note di Sviluppo

Dipendenze

  • Nunjucks: Per il templating
  • CSS: Framework Tailwind con prefisso rtds-
  • SVG Sprites: Icone tramite sprite system

Limiti Attuali

  • Solo rappresentazione statica: Nessuna logica JavaScript
  • Icone fisse: Usa sempre icona generica “file”
  • SVG Sprites: Richiede sprite system configurato

Best Practices

UX

  1. Stati chiari: Usa colori e icone distintive per ogni stato
  2. Messaggi specifici: Errori descrittivi e comprensibili
  3. Accessibilità: Sempre testo nascosto per screen reader
  4. Consistenza: Mantieni lo stesso pattern per tutti gli stati

Accessibilità

  1. Testo nascosto: Usa rtds-sr-only per contesto
  2. Semantica: Lista HTML per struttura logica
  3. Focus: Pulsanti accessibili da tastiera
  4. ARIA: Attributi appropriati per progress bar

Contenuto

  1. Nomi file: Mantieni nomi descrittivi ma non troppo lunghi
  2. Dimensioni: Mostra sempre la dimensione del file
  3. Stati: Usa terminologia coerente (completato, caricamento, errore)
  4. Messaggi: Errori specifici e azionabili