Chi come noi scrive (anche) CSS per vivere desidera spesso un mondo migliore. Abbiamo sempre necessità di risparmiare tempo e linee di codice e spesso abbiamo la sensazione di poter evitare di ripetere le stesse direttive all'interno del medesimo progetto.
In alcuni casi abbiamo potuto giovarci di sistemi che rendono più consistenti, puliti e mantenibili i nostri fogli di stile, grazie ai Framework CSS (ormai determinanti nella creazione di progetti web), in altri casi abbiamo semplicemente dovuto chinare la testa e fondere i tasti corrispondenti alle shortcut del copincolla (chi scrive ha una mela un po' più ammaccata).
In questo articolo vediamo come utilizzare LESS, una libreria che ci permette di utilizzare i concetti dei linguaggi di programmazione per scrivere CSS in modo semplice e più facilmente mantenibile.
Le aberrazioni del CSS
Il problema maggiore del CSS come da specifiche W3C è anche il suo maggiore vantaggio. Non essendo un linguaggio di programmazione permette una curva di apprendimento molto morbida e quindi una veloce metabolizzazione da parte di tutti.
L'assenza di una struttura formale forte però, permette anche una lunga serie di aberrazioni a cui noi tutti siamo abituati: tralasciando orrori di formattazione, scritture illegibili, mancati usi di shortcut nelle direttive, che faranno magari parte di una piccola galleria degli orrori a venire, quello su cui vorremmo soffermarci qui è la sostanziale ripetizione che affronta qualunque frontend developer per implementare il layout che il reparto grafico ha disegnato.
Come in tutti i prodotti grafici sensati, immaginiamo di avere una palette di colori di riferimento univoca e consistente. Detto in altri termini, abbiamo 4 massimo 5 colori (se sono di più siamo autorizzati ad inveire con il reparto grafico di cui sopra) che faranno capolino lungo tutte le migliaia di righe di CSS che scriveremo.
Assieme a questo avremo di fronte una serie di comportamenti visivi che si ripeteranno (al solito, sempre reparto creativo permettendo).
Il corretto stratagemma che tipicamente viene adottato è quello di stabilire una serie di classi CSS che assegnano alcuni comportamenti visuali e, fatto questo, assegnamo una o più di queste classi agli elementi, trasferendo loro i relativi comportamenti. Il CSS infatti ci permette di usare più classi come attributo di un tag, quindi usiamo questa feature per raggiungere il risultato voluto.
Vediamo un esempio:
.button {
color:#fff;
background: #dd0000;
...
}
.mainCTA {
font-size:24px;
font-family:"CustomFontFamily", Arial, Serif;
line-height:40px;
}
.CTA {
height:50px;
line-height:50px;
}
<input type="submit" class="button mainCTA CTA" />
Da un punto di vista formale questo è senz'altro corretto, ed il principio che c'è dietro in sostanza è quello di assegnare una classe diversa al tag per ogni variazione ("delta" in gergo), rispetto al comportamento più comune.
Soprattutto se dietro non c'è una strutturazione a monte, forte e progettata si farà molto presto ad arrivare ad un markup HTML di questo genere:
<input type="submit" class="button mainCTA CTA homepage user variant big" />
Ovviamente questo può essere bypassato usando le proprietà principali del CSS, ovvero cascading e ereditarietà delle direttive. Il problema che avremmo in questo caso però sarebbe ri-spostare la complessità all'interno del file CSS, per cui
.user-form .button {
direttive
}
#homepage .user-form .button {
altre direttive
}
#homepage .variant .user-form .button {
ancora altre direttive
}
Chiaramente il markup torna ad essere abbastanza pulito, ma il CSS aumenta la sua complessità in maniera esponenziale. Per un lungo periodo queste ed altre considerazioni sono state all'ordine del giorno come mali necessari nell'uso di CSS come linguaggio per stilare le pagine web e rendere efficaci, accattivanti e gradevoli.
È evidente però che l'esplosione delle righe di CSS dovuta alla giusta necessità di far ricadere su un foglio di stile l'aspetto grafico di una pagina, assieme alla complessità del Cascading necessaria a gestire i delta di cui dicevamo, contribuiscono - fra le altre cose - a rendere poco leggibile e ancor meno mantenibile un progetto web e il suo relativo CSS.
Il CSS come linguaggio di programmazione
Negli ultimi anni, se non mesi, sono proliferate una serie di alternative che rendono un po' più ibrido il linguaggio CSS, avvicinandolo un po' ai linguaggi di programmazione propriamente detti.
Il loro punto in comune infatti è quelli di mettere a disposizione del frontend developer una sintassi molto simile, ma non identica al CSS che viene processata per ottenere come risultato finale un vero file CSS compilato.
Dal punto di vista concettuale non è diverso da quel che succedeva con un pezzo di codice compilato ad esempio nel linguaggio C.
È chiaro: stiamo introducendo un livello di astrazione che può sembrare improprio comparato con l'uso che è assegnato ai CSS. Utilizzare concetti di compilazione, preprocessing, può sembrare fuori luogo.
Analizziamo però quali vantaggi hanno genericamente i linguaggi di CSS preprocessed in comune tra loro:
- Variabili (quindi definizioni di color, font-family, insiemi di direttive css)
- Funzioni native
- Funzioni custom
- "Blocchi" di direttive
Già solo con queste quattro caratteristiche possiamo intuire alcune potenzialità. Di tutti i linguaggi preprocessed che esistono in questa sede analizziamo le caratteristiche di LESSCSS. In particolare, per rendere quest'articolo più completo possibile analizziamo la sintassi che compete il compilatore disponibile da lesscss.org: less.js.
Installare LESS CSS
Per cominciare, vediamo come utilizzare LESS nelle nostre pagine. È molto semplice, dobbiamo importare il codice del compilatore e un foglio di stile di base:
<link rel="stylesheet/less" type="text/css" href="styles.less">
<script src="less.js" type="text/javascript"></script>
Come si vede l'attributo rel="stylesheet"
è esteso avendo scritto rel="stylesheet/less"
e subito dopo si aggiunge un js che compila a runtime il file .less.
Ovviamente questa pratica è ottima quando state sviluppando il sito web, ma è più che deprecabile se il sito è in produzione! In quel caso dovrete passare per un'estensione della vostra procedura di deploy, che unisca anche la compilazione e possibile minificazione del file css di output.
Per Mac OS X il compilatore si chiama LESS.app ed è gratuito. Per Windows ci sono diverse soluzioni come WinLess e lessc, ma esiste anche SimpLess una soluzione cloud molto comoda.
Con LESSCSS codice più compatto
Una delle cose che salta più velocemente agli occhi, analizzando un file less rispetto al suo omologo compilato CSS è la compattezza. Il motivo fondamentale è che LESSCSS introduce una notazione diversa per dichiarare blocchi che sono in gerarchia cascading: mentre nel classico CSS avremmo scritto:
.container {
…
}
.container .inner {
…
}
.container .inner h2 {
…
}
La notazione di LESSCSS è incredbilmente più compatta:
.container {
…
.inner {
…
h2 {
…
}
}
}
I blocchi assumono velocemente una complessità non indifferente che si può ovviare inserendo dei semplici commenti. Con la convenzione che consigliamo il blocco precedente diventa più leggibile:
.container {
…
.inner {
…
h2 {
…
// h2 end
}
// inner end
}
// container end
}
Questa notazione fa emergere un'altra caratteristica di LESSCSS: possiamo inserire i commenti per una singola riga con il doppio slash (//
), come in Javascript.
Le variabili
Inizialmente abbiamo anticipato la presenza di variabili, e queste in effetti sono una delle cose più utili e interessanti proposte dal framework.
Se abbiamo la sana abitudine di definire un commento iniziale in cui "dichiarare" le color palette del documento, le font-family, e le altre proprietà visive, ci sarà subito familare l'uso che consigliamo: all'inizio del vostro file .less è molto sensato, ma non obbligatorio, definire tutte le variabili, con la seguente sintassi:
@nomevariabile = VALORE
È consigliabile iniziare definendo le palette di colori:
@primary-color: #FF0000;
@secondary-color: #00FF00;
…
Un piccolo consiglio è quello di NON usare dei nomi che fanno riferimento a uno schema di colori preciso. Poiché sono variabili e con CSS oramai possiamo quasi del tutto evitare immagini che danno aspetti di colore ai layout, potrà capitare di cambiare completamente lo schema di colori. Cerchiamo di definire dei nomi astratti e semantici il più possibile, e di mantenere il più compatto possibile nel numero le variabili.
Il senso delle variabili LESSCSS è piuttosto esteso: si può assegnare a una variabile un'intera proprietà CSS. Quel che facciamo più estensivamente ad esempio è definire una variabile specifica per una font-size
di base, e una line-height
di base.
Operatori
Analogamente a quel che faremmo con l'unità di misura in em, LESSCSS introduce degli operatori aritmetici:
+ - * /
Questi applicano le corrispondenti e arcinote operazioni (che non rispiegheremo ;)).
Perciò, per definire una
@base-font-size: 13px;
ci può permettere all'interno del nostro stile di fare operazioni del tipo
.selector {
font-size: @base-font-size + 5px;
}
Questo può valere anche su posizioni, margini e quant'altro.
I mixin
Un altro vantaggio veramente interessante di LESSCSS è quello di poter definire dei mixin, parametrici e non. In sostanza si tratta di avere un blocco che può essere ripreso in maniera molto compatta durante lo sviluppo:
.fancy-font {
font-family: "CustomFamily", Arial, sans-serif;
font-size:16px;
color: @primary-color;
}
comincia ad avere un senso compiuto. Stiamo dicendo che la classe fancy-font
si occupa di definire quelle caratteristiche, ivi compresa una font custom.
Possiamo certamente assegnarla a un elemento HTML nel classico modo <h2 class="fancy-font">
ma la cosa più interessante è un'altra.
All'interno del nostro file .less possiamo, una volta definito prima questo blocco, come in tutti i linguaggi top-down, dichiarare un blocco del genere:
#header {
h1,h2,h3 {
.fancy-font;
}
h1 {
font-size: @base-font-size + 13px;
}
h2 {
font-size: @base-font-size + 7px;
}
}
La sua versione compilata sarà
.fancy-font {
font-family: "Arial", Helvetica, sans-serif;
}
#header h1, #header h2, #header h3 {
font-family: "Arial", Helvetica, sans-serif;
}
#header h1 {
font-size: 26px;
}
#header h2 {
font-size: 20px;
}
Ovviamente se scordiamo di chiudere una graffa o di mettere un punto e virgola il compilatore restituirà un errore o una versione sbagliata del file, e ce ne accorgeremo debuggando il sito. Questo è molto molto utile: spesso lavorando con i fogli di stile capita di avere fatto un errore veniale, e di non riuscure ad accorgercene se non dopo vari tentativi e altrettante maledizioni lanciate.
Mixin e funzioni
La naturale e più potente estensione dei mixin si verifica quando li utilizziamo in combinazione con le variabili, trasformando di fatto i mixin in vere funzioni.
.fancy-font(@header-size: 20px, @color, @padding) {
font-family: "CustomFamily", Arial, sans-serif;
font-size:@header-size;
color: @color;
padding: @padding;
}
In questo blocco vediamo alcune cose notevoli. Intanto lo scope delle variabili dichiarate come argomento della funzione è ovviamente legato al namespace della funzione stessa. Quindi la variabile @header-size
non esiste al di fuori di questa funzione.
Altra cosa notevole è la potenza che si mostra in questo uso: ovviamente richiamare la funzione con dei valori diversi restituirà del CSS compilato diverso, con alcune caratteristiche di base. Questo è davvero molto utile se ad esempio, come dovrebbe, nel nostro sito progettato ci sono classi di elementi che hanno dei comportamenti visuali del tutto simili.
Facendo lo sforzo di astrarre i comportamenti simili e utilizzando questa funzione, durante lo sviluppo è possibile non solo rendere più compatto il codice:
.fancy-font(30px, #ff0000, 10px);
.fancy-font(20px, #00ff00, 10px);
.fancy-font(10px, @secondary-color, 10px);
è decisamente più rapido da scrivere, ma sarà anche più compatto il processo mentale di scrittura perché assegneremo un comportamento visuale a una funzione e, una volta metabolizzato, ci risulterà davvero veloce la scrittura dello stile, risparmiando odiose e inutili ripetizioni, che invece lasciamo al compilatore:
#header h1 {
font-family: "CustomFamily", Arial, sans-serif;
font-size: 30px;
color: #ff0000;
padding: 10px;
}
#header h2 {
font-family: "CustomFamily", Arial, sans-serif;
font-size: 20px;
color: #00ff00;
padding: 10px;
}
#header h3 {
font-family: "CustomFamily", Arial, sans-serif;
font-size: 10px;
color: #ff00ff;
padding: 10px;
}
#header h1 {
font-size: 26px;
}
#header h2 {
font-size: 20px;
}
È chiaro che si possono anche mischiare le due tecniche di mixin: se infatti abbiamo l'abitudine di definire:
.heading {
margin:0;
font-weight:700;
text-transform:uppercase;
}
potrmo cambiare la funzione precedente così:
.fancy-font(@header-size: 20px, @color, @padding) {
font-family: "CustomFamily", Arial, sans-serif;
font-size:@header-size;
color: @color;
padding: @padding;
.heading;
}
compilando il file less, avremo:
.heading {
margin: 0;
font-weight: 700;
text-transform: uppercase;
}
#header h1 {
font-family: "CustomFamily", Arial, sans-serif;
font-size: 30px;
color: #ff0000;
padding: 10px;
margin: 0;
font-weight: 700;
text-transform: uppercase;
}
#header h2 {
font-family: "CustomFamily", Arial, sans-serif;
font-size: 20px;
color: #00ff00;
padding: 10px;
margin: 0;
font-weight: 700;
text-transform: uppercase;
}
#header h3 {
font-family: "CustomFamily", Arial, sans-serif;
font-size: 10px;
color: #ff00ff;
padding: 10px;
margin: 0;
font-weight: 700;
text-transform: uppercase;
}
#header h1 {
font-size: 26px;
}
#header h2 {
font-size: 20px;
}
È chiaro però che questa libertà può portare a delle incongruenze che dipendono solo da noi. Infatti in questo caso avremmo associato una classe che sembra appartenere ai titoli del nostro sito a tutti coloro che usano .fancy-font()
.
Non è sbagliato nè giusto: basta pensare bene a cosa fare.
Altre interessanti fattispecie da analizzare per LESSCSS è quella delle funzioni di modifica di colore:
lighten(@color, 10%); // return a color which is 10% *lighter* than @color
darken(@color, 10%); // return a color which is 10% *darker* than @color
saturate(@color, 10%); // return a color 10% *more* saturated than @color
desaturate(@color, 10%); // return a color 10% *less* saturated than @color
fadein(@color, 10%); // return a color 10% *less* transparent than @color
fadeout(@color, 10%); // return a color 10% *more* transparent than @color
spin(@color, 10); // return a color with a 10 degree larger in hue than @color
spin(@color, -10); // return a color with a 10 degree smaller hue than @color
Anche queste caratteristiche danno una grandissima libertà al designer perché, in effetti, una volta definita una limitata palette di colori, si può declinare tutta una serie di altre tonalità a partire proprio da quelle variabili colore di base.
Namespace e scope delle variabili
Introduciamo un paio di concetti legati alla programmazione: namespace e scope delle variabili. Per quanto riguarda il primo concetto, è chiaro che un namespace ha un carattere di organizzazione del codice.
#bundle {
.button () {
display: block;
border: 1px solid black;
background-color: grey;
&:hover { background-color: white }
}
.tab { ... }
.citation { ... }
}
Se volessimo assegnare a un #header
a solo le proprietà di button()
dentro #bundle
potremmo scrivere:
#header a {
color: orange;
#bundle > .button;
}
Questo fa in modo di "pescare" solo le proprietà che ci interessano e null'altro.
Il concetto di scope è invece più legato alla "validità" delle variabili nei blocchi di codice, un po' come succede nei linguaggi di programmazione tradizionali. Vediamo un esempio:
@var: red;
#page {
@var: white;
#header {
color: @var; // white
}
}
#footer {
color: @var; // red
}
Import
È spesso necessario importare dei css all'interno del proprio foglio di stile. Ebbene con questa strategia quel che vi consigliamo di fare è avere un file separato che sia di definition di variabili che poi importerete con una notazione identica a quella di CSS.
@import "definition.less";
Il risultato di questa operazione sarà, una volta compilato il file, un unico file in cui tutto viene incluso. Il vantaggio che avremo, potendo minificare il file, sarà minimizzare le chiamate HTTP di caricamento che quindi ottimizzerà di gran lunga caching e prestazioni di caricamento del sito.
Stringhe
Chiudiamo ricordando che spesso la manutenibilità e la riproducibilità di ambienti diversi può rendere utile definire come variabili anche delle stringhe.
A cosa servono? Ebbene supponiamo di dover spostare un progetto cambiando il file system dal server di sviluppo al server di produzione o per qualunque altra ragione che rende non perfettamente allineati gli ambienti, in questi casi definire:
@base-url: "http://www.example.com";
background-image: url("@{base-url}/images/bg.png");
Ci permette di avere una stringa che contiene una base url per poter ridefinirla in qualunque momento.
Conclusioni
Se ci si chiede se abbia veramente senso usare un altro linguaggio per generare un CSS, la risposta può essere affermativa. Soprattutto è interessante per i possibili sviluppi:
- il codice è più manutenibile
- i blocchi innestati rendono più chiare le dipendenze e le ereditarietà
- i mixin permettono di gestire come abbiamo visto le proprietà più gustose di CSS3, senza dover ricordare tutte le singole fattispecie
Insomma, come dicono gli inglesi, vale un colpo.
Infine, per qualche ragione a noi ignota il porting PHP di less ha una sintassi leggermente ma significativamente diversa, che potete trovare qui. Imploriamo da queste colonne lo sviluppatore a voler unificare questa sintassi, perché troviamo piuttosto insensato deviare da una sintassi standard. A chi capita di lavorare su più tecnologie risulta davvero difficile doversi adattare, o passare da una all'altra sintassi.
Spesso optiamo per la soluzione unica, non server side, su less.js
.
Link utili
- SimpLess - compilatore LESS (applicazione cloud)
- WinLess - compilatore LESS per Windows
- lessc.exe - compilatore LESS per Windows
- less.app - Compilatore LESS per Mac OS
- Scrivere i CSS con Less (di Andrea Ottolina)