<div class="rtds-input-field rtds-gap-1  has-error">
    <label for="inputFileId" class="rtds-input-field__label">
        Text label

    </label>

    <div class="rtds-input has-error">

        <input id="inputFileId" class="rtds-text-sm rtds-input-placeholder rtds-pr-12" type="file" aria-describedBy="inputFileIdError" aria-invalid="true">

        <svg class="rtds-icon rtds-fill-current rtds-w-6 rtds-h-6 rtds-absolute rtds-right-4 rtds-content-placeholder rtds-content-error" aria-hidden="true" focusable="false" role="img">
            <use href="../../icons.svg#outline--exclamation-circle" />
        </svg>

    </div>

    <span class="rtds-input-field__hint">

        <span class="rtds-input-field__error" id="inputFileIdError" aria-live="assertive">
            il campo *nome campo* non può essere vuoto
        </span>

    </span>

</div>
{% extends '@input-field' %}
{
  "label": "Text label",
  "inputId": "inputFileId",
  "inputType": "file",
  "inputAriaDescribedError": "idInputErrorDescription",
  "errorDescription": "il campo *nome campo* non può essere vuoto",
  "inputHasErrorControl": true,
  "inputAriaInvalid": true,
  "inputIconAlert": true,
  "inputHasError": true
}
  • Content:
    /*
     * @file
     * Styles for the input file component.
     */
    :where(.rtds-input) input[type="file"] {
        width: 100%;
    }
    ::file-selector-button {
        font-size: var(--fontsize-text-lg);
        letter-spacing: 0.025em;
        transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
        transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
        display: inline-flex;
        justify-content: center;
        align-items: center;
        gap: var(--spacing-1);
        padding-left: var(--spacing-4);
        padding-right: var(--spacing-4);
        padding-top: var(--spacing-3);
        padding-bottom: var(--spacing-3);
        border-radius: 0.375rem;
        line-height: 1;
        font-weight: 700;
        transition-property: all;
        transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
        transition-duration: 200ms;
        transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
        border-width: 1px;
            background-color: var(--color-button-secondary);
            border-color: var(--color-button-secondary);
        --tw-text-opacity: 1;
        color: rgb(255 255 255 / var(--tw-text-opacity, 1));
    }
    ::file-selector-button:hover {
            background-color: var(--color-button-secondary-hover);
            border-color: var(--color-button-secondary-hover);
        }
    ::file-selector-button:focus {
            background-color: var(--color-button-secondary-hover);
            border-color: var(--color-button-secondary-hover);
        }
    ::file-selector-button:active {
            background-color: var(--color-button-secondary-active);
            border-color: var(--color-button-secondary-active);
        }
    [disabled]::file-selector-button {
            background-color: var(--color-background-02);
            color: var(--color-button-disabled);
            border-color: var(--color-background-02);
            cursor: not-allowed;
        }
    [aria-disabled="true"]::file-selector-button {
            background-color: var(--color-background-02);
            color: var(--color-button-disabled);
            border-color: var(--color-background-02);
            cursor: not-allowed;
        }
    .rtds-dialog__actions ::file-selector-button {
        flex: 1 1 0%;
    }
    @media (min-width: 1024px) {
        .rtds-dialog__actions ::file-selector-button {
            flex: none;
        }
    }
    ::file-selector-button {
        margin-right: var(--spacing-4);
        cursor: pointer;
        display: inline-flex;
        gap: var(--spacing-1);
        border-radius: 0.375rem;
        border-width: 1px;
        padding-top: var(--spacing-3);
        padding-bottom: var(--spacing-3);
        font-size: var(--fontsize-text-lg);
        font-weight: 700;
        line-height: 1;
        letter-spacing: 0.025em;
        transition-property: all;
        transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
        transition-duration: 200ms;
        transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
        justify-content: center;
        align-items: center;
        padding-left: var(--spacing-4);
        padding-right: var(--spacing-4);
    }
    /*:where(.rtds-input) input[type="file"]::before {
    }*/
    :where(.rtds-input--sm) ::file-selector-button {
        gap: var(--spacing-0-5);
        font-size: var(--fontsize-text-sm);
    }
    :where(.rtds-input--sm) :where(.rtds-btn--icon)::file-selector-button,:where(.rtds-input--sm) :where(.rtds-btn--icon-square)::file-selector-button {
        width: var(--spacing-6);
        height: var(--spacing-6);
    }
    :where(.rtds-input--sm) :where(.rtds-btn--icon-right)::file-selector-button,:where(.rtds-input--sm) :where(.rtds-btn--icon-left)::file-selector-button {
        gap: var(--spacing-1-5);
    }
    :where(.rtds-input--sm) :where(.rtds-btn--icon-right)::file-selector-button {
        padding-right: var(--spacing-3);
    }
    :where(.rtds-input--sm) :where(.rtds-btn--icon-left)::file-selector-button {
        padding-left: var(--spacing-3);
    }
    :where(.rtds-input--sm) :where(.rtds-btn--icon)::file-selector-button,:where(.rtds-input--sm) :where(.rtds-btn--icon-square)::file-selector-button {
        padding-left: var(--spacing-0);
        padding-right: var(--spacing-0);
    }
    :where(.rtds-input--lg) ::file-selector-button {
        padding-left: var(--spacing-6);
        padding-right: var(--spacing-6);
        font-size: var(--fontsize-text-xl);
        gap: var(--spacing-2);
        padding-top: var(--spacing-3);
        padding-bottom: var(--spacing-3);
    }
    :where(.rtds-input--lg) :where(.rtds-btn--icon)::file-selector-button,:where(.rtds-input--lg) :where(.rtds-btn--icon-square)::file-selector-button {
        height: var(--spacing-12);
        width: var(--spacing-12);
    }
    :where(.rtds-input--lg) :where(.rtds-btn--icon-right)::file-selector-button {
        padding-right: var(--spacing-4);
    }
    :where(.rtds-input--lg) :where(.rtds-btn--icon-left)::file-selector-button {
        padding-left: var(--spacing-4);
    }
    :where(.rtds-input--lg) :where(.rtds-btn--icon)::file-selector-button,:where(.rtds-input--lg) :where(.rtds-btn--icon-square)::file-selector-button {
        padding-left: var(--spacing-0);
        padding-right: var(--spacing-0);
    }
    
  • URL: /components/raw/input-file/input-file.built.css
  • Filesystem Path: components/03-molecules/forms/input-file/input-file.built.css
  • Size: 4.8 KB
  • Content:
    /*
     * @file
     * Styles for the input file component.
     */
    
    :where(.rtds-input) input[type="file"] {
        @apply rtds-w-full;
    }
    
    ::file-selector-button {
        @apply rtds-btn rtds-btn--secondary rtds-transition-all rtds-cursor-pointer rtds-mr-4 rtds-border-0;
    }
    
    /*:where(.rtds-input) input[type="file"]::before {
    }*/
    
    :where(.rtds-input--sm) ::file-selector-button {
        @apply rtds-btn--s;
    }
    
    :where(.rtds-input--lg) ::file-selector-button {
        @apply rtds-btn--l;
    }
  • URL: /components/raw/input-file/input-file.css
  • Filesystem Path: components/03-molecules/forms/input-file/input-file.css
  • Size: 464 Bytes

Input File

Campo di upload file. Estende @input-field con inputType: file, ereditandone struttura, label, messaggi di errore e gestione degli stati. Disponibile nelle dimensioni default e sm.


Comportamento

Il componente viene renderizzato come <div class="rtds-input-field"> contenente una <label> e un <input type="file">. Eredita tutti i comportamenti di @input-field: label visibile o nascosta, messaggi di errore con aria-live, descrizioni aggiuntive.

Il selettore file nativo del browser viene attivato al click sulla label o sull’input.


Varianti

Varianti di Stato

  • default: campo base con label
  • is-required: campo obbligatorio (aggiunge aria-required="true" e indicatore *)
  • has-description: con testo descrittivo aggiuntivo collegato tramite aria-describedby
  • has-error: con messaggio di errore e stato aria-invalid="true"
  • has-error-markup: variante per errori gestiti via markup esterno
  • has-error-and-description: combinazione di errore e descrizione

Varianti di Dimensione

  • default: dimensione standard
  • sm: dimensione ridotta (estende @input-field--sm)

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

Parametri Base

  • label (stringa, obbligatorio): testo della label
  • inputId (stringa, obbligatorio): ID univoco per collegare <label for> e <input id>
  • inputType (stringa): sempre "file" per questo componente

Parametri di Accessibilità

  • inputRequired (booleano, opzionale): aggiunge aria-required="true"
  • inputAriaDescribedBy (stringa, opzionale): ID dell’elemento con descrizione aggiuntiva
  • inputAriaDescribedError (stringa, opzionale): ID del messaggio di errore
  • inputAriaInvalid (booleano, opzionale): aggiunge aria-invalid="true" in stato di errore
  • inputHasErrorControl (booleano, opzionale): attiva la visualizzazione del messaggio di errore
  • inputHasError (booleano, opzionale): applica lo stile di errore al campo
  • inputIconAlert (booleano, opzionale): mostra icona di alert in stato di errore

Parametri Errore

  • errorDescription (stringa, opzionale): testo del messaggio di errore
  • inputDescription (stringa, opzionale): testo della descrizione aggiuntiva

Accessibilità

Documenta i requisiti WCAG 2.1 e il comportamento del markup HTML per tecnologie assistive.

WCAG 2.1

Livello AA — criteri pertinenti:

  • 1.3.1 Informazioni e correlazioni — <label> associata all’<input> tramite for / id
  • 3.3.1 Identificazione degli errori — errori comunicati tramite aria-invalid e aria-live="assertive"
  • 3.3.2 Etichette o istruzioni — label sempre presente, visibile o nascosta per screen reader
  • 4.1.2 Nome, ruolo, valore — aria-required, aria-describedby, aria-invalid comunicano lo stato

Struttura e semantica

  • Il componente è renderizzato come <input type="file"> all’interno di <div class="rtds-input-field">
  • La <label> è associata all’input tramite for="" e rimane disponibile anche se nascosta visivamente tramite rtds-sr-only
  • I messaggi di errore usano <span aria-live="assertive"> per l’annuncio immediato
  • Le descrizioni aggiuntive usano un elemento separato con ID distinto da quello dell’errore

Attributi ARIA

Attributo Valori Condizione
aria-required "true" quando inputRequired: true
aria-describedby ID descrizione e/o errore quando presenti inputAriaDescribedBy o inputAriaDescribedError
aria-invalid "true" quando inputAriaInvalid: true (stato di errore)
aria-live "assertive" sullo span del messaggio di errore

Screen reader

  • La label è sempre disponibile per screen reader, anche se nascosta visivamente
  • Il messaggio di errore viene annunciato immediatamente tramite aria-live="assertive" al cambio di stato
  • L’icona di alert è decorativa e nascosta agli screen reader
  • Il browser annuncia nativamente l’apertura del selettore file e il nome del file selezionato

Esempi di utilizzo

<!-- Esempio base -->
<div class="rtds-input-field">
  <label for="upload-cv">Carica il tuo CV</label>
  <input type="file" id="upload-cv" name="cv">
</div>

<!-- Esempio con errore -->
<div class="rtds-input-field rtds-input-field--has-error">
  <label for="upload-doc">Documento <abbr title="obbligatorio">*</abbr></label>
  <input type="file" id="upload-doc" name="doc"
         aria-required="true"
         aria-invalid="true"
         aria-describedby="upload-doc-error">
  <span id="upload-doc-error" aria-live="assertive" class="rtds-input-field__error">
    Il campo documento non può essere vuoto
  </span>
</div>

Note

  • Estende @input-field — fare riferimento al README di input-field per la documentazione completa della struttura
  • La dimensione sm estende @input-field--sm
  • Il formato e la dimensione massima dei file accettati devono essere indicati nel testo della label o nella descrizione (has-description) per rispettare il criterio WCAG 3.3.2