Vai al contenuto principaleVai al footer

Realizzare un prodotto accessibile - parte 2

In questo post entriamo nel dettaglio di come rendere accessibili gli elementi interattivi più comuni di un prodotto web, partendo dai controlli di navigazione come il toggler di un menù. Verranno consigliate alcune librerie JS che sono decisive per rendere accessibile una pagina, e poi un nostro caso di utilizzo della media query prefers-reduced-motion.

Tempo di lettura: 5 minuti

Attributi ARIA: cosa sono e a cosa servono

ARIA, anche chiamato WAI-ARIA, è l'acronimo di "Web Accessibility Initiative-Accessible Rich Internet Applications". Questa iniziativa intende offrire agli sviluppatori un insieme di schemi e attributi per rendere più accessibili le loro creazioni.

Gli attributi ARIA dei tag HTML sono usati con vari scopi e in particolati contesti, che vedremo meglio dopo, e possono essere riassunti così:

  • interagire con l’accessibility tree;
  • fornire informazioni aggiuntive;
  • comunicare lo stato degli elementi interattivi;
  • rimuovere elementi dall’accessibility tree.

aria-hidden è utile per nascondere quello che viene ripetuto più volte nel DOM, ma è sufficiente che sia presente una volta sola nell’accessibility tree. Ad esempio un menù coi link social presente sia in header sia in footer.

Un altro caso d'uso per questo attributo riguarda le icone, ad esempio se gestite con delle immagini SVG. Per evitare che lo screen reader provi a leggere il contenuto del codice SVG possiamo nasconderlo, offrendo un elemento leggibile come alternativa.

<a href="https://www.twitter.com/cantierecreativo" title="Twitter">
<svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 16 16">...</svg>
<span class="sr-only">Twitter</span>
</a>

Tabs, accordions e menù togglers

In questo caso abbiamo una parte della UI che viene resa disponibile dopo essere stata attivata dall'utente con un azione. Ad esempio i menù di navigazione mobile che vengono rivelati facendo click/tap sul classico tasto "hamburger". Ma anche gli accordion menu e la navigazione a tab di contenuti.

Rendere accessibili via tastiera e screen reader questi elementi richiede alcuni accorgimenti per assicurarsi che l'interfaccia vocale sia chiara e funzionale. Dobbiamo quindi:

  • dare un ID agli elementi toggler e l'elemento controllato;
  • usare gli attributi aria per referenziare l'ID del toggler e quello dell’elemento controllato;
  • usare gli attributi aria per esprimere se l’elemento controllato è visibile/aperto o meno.

Ecco un esempio in codice HTML della struttura dei tag:

<button
id="toggler_id"
aria-controls="nav_id"
aria-expanded="false"
aria-label="Apri/chiudi il menù di navigazione principale"
>
<i class="icon" data-icon="hamburger" aria-hidden="true"></i>
</button>

<nav
id="nav_id"
aria-labelledby="toggler_id"
>
<ul>
<li><a href="#">Home</a></li>
   <li><a href="#">Prodotti</a></li>
   <li><a href="#">Contatti</a></li>
</ul>
</nav>

Ed il codice JS per controllare questo elemento e i relativi attributi. 

const menu = document.querySelector("#nav_id");
const toggler = document.querySelector("#toggler_id");

toggler.addEventListener("click", (e) => {
e.preventDefault();
menu.classList.toggle("is-open"); // classe CSS per gestire la visibilità del menù
if (toggler.getAttribute("aria-expanded") === "false") {
   toggler.setAttribute("aria-expanded", "true");
} else {
toggler.setAttribute("aria-expanded", "false");
});

Form basics: input labels e bottoni

I form sono una parte centrale dell'accessibilità dei prodotti web in quanto è un contenitore di elementi interattivi, gli input, che devono essere resi utilizzabili con dei controlli particolari.

Prima di entrare nel dettagli degli input guardiamo alcune indicazioni sul loro contesto:

  1. Ad ogni input deve corrispondere una label collegata.
  2. Gli input devono avere validazioni e mostrare degli errori comprensibili, è raccomandato specificare il type e usare le validazioni HTML se possibile.
  3. I form devono avere un submit button, e deve essere specificato l'attributo type perché browser differenti potrebbero avere dei type di default differenti.

Ecco un esempio:

<form>
<label for="field_id">Email</label>
<input type="email" id="field_id" required="" placeholder="Inserisci qua la tua email" />
<button type="submit">Invia</button>
</form>

Altri input, select, select multiple

Quasi ogni input type ha i suoi potenziali problemi di accessibilità, perché ogni browser e sistema operativo può darne un interpretazione diversa, e dunque la sua trasposizione in interfaccia accessibile può variare. Online si trova molta ricerca, basata su user test, che permette di valutare la migliore implementazione possibile per il proprio caso.

Dunque la ricerca e l'approfrondimento sono i punti di partenza che raccomandiamo. Ad esempio questo articolo illustra le problematiche e le soluzione adottate per un input type number. Ma come vedremo tra poco, spesso ci sono plugin già accessibili pronti per risolvere il problema ed evitare grattacapi.

Un caso molto complesso e interessante è quello delle select e select multiple, che viene egregiamente affrontato da Sarah Higley in questa serie di articoli in due parti.

Se avete necessità di creare un input custom non vi spaventate, l'importante è lavorare sempre con l'accessibilità come punto di riferimento. In questo ottimo articolo viene riportato l'esempio di un input numerico relativo ad una quantità, come potrebbe essere il form di aggiunta di un prodotto ad un carrello in contesto e-commerce:

<form action="">
  <fieldset>
    <legend>Adjust Quantity</legend>
    <div>
      <label for="qty-element">Current Quantity</label>
      <input type="text" role="alert" aria-live="assertive" value="0" id="qty-element" />
      <button type="button" aria-label='Add to Quantity' aria-controls="qty-element">+</button>
      <button type="button" aria-label='Subtract from Quantity' title="subtract 10" aria-controls="qty-element">=</button>
    </div>
  </fieldset>
</form>

Librerie accessibili che stiamo utilizzando

Ecco alcune librerie e plugin che stiamo utilizzando per creare componenti accessibili:

SwiperJS - Slider
https://swiperjs.com/

Tabpanel Widget - Tabs & Accordions
https://tabpanelwidget.com/

a11y-dialog - Modali
https://github.com/KittyGiraudel/a11y-dialog

Per una raccolta completa di componenti accessibili fare riferimento a questo articolo estremamente completo di Vitaly Friedman aggiornato a marzo 2021.

Reduced motion: una soluzione ad-hoc

La Media Query prefers-reduced-motion comunica al nostro prodotto una cosa molto importante: l'utente ha impostato il suo sistema operativo per ridurre le animazioni di movimento. Questo perché chi ha la sindrome vestibolare o labirintite avverte un senso di vertigini quando vede movimenti eccessivi.

Nelle pagine web questo può accadere nel caso di:

  • transizioni di movimento sui carousel;
  • scroll della pagina con transizioni in ingresso;
  • scroll morbido;
  • zoom ed ingrandimenti.

Considerato che questa Media Query ha un supporto del 92% dei browser abbiamo provato ad utilizzarla per rendere ancora più accessibile un progetto che abbiamo pubblicato di recente.

In questo caso ci ha permesso di impostare in un componente card una transizione particolare, basata su fade e non su zoom:

.card {
&:hover {
.card__image {
transform: scale(1.1);
@media (prefers-reduced-motion) {
transform: none;
opacity: 0.8;
}
}
}
}

Con questo codice abbiamo impostato uno smooth-scroll a livello globale, ma disattivandolo in caso di questa preferenza:

html {
scroll-behaviour: smooth;
@media (prefers-reduced-motion) {
   scroll-behaviour: auto;
}
}

Infine nel nostro SwiperJS abbiamo cambiato la transizione delle slide grafiche, anche in questo caso non più con un movimento ma con un fade:

const isMotionReduced = () => {
  if ('matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches) {
    return true;
  }
  return false;
}
const bigSlider = (container) => {
if (isMotionReduced()) {
    effectType = 'fade';
  }
 const bigGallery = new Swiper(container, {
 preloadImages: false,
lazy: true,
effect: effectType,
}
}
Hai trovato questo post interessante?Scopri chi siamo