Corso di CSS / Organizzare il CSS: stili condivisi, nesting e layer della cascata

Un CSS facile da mantenere separa valori condivisi, stili di base, componenti e aggiustamenti condizionali in base ai loro ruoli. La nidificazione nativa delimita i selettori correlati, mentre i layer della cascata ordinano esplicitamente i gruppi di regole e introducono le proprie regole di precedenza.

Riduci la duplicazione e assegna le responsabilità dei file

Per prima cosa, cerca duplicati dello stesso colore e spaziatura, selettori obsoleti e regole dei pulsanti quasi identiche. I valori con lo stesso ruolo vengono raggruppati in variabili, e per pulsanti diversi si dividono le regole comuni dalle differenze necessarie. Può essere meglio mantenere due componenti indipendenti piuttosto che creare condizioni complesse per eliminare tutta la duplicazione. Quando raggruppi valori comuni, controlla l'ambito e le condizioni di ereditarietà della variabile in Variabili CSS: var(), ambito, ereditarietà e valori di ripiego.

/* Before cleanup: Same value repeated in multiple places */
.lesson-button { padding: .65rem 1rem; color: #166b92; }
.notice-button { padding: .65rem 1rem; color: #166b92; }

/* After organizing: sharing common shapes and roles */
.button { padding: .65rem 1rem; color: var(--accent); }
.button.text-alert { color: #9d2632; }

Il confronto sopra è un codice parziale che spiega il criterio della riorganizzazione. Una classe di pulsante comune deve essere assegnata anche agli elementi HTML, e se rinomini solo il CSS e lasci l'HTML com'è, gli stili previsti potrebbero non applicarsi. L'intero esercizio qui sotto esegue questa configurazione suddivisa in file e livelli.

file responsabilità Domanda da porsi durante la revisione
tokens.css Colori comuni e spaziatura È un valore condiviso da più componenti?
base.css Font, margine predefinito, indicatore di focus Questo è il comportamento predefinito per l'intero documento?
layout.css Area dei contenuti e layout È la relazione posizionale dei componenti?
components.css Forma e stato di schede e pulsanti È legato a un solo componente?
utilities.css Piccole classi ausiliarie esplicite È un'eccezione che si applicherà solo in pochi casi?

Per i documenti piccoli, è sufficiente organizzare le sezioni con commenti all’interno di un solo file. Considera di separare i file quando più persone modificano componenti diversi o quando aumenta l'ambito di riutilizzo. I nomi dei file aiutano a orientarsi e la semplice divisione del file non elimina i conflitti dei selettori. Bisogna anche gestire i percorsi di collegamento HTML e la cascata.

Distingui il genitore e i discendenti nell'annidamento

L'annidamento CSS nativo viene interpretato direttamente dal browser. Questo è diverso dal modo in cui i preprocessori come Sass generano CSS differenti durante la fase di build. Il codice sottostante posiziona il titolo della .card vicino allo stato della card stessa.

.card {
  & h2 { color: var(--accent); }
  &:hover { border-color: var(--accent); }
}

& h2 rappresenta l'h2 all'interno della scheda, e &:hover rappresenta l'hover della scheda stessa. Se inserisci uno spazio come & :hover, il risultato sarà diverso perché seleziona il discendente al passaggio del mouse. Distinguere anche tra &.warning, che seleziona una classe ausiliaria dello stesso elemento, e & .warning, che seleziona i discendenti.

/* The simple overlap above is expanded */
.card h2 { color: var(--accent); }
.card:hover { border-color: var(--accent); }

Con questo selettore genitore singolo, l'annidamento può essere confrontato direttamente con le regole espanse. Un elenco di genitori separati da virgole può far sì che la specificità del selettore annidato dipenda dal selettore più specifico in quell'elenco. Pertanto, l'annidamento complesso non è semplice concatenazione di stringhe. Evita profondità inutili che legano il CSS a una gerarchia HTML rigida. Per ulteriori dettagli, vedi la guida all'uso dell'annidamento MDN, e la spiegazione del nesting e della specificità MDN: nesting e specificità.

Verifica il supporto all'annidamento del browser che devi supportare, e se hai bisogno di un ambiente che non lo supporta, scegli il CSS espanso come sopra o un adeguato strumento di build. Solo perché usi una nuova sintassi, non dare per scontato che si applichi a tutti i tuoi visitatori.

Confronta i layer della cascata prima della specificità

Per le dichiarazioni normali dello stesso autore, le dichiarazioni fuori dai layer prevalgono su quelle nei layer. All’interno dei layer, quelli successivi nell’ordine stabilito prevalgono sui precedenti. Una regola di classe in un layer successivo può quindi prevalere su una regola con ID in un layer precedente. A parità delle condizioni precedenti, inclusi layer e importanza, si confrontano poi la specificità descritta in Ereditarietà, specificità e cascata CSS, la prossimità dell’ambito e l’ordine delle dichiarazioni.

@layer tokens, base, layout, components, utilities;
@layer base { #layer-target { color: #183044; } }
@layer utilities { .text-alert { color: #9d2632; } }

Il pulsante di esempio corrisponde a entrambi i selettori. Tra le dichiarazioni normali, il layer utilities viene dopo, quindi si applica il colore rosso. Prevedere il risultato solo perché un pulsante ha una specificità elevata dovuta a un ID può essere sbagliato. L'ordine dei livelli è determinato dall'ordine in cui i loro nomi vengono registrati per la prima volta, e anche se riapri lo stesso livello più tardi, quel layer non cambia posizione nell’ordine stabilito.

Le dichiarazioni !important hanno la precedenza sulle dichiarazioni normali nel confronto qui descritto, e l'ordine dei livelli per le dichiarazioni importanti è invertito. Le dichiarazioni importanti di un layer precedente prevalgono su quelle di un layer successivo; le dichiarazioni importanti nei layer prevalgono su quelle importanti fuori dai layer. Questo esempio è limitato al confronto tra livelli di CSS dello stesso autore e le loro proprietà. Non si estende ad altre condizioni della cascata, come stili utente/browser, dichiarazioni inline e transizioni in corso, dicendo: “Devi solo guardare i livelli.” È possibile verificare le condizioni complete nel documento MDN: @layer.

Crea il file HTML e cinque file CSS

Crea la struttura sottostante in una cartella vuota. Tutti i file sono salvati in UTF-8. Poiché index.html utilizza il percorso relativo styles/..., il file CSS deve trovarsi nella sottocartella styles. Puoi aprire index.html con un browser senza un server separato o una libreria esterna. Se non riesci a leggere il file, confronta la posizione del file e l'indirizzo di connessione con Applicare CSS con stili inline, interni ed esterni.

stylesheet-project/
  index.html
  styles/
    tokens.css
    base.css
    layout.css
    components.css
    utilities.css

index.html contiene i tag link e le classi effettive. Il pulsante di confronto non è un pulsante per eseguire un’azione, ma serve nell’esercizio per verificare il colore del testo applicato e il focus.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>CSS files and layers by responsibility</title>
  <link rel="stylesheet" href="styles/tokens.css">
  <link rel="stylesheet" href="styles/base.css">
  <link rel="stylesheet" href="styles/layout.css">
  <link rel="stylesheet" href="styles/components.css">
  <link rel="stylesheet" href="styles/utilities.css">
</head>
<body>
  <main class="demo">
    <h1>Organizing a small stylesheet</h1>
    <article class="card">
      <h2>Scope nested rules to the card</h2>
      <p>The card heading uses the accent color. Compare it with the heading outside the card.</p>
      <button class="button" type="button">Regular button</button>
    </article>
    <section><h2>Heading outside the card</h2><p>Use the default font color.</p></section>
    <button id="layer-target" class="button text-alert" type="button">Layer comparison button</button>
  </main>
</body>
</html>

styles/tokens.css registra prima l’ordine dei livelli e dichiara i valori comuni. Gli altri file riaprono un layer con lo stesso nome per aggiungere regole al suo interno.

@layer tokens, base, layout, components, utilities;
@layer tokens {
  :root { --ink: #183044; --accent: #166b92; --space-card: 1.25rem; }
}

styles/base.css definisce l'aspetto predefinito del documento e l'indicatore di messa a fuoco. La regola con ID permette di confrontare l’effetto dei layer sulla precedenza.

@layer base {
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; padding: 1rem; color: var(--ink); font-family: sans-serif; line-height: 1.6; }
  h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
  button { font: inherit; cursor: pointer; }
  :focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
  #layer-target { color: #183044; }
}

styles/layout.css gestisce lo spazio tra le aree di contenuto e le sezioni.

@layer layout {
  .demo { display: grid; gap: 1rem; max-width: 44rem; margin-inline: auto; }
  .demo > h1 { margin-block: 0 .5rem; }
  .demo > button { justify-self: start; }
}

styles/components.css gestisce schede e pulsanti. Il colore del titolo si applica solo all'interno della scheda, e il bordo cambia quando si passa il cursore sulla scheda o si mette a fuoco il pulsante al suo interno.

@layer components {
  .card {
    padding: var(--space-card); border: 2px solid #90a4b2; border-radius: .5rem;
    & h2 { margin-top: 0; color: var(--accent); }
    &:hover, &:focus-within { border-color: var(--accent); }
  }
  .button {
    padding: .65rem 1rem; color: var(--accent); background: #eaf4fa;
    border: 1px solid #166b92; border-radius: .3rem;
  }
}

styles/utilities.css gestisce una classe ausiliaria per il pulsante di confronto. Le dichiarazioni normali in questo file prevalgono sulle regole ID/pulsante dei livelli precedenti.

@layer utilities {
  .text-alert { color: #9d2632; }
}

Verificare separatamente file, nesting e layer

  1. Sulla schermata principale, assicurati che il titolo della scheda e il pulsante generale siano blu, il titolo fuori dalla scheda sia del colore predefinito e il pulsante di confronto sia rosso. Premi Tab per verificare se il pulsante ha un indicatore di focus.
  2. Commenta solo la regola & h2 all'interno della scheda in components.css. I titoli delle schede tornano anche al loro colore predefinito. Dopo la conferma, rimuovi il commento.
  3. Applica la classe text-alert a entrambi i pulsanti in HTML. Anche i pulsanti all'interno della scheda saranno rossi. Rimuovi poi soltanto la classe text-alert appena aggiunta al pulsante interno.
  4. Alla fine di components.css, aggiungi solo .button { color: #28633b; } al di fuori del blocco @layer. Entrambi i pulsanti diventeranno verdi. La dichiarazione normale fuori dai layer prevale su quelle normali contenute nei layer. Dopo la conferma, elimina la nuova regola.
  5. Aggiungi !important solo alla dichiarazione del colore ID in base.css. Il pulsante di confronto cambia al colore predefinito. In quello stato, se aggiungi !important al colore di utilities.css, il colore di base del livello precedente verrà mantenuto. Dopo aver confrontato, rimuovi entrambi gli !important.
  6. Rimuovi temporaneamente solo il link a components.css dall'HTML. Le forme dei componenti della scheda e del pulsante scompaiono. Riconnetti il percorso al suo stato originale e aggiorna per ripristinarlo.

Diagnostica gli stili mancanti

Controlla separatamente i problemi di separazione dei file e i conflitti dei selettori. Prima guarda in Rete o Sorgenti negli strumenti per sviluppatori del tuo browser per vedere se il file è stato effettivamente caricato, poi seleziona l'elemento per vedere se le regole corrispondono negli Stili e se qualche dichiarazione è stata sovrascritta. Se non vedi il file, controlla il percorso relativo, il nome del file e l'estensione, e se non vedi la regola, controlla la classe e il supporto della sintassi.

Anche se esiste una regola corrispondente, se una dichiarazione appare barrata, confronta layer, importanza, specificità, ambito e ordine delle dichiarazioni. Se le variabili comuni non sono come previsto, controlla anche l'ambito di dichiarazione delle variabili. Dopo la separazione, è utile per la manutenzione ricontrollare la schermata principale e il focus/hover, e correggere le divisioni di responsabilità e i selettori che hanno causato conflitti invece di continuare ad aggiungere dichiarazioni importanti non necessarie.