<div class="rtds-input-field rtds-input-field--password rtds-gap-1">
    <label for="passwordId" class="rtds-input-field__label">
        Password

        <abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr>

    </label>
    <div class="rtds-input-field__wrapper">
        <div class="rtds-input">
            <input id="passwordId" type="password" class="rtds-text-sm rtds-input-placeholder rtds-input--password" autocomplete="current-password" aria-required="true">
        </div>

        <button type="button" aria-pressed="false" class="rtds-input-field__toggle-btn">
            <svg class="rtds-icon rtds-fill-current rtds-w-5 rtds-h-5 rtds-input-field__toggle-icon" aria-hidden="true" focusable="false" role="img">
                <use href="../../icons.svg#outline--eye" />
            </svg>

            <span class="rtds-sr-only">Mostra password</span>
        </button>
    </div>

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

        <span class="rtds-input-field__error" id="passwordIdError" aria-live="assertive">

        </span>

    </span>

    <span class="rtds-sr-only" aria-live="assertive" aria-atomic="true"></span>
</div>
<div class="rtds-input-field rtds-input-field--password rtds-gap-1{% if classes %} {{ classes }}{% endif %}">
  <label for="{{ inputId }}" class="rtds-input-field__label">
    {{ label }}
    {% if inputRequired %}
      <abbr class="rtds-font-bold rtds-no-underline" title="obbligatorio">*</abbr>
    {% endif %}
  </label>
  <div class="rtds-input-field__wrapper">
    {% render '@input--password', {
      id: inputId,
      autocomplete: inputAutocomplete,
      fieldRequired: inputRequired,
      ariaDescribedBy: inputAriaDescribedBy,
      disabled: inputDisabled
    }, true %}
    <button type="button" aria-pressed="false" class="rtds-input-field__toggle-btn">
      {% render '@icon', {id: 'outline--eye', classes: 'rtds-input-field__toggle-icon', size: 'rtds-w-5 rtds-h-5'}, true %}
      <span class="rtds-sr-only">Mostra password</span>
    </button>
  </div>
  {% if inputRequired or inputAriaDescribedBy %}
    <span class="rtds-input-field__hint">
      {% if inputRequired %}
        <span class="rtds-input-field__error" id="{{ inputId }}Error" aria-live="assertive">
          {{ errorDescription }}
        </span>
      {% endif %}
      {% if inputDescription %}
        <span class="rtds-input-field__description" id="{{ inputAriaDescribedBy }}">
          {{ inputDescription }}
        </span>
      {% endif %}
    </span>
  {% endif %}
  <span class="rtds-sr-only" aria-live="assertive" aria-atomic="true"></span>
</div>
{
  "label": "Password",
  "inputId": "passwordId",
  "inputAutocomplete": "current-password",
  "inputRequired": true
}
  • Content:
    /**
     * INPUT FIELD
     *
    */
    .rtds-input-field {
        display: grid;
        align-content: flex-start;
    }
    .rtds-input-field__label {
        display: block;
        font-size: var(--fontsize-text-sm);
            color: var(--color-content-03);
        font-weight: 700;
    }
    legend:where(.rtds-input-field__label) {
        margin-bottom: var(--spacing-1);
    }
    .rtds-input-field__hint {
        display: grid;
    }
    .rtds-input-field__error {
        font-size: var(--fontsize-text-sm);
        font-weight: 500;
            color: var(--color-content-error);
    }
    .rtds-input-field__description {
        font-size: var(--fontsize-text-sm);
        font-weight: 500;
            color: var(--color-content-03);
    }
    .rtds-input-field.has-error input {
        border-width: 2px;
        border-color: var(--color-error-dark);
    }
    /* DISABLED STATE */
    :where(.rtds-input-field--disabled) .rtds-input-field__label,
        :where(.rtds-input-field--disabled) .rtds-input-field__description {
            color: var(--color-content-disabled);
        }
    /* INPUT FIELD INLINE */
    .rtds-field-inline {
        display: flex;
        align-items: center;
        gap: var(--spacing-2);
    }
    .rtds-field-inline .rtds-input,
        .rtds-field-inline .rtds-select {
        flex: 1 1 0%;
    }
    
  • URL: /components/raw/input-field/input-field.built.css
  • Filesystem Path: components/03-molecules/forms/input-field/input-field.built.css
  • Size: 1.2 KB
  • Content:
    /**
     * INPUT FIELD
     *
    */
    @layer components {
        .rtds-input-field {
            @apply rtds-grid rtds-content-start;
        }
    
        .rtds-input-field__label {
            @apply rtds-block rtds-text-sm rtds-font-bold rtds-content-03;
        }
    
        legend:where(.rtds-input-field__label) {
            @apply rtds-mb-1;
        }
    
        .rtds-input-field__hint {
            @apply rtds-grid;
        }
    
        .rtds-input-field__error {
            @apply rtds-text-sm rtds-font-medium rtds-content-error;
        }
    
        .rtds-input-field__description {
            @apply rtds-text-sm rtds-font-medium rtds-content-03;
        }
    
        .rtds-input-field.has-error input {
            @apply rtds-border-2 rtds-border-error-dark;
        }
    
        /* DISABLED STATE */
        :where(.rtds-input-field--disabled) .rtds-input-field__label,
        :where(.rtds-input-field--disabled) .rtds-input-field__description {
            color: var(--color-content-disabled);
        }
    
        /* INPUT FIELD INLINE */
        .rtds-field-inline {
            @apply rtds-flex rtds-items-center rtds-gap-2;
        }
    
        .rtds-field-inline .rtds-input,
        .rtds-field-inline .rtds-select {
            @apply rtds-flex-1;
        }
    }
  • URL: /components/raw/input-field/input-field.css
  • Filesystem Path: components/03-molecules/forms/input-field/input-field.css
  • Size: 1.1 KB
  • Content:
    window.addEventListener('load', function () {
      var fields = document.querySelectorAll('.rtds-input-field--password');
      if (fields.length === 0) return;
    
      fields.forEach(function (field) {
        var input = field.querySelector('input[type="password"], input[type="text"]');
        var btn = field.querySelector('.rtds-input-field__toggle-btn');
        var liveRegion = field.querySelector('[aria-live][aria-atomic]');
        var icon = btn ? btn.querySelector('use') : null;
    
        if (!input || !btn) return;
    
        btn.addEventListener('click', function () {
          var isVisible = input.type === 'text';
          input.type = isVisible ? 'password' : 'text';
          btn.setAttribute('aria-pressed', isVisible ? 'false' : 'true');
          if (liveRegion) {
            liveRegion.textContent = isVisible ? 'Password nascosta' : 'Password visibile';
          }
          if (icon) {
            var currentHref = icon.getAttribute('href') || '';
            var base = currentHref.replace(/#.*$/, '');
            icon.setAttribute('href', base + (isVisible ? '#outline--eye' : '#outline--eye-slash'));
          }
        });
      });
    });
    
  • URL: /components/raw/input-field/input-field.js
  • Filesystem Path: components/03-molecules/forms/input-field/input-field.js
  • Size: 1.1 KB

Input Field

Componente composito per campi di input. Include un componente input personalizzabile con supporto completo per l’accessibilità e la validazione.


Comportamento

Il componente Input Field gestisce:

  • Gestione delle etichette (visibili o nascoste)
  • Supporto per messaggi di errore
  • Descrizioni del campo
  • Icone di placeholder e alert
  • Validazione dei campi
  • Supporto per l’autocompletamento
  • Gestione degli stati di errore

Varianti

Varianti di Stile

  • default: Input di tipo testo base
  • search: Input di ricerca con icona di lente d’ingrandimento

Varianti di Stato

  • has-error-markup: Input con markup per la gestione degli errori
  • is-required: Input obbligatorio
  • has-description: Input con descrizione
  • has-error: Input con stato di errore
  • has-error-and-description: Input con stato di errore e descrizione
  • disabled: Campo disabilitato — label e campo visivamente muted
  • readonly: Campo non modificabile — nessuno stile aggiuntivo, label invariata

Varianti Speciali

  • password: Campo password con toggle mostra/nascondi accessibile — usa il template input-field--password.njk

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

  • label (stringa, obbligatorio): Testo dell’etichetta del campo
  • inputId (stringa, obbligatorio): ID univoco per il campo input
  • labelClasses (stringa, opzionale): Classi CSS aggiuntive per l’etichetta
  • hiddenLabel (booleano, opzionale): Se true, nasconde visivamente l’etichetta (rimane disponibile per screen reader)
  • inputShortLabel (stringa, opzionale): Versione abbreviata dell’etichetta per la gestione degli errori JavaScript

Parametri di Stile e Layout

  • gap (stringa, opzionale): Personalizzazione dello spazio interno (default: gap-1)
  • inputPadding (stringa, opzionale): Override del padding dell’input
  • inputBorder (stringa, opzionale): Override del bordo dell’input
  • inputRadius (stringa, opzionale): Override del border-radius dell’input
  • inputClasses (stringa, opzionale): Classi CSS aggiuntive per l’input

Parametri Funzionalità Input

  • inputType (stringa, opzionale): Tipo di input (text, tel, email, search, ecc.)
  • inputPlaceholder (stringa, opzionale): Testo del placeholder (da usare con moderazione)
  • inputRequired (booleano, opzionale): Se true, il campo è obbligatorio
  • inputAutocomplete (stringa, opzionale): Valore per l’autocompletamento (riferimento: MDN Web Docs)

Parametri di Accessibilità

  • inputAriaDescribedBy (stringa, opzionale): ID dell’elemento che descrive il campo
  • inputDescription (stringa, opzionale): Descrizione testuale del campo
  • inputAriaInvalid (booleano, opzionale): Indica se il campo contiene un errore

Parametri di Gestione Errori

  • inputHasErrorControl (booleano, opzionale): Se il campo necessita di validazione
  • errorDescription (stringa, opzionale): Descrizione dell’errore
  • inputIconAlert (booleano, opzionale): Se true, mostra l’icona di errore (⚠) a destra dell’input. Per inputType con picker nativo (date, time, datetime-local, month, week) l’icona si posiziona automaticamente a sinistra del controllo nativo del browser
  • inputHasError (booleano, opzionale): Stile di errore per l’input

Parametri di Stato

  • inputDisabled (booleano, opzionale): Se true, aggiunge disabled all’input e la classe rtds-input-field--disabled al wrapper — label e descrizione appaiono muted
  • inputReadonly (booleano, opzionale): Se true, aggiunge readonly all’input — nessuno stile aggiuntivo, label invariata, valore inviato nel form

Parametri variante password

  • label (stringa, obbligatorio): Testo della label (es. “Password”, “Conferma password”)
  • inputId (stringa, obbligatorio): ID univoco del campo
  • inputAutocomplete (stringa, opzionale): Valore autocomplete — usare current-password per login, new-password per registrazione/cambio password
  • inputRequired (booleano, opzionale): Se true, aggiunge aria-required="true" e il simbolo * nella label
  • inputDisabled (booleano, opzionale): Disabilita il campo e il toggle button (via CSS pointer-events: none)
  • inputAriaDescribedBy (stringa, opzionale): ID dell’elemento descrittivo collegato tramite aria-describedby
  • inputDescription (stringa, opzionale): Testo descrittivo del campo
  • errorDescription (stringa, opzionale): Messaggio di errore mostrato sotto il campo

Parametri Icone

  • inputIconPlaceholder (stringa, opzionale): ID dell’icona da mostrare come placeholder a sinistra dell’input

Accessibilità

WCAG 2.1

Livello AA — criteri pertinenti:

  • 1.3.1 Informazioni e correlazioni — la <label> è sempre associata all’<input> tramite for / id; i messaggi di errore e le descrizioni sono collegati tramite aria-describedby
  • 3.3.1 Identificazione degli errori — gli errori sono comunicati tramite aria-invalid e annunciati con aria-live="assertive"
  • 3.3.2 Etichette o istruzioni — ogni campo ha una <label> visibile o nascosta disponibile per screen reader
  • 4.1.2 Nome, ruolo, valore — aria-required, aria-describedby e aria-invalid comunicano lo stato del campo

Struttura e semantica

  • Il componente è renderizzato come <div class="rtds-input-field"> contenente una <label> e il componente @input annidato
  • La <label> è associata all’input tramite for="" e rimane disponibile anche quando nascosta visivamente tramite rtds-sr-only
  • L’indicatore di campo obbligatorio è un <abbr title="obbligatorio">*</abbr> nella label
  • I messaggi di errore sono racchiusi in <span aria-live="assertive"> per garantire l’annuncio immediato agli screen reader
  • Le descrizioni informative sono separate dai messaggi di errore tramite elementi distinti con ID differenti

Attributi ARIA

Attributo Valori Condizione
aria-required "true" quando inputRequired è true (gestito sul <input> interno)
aria-describedby ID del messaggio di errore e/o descrizione quando presenti errori o descrizione (gestito sul <input> interno)
aria-invalid "true" quando inputAriaInvalid è true (gestito sul <input> interno)
aria-live "assertive" sullo span del messaggio di errore

Accessibilità variante password

Pattern Motivazione
type="button" esplicito sul toggle Evita submit accidentale della form
aria-pressed="false/true" sul toggle Espone lo stato nascosto/visibile agli screen reader
Etichetta fissa “Mostra password” Non cambia al toggle — lo stato è comunicato da aria-pressed (scelta intenzionale)
<span aria-live="assertive"> nascosto Annuncia “Password visibile” / “Password nascosta” immediatamente dopo il toggle
Icona aria-hidden="true" Decorativa — il testo .rtds-sr-only è il label accessibile del button

Nota: L’etichetta del toggle resta “Mostra password” sia prima che dopo il click. Il cambio di stato è comunicato da aria-pressed (letto dagli screen reader) e dall’elemento aria-live (annuncio vocale immediato). Questo è il comportamento atteso e documentato.

Screen reader

  • Quando la label è nascosta (hiddenLabel: true), rimane disponibile per screen reader tramite rtds-sr-only
  • I messaggi di errore vengono annunciati immediatamente grazie ad aria-live="assertive"
  • L’indicatore di obbligatorietà (*) è leggibile tramite l’attributo title="obbligatorio" sull’<abbr>
  • Le icone decorative (placeholder, alert) sono nascoste agli screen reader

Esempi di utilizzo

<!-- Esempio base -->
<div class="rtds-input-field rtds-gap-1">
  <label for="nome" class="rtds-input-field__label">Nome</label>
  <input type="text" id="nome" name="nome">
</div>

<!-- Esempio obbligatorio con errore -->
<div class="rtds-input-field rtds-gap-1 rtds-input-field--has-error">
  <label for="email" class="rtds-input-field__label">
    Email <abbr title="obbligatorio">*</abbr>
  </label>
  <input type="email" id="email" name="email"
         aria-required="true" aria-invalid="true" aria-describedby="email-error">
  <span class="rtds-input-field__hint">
    <span id="email-error" aria-live="assertive" class="rtds-input-field__error">
      Inserire un indirizzo email valido
    </span>
  </span>
</div>