3.43
In questo articolo faremo una sintetica rassegna di alcune tecniche utili per la personalizzazione dell’aspetto di checkbox e radio button. Ad accomunare le varie soluzioni il fatto che in nessun caso si ricorre all’ausilio di Javascript. Tutto verrà realizzato esclusivamente tramite i CSS.
Per avere da subito chiaro ciò che realizzeremo, ecco uno screenshot della prima demo,
CSS checkbox style: la tecnica di base
Gli esempi che andremo via via analizzando e che sono disponibili in allegato per il download condividono l’approccio di base. Sono infatti tutti fondati sul cosiddetto ‘checkbox hack’, argomento cui abbiamo dedicato nei mesi scorsi un articolo che va considerato rispetto al tema di questo un fondamentale prerequisito. Per non appesantire questo articolo con inutili ripetizioni, ad esso rimandiamo per i dettagli tecnici.
Altra lettura vivamente consigliata è quella della lezione dedicata ai selettori di relazione della guida CSS3.
La prima applicazione di questo semplice trucchetto alla personalizzazione di checkbox e radio button è stata presentata in un tutorial di CSS Ninja. Tutte le varianti che vedremo nel nostro articolo e quelle che potrete rintracciare in rete sono di fatto derivazioni dell’esperimento di Ryan Seddon. Cerchiamo dunque di comprendere la tecnica di base.
Essenzialmente, il procedimento si compone di tre fasi:
- nel markup HTML si inserisce un input (
checkbox
oradio
) con unalabel
associata; - si nascondono i checkbox e radio button standard, ovvero quelli che presentano l’aspetto caratteristico di ciascun sistema operativo;
- si sostituiscono i controlli iniziali con controlli personalizzati nella grafica che nel loro funzionamento emulano i checkbox e i radio button.
Perché tutto possa funzionare è necessario strutturare il markup in un modo preciso. Tutti i nostri esempi hanno perciò questa configurazione nel codice HTML:
<input type="checkbox" id="html" value="HTML">
<label for="html">HTML</label>
Il checkbox o il radio button devono precedere immediatamente la label
. Quest’ultima va associata all’input corrispondente tramite l’attributo for
, che assume come valore l’id dell’input stesso.
Leggi Come personalizare e rendere accessibili Checkbox e Radio buttons.
Usare immagini per personalizzare i controlli Radio button
Le varianti che d’ora in poi analizzeremo possono essere suddivise in due categorie: quelle che sostituiscono i controlli originali con immagini e quelle che sfruttano il contenuto generato dei CSS. Iniziamo dalla prima.
La tecnica che usiamo nella prima demo è tra le più semplici.
Questo è il markup HTML:
<form action="">
<h2>Checkbox</h2>
<p>
<input type="checkbox" id="html" value="HTML">
<label for="html">HTML</label>
</p>
<p>
<input type="checkbox" id="css" value="CSS">
<label for="css">CSS</label>
</p>
<p>
<input type="checkbox" id="javascript" value="Javascript">
<label for="javascript">Javascript</label>
</p>
<h2>Radio button</h2>
<p>
<input type="radio" id="java" name="linguaggi" value="Java">
<label for="java">Java</label>
</p>
<p>
<input type="radio" id="php" name="linguaggi" value="PHP">
<label for="php">PHP</label>
</p>
<p>
<input type="radio" id="xml" name="linguaggi" value="XML">
<label for="xml">XML</label>
</p>
</form>
Nel CSS procediamo prima di tutto a nascondere i controlli standard:
input[type='radio'],
input[type='checkbox'] {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
}
Per rispettare il più possibile l’accessibilità, qui e in tutte le demo, usiamo questa soluzione basata sulla proprietà clip
al posto di display:none
.
I controlli nascosti saranno sostituiti con un’immagine iniziale (‘start.png’) che rappresenta i controlli disattivati. Sarà applicata come sfondo della label:
input[type='radio'] + label,
input[type='checkbox'] + label {
margin: 0;
padding: 2px 0 0px 24px;
cursor: pointer;
background: url('start.png') left center no-repeat;
}
Padding sul checkbox CSS
Oltre alla riga per la proprietà background
, è importante quella per il padding. Per lasciare spazio all’immagine/sfondo, infatti, dovremo usare un padding sinistro adeguato. Il padding sugli altri lati (specie superiore e inferiore) andrà sistemato per ottenere una resa cross-browser consistente rispetto all’allineamento e al posizionamento.
Il cursore sarà di tipo pointer
perché l’area è cliccabile.
Ci siamo quasi. Dobbiamo ora emulare il comportamento di checkbox e radio button. Come? Quando clicchiamo e i controlli passano allo stato :checked
, l’immagine di sfondo iniziale della label sarà sostituita con quelle specifiche che sceglieremo per i radio button e per i checkbox:
input[type='radio']:checked + label {
background-image: url('radiobutton.png');
}
input[type='checkbox']:checked + label {
background-image: url('checkbox.png');
}
Tutto qui. Dobbiamo solo chiarire un aspetto importante, quello della compatibilità sui vari browser.
L’esempio funziona come è stato concepito su tutti i browser più recenti, compreso IE9. IE7 e IE8, però, non supportano la pseudoclasse :checked
. Abbiamo due modi per risolvere:
- usare una libreria Javascript come Selectivizr per estendere il supporto della pseudoclasse su questi browser;
- adottare una strategia di graceful degradation.
Per implementare il tutto è sufficiente comporre tutte le dichiarazioni CSS che riguardano la personalizzazione dei controlli in questo modo:
p:not(#foo) > input[type='radio'],
p:not(#foo) > input[type='checkbox'] {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
}
p:not(#foo) > input[type='radio'] + label,
p:not(#foo) > input[type='checkbox'] + label {
[...]
}
[...]
Dato che IE7 e IE8 non supportano nemmeno la pesudoclasse :not
, non interpreteranno le regole precedute da p:not(#foo) >
. Questo piccolo snippet va adeguato al vostro markup. Nel nostro caso si è usato p
perché gli input sono inseriti in un paragrafo. Se li racchiudete in un div scriverete:
div:not(#foo) > input[type='radio']
Quindi: si prende l’elemento che contiene gli input, si associa ad esso la pseudoclasse :not
, si usa come valore un id non usato in nessuna parte della pagina (nell’esempio #foo
, ma può essere pure #blahblahblah
). Tutti gli esempi usano questa tecnica.
Per implementare il tutto è sufficiente comporre tutte le dichiarazioni CSS che riguardano la personalizzazione dei controlli in questo modo:
p:not(#foo) > input[type='radio'],
p:not(#foo) > input[type='checkbox'] {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
}
p:not(#foo) > input[type='radio'] + label,
p:not(#foo) > input[type='checkbox'] + label {
[...]
}
[...]
Dato che IE7 e IE8 non supportano nemmeno la pesudoclasse :not
, non interpreteranno le regole precedute da p:not(#foo) >
. Questo piccolo snippet va adeguato al vostro markup. Nel nostro caso si è usato p
perché gli input sono inseriti in un paragrafo. Se li racchiudete in un div scriverete:
div:not(#foo) > input[type='radio']
Quindi: si prende l’elemento che contiene gli input, si associa ad esso la pseudoclasse :not
, si usa come valore un id non usato in nessuna parte della pagina (nell’esempio #foo
, ma può essere pure #blahblahblah
). Tutti gli esempi usano questa tecnica.
Usare gli sprite
Nella prima demo abbiamo usato per implementare la tecnica tre immagini singole come sfondo. Nulla vieta di usare uno sprite come avviene nel secondo esempio:
La differenza con la prima tecnica consiste nel fatto che quando i controlli sono attivati non si modifica l’immagine di sfondo, ma la posizione dello sfondo in base alla configurazione dello sprite usando la proprietà background-position
:
Questo il codice CSS (nell’esempio abbiamo sfruttato solo alcuni stati):
p:not(#foo) > input[type='checkbox'] + label {
background: url('sprite.png') 0 0;
}
p:not(#foo) > input[type='checkbox']:checked + label {
background-position: 0 -60px;
}
p:not(#foo) > input[type='radio'] + label {
background: url('sprite.png') 0 -120px;
}
p:not(#foo) > input[type='radio']:checked + label {
background-position: 0 -180px;
}
Adoperando gli sprite si guadagna su un versante (una sola immagine invece che diverse), ma si deve porre più attenzione all’allineamento tra la label e il controllo. Ecco la regola che abbiamo dovuto scrivere per posizionare e allineare al meglio l’immagine/sfondo della label rispetto al testo:
p:not(#foo) > input[type='checkbox'] + label, /* Stili per le label */
p:not(#foo) > input[type='radio'] + label {
display: inline-block; /* Display */
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box; /* Border-box */
width:30px; /* Larghezza della label = all'immagine */
height:30px; /* Altezza della label = all'immagine */
padding: 8px 0 6px 30px; /* Padding per lasciare spazio allo sfondo (a sinistra) e per posizionare al meglio il testo rispetto al controllo */
cursor:pointer; /* Imposta il cursore */
}
I commenti al codice sono sufficienti per rendere chiara la funzione di ciascuna regola.
La questione del posizionamento e dell’allineamento è in effetti uno degli aspetti più insidiosi da affrontare sfruttando questa tecnica (ma in generale nella strutturazione dei form con i CSS). Non solo si deve giocare quasi sempre a livello di pochi pixel, ma ci si scontra sovente con piccole inconsistenze nella resa tra i vari browser che rendono necessario testare sempre con cura per evitare differenze macroscopiche.
Nello specifico della tecnica di cui si parla in questo articolo, molte soluzioni reperibili in rete usano uno span
aggiuntivo all’interno della label
nel markup HTML come appoggio per i CSS, con la finalità di rendere meno complicata la gestione dei controlli e del loro posizionamento. Il codice HTML assume tipicamente questa forma:
<input type="checkbox" id="html" value="HTML">
<label for="html"><span></span> HTML</label>
Su questo approccio è basato il terzo dei nostri esempi.