Guida introduttiva alle query Stili

La possibilità di eseguire query sui valori delle unità di query del container e della dimensione in linea di un elemento padre ha recentemente raggiunto un supporto stabile in tutti i motori dei browser moderni.

Supporto dei browser

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

Origine

Tuttavia, le specifiche di contenimento includono molto più che semplici query sulle dimensioni, consente inoltre di eseguire query sui valori di stile di un elemento padre. 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.

Supporto dei browser

  • Chrome: 111.
  • Edge: 111.
  • Firefox: non supportato.
  • Safari: 18.

Origine

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, nel lancio di questa funzionalità, al momento le query di stile funzionano solo con i valori delle proprietà personalizzate CSS. 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:

Guida introduttiva alle 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 query di stile, devi prima configurare un elemento contenitore. Ciò richiede un approccio leggermente diverso, a seconda che si stia eseguendo una query su un elemento principale diretto o indiretto.

Esecuzione di query su elementi padre diretti

Diagramma di una query di stile.

A differenza delle query di stile, non è necessario applicare il contenimento utilizzando la proprietà container-type o container a .card-container affinché .card sia in grado di eseguire query sugli stili dell'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 a cui stiamo interrogando sull'elemento diretto a cui stiamo definendo utilizzando tale query perché ciò potrebbe causare loop 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; 
    ...
  }
}

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

Stili dei genitori 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, una best practice consiste nel assegnare un nome ai container per chiarire su cosa stai eseguendo query e per sbloccare la possibilità di accedervi più facilmente. 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 qualche esempio:

Query di stile in azione

Immagine demo con più schede di prodotto, alcune con tag &quot;nuovo&quot; o &quot;disponibilità limitata&quot; e la scheda &quot;disponibilità limitata&quot; con uno 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 determinati casi. Questo esempio mostra un insieme di schede di prodotto che condividono lo stesso componente. Alcune schede di prodotto presentano note/dettagli aggiuntivi, ad esempio "Nuovo" o "Disponibilità in esaurimento", attivati da una proprietà personalizzata denominata --detail. Inoltre, se un prodotto è in "Bassa disponibilità", il bordo diventa rosso scuro. Questo tipo di informazioni viene probabilmente visualizzato dal server e può essere applicato alle schede tramite stili in linea, ad esempio:

 <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 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 mischiarli utilizzando anche più proprietà personalizzate ed eseguendo query. Considera 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 definire lo stile di 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 sia nuvolosa che soleggiata potrebbe essere:

@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 queste demo, c'è un vantaggio strutturale nel separare il livello dati (DOM di cui verrebbe eseguito il rendering sulla pagina) dagli stili applicati. 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. La parte migliore è che puoi anche combinare questi valori. Se controlli sia --sunny sia --cloudy, lo stile potrebbe essere 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 con poche righe di codice aggiorna il --theme di un pulsante e applica gli stili utilizzando query di stile e la proprietà personalizzata (--theme):

Applica uno stile alla scheda mediante 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. Le query dei container sono molto utili per 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. Fa già parte del livello di specifica corrente, ma non è ancora implementato in alcun browser. La valutazione del contesto booleano dovrebbe essere aggiunta al livello della specifica corrente quando il problema in sospeso viene risolto, mentre è pianificata l'esecuzione di query sull'intervallo per il livello successivo della specifica.