Corso di CSS / Container query CSS per componenti riutilizzabili

Le query sulla dimensione dei contenitori permettono a un componente di rispondere alla dimensione di un contenitore antenato idoneo piuttosto che solo al viewport. Dichiara un contenitore di query con container-type, poi usa @container per modificare gli stili dei discendenti alla dimensione appropriata.

Confronta le condizioni del viewport e del contenitore

Anche se la larghezza del browser è di 1200px, il posizionamento corretto varierà a seconda che la scheda si trovi nell'area del testo principale di 700px o nell'area della barra laterale di 300px. È difficile esprimere questa differenza utilizzando soltanto le media query CSS e i breakpoint basati sul contenuto, dove le due schede condividono le stesse condizioni di viewport. Quando si riutilizzano parti, è possibile ridurre le regole di eccezione specifiche del layout modificando la loro configurazione interna in base alla larghezza del genitore.

divisione Criteri di giudizio Ruolo appropriato
@media Ambiente come la larghezza del viewport Numero di colonne nella pagina, posizione della barra laterale
@container dimensione del contenitore antenato selezionato, ecc. Posizionamento di immagini e testo all'interno della scheda, spaziatura tra le parti

Le query dei contenitori confrontano anche la larghezza dell'espressione condizionale piuttosto che “disporre orizzontalmente se lo schermo è orizzontale.” Quando la barra laterale raggiunge almeno 30rem, le schede al suo interno possono essere disposte anche orizzontalmente. Se vuoi sempre una barra laterale stretta, dovresti anche specificare una larghezza massima nel layout del genitore.

Dichiarare un contenitore per query di dimensione

.slot { container: panel / inline-size; }
@container panel (min-width: 30rem) {
  .card { display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: 1rem; }
}

container: panel / inline-size dichiara il nome panel e il contenimento della dimensione inline. Nella scrittura orizzontale, la dimensione in linea è orizzontale; la scrittura verticale cambia tale corrispondenza. Fornisci la larghezza del contenitore tramite il suo layout di blocco esterno o la traccia della griglia in modo che la scheda abbia una dimensione significativa da interrogare. Vedi Basi di CSS Grid: tracce, linee e posizionamento.

Il genitore da valutare per la condizione è .slot e il discendente che riceve lo stile condizionale è .card. Non scrivere in una struttura che modifichi la scheda stessa cercando di ottenere la dimensione della scheda stessa. Per evitare il problema delle condizioni che si ribaltano continuamente man mano che la dimensione cambia, le query di dimensione operano in relazione agli antenati appropriati.

Il contenimento delle dimensioni (size containment) inline-size rende la dimensione in linea del contenitore indipendente dai contributi di contenuto dei suoi discendenti. Un contenitore shrink-to-fit può quindi diventare inaspettatamente piccolo. Qui, un blocco o una traccia Grid forniscono la sua larghezza. L’uso del contenimento size influisce anche sull'altro asse e può comprimere un'altezza dimensionata in base al contenuto, quindi scegli il contenimento con attenzione. Per ulteriori dettagli, vedi MDN: container-type e contenimento delle dimensioni.

Riutilizza una scheda in diverse larghezze dei contenitori superiori

Salvalo come container-card.html e aprilo nel tuo browser. La casella colorata è un elemento di esempio che rappresenta l'area dell'immagine, quindi non c'è un file immagine separato. Su schermi più grandi, separa il testo principale da una barra laterale fino a 20rem, e a 44rem o meno, posiziona le due aree in una colonna.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>container-card.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
.layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); gap: 1rem; width: min(100%, 75rem); margin-inline: auto; padding: 1rem; }
.slot { min-width: 0; container: panel / inline-size; }
.card { min-width: 0; padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.media { min-height: 7rem; background: #287699; }
.card h2 { font-size: 1.25rem; } .card p { margin-bottom: 0; }
@container panel (min-width: 30rem) {
  .card { display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: 1rem; align-items: start; }
}
@media (max-width: 44rem) { .layout { grid-template-columns: minmax(0, 1fr); } }
</style>
</head>
<body>
<div class="layout">
<main class="slot"><article class="card"><div class="media" aria-hidden="true"></div><div><h2>Card in the main content area</h2><p>The image and text appear side by side when the parent is at least 30rem wide.</p></div></article></main>
<aside class="slot"><article class="card"><div class="media" aria-hidden="true"></div><div><h2>The same card in a sidebar</h2><p>The sidebar in this example stays at or below 20rem even on a wide screen, so the card keeps its vertical layout.</p></div></article></aside>
</div>
</body>
</html>

In un ambiente predefinito dove il font della radice è di 16px, 30rem corrispondono a 480px. Se il corpo genitore su uno schermo grande è di 480px o più, l'interno della scheda avrà due colonne, e in una barra laterale fino a 320px, l'interno della scheda sarà disposto verticalmente. Su schermi stretti, man mano che la barra laterale si sposta verso il basso, il suo genitore potrebbe diventare più largo. Pertanto, è normale che entrambe le schede siano posizionate orizzontalmente su uno schermo da 600px finché la larghezza del genitore soddisfa le condizioni.

La condizione esterna di 44rem e la condizione interna di 30rem risolvono problemi diversi. La prima condizione determina come posizionare l'area sull'intera pagina, e la seconda condizione determina come posizionare immagini e testo all'interno dello spazio assegnato a ciascuna scheda. Ogni scheda seleziona il più vicino antenato idoneo chiamato panel e poi ne valuta la condizione di dimensione. La selezione avviene prima del confronto: una condizione falsa non fa cercare automaticamente un altro antenato più lontano.

Scegli contenitori vicini e nominati

Una query di dimensione non nominata seleziona il contenitore antenato più vicino idoneo alla caratteristica di dimensione richiesta. Anche se l'area esterna è ampia, i risultati possono variare se viene aggiunto un contenitore più stretto in mezzo. Puoi vedere questa differenza separatamente salvando il seguente esempio come container-nested.html.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>container-nested.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
main { width: min(100%, 42rem); padding: 1rem; }
.outer { width: 100%; container: outer / inline-size; border: 2px solid #789; padding: 1rem; }
.inner { width: min(100%, 17rem); container-type: inline-size; }
.card { padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.media { min-height: 5rem; background: #287699; }
@container (min-width: 30rem) {
  .card { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: 1rem; }
}
</style>
</head>
<body>
<main><h1>Which parent's width is queried?</h1><div class="outer"><div class="inner"><article class="card"><div class="media" aria-hidden="true"></div><p>The nearest inner container has a maximum of 17rem.</p></article></div></div></main>
</body>
</html>

Su schermi ampi, il contenitore esterno è più grande di 30rem, ma il contenitore interno è al massimo di 17rem. La condizione senza nome seleziona il più vicino all'interno, quindi le schede sono disposte verticalmente. Mantieni l'altro codice e sostituisci @container (min-width: 30rem) con quanto segue:

@container outer (min-width: 30rem) {
  .card { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: 1rem; }
}

Ora selezioniamo l'antenato esterno chiamato outer, quindi la scheda è disposta orizzontalmente sullo schermo ampio. La scheda stessa rimane comunque entro il massimo di 17rem, quindi dovrai controllare separatamente se la larghezza del corpo è sufficiente. Il nome non è una proprietà che corregge automaticamente standard errati, ma una funzione che specifica quale antenato utilizzare come standard. Dopo il confronto, ripristina la condizione senza nome.

Diagnostica le query che non si applicano

  1. Verifica se il target del selettore condizionale è un discendente del contenitore. Controlla di non cercare di applicare lo stesso stile al genitore stesso.
  2. Controlla il container-type del genitore e la larghezza effettiva del contenuto negli strumenti per sviluppatori. Non ci deve essere confusione tra la larghezza esterna, includendo padding e bordo, e la dimensione da ricercare.
  3. Verifica se è stato creato un nuovo contenitore e, se necessario, specifica i criteri con container-name.
  4. Nel primo esempio imposta temporaneamente container-type: normal su .slot, mantenendo le altre regole. Poiché non rimane un antenato idoneo per quella size query, lo stile condizionale non si applica; poi ripristina inline-size. Nel secondo esempio, rimuovere il contenimento da .inner potrebbe invece fare selezionare .outer: controlla sempre quale antenato viene interrogato.

Mantieni una scheda verticale leggibile come base, poi aggiungi il posizionamento orizzontale quando si applica la query del contenitore. La base serve anche ai browser che non supportano la query di dimensione. Controlla il supporto e il comportamento nei browser di cui i tuoi lettori hanno effettivamente bisogno. Vedi MDN: Dimensioni e query di stile del contenitore.