Reattività dei token di progettazione

Funzionalità web: Query di stile del container

Contesto e panoramica

Spesso un autore deve apportare modifiche contestuali al design di un componente. In passato, gli autori dovevano utilizzare i selettori per applicare queste modifiche. Ciò significava spesso che, mentre molti dei loro token di progettazione potevano esistere come proprietà personalizzate, i token di progettazione di ordine superiore potevano essere codificati solo come pattern di selettore (ovvero utilizzando un nome di classe o una convenzione di attributo) o come proprietà/contesto in un framework JavaScript.

Le query sugli stili dei container consentono agli autori di applicare stili agli elementi in base al valore della proprietà personalizzata calcolata di un elemento predecessore. Ciò significa che gli autori possono scrivere valori significativi dei token di progettazione all'interno dei loro fogli di stile, anziché fare affidamento sul markup o su JavaScript per la presentazione.

Come implementare

L'implementazione di un token di design reattivo utilizzando una query di stile del container è piuttosto semplice:

  1. Imposta il token di progettazione di ordine superiore come proprietà personalizzata su un container. Non è necessario che si tratti di una proprietà personalizzata registrata.
  2. Utilizza la regola @container style() per eseguire una query sul valore della proprietà personalizzata.
  3. Applica gli stili appropriati a un elemento discendente all'interno del container.

Alcuni dettagli da tenere presenti:

  • Il container su cui viene eseguita la query con una query di stile non deve avere un container-type o un container-name impostato, tuttavia un container-name può consentire query più specifiche.
  • Il container stesso non può essere formattato dalla query di stile del container.

Di seguito è riportato un esempio di base dei passaggi di implementazione descritti sopra.

<div class="features">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
.features {
  --density: spacious;
}

.bugs {
  --density: compact;
}

@container style(--density: compact) {
  .card {
    padding: 8px;
  }
}

@container style(--density: spacious) {
  .card {
    padding: 24px;
  }
}

Strategie di riserva

Finché non sarà disponibile il supporto di Baseline per le query sugli stili dei container, NON È CONSIGLIATO utilizzarle per le funzionalità principali che devono essere disponibili in tutti i browser, poiché non è semplice creare un fallback che non ne riduca i vantaggi o che non abbia limitazioni proprie. Ad esempio, se una preferenza utente relativa alla densità della UI non è considerata una funzionalità principale che deve essere disponibile in tutte le esperienze, è possibile utilizzare le query di stile del container per implementare la funzionalità senza un comportamento di riserva.

Utilizzo dei selettori

Per le funzionalità principali, è necessario utilizzare un approccio alternativo che utilizzi i selettori. Questo esempio utilizza un attributo data-density per codificare il token di progettazione della densità nel markup anziché come proprietà personalizzata:

<div class="features" data-density="spacious">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs" data-density="compact">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
/* This example uses `:where()` to avoid increasing specificity */
:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

Una limitazione importante di questo approccio di riserva è che non supporta l'annidamento di elementi con l'attributo data-density impostato, poiché la specificità del selettore è la stessa, l'ordine di visualizzazione verrà utilizzato per determinare gli stili (ovvero [data-density="spacious"] avrà sempre la precedenza su [data-density="compact"]).

Utilizzo delle query di stile come potenziamento progressivo

Anche se NON È CONSIGLIATO, se vuoi utilizzare le query di stile come potenziamento progressivo per una funzionalità principale, per evitare la duplicazione puoi creare alcune proprietà personalizzate e poi includere le query di stile. Assicurati che l'approccio di riserva utilizzi :where() quando selezioni gli elementi del contenitore per evitare di aumentare la specificità.

.card {
  --card-padding-compact: 8px;
  --card-padding-spacious: 24px;
}

:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

/* Use style queries as a progressive enhancement: same specificity, so order of appearance is used */

@container style(--density: compact) {
  .card {
    padding: var(--card-padding-compact);
  }
}

@container style(--density: spacious) {
  .card {
    padding: var(--card-padding-spacious);
  }
}

Controllo delle funzionalità solo con CSS

Se devi verificare lo stile del contenitore per visualizzare in modo condizionale una UI che si basa su questo stile, puoi utilizzare una query di stile:

:root {
  --style-queries-supported: check;
}

.density-toggle {
  display: none;
}

@container style(--style-queries-supported) {
  .density-toggle {
    display: revert;
  }
}

Controllo delle funzionalità con JavaScript

Per il controllo delle funzionalità con JavaScript, è un po' più complicato, poiché l'interfaccia CSSContainerRule esisteva prima dell'aggiunta delle query sugli stili dei container, quindi non è affidabile per questo scopo. Dovrai invece controllare il valore calcolato di una proprietà nota che viene impostata con una query di stile.

Questo esempio utilizza una proprietà personalizzata, in quanto non avrà alcun effetto visivo:

:root {
  --style-queries-supported: check;
}

@container style(--style-queries-supported: check) {
  body {
    --style-queries-supported: yes;
  }
}

Poi controlla il valore calcolato in JavaScript in questo modo:

if (getComputedStyle(document.body).getPropertyValue("--style-queries-supported") === "yes") {
  // Use container style queries
} else {
  // Use fallback strategy
}