In questo articolo impareremo a realizzare, usando solo i CSS, un menu slider con immagini come quello che potete testare sin da ora nella nostra prima demo.
La base della tecnica è rappresentata da questo esperimento reperibile insieme ad altri su The Code Player. Lo abbiamo modificato in alcuni punti e abbiamo poi preparato una variante che mostra all'interno dei pannelli un paragrafo di testo aggiuntivo. Iniziamo subito.
Il codice HTML
Il markup HTML della demo ricalca nella sua essenza quello dell'esempio di The Code Player. Vediamolo.
<div id="container">
<ul>
<li>
<div class="titolo">
<h2><a href="#" class="block">Lorem ipsum</a></h2>
</div>
<div class="immagine">
<a href="#"><img src="1.jpg" alt=""></a>
</div>
</li>
[...]
</ul>
</div>
Il menu è racchiuso in un div con id #container
.
La struttura del menu è basata su una lista non ordinata (ul
). Ciascun item della lista (li
) rappresenta e racchiude i singoli pannelli (5) che compongono il menu.
All'interno di ogni li
abbiamo inserito un div con classe .titolo
per la scritta laterale dei pannelli e un div con classe .immagine
che ospita, appunto, l'immagine di ogni pannello.
Sia il titolo h2
che l'immagine sono dei link: trattandosi di un menu si suppone che puntino ad una risorsa o pagina del sito.
La scelta della lista per la strutturazione del menu non è solo semanticamente valida, ma si rivela ideale per gestire al meglio l'interazione sul menu e il passaggio ai vari stati.
Il codice CSS
Nel momento in cui vogliate implementare una soluzione del genere, prima di mettere mano al CSS, è fondamentale conoscere bene la struttura e il funzionamento del menu. Sono dati su cui baseremo tutta una serie di calcoli per definire nel foglio di stile le dimensioni dei vari elementi.
Il menu si presenta in due stati. Nello stato iniziale, i pannelli occupano ciascuno uno spazio uguale (per noi è di 178.4px). Il menu e il contenitore risultano pertanto suddivisi in cinque aree. Ognuna corrisponde ad una porzione dei singoli pannelli.
Se passiamo con il mouse su un pannello, quest'ultimo apparirà nella sua dimensione intera (700px), mentre gli altri quattro si ridurranno in larghezza fino a raggiungere una dimensione orizzontale che fisseremo nel CSS (nel nostro caso la dimensione è pari a 48px).
Una volta compreso questo meccanismo, possiamo passare a fare due calcoli.
Ci servono in partenza tre dati:
- la larghezza delle immagini che compongono il menu (dovranno essere identiche nelle dimensioni);
- l'altezza delle immagini;
- la larghezza che vogliamo impostare per i pannelli quando si chiudono (si veda il secondo screenshot).
Nella nostra demo questi dati sono:
- larghezza delle immagini: 700px;
- altezza delle immagini: 300px;
- larghezza dei pannelli chiusi: 48px.
Ed ora i calcoli.
La larghezza del contenitore (il div #container
) l'abbiamo impostata facendo questa computazione: larghezza dell'immagine + larghezza dei pannelli chiusi*4 (dove 4 è il numero delle immagini -1). Quindi: 700 + 48*4, che fa 700 + 192, quindi 892.
La larghezza dei pannelli nello stato iniziale è stata calcolata invece in questo modo: larghezza del contenitore/numero dei pannelli. Dunque: 892/5, che è uguale a 178.4.
Tutto ciò per essere precisi al pixel. Arrotondando, per esempio, 178.4 a 179 non succede niente di catastrofico. Tutto funziona. L'ultimo pannello risulterà di pochissimo più stretto, una roba pressoché impercettibile.
Per quanto riguarda la larghezza dei pannelli chiusi: se si sceglie di disporre i titoli dei pannelli come nell'esempio originario di The Code Player, potrà essere impostata a piacere.
Se invece si sceglie di disporre il testo verticalmente come abbiamo fatto nella demo, per ottenere la perfezione bisognerà impostare esplicitamente l'altezza (height
) del div con il titolo h2
. Ne parleremo in dettaglio più avanti.
E ora finalmente il codice, che analizzeremo solo nelle sezioni rilevanti.
Impostiamo border-box
per la proprietà box-sizing
:
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
Creiamo una classe .block
che assegneremo al link del titolo per estenderlo a tutto l'elemento:
a.block {
display: block;
}
Resettiamo il bordo sulle immagini soprattutto per annullare un fastidioso effetto su IE (spazio bianco di 1px intorno alle immagini racchiuse in un link):
img {border: 0;}
Lavoriamo ora sul div contenitore:
#container {
width: 892px;
height: 300px;
overflow: hidden;
margin: 150px auto;
box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.35);
-webkit-box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.35);
-moz-box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.35);
}
Le regole fondamentali sono le prime tre. width
prende come valore gli 892px di cui abbiamo parlato sopra. height
corrisponde all'altezza delle immagini. overflow
va su hidden
. Il resto è codice aggiuntivo per la demo (margini) e puramente decorativo (ombreggiatura).
Una piccola regola per la lista ul
:
#container ul {
width: 2000px;
margin: 0;
padding: 0;
}
La larghezza a 2000px è una piccola precauzione suggerita dall'autore dell'esprimento originario per evitare problemi di flickering su alcuni browser, specie sull'ultimo elemento. Margini e padding li azzeriamo come sempre quando vogliamo annullare gli stili di default delle liste.
E ora i li
, in pratica gli stili di base dei singoli pannelli:
#container li {
position: relative;
list-style: none;
margin: 0;
padding: 0;
display: block;
float: left;
width: 178.4px;
height: 300px;
border-left: 1px solid #888;
box-shadow: 0 0 25px 10px rgba(0, 0, 0, 0.5);
-webkit-box-shadow: 0 0 25px 10px rgba(0, 0, 0, 0.5);
-moz-box-shadow: 0 0 25px 10px rgba(0, 0, 0, 0.5);
transition: all 0.5s;
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
}
Fondamentale il posizionamento relativo, nel nostro caso. Ci serve per posizionare più avanti il testo in verticale del titolo.
Annulliamo poi tutti gli stili di lista e resettiamo margini e padding.
Impostiamo la visualizzazione come blocchi e floattiamo a sinistra.
Per la proprietà width
usiamo il valore 178.4px che risultava dal calcolo visto in precedenza. È la larghezza dei pannelli nello stato iniziale del menu.
L'altezza è uguale anche qui a quella delle immagini.
Il resto (bordo e ombreggiatura) è decorazione, ma di sicura efficacia per rendere perfetto e gradevole l'aspetto del menu.
Usiamo anche una transizione per rendere più fluido il movimento di apertura e chiusura dei pannelli.
Bene, siamo arrivati agli stili per il titolo. Ecco il codice:
.titolo {
position: absolute;
left: 0;
top: 0;
background: rgba(0,0,0,0.8);
transform: translateX(-100%) rotate(-90deg);
transform-origin: right top;
width: 300px;
height: 48px;
}
Vogliamo ottenere quattro cose:
- il titolo sarà posizionato all'estremità sinistra del pannello;
- lo sfondo dovrà essere leggermente trasparente;
- il testo dell'
h2
dovrà essere disposto verticalmente; - il div dovrà occupare l'intera altezza del suo contenitore, ovvero il
li
.
Per il posizionamento basterà usare position: absolute
e i valori left: 0
e top: 0
.
Per la trasparenza ricorriamo alla definizione del colore con la sintassi RGBa.
Per disporre il testo verticalmente adoperiamo le trasformazioni CSS3. Nello specifico, ruotiamo di 90° il div in senso antiorario (-90°
) e poi trasliamo verso sinistra. Come origine della trasformazione usiamo l'angolo superiore destro.
Il processo per cui il div viene trasformato è sintetizzato da questi screenshot:
Il valore per width
, dato che il div sarà ruotato, deve corrispondere all'altezza del contenitore, quindi 300px.
L'altezza (height
), invece, va fissata esplicitamente. Il valore usato corrisponde ai 48px di cui si parlava più sopra. Ovviamente, non ci si faccia ingannare dalle apparenze. Con il div ruotato, quella che è in origine la dimensione verticale, ora visualmente, ma solo visualmente, corrisponde alla dimensione orizzontale.
Al titolo h2
contenuto nel div .titolo
assegniamo questi stili:
.titolo h2 {
width: 300px;
height: 48px;
margin: 0;
padding: 10px 15px;
font-size: 24px;
color: #fff;
}
Dichiariamo esplicitamente anche la larghezza e l'altezza con valori corrispondenti a quelli del div .titolo
per una resa cross-browser più consistente.
Per finire le due regole che gestiscono l'apertura e la chiusura dei pannelli. Sfruttiamo ovviamente la pseudoclasse :hover
:
#container ul:hover li {width: 48px;}
#container ul li:hover {width: 700px;}
Con la prima regola diciamo al browser di ridurre a 40px la larghezza dei pannelli (i li
) quando si passa col mouse sulla lista ul
.
Nel contempo, il li
che riceve l'hover e solo quello si allarga fino a 700px.
Compatibilità con IE8
La demo funziona su tutti i i browser più recenti. IE9 non sfrutta un unico effetto: le transizioni.
Più complesso il discorso per IE8. Vediamo come sistemare le cose.
Questa versione del browser di Microsoft, intanto, non supporta le trasformazioni. Abbiamo un'alternativa. È lo script/polyfill Transformie di Paul Baukaus. È basato su jQuery e si appoggia per i calcoli matematici ad un altro script, sylvester.js.
Ci basta inserire gli script nella head
del documento senza dover fare altro, perché Transformie mappa automaticamente le dichiarazioni per le trasformazioni CSS3 ai filtri di Microsoft. E dunque, usando i commenti condizionali:
<!--[if lte IE 8]>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="sylvester.js"></script>
<script type="text/javascript" src="transformie.js"></script>
<![endif]-->
Non tutto è a posto però. Transformie non supporta purtroppo né transform-origin
né la traslazione. Ma possiamo risolvere facilmente.
All'inizio del docuemento HTML abbiamo usato le classi condizionali per IE. Dunque, l'elemento html
su IE8 assume una classe .lt-ie9
. A questo punto, nel CSS, possiamo creare una regola CSS ad hoc per IE8:
.lt-ie9 .titolo {
position: absolute;
left: -125px;
top: 127px;
width: 300px;
height: 300px;
transform: rotate(-90deg);
background: #000;
}
Il div sarà solo ruotato di -90°. Per posizionarlo dove ci interessa, invece che la traslazione, utilizziamo valori ad hoc per top
e left
.
Nella regola è contenuta anche una dichiarazione per lo sfondo: background: #000
. Dato che IE8 non supporta nemmeno RGBa, definiamo un colore di sfondo tradizionale e senza trasparenza.
Il risultato è questo:
Una variante
Lo screenshot di IE8 ci porta a parlare della variante che abbiamo preparato per il secondo esempio partendo da questa base.
La differenza di fondo consiste nel fatto che abbiamo aggiunto per ciascun pannello un testo di accompagnamento alle immagini.
Il markup HTML cambia così:
<div id="container">
<ul>
<li id="pannello-1">
<div class="titolo">
<h2><a href="#" class="block">Lorem ipsum</a></h2>
</div>
<div class="contenuto">
<p class="testo">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore aliqua.</p>
</div>
</li>
[...]
</ul>
</div>
Non compare più l'immagine nel codice HTML perché ora essa viene impostata come sfondo dei pannelli, ovvero dei li
, cui abbiamo assegnato nell'HTML uno specifico id:
#pannello-1 {background: url(1.jpg);}
#pannello-2 {background: url(2.jpg);}
#pannello-3 {background: url(3.jpg);}
#pannello-4 {background: url(4.jpg);}
#pannello-5 {background: url(5.jpg);}
Il testo viene inserito e posizionato usando semplicemente i margini:
.testo {
width: 200px;
margin-left: 430px;
margin-top: 140px;
background: rgba(0,0,0,0.4);
color: white;
padding: 10px;
border-radius: 5px;
}
Per i dettagli rimandiamo allo studio del codice degli esempi, disponibili come sempre in allegato.