Il passaggio di consegne designer-ingegnere fallisce l’accessibilità
uno studio su 50 file Figma
Sono stati ottenuti accessi in sola visualizzazione a 50 file Figma di produzione di 28 team di prodotto, con autorizzazione e piena anonimizzazione, e ciascuno è stato esaminato con un’unica domanda: quando l’ingegnere apre questo file e inizia l’implementazione, quali decisioni di accessibilità ha già preso il designer — e quali vengono lasciate all’ingegnere da inventare alle 16 di un venerdì? La risposta, file dopo file, è che la maggior parte di esse vengono ancora inventate alle 16.
1. Come sono stati sottoposti ad audit i 50 file
Il campione è composto da 50 file Figma di 28 team di prodotto in ambito SaaS, retail, fintech, settore pubblico ed edtech. L’accesso in sola visualizzazione è stato negoziato su base non attribution: nulla in questo articolo identifica un brand, un team o un designer. I file sono stati scelti per riflettere ciò che un ingegnere riceverebbe effettivamente all’handoff — non uno studio di caso rifinito da un portfolio — quindi a ogni team è stato chiesto di condividere il file da cui è stata consegnata la funzionalità più recente, non il file di cui erano più orgogliosi. Dodici dei file provenivano da team con una pratica dedicata di design system; gli altri 38 erano file a livello di prodotto che importavano una libreria di sistema o gestivano i propri componenti inline.
Ogni file è stato esaminato alla ricerca di cinque proprietà di accessibilità: design dello stato focus su ogni componente interattivo, annotazioni del testo alternativo su ogni immagine o icona non decorativa, documentazione dell’ordine di lettura nel layout, gestione delle preferenze di movimento per qualsiasi elemento animato o in transizione, e specifica del contrasto in dark mode per qualsiasi componente consegnato sia in tema chiaro che scuro. Per ogni proprietà, un file ha ricevuto il punteggio «documentato» solo se un ingegnere competente potesse implementare il design senza dover inventare la risposta autonomamente. «Menzionato in un post-it» non contava. «Colore esadecimale specificato in un singolo stato hover» non contava. Il livello era: la decisione è nel file in una forma su cui l’ingegnere può agire senza chiedere?
Il risultato principale è che l’handoff, secondo questo parametro, manca le decisioni di accessibilità molto più spesso di quanto le includa. Il design dello stato focus è apparso su circa il 40% dei componenti interattivi nel corpus. Le annotazioni del testo alternativo sono apparse su circa il 22% delle immagini che ne avevano bisogno. L’ordine di lettura era esplicitamente documentato nel 16% dei file. Le preferenze di movimento erano affrontate nel 10%. Il contrasto in dark mode — per i 31 file che consegnavano entrambi i temi — era specificato per il 30% dei componenti. Il divario non riguarda una singola proprietà. Riguarda tutte e cinque, e l’ingegnere è lasciato a colmarle un giudizio alla volta.
È stato utilizzato il parametro «l’ingegnere legge e implementa». Uno stato focus conta come documentato se il file mostra la specifica visiva — colore del contorno, larghezza, offset, contrasto rispetto allo sfondo dell’elemento con focus — in una forma che l’ingegnere può mappare su un token CSS. Un messaggio Slack vicino che dice «usa il blu del brand» non conta, perché i messaggi Slack non sopravvivono all’handoff. Il file deve portare la decisione da solo.
«L’handoff non sta fallendo perché i designer non si preoccupano dell’accessibilità. Sta fallendo perché il formato del file tratta l’accessibilità come un’annotazione nei commenti quando dovrebbe essere una proprietà di primo livello di ogni componente.»
2. Design dello stato focus: il divario del 60%
Degli circa 1.800 componenti interattivi toccati nel corpus — pulsanti, link, input, checkbox, switch, tab, combobox, voci di menu, card-come-pulsante, qualsiasi elemento raggiungibile da un utente da tastiera — circa il 40% consegnava uno stato focus progettato. Il restante 60% consegnava uno stato predefinito, uno attivo e uno hover, e poi si fermava. L’ingegnere che costruisce il componente sceglie un outline di focus al momento dell’implementazione, di solito copiando il default del browser, di solito senza verificare che il default abbia un contrasto 3:1 rispetto alla superficie del componente sia nel tema chiaro che in quello scuro consegnati dal file.
Come appare «nessun design dello stato focus» nella pratica? Appare come un componente pulsante con tre varianti nel canvas — riposo, hover, premuto — e nessuna quarta variante. Appare come un campo input con un bordo stilizzato e nessun secondo stile di bordo per lo stato con focus. Appare come un primitivo checkbox con un anello di focus solo sulla variante di riposo, con l’ingegnere lasciato a indovinare se lo stesso anello debba apparire sulla variante selezionata o indeterminata. Lo schema si ripete tra componenti, tra team, tra settori. È il singolo divario di accessibilità più grande nel corpus e il più semplice da progettare.
I team che hanno progettato bene gli stati focus avevano uno di due elementi a loro favore. Il primo era una regola esplicita del design system: ogni componente interattivo deve consegnare una variante il cui nome inizia con focus-, e il componente non viene rilasciato nella libreria finché quella variante non esiste. Il secondo era una proprietà di componente Figma chiamata state con focus, focus-visible e focus-within come valori enumerati, così che il browser dei componenti del file evidenzi visivamente le varianti mancanti. I team senza uno di questi due scaffold lasciavano lo stato focus all’ingegnere circa nove volte su dieci.
Componente pulsante, quattro varianti: state=default, state=hover, state=pressed, state=focus-visible. La variante focus-visible mostra un contorno da 2px, offset 2px, token colore —focus-ring (che a sua volta è mappato su un esadecimale che supera il 3:1 rispetto alla superficie del pulsante in entrambi i temi). L’ingegnere legge il pannello di ispezione e copia il riferimento al token; nulla viene inventato.
Stesso componente pulsante, tre varianti: default, hover, pressed. Nessuna variante focus nel canvas. Un post-it del designer dice «usa l’anello di focus del sistema». L’ingegnere cerca nella libreria del design system, trova due anelli di focus candidati (uno dai pulsanti, uno dagli input, larghezze leggermente diverse), ne sceglie uno, lo consegna, e il controllo QA tre settimane dopo lo segnala perché l’anello scelto scende sotto 3:1 sulla superficie del pulsante secondario disabilitato ma ancora con focus.
Quando lo stato focus non è nel file, gli ingegneri spesso consegnano il default del browser — e il default del browser viene sovrascritto dal *:focus { outline: none } globale nella maggior parte dei CSS reset che lo stesso ingegnere ha aggiunto sei mesi prima per eliminare un commento di revisione diverso. Il risultato è un componente che appare corretto nell’anteprima Figma, appare corretto nell’ambiente di sviluppo con il reset disabilitato, e viene consegnato senza alcun indicatore di focus visibile.
3. Annotazioni del testo alternativo: per lo più assenti
Dei file nel corpus che includevano immagini di contenuto — fotografie di prodotto, illustrazioni hero, pulsanti solo icona, figure infografiche — il 78% non aveva annotazioni del testo alternativo sui livelli immagine. L’immagine era posizionata, dimensionata e stilizzata; l’equivalente testuale che ci si aspettava l’ingegnere inserisse nell’<img> reso non era nel file. Otto dei 50 file avevano il testo alternativo su alcune immagini ma non su tutte, di solito con l’illustrazione hero annotata e le immagini di contenuto nel corpo vuote. Tre file avevano il testo alternativo su ogni immagine. L’ingegnere, in 47 file su 50, era tenuto a inventare il testo alternativo — e nella pratica spesso lo ereditava dal nome del file, dalla didascalia o da qualsiasi stringa si adattasse al ritmo visivo.
Il divario è strutturale al primitivo immagine di Figma. Non esiste una proprietà «alt» nativa su un riempimento immagine o un livello immagine; il testo alternativo deve essere portato come nome del livello, un commento, un post-it, un documento di specifica separato o un campo aggiunto da un plugin. Nessuna di queste opzioni passa attraverso il pannello di ispezione per impostazione predefinita, quindi l’ingegnere che legge il file nella visualizzazione di ispezione non vede il testo alternativo anche se il designer lo ha scritto altrove. I team che hanno colmato il divario in modo coerente utilizzavano uno dei tre approcci: campi di testo alternativo gestiti da plugin su ogni variante immagine, una convenzione documentata secondo cui il nome del livello è il testo alternativo, oppure un foglio di calcolo separato con il testo alternativo indicizzato per nome file che veniva consegnato insieme al file.
I pulsanti solo icona erano un sotto-fallimento all’interno di questo schema. In 41 dei 50 file, i pulsanti icona — la lente di ingrandimento, l’hamburger menu, la X di chiusura, la freccia di condivisione — non avevano alcuna annotazione del nome accessibile, lasciando all’ingegnere il compito di scrivere aria-label=“Cerca” dal contesto visivo senza conferma che «Cerca» fosse la parola giusta nel tono del brand (era «Trova»? era «Ricerca»? era nulla perché il pulsante apre un pannello già etichettato altrove?). La nomenclatura delle icone è esattamente il tipo di decisione redazionale micro che beneficia della penna di un designer, ed esattamente il tipo che l’handoff perde.
Ogni immagine è decorativa (il testo alternativo dovrebbe essere vuoto, lo screen reader la salta) o informativa (il testo alternativo porta le informazioni che il visivo trasmette). Questa scelta è una decisione di contenuto, e appartiene al designer o al redattore, non all’ingegnere che ci ragiona a mezzanotte. Un file che non dice nulla su quali immagini sono decorative consegna o troppo testo alternativo (ogni immagine è descritta verbosamente, incluse quelle che sono puro ornamento) o troppo poco (l’illustrazione hero è descritta, ogni immagine nel corpo riceve alt="" perché l’ingegnere ha scelto la strada più sicura).
4. Ordine di lettura, movimento, contrasto in dark mode
Le restanti tre proprietà presentavano forme distinte di fallimento. L’ordine di lettura — l’ordine in cui uno screen reader narrerà la pagina, che nei layout responsive moderni non è più garantito corrispondere all’ordine visivo dall’alto verso il basso — era documentato nel 16% dei file. La documentazione, dove esisteva, era di solito una sovrapposizione numerata nel canvas (1, 2, 3 …) aggiunta con un plugin. L’altro 84% lasciava all’ingegnere la mappatura dell’ordine di lettura dall’ordine DOM che si trovava a scrivere, che su un layout CSS Grid con posizionamento esplicito riga-colonna può divergere dal layout visivo di un’intera colonna.
Le preferenze di movimento hanno avuto i risultati peggiori. Il dieci percento dei file menzionava prefers-reduced-motion tout court. Il restante 90% specificava animazioni e transizioni — entrate modali, espansioni accordion, slide di snackbar, transizioni di pagina — senza specificare cosa avrebbe dovuto fare lo stesso componente quando l’utente ha abilitato il movimento ridotto. L’ingegnere o costruiva il caso di movimento ridotto al momento dell’implementazione (spesso senza un riferimento visivo) o consegnava la stessa animazione a tutti, che è il default e che viola il criterio 2.3.3 Animazione da interazioni per gli utenti che impostano la preferenza.
Il contrasto in dark mode era specificato per il 30% dei componenti nei file che consegnavano entrambi i temi. L’altro 70% specificava il contrasto del tema chiaro — di solito con un’annotazione Stark o di un verificatore di contrasto nel file — e poi rilasciava il tema scuro con una palette con colori invertiti, lasciando all’ingegnere il compito di verificare se la coppia invertita superasse ancora il 4,5:1 per il testo del corpo e il 3:1 per i componenti UI. In circa un quinto dei 31 file dual-theme, almeno un componente scendeva sotto la soglia di contrasto nel tema scuro perché la superficie scura e il testo scuro erano entrambi calibrati per la matematica del contrasto del tema chiaro, non di quello scuro.
La matrice traccia il «tasso di completamento» per ogni proprietà nel corpus — la quota di file in cui la proprietà era documentata secondo il parametro «l’ingegnere legge e implementa». Le colonne suddividono il tasso in base al fatto che il file provenisse da un team con una pratica dedicata di design system o da un team di prodotto che gestisce i componenti inline; il divario tra le due colonne è il delta sistema-vs-no-sistema.
| Proprietà di accessibilità | Tutti i 50 file | Team con design system (12) | Team di prodotto (38) | Delta sistema-vs-prodotto |
|---|---|---|---|---|
| Design dello stato focus (componenti interattivi) | 40% | 75% | 29% | +46pp |
| Annotazioni testo alternativo (immagini di contenuto) | 22% | 50% | 13% | +37pp |
| Ordine di lettura (livello canvas) | 16% | 42% | 8% | +34pp |
| Preferenze di movimento (elementi animati) | 10% | 33% | 3% | +30pp |
| Contrasto dark mode (solo file dual-theme, n=31) | 30% | 55% | 19% | +36pp |
«I team con design system documentano le decisioni di accessibilità a circa il doppio del tasso dei team di prodotto — ma anche i team con il sistema superano il parametro su una sola proprietà su cinque per la maggior parte del tempo.»
5. Stark e Able: adozione discontinua
I due plugin che compaiono più spesso nel corpus sono Stark e Able. Entrambi sono maturi, entrambi sono ben considerati, ed entrambi offrono funzionalità che colmano diversi dei divari documentati sopra. Stark aggiunge un verificatore di contrasto, una sovrapposizione dell’ordine di focus, un’anteprima del movimento ridotto e un campo di annotazione del testo alternativo sui livelli immagine. Able aggiunge un ispettore del contrasto cromatico, una sovrapposizione di simulazione visiva e un verificatore delle dimensioni dei target di tocco. Entrambi i plugin, usati in modo coerente su un file, porterebbero quel file fuori dal quartile inferiore del corpus.
Usati in modo coerente è la frase operativa. Nei 50 file, Stark era installato e visibilmente usato in 18, e Able in 11. Nei file in cui il plugin era usato, veniva di solito usato sul componente hero e sul CTA principale — i componenti più probabilmente sul canvas quando il designer ha aperto il plugin — e solo sporadicamente altrove. Sei file usavano Stark su un passaggio globale; uno usava Able su un passaggio globale. Lo schema è: i plugin esistono, i designer li conoscono, vengono utilizzati per controlli spot, e poi il controllo spot si ferma ai componenti che il designer stava guardando quando il plugin era aperto.
I due team che hanno chiuso l’audit sull’utilizzo dei plugin hanno fatto una cosa diversa: hanno eseguito la funzionalità di audit del plugin su ogni pagina del file come passaggio di release-gate prima che il file fosse condiviso con l’ingegneria. L’audit girava nel file, produceva un rapporto, e il rapporto doveva essere vuoto (o le sue eccezioni documentate) prima che il file passasse da «in design» a «pronto per l’ingegneria». Questo è il plugin-come-flusso-di-lavoro piuttosto che il plugin-come-controllo-spot, ed è la differenza tra l’80% di copertura e il 20% di copertura nel campione.
Un plugin alza il livello minimo: il verificatore di contrasto individua gli evidenti fallimenti 2,1:1, il campo del testo alternativo dà al designer un posto dove digitare. Nulla di tutto ciò aiuta se il plugin gira su tre componenti e non sui restanti 27. La soluzione è inserire il plugin nel flusso di lavoro — un passaggio di release-gate, una checklist pre-handoff, un branch Figma che non può essere unito senza un rapporto vuoto del plugin — piuttosto che nella discrezione del designer nel momento in cui ricorda che esiste.
6. Una checklist per l’handoff e un contratto di token
L’audit produce una checklist e un contratto. La checklist è ciò che un designer dovrebbe essere in grado di spuntare prima che il file venga condiviso con l’ingegneria. Il contratto è la forma dei design token che accompagnano il file affinché l’ingegnere mappi le variabili Figma sulle custom property CSS senza inventare valori intermedi. Entrambi sono brevi per scelta: ogni voce della checklist è una proprietà misurata dall’audit, e ogni token nel contratto è un valore che ha colmato un divario nel corpus.
Ogni componente interattivo consegna una variante state=focus-visible.
Non «il sistema ha un anello di focus». Una variante chiamata focus-visible sul componente stesso, con il colore del contorno, la larghezza e l’offset legati al token dell’anello di focus. La variante è ciò che l’ingegnere copia nell’implementazione; senza di essa, l’ingegnere indovina.
Ogni immagine di contenuto ha il testo alternativo in un campo gestito da plugin o in una convenzione di nome livello documentata.
Occorre scegliere una posizione e applicarla in modo coerente. Il campo del testo alternativo di Stark, il nome del livello trattato come alt, o un foglio di calcolo sidecar — ognuno dei tre funziona, ma solo se ogni immagine nel file usa lo stesso. I pulsanti solo icona ricevono anche un’annotazione del nome accessibile, nella stessa posizione, con la stringa esatta che l’ingegnere dovrebbe inserire in aria-label.
L’ordine di lettura è documentato su qualsiasi pagina in cui l’ordine DOM divergerà dall’ordine visivo.
La documentazione più semplice è una sovrapposizione numerata aggiunta con un plugin (Stark ne ha una, così come diversi plugin della community). Per le pagine il cui ordine è banalmente dall’alto verso il basso e da sinistra a destra, è possibile omettere la sovrapposizione; per qualsiasi cosa che utilizzi posizionamento CSS Grid, named-areas o posizionamento assoluto, la sovrapposizione è obbligatoria.
Ogni elemento animato o in transizione ha una variante con movimento ridotto nel canvas.
Un secondo frame, una seconda variante o una versione documentata «senza animazione». L’ingegnere non dovrebbe inventare il caso di movimento ridotto — il designer dovrebbe specificare se il modal fa un cross-fade invece di scorrere, lo snackbar appare istantaneamente invece di scorrere, la transizione di pagina viene completamente omessa.
Per i file dual-theme, il contrasto viene verificato nel tema scuro separatamente, non derivato dal tema chiaro.
La matematica del contrasto in dark mode è un problema a sé; invertire la palette non è sufficiente. Occorre eseguire Stark o Able su ogni componente in dark mode, non solo in light. Documentare il rapporto di contrasto nelle note della variante in modo che l’ingegnere possa verificare che l’implementazione corrisponda.
Il file viene consegnato insieme a un contratto di token: una lista piatta di ogni variabile Figma mappata alla sua custom property CSS.
Il contratto è il ponte tra il file e il codebase. Un contratto tipico assomiglia alla tabella seguente: ogni riga nomina una variabile Figma, la custom property CSS a cui l’ingegnere dovrebbe associarla, il valore nel tema chiaro, il valore nel tema scuro e il criterio WCAG a cui il token partecipa.
| Variabile Figma | Custom property CSS | Valore tema chiaro | Valore tema scuro | Legami WCAG |
|---|---|---|---|---|
| color/focus-ring | —focus-ring | #0B57D0 | #A8C7FA | 2.4.7, 1.4.11 |
| color/text/body | —text-body | #1F1F1F | #E3E3E3 | 1.4.3 (4,5:1 sulla superficie) |
| color/surface/raised | —surface-raised | #FFFFFF | #1F1F1F | 1.4.11 (3:1 rispetto all’adiacente) |
| size/touch-target/min | —touch-target-min | 44px | 44px | 2.5.5, 2.5.8 |
| motion/duration/standard | —motion-standard | 200ms | 200ms | 2.3.3 (salta se reduced-motion) |
| motion/duration/reduced | —motion-reduced | 0ms | 0ms | 2.3.3 |
Una volta che il contratto esiste, il lavoro dell’ingegnere è meccanico: associare la custom property CSS alla variabile Figma, consegnare l’implementazione, verificare confrontando i valori resi con il contratto. Senza il contratto, ogni associazione è una scelta discrezionale, e le scelte discrezionali si accumulano nel divario del 60%. Il contratto è il singolo artefatto che sposta l’accessibilità da «l’ingegnere è responsabile al momento dell’handoff» a «il sistema è responsabile al momento della progettazione».
Conclusione: il file è il contratto
L’audit su 50 file si chiude con una semplice osservazione. L’handoff sta fallendo l’accessibilità non perché i designer non si preoccupino e non perché gli ingegneri non si preoccupino, ma perché il file — il file Figma, il singolo artefatto che ogni parte legge — non porta le decisioni di accessibilità come proprietà di primo livello. Stati focus, testo alternativo, ordine di lettura, preferenze di movimento, contrasto in dark mode: ciascuna di esse è una decisione di design, ciascuna appartiene al file, ciascuna si trova attualmente da qualche altra parte. In un post-it, in un messaggio Slack, in un foglio di calcolo separato, nella testa dell’ingegnere alle 16 di un venerdì.
La soluzione non è un designer eroico o un ingegnere eroico. È un cambiamento del flusso di lavoro a livello di team: ogni componente interattivo consegna una variante focus, ogni immagine porta il testo alternativo in un’unica posizione gestita da plugin, l’ordine di lettura viene sovrapposto su qualsiasi pagina non banale, le animazioni specificano la loro controparte con movimento ridotto, il contrasto in dark mode viene verificato separatamente da quello chiaro, e il file viene consegnato insieme a un contratto di token che nomina ogni variabile a cui l’implementazione si associa. Nessuno di questi passaggi è nuovo, nessuno richiede uno strumento che non si abbia già, e qualsiasi team che li adotti come passaggi di release-gate colmerà la maggior parte dei divari misurati in un singolo ciclo di rilascio.
Il risultato più profondo è che i team con design system lo fanno già a circa il doppio del tasso dei team di prodotto. Il contributo che i team con design system forniscono è esattamente il contributo che impone la disciplina di costruire un sistema: i componenti sono nominati, le proprietà sono enumerate, le varianti sono visibili, i token sono espliciti. Portare la stessa disciplina ai file a livello di prodotto — anche senza un design system completo alla base — colma la maggior parte del divario dell’handoff. Non è più un problema di strumenti. È una scelta di flusso di lavoro.
«Il file dovrebbe arrivare con le decisioni di accessibilità già prese. Qualsiasi altra cosa è l’ingegnere che le inventa nel momento peggiore, con il minimo contesto, nella scadenza più stringente.»