Sviluppo web

Cascade layers con un reset: l'ordine che toglie gli !important

@layer decide l'ordine di vittoria prima della specificità, così un reset non richiede più !important. L'ordine corretto e i tre casi in cui non basta.

In sintesi

  • Lo strato dichiarato per primo perde: l'ordine della prima dichiarazione conta più del punto in cui scrivi la regola.
  • Un reset in uno strato inferiore batte il componente anche con specificità minore: è il guadagno.
  • Il CSS di terze parti non stratificato vince su tutti gli strati: importalo con layer(vendor).

Risposta breve: @layer decide l'ordine di vittoria delle regole prima della specificità, quindi un reset in uno strato inferiore non richiede più !important. Quattro strati dichiarati in testa al foglio sostituiscono gran parte dei workaround.

L'ordine è la regola

Lo strato dichiarato per primo perde. Non conta dove scrivi la regola nel file, conta l'ordine della prima dichiarazione di ogni nome: una riga @layer reset, tokens, base, components, utilities; in testa al foglio fissa tutto il resto.

@layer reset, tokens, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; }
  img { display: block; max-width: 100%; }
  :where(h1, h2, h3) { margin: 0; font-size: inherit; font-weight: inherit; }
}

@layer tokens {
  :root { --radius: 0.5rem; }
}

@layer base {
  body { font: 1rem/1.6 system-ui, sans-serif; }
}

@layer components {
  .btn { font-weight: 600; }
}

Il reset dentro reset perde contro .btn dentro components anche quando il selettore del reset è più specifico. Questo è il guadagno: la specificità smette di essere il criterio con cui si litiga.

Dove un reset a strati va in crisi

SituazioneCosa succedeUscita
CSS di terze parti non stratificatoLe sue regole stanno fuori dagli strati e vincono su tuttoImporta con @import url(...) layer(vendor)
Stile inlineLo stile inline batte ogni stratoRimuovi lo stile inline o passa da una variabile
!important dentro uno stratoL'ordine si inverte e vince lo strato più in bassoNiente !important nel reset

Il supporto copre Chrome 99, Firefox 97 e Safari 15.4. Non ha senso un polyfill: su un browser che non conosce @layer le regole restano valide, perdono solo l'ordine garantito.

Il limite

Gli strati non risolvono i conflitti dentro lo stesso strato: lì torna la specificità. Un reset scritto con :where() ha specificità zero e resta sotto controllo; un reset con selettori lunghi riporta il problema dentro lo strato basso, dove è più difficile da vedere.

Come verificarlo

  • Apri il pannello Styles: le regole sono raggruppate per strato e l'ordine è visibile a colpo d'occhio.
  • Cerca !important nel tuo CSS: dopo un reset a strati non dovrebbe servire.
  • Aggiungi un componente nuovo e controlla di non dover alzare la specificità per farlo vedere.
  • Rinomina uno strato e verifica che l'ordine in testa al foglio sia l'unico posto da toccare.

Dove continuare

Nel catalogo: componenti shadcn, componenti e blocchi. Le guide: cosa è Tailwind CSS e tematizzare shadcn; il confronto: Tailwind contro CSS Modules.

Domande frequenti

Devo riscrivere tutto il CSS?

No. Basta dichiarare gli strati in testa e spostare un foglio alla volta: il codice fuori dagli strati continua a funzionare e vince, quindi il passaggio è graduale.

Come convivono con Tailwind?

Tailwind vive in uno strato suo, di solito <code>utilities</code>. Il reset va in uno strato precedente: così una classe del componente vince sulla base senza <code>!important</code>.

L'ordine cambia se importo un foglio dopo?

No, se gli strati sono già dichiarati. L'ordine vale dalla prima dichiarazione: un <code>@import</code> successivo entra nello strato che nomina, non in cima.

Gli strati annullano :where()?

No, sono strumenti diversi. Lo strato decide fra blocchi di regole, <code>:where()</code> azzera la specificità dentro un blocco. Si usano insieme.

Perché il mio reset non vince su un componente?

Quasi sempre perché il CSS del componente non è stratificato: le regole fuori dagli strati battono ogni strato. Importalo con <code>layer()</code> o spostalo.