Questa è la traduzione dell'articolo Showing Good Form di James Edwards pubblicato originariamente su 24 Ways l'11 dicembre 2006.
Recentemente (non ricordo bene quando) stavo lavorando sul sito di un cliente che aveva bisogno di un widget come quello raffigurato qui sotto (progettato dal mio collega Cameron Adams):
Costruirlo si è rivelata una sfida, non solo per quel che riguarda i CSS, ma anche per la scelta del markup adeguato. In pratica ho dovuto chiedermi: come deve essere realizzato un widget di questo genere?
Mmm... markup
Mi sembrava che ci fossero due questioni chiave con cui confrontarsi:
- La funzione dell'interfaccia è quella di inserire informazioni. Semanticamente, dunque, si tratta di un form. Dovremmo allora trovare un modo per costruirlo usando elementi di form:
fieldset
,legend
,label
einput
. - Non possiamo usare una tabella per il layout del form, anche se sarebbe la soluzione più comoda.
Abusare di tabelle per il layout non è mai una cosa buona: semanticamente le tabelle non servono a questo scopo. Ma anche se questi dati possono essere descritti come una tabella, non dovremmo mischiare il markup dei form con quello delle tabelle per via del comportamento particolare di certi screen reader.
Per fare solo un esempio, lo screen reader JAWS ha una modalità specifica per l'intereazione con i moduli (detta "forms mode"). Quando il programma è eseguito con questa modalità il suo output include solo gli elementi rilevanti, ovvero legende, label e controlli del form. Tutto il markup che non è parte del form, come il testo in una cella di tabella precedente o un paragrafo, è semplicemente ignorato. In questa situazione, l'utente dovrebbe alternare la modalità form con quella normale per ascoltare tutto il contenuto (per approfondire la questione, anche con esempi, potete consultare un thread sul forum di Accessify).
Un ulteriore problema per chi usa uno screen reader è dato dal design in sé: i campi di input sono associati insieme in righe e colonne. Un utente senza problemi di vista può facilmente districarsi per compiere queste associazioni; ma un non vedente non può farlo. Per un utente con questo problema i dati di intestazione per righe e colonne dovranno essere lì al loro posto su ogni asse. In altre parole, il layout dovrebbe essere come questo:
E soprattutto dovrebbe essere realizzato con il giusto markup semantico al fine di veicolare quelle relazioni. Avendo chiaro ciò, la scelta giusta degli elementi sembra essere questa: ogni riga è un fieldset, l'intestazione della riga è una legend, ogni intestazione di colonna è una label, associata ad un input.
Ecco come appare il form senza CSS:
Ed ecco un po' di markup per la prima riga (la maggior parte degli attributi è stata rimossa per mantenere l'esempio succinto e sintetico):
<fieldset>
<legend>
<span>Match points</span>
</legend>
<label>
<span>Win</span>
<input value="3" />
</label>
<label>
<span>Draw</span>
<input value="1" />
</label>
<label>
<span>Lose</span>
<input value="0" />
</label>
<label>
<span>Played</span>
<input value="0" />
</label>
</fieldset>
L'elemento span
all'interno di ciascuna legend
è inserito perché gli elementi legend
sono altamente resistenti agli stili applicati via CSS! Dio solo sa quanto ho dovuto lottare con bug e inconsistenze. Finché non mi è venuta in mente un'alternativa ovvia! L'elemento legend
è in sé solo un container, mentre tutti gli stili sono applicati allo span
contenuto al suo interno.
Passiamo al CSS
Non mi soffermerò più di tanto sul CSS. Potete consultarlo estensivamente nella demo o direttamente attingendo al codice.
Voglio però toccare quello che mi pare il punto più interessante: dove arriviamo partendo da un layout con intestazioni su ogni riga ad uno dove solo la riga superiore le comprende (è così che il widget appare nei browser grafici). Per gli screen reader, come abbiamo osservato, abbiamo bisogno di queste intestazioni su ogni riga, e così dovremo appoggiarci a qualche regola CSS per nasconderle senza rimuoverle dall'output.
Il CSS di base per ciascuna label span
è questo:
label span
{
display:block;
padding:5px;
line-height:1em;
background:#423221;
color:#fff;
font-weight:bold;
}
Ma nelle righe sotto all'intestazione hanno qualche regola aggiuntiva:
fieldset.body label span
{
padding:0 5px;
line-height:0;
position:relative;
top:-10000em;
}
La larghezza dell'elemento è preservata, assicurandoci che la label circostante sia della stessa larghezza di quella presente nella riga delle intestazioni che sta sopra. Così viene preservata una colonna con larghezza unificata in tutto il layout. L'elemento però non ha di fatto altezza ed è pertanto invisibile. Lo stile viene applicato così e non impostando height
su 0 o usando overflow:hidden
perché facendo così avremmo creato problemi con un altro celebre screen reader (l'output sarebbe stato nascosto su Window Eyes, come mostrato in questo esempio).
Il widget completato
Insomma, una cosa estremamente intricata! Ma alla fine ecco il lavoro completato:
Non è perfetto, soprattutto perché le legende devono avere una larghezza fissa; potremmo definirla in em
per consentire il ridimensionamento del testo, ma ciò non consente al contenuto di essere spezzato su più righe. Inoltre, sono stati necessari alcuni hack CSS per IE6 e IE7.
Tutto sommato ha funzionato bene per il nostro scopo e ha soddisfatto completamente il cliente. Soprattutto ha rafforzato la mia fede su un punto: che non c'è bisogno di abusare di tabelle per il layout.
James Edwards (aka brothercake) è uno sviluppatore web freelance. È specializzato nella programmazione avanzata in Javascript e nello sviluppo di siti web accessibili. È un promotore degli standard web, membro attivo del WaSP e creatore del sistema Ultimate Drop Down Menu, il primo menu commerciale in DHTML compatibile con le WCAG. È anche autore di The JavaScript Anthology, pubblicato da SitePoint nel 2006.