Esplorazione di SMACSS: architettura scalabile e modulare per CSS
Pubblicato: 2022-03-11Quando lavoriamo su grandi progetti o con gruppi di sviluppatori, spesso scopriamo che il nostro codice è disordinato, difficile da leggere e difficile da estendere. Questo è particolarmente vero dopo che il tempo passa e torniamo a guardarlo di nuovo: dobbiamo cercare di essere nella stessa mentalità in cui eravamo quando lo abbiamo scritto.
Quindi ciò che molte persone hanno fatto è creare architetture CSS per aiutare a definire lo stile del loro codice in modo che i CSS diventino più leggibili. SMACSS , ovvero l'architettura scalabile e modulare per CSS , mira a fare proprio questo. È un insieme particolare di linee guida sull'architettura CSS di Jonathan Snook che ho adottato.
Ora, l'approccio architettonico di SMACSS è leggermente diverso da un framework CSS come Bootstrap o Foundation. Invece, è un insieme di regole, più come un modello o una guida. Analizziamo quindi alcuni modelli di progettazione CSS per scoprire come utilizzarli per rendere il nostro codice migliore, più pulito, più facile da leggere e più modulare.
Ogni struttura di progetto SMACSS utilizza cinque categorie:
- Base
- Disposizione
- Moduli
- Stato
- Tema
Base
In SMACSS, gli stili di base definiscono come dovrebbe apparire un elemento in qualsiasi punto della pagina. Sono le impostazioni predefinite. Se stai utilizzando un foglio di stile di ripristino, ciò garantisce che gli stili risultanti siano gli stessi in tutti i browser nonostante le differenze tra i valori predefiniti CSS di base interni e codificati.
In uno stile di base, dovresti includere solo selettori di elementi semplici o quelli con pseudo-classi, ma non con selettori di classe o ID. (Dovresti avere ottime ragioni per inserire la classe o l'ID al suo interno, forse solo se stai applicando uno stile agli elementi di un plug-in di terze parti e devi sovrascrivere lo stile predefinito per quel particolare elemento.)
Ecco un esempio di come dovrebbe apparire un'unità di file di base:
html { margin: 0; font-family: sans-serif; } a { color: #000; } button { color: #ababab; border: 1px solid #f2f2f2; }
Quindi dovrebbe includere dimensioni, margini, colori, bordi predefiniti e qualsiasi altro valore predefinito che prevedi di utilizzare nel tuo sito web. La tua tipografia e gli elementi del tuo modulo dovrebbero avere stili unificati che appaiono su ogni pagina e dare la sensazione e l'aspetto che fanno parte dello stesso design e tema.
SMACSS o meno, consiglio vivamente di evitare l'uso di !important
il più possibile e di non utilizzare il deep nesting, ma ne parlerò più avanti in questo post. Inoltre, se la tua pratica è usare reset CSS, questo è il posto in cui dovresti includerlo. (Preferisco usare Sass, quindi lo includo semplicemente nella parte superiore del file, piuttosto che doverlo copiare o fare riferimento ad esso separatamente dall'elemento <head>
di ciascuna pagina.)
Disposizione
Gli stili di layout divideranno la pagina in sezioni principali, non sezioni come la navigazione o forse la fisarmonica, ad esempio, ma in realtà divisioni di primo livello:
Questi layout conterranno più moduli CSS come scatole, schede, elenchi non ordinati, gallerie e simili, ma parlerò di più dei moduli nella prossima sezione. Consideriamo una pagina web di esempio per vedere cosa possiamo dividere in layout:
Qui abbiamo intestazione, principale e piè di pagina. Questi layout hanno moduli come collegamenti e logo nell'intestazione in alto, riquadri e articoli in principale e collegamenti e copyright per il piè di pagina. Di solito diamo ai layout un selettore ID, poiché non si ripetono sulla pagina e sono univoci.
Inoltre, dovresti anteporre le regole per gli stili di layout con la lettera l
per distinguerle dagli stili di modulo. Di solito qui definiresti cose specifiche per il layout, come bordo, allineamenti, margini, ecc. Anche lo sfondo di quella parte della pagina potrebbe avere senso, anche se non sembra essere altrettanto specifico del layout.
Ecco un esempio di come dovrebbe apparire:
#header { background: #fcfcfc; } #header .l-right { float: right; } #header .l-align-center { text-align: center; }
Puoi anche aggiungere questi helper per gli allineamenti che puoi usare per posizionare facilmente gli elementi semplicemente aggiungendo la classe appropriata al suo figlio o per allineare il suo testo.
Per un altro esempio, potresti utilizzare alcuni margini predefiniti su una casella di layout, come .l-margin
con un margine di 20px
. Quindi, ovunque tu voglia riempire un contenitore, un elemento, una scheda o una scatola, aggiungi semplicemente la classe l-margin
. Ma vuoi qualcosa di riutilizzabile:
.l-full-width { width: 100%; }
Non qualcosa di accoppiato internamente come questo:
.l-width-25 { width: 25px; }
Voglio prendermi un momento per parlare delle convenzioni di denominazione in SMACSS. Se non hai mai sentito parlare del concetto di namespace nei CSS, in pratica sta aggiungendo il nome all'inizio di un altro elemento per distinguerlo da qualsiasi altra cosa. Ma perché ne abbiamo bisogno?
Non so se ti sei mai imbattuto nel seguente problema. Stai scrivendo CSS e hai un'etichetta su qualcosa: inserisci gli stili che preferisci e chiami la tua classe .label
. Ma poi arrivi a un altro elemento in seguito e vuoi anche che sia .label
, ma con uno stile diverso. Quindi due cose diverse hanno lo stesso nome: un conflitto di nomi.
Lo spazio dei nomi ti aiuta a risolvere questo problema. In definitiva, sono chiamati la stessa cosa su un livello, ma hanno uno spazio dei nomi diverso, un prefisso diverso, e quindi possono rappresentare due stili diversi:
.box--label { color: blue; } .card--label { color: red; }
Modulo
Come accennato in precedenza, i moduli SMACSS sono frammenti di codice più piccoli che sono riutilizzabili nella pagina e fanno parte di un unico layout. Queste sono parti di CSS che vogliamo archiviare in una cartella separata, poiché ne avremo molte su una singola pagina. E man mano che un progetto cresce, possiamo dividere utilizzando le migliori pratiche della struttura delle cartelle, ad esempio per moduli/pagine:

Quindi nell'esempio precedente avevamo un articolo, che può essere un modulo a sé stante. Come dovrebbero essere strutturati i CSS qui? Dovremmo avere una classe .article
che può avere elementi figlio title
e text
. Quindi per poterlo mantenere nello stesso modulo, dobbiamo anteporre gli elementi figlio:
.article { background: #f32; } .article--title { font-size: 16px; } .article--text { font-size: 12px; }
Potresti notare che stiamo usando due trattini dopo il prefisso del modulo. Il motivo è che a volte i nomi dei moduli hanno due parole o i loro prefissi come big-article
. Abbiamo bisogno di due trattini per dire quale parte di esso è l'elemento figlio, ad esempio confronta big-article-title
big-article--title
big-article--text
.
Inoltre, puoi annidare i moduli all'interno dei moduli se un particolare modulo occupa un'ampia porzione della pagina:
<div class="box"> <div class="box--label">This is box label</div> <ul class="box--list list"> <li class="list--li">Box list element</li> </ul> </div>
Qui, in questo semplice esempio, puoi vedere che box
è un modulo e list
è un altro modulo al suo interno. Quindi list--li
è figlio del modulo list
e non del box
. Uno dei concetti chiave qui è utilizzare due selettori al massimo per ogni regola CSS, ma nella maggior parte degli scenari avere un solo selettore con prefissi.
In questo modo, possiamo evitare la duplicazione delle regole e anche avere selettori aggiuntivi sugli elementi figlio con gli stessi nomi, migliorando così la velocità. Ma ci aiuta anche a evitare di usare le regole di stile !important
indesiderate, che sono un segno di progetti CSS mal strutturati.
Buono (notare il selettore singolo):
.red--box { background: #fafcfe; } .red-box--list { color: #000; }
Male (notare la ripetizione all'interno dei selettori e il metodo di riferimento sovrapposto):
.red .box { background: #fafcfe; } .red .box .list { color: #000; } .box ul { color: #fafafa; }
Stato
Ciò che lo stato definisce in SMACSS è un modo per descrivere l'aspetto dei nostri moduli in diverse situazioni dinamiche. Quindi questa parte è davvero per l'interattività: vogliamo un comportamento diverso se un elemento è considerato nascosto, espanso o modificato. Ad esempio, una fisarmonica jQuery avrà bisogno di aiuto per definire quando puoi o non puoi vedere il contenuto di un elemento. Ci aiuta a definire lo stile di un elemento in un momento specifico.
Gli stati vengono applicati allo stesso elemento del layout, quindi stiamo aggiungendo una regola aggiuntiva che sovrascriverà quelle precedenti, se presenti. La regola statale ha la precedenza, in quanto è l'ultima nella catena di regole.
Come per gli stili di layout, qui tendiamo a usare i prefissi. Questo ci aiuta a riconoscerli e a dar loro la priorità. Qui usiamo il prefisso is
, come in is-hidden
o is-selected
.
<header> <ul class="nav"> <li class="nav--item is-selected">Contact</li> <li class="nav--item">About</li> </ul> </header>
.nav--item.is-selected { color: #fff; }
Qui può essere utilizzato !important
, poiché lo stato viene spesso utilizzato come modifica JavaScript e non al momento del rendering. Ad esempio, hai un elemento nascosto al caricamento della pagina. Al clic del pulsante, vuoi mostrarlo. Ma la classe predefinita è così:
.box .element { display: none; }
Quindi se aggiungi solo questo:
.is-shown { display: block; }
Rimarrà nascosto anche dopo aver aggiunto la classe .is-shown
all'elemento tramite JavaScript. Questo perché la prima regola è profonda due livelli e la sovrascriverà.
Quindi puoi invece definire la classe di stato in questo modo:
.is-shown { display: block !important; }
Questo è il modo in cui distinguiamo i modificatori di stato da quelli di layout, che si applicano solo al caricamento iniziale di una pagina. Questo funzionerà ora mantenendo i vantaggi dei selettori minimi.
Tema
Questo dovrebbe essere il più ovvio, poiché è usato per contenere le regole dei colori primari, forme, bordi, ombre e così via. Per lo più questi sono elementi che si ripetono in un intero sito web. Non vogliamo ridefinirli ogni volta che li creiamo. Invece vogliamo definire una classe univoca che aggiungeremo solo in seguito a un elemento predefinito.
.button-large { width: 60px; height: 60px; }
<button class="button-large">Like</button>
Non confondere queste regole del tema SMACSS con quelle di base, poiché le regole di base mirano solo all'aspetto predefinito e tendono ad essere qualcosa come il ripristino delle impostazioni predefinite del browser, mentre un'unità tematica è più un tipo di stile in cui dà l'aspetto finale, unico per questa specifica combinazione di colori.
Le regole dei temi possono essere utili anche se un sito ha più di un singolo stile o forse un paio di temi utilizzati in stati diversi e quindi possono essere facilmente modificati o scambiati durante alcuni eventi su una pagina, ad esempio con un pulsante di cambio tema. Per lo meno, mantengono tutti gli stili dei temi in un unico posto in modo da poterli modificare facilmente e mantenerli ben organizzati.
Metodologie organizzative CSS
Ho trattato alcuni dei concetti chiave di questa idea di architettura CSS. Se vuoi saperne di più su questo concetto puoi visitare il sito ufficiale di SMACSS e approfondire.
Sì, probabilmente puoi utilizzare metodologie più avanzate come OOCSS e BEM. Quest'ultimo copre quasi l'intero flusso di lavoro del frontend e le sue tecnologie. I selettori BEM possono funzionare benissimo per alcune persone, ma alcuni potrebbero trovarli troppo lunghi e opprimenti e anche troppo complicati da usare. Se hai bisogno di qualcosa di più semplice che sia più facile da raccogliere e incorporare nel tuo flusso di lavoro, e anche qualcosa che definisca le regole di base per te e il tuo team, SMACSS è la soluzione perfetta.
Sarà facile per i nuovi membri del team non solo capire cosa hanno fatto gli sviluppatori precedenti, ma anche iniziare a lavorarci istantaneamente, senza differenze nello stile di codifica. SMACSS è solo un'architettura CSS e fa quello che dice sulla scatola, niente di più e niente di meno.