Guida introduttiva alle query Stili

La possibilità di eseguire query sulle dimensioni in linea di un elemento principale e sui valori delle unità di query del contenitore ha recentemente raggiunto il supporto stabile in tutti i motori dei browser moderni.

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Safari: 16.

Source

Tuttavia, la specifica di contenimento include più di semplici query sulle dimensioni; consente anche di eseguire query sui valori di stile di un elemento principale. A partire da Chromium 111, potrai applicare il contenimento degli stili per i valori delle proprietà personalizzate e eseguire query su un elemento principale per il valore di una proprietà personalizzata.

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: not supported.
  • Safari: 18.

Source

Ciò significa che abbiamo un controllo ancora più logico degli stili in CSS e consente una separazione migliore del livello di logica e dati di un'applicazione dai suoi stili.

La specifica del Modulo di contenimento CSS di livello 3, che riguarda le query su dimensioni e stili, consente di eseguire query su qualsiasi stile da un elemento principale, incluse coppie di proprietà e valori come font-weight: 800. Tuttavia, durante l'implementazione di questa funzionalità, le query sugli stili funzionano attualmente solo con i valori delle proprietà CSS personalizzate. Questa operazione è ancora molto utile per combinare gli stili e separare i dati dal design. Vediamo come utilizzare le query di stile con le proprietà CSS personalizzate:

Iniziare a utilizzare le query sugli stili

Supponiamo di avere il seguente codice HTML:

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div>
  </li>
</ul>

Per utilizzare le query di stile, devi prima configurare un elemento contenitore. Questo richiede un approccio leggermente diverso a seconda che tu stia eseguendo una query su un account principale diretto o indiretto.

Eseguire query sui gruppi di primo livello

Diagramma di una query sullo stile.

A differenza delle query sugli stili, non è necessario applicare il contenimento utilizzando la proprietà container-type o container a .card-container affinché .card possa eseguire query sugli stili del suo elemento principale diretto. Tuttavia, dobbiamo applicare gli stili (in questo caso valori delle proprietà personalizzate) a un contenitore (in questo caso .card-container) o a qualsiasi elemento che contenga l'elemento a cui vogliamo applicare lo stile nel DOM. Non possiamo applicare gli stili su cui eseguiamo la query all'elemento diretto a cui applichiamo lo stile utilizzando la query perché ciò potrebbe causare cicli infiniti.

Per eseguire una query direttamente a un genitore, puoi scrivere:

/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

Potresti aver notato che la query di stile inserisce la query tra style(). Questo serve a distinguere i valori delle taglie dagli stili. Ad esempio, puoi scrivere una query per la larghezza del contenitore come @container (min-width: 200px) { … }. Gli stili verranno applicati se il contenitore principale ha una larghezza di almeno 200 px. Tuttavia, min-width può essere anche una proprietà CSS e puoi eseguire una query per il valore CSS di min-width utilizzando le query di stile. Ecco perché dovresti utilizzare il wrapper style() per chiarire la differenza: @container style(min-width: 200px) { … }.

Aggiungere stili agli elementi principali non diretti

Se vuoi eseguire query sugli stili per qualsiasi elemento che non sia un elemento principale diretto, devi assegnare a quell'elemento un container-name. Ad esempio, possiamo applicare stili a .card in base agli stili di .card-list assegnando a .card-list un container-name e facendovi riferimento nella query di stile.

/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

In genere, è buona norma assegnare un nome ai contenitori per chiarire su cosa stai eseguendo una query e per poter accedere più facilmente a questi contenitori. Un esempio di come questa funzionalità può essere utile è se vuoi applicare stili direttamente agli elementi all'interno di .card. Senza un contenitore denominato su .card-container, non possono eseguire query direttamente.

Ma tutto questo ha molto più senso nella pratica. Vediamo alcuni esempi:

Query di stile in azione

Immagine di esempio con più schede di prodotto, alcune con i tag &quot;Nuovo&quot; o &quot;Pronta consegna&quot; e la scheda &quot;Pronta consegna&quot; con sfondo rosso.

Le query sugli stili sono particolarmente utili quando hai un componente riutilizzabile con più varianti o quando non hai il controllo su tutti gli stili, ma devi applicare modifiche in alcuni casi. Questo esempio mostra un insieme di schede di prodotto che condividono lo stesso componente della scheda. Alcune schede di prodotto contengono dettagli/note aggiuntive, come "Nuovo" o "Disponibilità limitata", attivate da una proprietà personalizzata denominata --detail. Inoltre, se un prodotto è in "Disponibilità limitata", viene visualizzato con un bordo di sfondo rosso scuro. È probabile che questo tipo di informazioni venga visualizzato sul server e possa essere applicato alle schede tramite stili in linea come segue:

 <div class="product-list">
  <div class="product-card-container" style="--detail: new">
    <div class="product-card">
      <div class="media">
        <img .../>
      <div class="comment-block"></div>
    </div>
  </div>
  <div class="meta">
    ...
  </div>
  </div>
  <div class="product-card-container" style="--detail: low-stock">
    ...
  </div>
  <div class="product-card-container">
    ...
  </div>
  ...
</div>

Dati questi dati strutturati, puoi passare valori a --detail e utilizzare questa proprietà CSS personalizzata per applicare gli stili:

@container style(--detail: new) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'New';
    border: 1px solid currentColor;
    background: white;
    ...
  }
}

@container style(--detail: low-stock) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'Low Stock';
    border: 1px solid currentColor;
    background: white;
    ...
  }
  
  .media-img {
    border: 2px solid brickred;
  }
}

Il codice riportato sopra ci consente di applicare un chip per --detail: low-stock e --detail: new, ma potresti aver notato una certa ridondanza nel blocco di codice. Al momento, non è possibile eseguire query solo per la presenza di --detail con @container style(--detail), il che consentirebbe una migliore condivisione degli stili e meno ripetizioni. Questa funzionalità è attualmente in discussione nel gruppo di lavoro.

Schede meteo

L'esempio precedente utilizzava una singola proprietà personalizzata con più valori possibili per applicare gli stili. Tuttavia, puoi anche utilizzare e eseguire query su più proprietà personalizzate. Prendiamo questo esempio di scheda meteo:

Demo delle schede meteo.

Per applicare uno stile ai gradienti e alle icone di sfondo di queste schede, cerca le caratteristiche del meteo, ad esempio "nuvoloso", "piovoso" o "soleggiato":

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

In questo modo, puoi applicare uno stile a ogni scheda in base alle sue caratteristiche uniche. Tuttavia, puoi anche applicare uno stile alle combinazioni di caratteristiche (proprietà personalizzate) utilizzando il combinatore and nello stesso modo delle query sui media. Ad esempio, una giornata nuvolosa e soleggiata avrà il seguente aspetto:

@container style(--sunny: true) and style(--cloudy: true) {
    .weather-card {
      background: linear-gradient(24deg, pink, violet);
    }
  
  .weather-card:after {
      content: url(<data-uri-for-demo-brevity>);
      background: violet;
  }
}

Separazione dei dati dal design

In entrambe le demo, la separazione del livello dati (DOM che verrà visualizzato nella pagina) dagli stili applicati presenta un vantaggio strutturale. Gli stili sono scritti come possibili varianti all'interno dello stile dei componenti, mentre un endpoint potrebbe inviare i dati che verranno poi utilizzati per applicare lo stile al componente. Puoi utilizzare un singolo valore, come nel primo caso, aggiornando il valore --detail, o più variabili, come nel secondo caso (impostando --rainy o --cloudy o --sunny. E la parte migliore è che puoi combinare anche questi valori. Se controlli sia --sunny che --cloudy, potresti mostrare uno stile parzialmente nuvoloso.

L'aggiornamento dei valori delle proprietà personalizzate tramite JavaScript può essere eseguito senza problemi durante la configurazione del modello DOM (ovvero durante la creazione del componente in un framework) o in qualsiasi momento utilizzando <parentElem>.style.setProperty('--myProperty’, <value>). I

Ecco una demo che, in poche righe di codice, aggiorna il --theme di un pulsante e applica gli stili utilizzando le query di stile e la proprietà personalizzata (--theme):

Personalizza la scheda utilizzando le query di stile. Il codice JavaScript utilizzato per aggiornare i valori delle proprietà personalizzate è:

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

Le funzionalità descritte in questo articolo sono solo l'inizio. Puoi aspettarti di più dalle query dei contenitori per aiutarti a creare interfacce dinamiche e reattive. Per quanto riguarda specificamente le query sui stili, ci sono ancora alcuni problemi aperti. Una è l'implementazione di query sugli stili per gli stili CSS oltre alle proprietà personalizzate. Questo fa già parte del livello di specifiche attuale, ma non è ancora implementato in nessun browser. La valutazione del contesto booleano dovrebbe essere aggiunta al livello attuale della specifica quando il problema in sospeso sarà risolto, mentre la query sull'intervallo è prevista per il livello successivo della specifica.