Skip to content

Appunti SCSS

Published:  at 10:00 AM

Queste sono le note della lezione del corso su SCSS. Nella lezione su CSS abbiamo dato un aspetto alla pagina del menu di Awesome Pizza. Da allora lo stylesheet è cresciuto, e con lui i problemi: il rosso del logo è ripetuto in dieci punti diversi, gli stili della card stanno lontani da quelli del suo hover, e ogni modifica richiede una caccia al tesoro.

CSS, da solo, offre pochi strumenti per organizzare il codice. I preprocessori nascono per questo: scriviamo in un linguaggio più ricco, e un compilatore lo traduce in CSS normale. Il browser non sa nemmeno che esistono, perché quello che riceve è sempre e solo CSS.

Noi useremo Sass, il preprocessore più diffuso, nella sua sintassi SCSS. Esiste anche una sintassi alternativa basata sull’indentazione (i file .sass), ma la SCSS ha un vantaggio che ci semplifica la vita: è un soprainsieme del CSS, quindi qualsiasi file CSS valido è già un file SCSS valido. Potete rinominare style.css in style.scss e partire da lì.

Variabili

Il primo problema da risolvere è il rosso ripetuto in dieci punti. SCSS permette di dichiarare variabili con il prefisso $:

$rosso-pomodoro: #e63312;
$grigio-testo: #333333;
$spazio-base: 16px;

.card {
  padding: $spazio-base;
  color: $grigio-testo;
}

.prezzo {
  color: $rosso-pomodoro;
}

Il giorno in cui la pizzeria rifà il logo e cambia il rosso, lo cambiate in una riga sola.

Una precisazione doverosa: anche il CSS moderno ha le sue variabili, le custom properties (--rosso-pomodoro e var(--rosso-pomodoro)). Vivono nel browser e si possono cambiare a runtime, mentre le variabili SCSS spariscono alla compilazione. Sono strumenti diversi che convivono benissimo; per gli scopi di questa lezione le variabili SCSS ci bastano.

Nesting

In CSS, gli stili della card e quelli delle sue parti sono regole separate, anche se concettualmente sono un blocco unico. SCSS permette di annidarle:

.card {
  padding: $spazio-base;
  border: 1px solid #dddddd;

  h3 {
    margin-top: 0;
  }

  .prezzo {
    color: $rosso-pomodoro;
    font-weight: bold;
  }

  &:hover {
    border-color: $rosso-pomodoro;
  }
}

Il compilatore genera i selettori per noi: .card h3, .card .prezzo e così via. Il carattere & rappresenta il selettore corrente, e serve quando il selettore generato non deve avere lo spazio in mezzo: &:hover diventa .card:hover, che è quello che vogliamo.

Un’avvertenza che vi risparmierà parecchio refactoring: fermatevi a due, massimo tre livelli di annidamento. Il nesting profondo genera selettori chilometrici con specificità altissima, e nella lezione su CSS abbiamo visto dove porta la corsa alla specificità. Se vi trovate al quarto livello, è il momento di dare una classe all’elemento.

Partials e @use

Il secondo problema era lo stylesheet monolitico. SCSS permette di spezzarlo in file più piccoli, i partials, che per convenzione iniziano con un underscore:

styles/
├── _variables.scss
├── _menu.scss
├── _form.scss
└── main.scss

L’underscore dice al compilatore di non produrre un file CSS per quel partial: verranno inclusi tutti in main.scss, l’unico file compilato davvero.

// main.scss
@use "variables" as *;
@use "menu";
@use "form";

Con @use ogni file è un modulo, e le variabili di un modulo si usano con il prefisso del modulo stesso (variables.$rosso-pomodoro). Il suffisso as * toglie il prefisso, comodo per il file delle variabili che usiamo ovunque.

Nei progetti più datati troverete @import al posto di @use: faceva un lavoro simile ma con tutte le variabili in un unico calderone globale, ed è ufficialmente deprecato. Se partite da zero, usate @use.

Mixin

Un mixin è un blocco di dichiarazioni riutilizzabile. Il classico esempio è la centratura con flexbox, che in un progetto si scrive decine di volte:

@mixin flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.menu {
  @include flex-center;
  flex-wrap: wrap;
  gap: $spazio-base;
}

I mixin accettano anche argomenti, con eventuali valori di default:

@mixin card($colore-bordo: #dddddd) {
  padding: $spazio-base;
  border: 1px solid $colore-bordo;
  border-radius: 8px;
}

.card-pizza {
  @include card;
}

.card-ordine {
  @include card($rosso-pomodoro);
}

La card delle pizze e quella del riepilogo ordine condividono la struttura e cambiano solo il bordo, e la definizione sta in un punto solo.

Extend e placeholder

Esiste un secondo meccanismo di riuso, @extend, che si appoggia ai placeholder (selettori con il prefisso % che non producono CSS da soli):

%bottone-base {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
}

.bottone-ordina {
  @extend %bottone-base;
  background-color: $rosso-pomodoro;
}

La differenza rispetto al mixin sta nel CSS generato: il mixin copia le dichiarazioni dentro ogni selettore, @extend raggruppa i selettori sulla stessa regola. Sulla carta @extend produce CSS più compatto; in pratica il modo in cui riscrive e sposta i selettori riserva sorprese, soprattutto combinato con il nesting. Il mio consiglio spassionato: quando siete in dubbio, usate un mixin.

Funzioni

SCSS include funzioni per manipolare i valori, e quelle sui colori sono le più usate: per l’hover del bottone, invece di inventare un secondo rosso a occhio, possiamo derivarlo dal primo.

.bottone-ordina:hover {
  background-color: darken($rosso-pomodoro, 10%);
}

Nelle versioni più recenti di Sass al posto di darken e lighten si preferiscono le funzioni del modulo sass:color, come color.scale. Il concetto resta identico: i colori derivati si calcolano, non si inventano.

Come si compila

Il compilatore si installa con npm e si lancia da terminale:

sass styles/main.scss dist/main.css --watch

L’opzione --watch ricompila a ogni salvataggio. Nella pagina HTML, come sempre, si collega il CSS generato, perché è l’unica cosa che il browser capisce.

Questo è anche il momento giusto per dirvi come stanno le cose nei progetti reali: non lancerete quasi mai sass a mano, perché ci pensa il bundler o il framework di turno. Voi scrivete file .scss, la toolchain fa il resto. Il meccanismo però è esattamente quello che avete visto qui, e quando qualcosa si rompe fa la differenza sapere che sotto c’è un compilatore con i suoi input e il suo output.

Per approfondire