Container Queries: uniamo le forze un’altra volta!

Noi del RICG siamo gente perseverante. Le immagini responsive sembravano essere morte e sepolte in maniera molto drammatica per quante, due o tre volte? Però “morte e sepolte per sempre” non è durato a lungo, grazie a tutti voi. Ogni volta che le immagini responsive sono state messe al tappeto, si sono rialzate un po’ più forti di prima.

L’articolo prosegue sotto

Non è difficile capire perché il percorso è stato duro: cambiare una feature così vecchia in termini di software, così stabile come l’elemento img non è una richiesta da nulla. Ma i designer e gli sviluppatori hanno emesso il verdetto: volevamo una feature che potesse far risparmiare ai nostri utenti un’enorme quantità di banda e per realizzare tale feature ci siamo ritagliati un posto allo stesso tavolo dei rappresentanti dei vari browser e degli enti che da lungo tempo si occupano degli standard. È stato un processo lungo e costellato di fallimenti, ma ci ha portato alle soluzioni che abbiamo oggi: non solo un nuovo elemento, ma anche un’intera nuova suite di miglioramenti all’elemento img. Adesso abbiamo a nostra disposizione opzioni per inviare gli asset in maniera intelligente, basandoci su una combinazione di dimensione della viewport, pixel density e supporto dei formati di file.

Dopo tutto questo progresso guadagnato così duramente, non aveva senso mollare la nostra organizzazione su GitHub e tornarcene a casa. Perciò, abbiamo cambiato la “I” in “RICG” da “Images” a “Issues” e abbiamo cambiato obiettivo: adesso puntiamo a cambiare il modo in cui tutti noi scriviamo CSS.

Il problema delle media queries#section1

Assegnare stili a un modulo che andrà ad occupare più container con una media query basata sulla viewport significa assegnare gli stili di pertinenza di quel modulo a tutti i container che potrebbe occupare. Non è la frase cosa più semplice al mondo da capire, ma è qualcosa con cui scommetto che avete tutti familiarità nella pratica.

Il sito responsive ideale è un sistema di componenti flessibili, modulari, che possono essere riassegnati a seconda del contesto. Utilizzando le librerie di pattern e strumenti come Pattern Lab di Brad Frost e Dave Olsen, dovremmo essere in grado di assegnare stili alle parti fondamentali di un sito web in maniera indipendente dal resto, assemblarli in moduli logici indipendenti dalla pagina e poi includerli nel layout della pagina in qualsiasi contesto dovrebbero occupare. Ma la realtà non è così precisa come potrebbe sembrare.

Ai fini di questa discussione, supponiamo che ci sia stato assegnato il compito di costruire una landing page per un negozio che vende degli attrezzi basati su Whitworth, un sistema di misura noto solo alle persone sufficientemente coraggiose – o sufficientemente pazze – da possedere un veicolo molto vecchio prodotto dai britannici. Io mi metto in quest’ultimo gruppo.

Wireframe che mostrano quattro larghezze di un layout di pagina di un prodotto di base.

Si tratta di un design piuttosto semplice. L’intera pagina è composta da moduli prodotto che occupano un container centrale grande e un identico modulo “featured item” che occupa un container secondario. Il layout complessivo della pagina cambia solo in un singolo breakpoint, in cui il container “featured item” si sposta nella sidebar. Ecco una demo molto scarsa che illustra quel cambiamento nel layout. Il suo CSS è facilmente comprensibile:

.col-a,
.col-b {
	clear: both;
	float: left;
}
@media( min-width: 960px ) {
  .col-a,
	  .col-b {
		clear: none;
	}
	.col-a {
		width: 70%;
	}
	.col-b {
		width: 27%;
		float: right;
	}
}

Abbiamo a che fare davvero solo con i moduli in due contesti differenti e solo nel nostro breakpoint più grande: il container primario e i “featured container” sono della stessa larghezza finché il “featured container” non diventa una sidebar. Aggiungiamo una class .featured a quel container così da poter mettere in seguito nel suo “scope” i nostri stili.

Ora che abbiamo sistemato il layout di pagina, possiamo concentrarci sul layout dei singoli moduli, che passano da un layout verticale a uno orizzontale, a seconda di quale vada meglio per lo spazio disponibile:

Il layout verticale mette il nome del prodotto sopra all'immagine del prodotto e alla descrizione; il prezzo e il pulsante “add to cart” finiscono sotto a questi. Nel layout orizzontale, l'immagine è allineata a sinistra, mentre il nome del prodotto, la descrizione, il prezzo e altre meta informazioni sono alla destra dell'immagine.

Per il layout verticale non servono molte finezze in CSS. Il flusso naturale del nostro markup fa la maggior parte del lavoro per noi: aggiungiamo giusto qualche leggera modifica per vincolare la dimensione delle immagini del nostro prodotto e per centrarle al di là della loro dimensione:

.mod img {
	display: block;
	margin: 0 auto;
	width: 100%;
	max-width: 250px;
}

Gli stili per il layout orizzontale non sono molto più difficili. Per ora, ci concentreremo sul container primario, quindi non mettiamo questi stili nello scope della nostra class .featured. Dal momento che vogliamo che i moduli ritornino al layout verticale sopra agli 800px per il layout a tre colonne, dobbiamo solo applicare gli stili di layout orizzontale tra i 400px e i 799px:

@media( min-width: 400px ) and ( max-width: 799px ) {
	.mod img {
		float: left;
		width: 30%;
		max-width: 999px;
	}
	.mod .mod-header,
	  .mod .mod-desc {
		float: right;
		width: 68%;
		padding-left: 2%;
	}
}

Ecco come funzionano questi stili in una pagina reale. Funzionano bene, fino a un determinato punto: nel nostro container “featured item”, in cui ci sarà sempre solo un modulo alla volta, nei breakpoint medi gli stili non sono proprio perfetti:

Screenshot che mostra che, nei breakpoint medi, il modulo “featured” sta usando lo stesso layout verticale degli altri moduli sulla pagina, ma è troppo largo. C'è spazio a sufficienza per usare il layout orizzontale.

Non è l’ideale ma si può sistemare: basta semplicemente scrivere una nuova media query che va a sovrascrivere a quella creata per i moduli nel container primario e poi mettere tutti i nostri stili nello scope della class .featured, che assegniamo all’elemento del container secondario:

@media( min-width: 40em ) and ( max-width: 60em ) {
	.featured .mod img {
		float: left;
		width: 30%;
	}
	.featured .mod .mod-header,
	  .featured .mod .mod-desc {
		float: right;
		width: 68%;
	}
}

Bene, ok. Questo funziona bene, come vedete qui, ma, in termini di codice, è un po’ disgustoso. Di sicuro, non è un approccio DRY alla scrittura del CSS.

Possiamo ancora tollerare questa cosa quando lavoriamo con design così semplici, ma può peggiorare molto quando si cominciano a raffinare i dettagli. Per esempio, il layout verticale vuole che il pulsante “add to cart” appaia sul lato destro del modulo, ma non c’è abbastanza spazio quando passiamo al layout a tre colonne nei breakpoint medi:

Screenshot che mostra che il layout verticale spinge il pulsante 'add to cart' e i moduli 'quantity remaining' sotto al prezzo.

Se miglioriamo i nostri stili così che i moduli “add to cart” e “quantity remaining” vadano deliberatamente sotto ai prezzi dei prodotti a queste dimensioni – allineati a sinistra invece di andare a posizionarsi nello spazio sulla destra – il nostro foglio di stile si aggroviglia un po’ di più.

Scambiare l’allineamento da sinistra a destra per questi elementi è semplice in termini degli stili stessi: stiamo semplicemente gestendo solo le proprietà text-align e float su “quantity remaining” e sul pulsante “add to cart”. Per le media queries che controllano questi semplici stili, tuttavia, abbiamo una nuova lista di considerazioni su cui lavorare. Dobbiamo aggiungere due breakpoint a entrambe i breakpoint esistenti che gestiscono l’allineamento verticale. Questi stili impatteranno anche sul nostro modulo “featured”, quindi dovremo sovrascrivere questi nuovi stili usando la nostra class di scoping, a cui abbinare le media queries. Dal momento che il module “featured” ha un po’ più di spazio per lavorarci rispetto ai moduli allineati three-across, il pulsante “add to cart” può ritornare prima a destra in quel contesto, nel breakpoint più alto – un altro breakpoint. Ma un attimo, c’è di più: questi stili non sono a prova di futuro. Se mai volessimo aggiungere un modulo a un nuovo contesto, dovremmo di nuovo fare copia-incolla di tutti i nostri stili, con una nuova class di scoping e una manciata di nuove media queries. Se la dimensione del layout della pagina cambia in qualunque modo (più o meno padding, margini, un qualsiasi cambiamento degli stili degli elementi di layout basato sulla dimensione), dovremmo aggiustare tutte quante quelle media queries. Adesso dobbiamo trovare una nuova dimensione della viewport in cui i nostri breakpoint abbiano senso. Le nostre decisioni sono disconnesse dal nostro layout.

Questa pagina non si avvicina nemmeno al livello di complessità che potremmo incontrare in un progetto reale, ma abbiamo già uno stylesheet complesso da mantenere.

Facciano il loro ingresso le element queries#section2

Tutte queste complicazioni derivano dalla nostra dipendenza dalla viewport per prendere decisioni riguardanti gli stili. Le media queries funzionano bene per i nostri layout di pagina ad alto livello ma non si avvicinano nemmeno ad essere utili per i componenti modulari. Quello di cui abbiamo davvero bisogno è la capacità di assegnare stili ai componenti della pagina basandoci sullo spazio che occupano piuttosto che sulla dimensione della viewport.

Le “element queries” assumono una sintassi che fa esattamente questo: siamo abituati a scrivere una media query per gli stili che si applicano solo quando la viewport è più grande di 40em, così:

@media( min-width: 40em ) {
}

Al contrario, immaginiamo una sintassi in cui la query stessa sia nello scope di un elemento e popolata con stili che si applicano solo quando quell’elemento è più grande di 40em:

.mod:media( min-width: 40em ) {
}

Bene, questo cambierebbe quasi tutto quello che abbiamo scritto finora per la nostra pagina Whitworth. Potremmo ancora usare le media queries per il cambio di layout maggiore della nostra pagina, quello in cui passiamo da un layout lineare all’avere un elemento primario più grande e una sidebar: ha senso basarlo sulla dimensione della viewport. Tuttavia, una volta che avremo iniziato a costruire i moduli stessi, questa nuova sintassi ci permetterebbe di scrivere una sola volta tutti gli stili di cui potrebbero mai aver bisogno:

.mod img {
	display: block;
	margin: 0 auto;
	width: 100%;
	max-width: 250px;
}

.mod:media( min-width: 425px ) img {
	float: left;
	width: 30%;
	max-width: 9999px;
}
.mod:media( min-width: 425px ) .mod-header,
.mod:media( min-width: 425px ) .mod-desc {
	float: right;
	width: 68%;
	padding-left: 2%;
}

Ecco fatto: questi sono tutti gli stili di cui avranno bisogno i layout del nostro modulo – guardate voi stessi questa demo, che usa uno shim JavaScript per far funzionare nella pratica questa sintassi ancora teorica. Quando il modulo è più largo di 425px, il browser utilizza il layout orizzontale. Se il modulo è più piccolo di 425px, il browser usa il layout verticale. Gli stili ora dipendono al 100% dal container che il modulo occupa: se ha spazio a sufficienza per il layout orizzontale, questi sono gli stili che verranno usati. Se cambiamo il layout da tre colonne a due colonne, con ciascun modulo occupante la metà del container, o se introducessimo un contesto completamente nuovo e imprevedibile per uno di questi moduli, non avremmo bisogno di cambiare nulla dei suoi stili. Immaginatevi quanto sarebbe più utile una pattern library se rimuovessimo la dipendenza contestuale da questi tipi di moduli indipendenti: sareste in grado di prendere ciascun componente dalla pagina e spostarlo in un qualunque container nella pagina, senza dover rimettere mano a centinaia di righe di CSS, anzi, senza dover mettere mano ad alcun CSS.

Per dettagli di precisione come la posizione del pulsante “add to cart” e delle “quantity remaining”, le cose non si complicano troppo. Dal momento che non dobbiamo gestire lo scope o raffinare i breakpoint del modulo per farli andare bene con i breakpoint del layout basati sulla viewport, abbiamo solo un problema: “Il modulo è troppo piccolo per avere il prezzo e il pulsante ‘aggiungi al carrello’ uno accanto all’altro?” Un po’ di esperimenti ci dicono che non c’è abbastanza spazio per entrambe gli elementi finché il modulo non sarà più grande di 320px e qui è dove metteremo il nostro breakpoint basato sul modulo:

.mod:media( min-width: 320px ) .mod-cost {
	float: left;
}
.mod:media( min-width: 320px ) .mod-buy {
	float: right;
}
.mod:media( min-width: 320px ) .buy-remaining {
	text-align: right;
}

Siamo passati da dozzine di righe di CSS ridondante, di media queries e di overrides a nove righe contenenti gli stili di cui abbiamo bisogno. Ha ancora più importanza il fatto che non si romperebbe nulla se altri stili sulla pagina cambiassero. Potete provarlo voi stessi giocando un po’ con gli stili nei dev tool del browser in questa demo, che utilizza uno script che simula il comportamento della element query. Non è qualcosa che dovreste usare sui siti in produzione, ma è un ottimo modo per capire questo concetto.

Se questa sintassi esistesse, finiremmo il nostro progetto Whitworth in un paio di righe di CSS.

Ma le element queries non possono esistere, o perlomeno, non nel modo in cui le abbiamo immaginate. Proprio come per la prima versione della specifica di picture, le element queries sono morte e sepolte per sempre, ma come la ricerca per le immagini responsive, non lasceremo che finiscano così, non finché avremo da risolvere un problema così evidente.

Escano le element queries#section3

Questo concetto non è difficile da comprendere, non per noi developer comunque, ma non lo erano nemmeno le immagini responsive. Una volta che ci siamo imbattuti nei dettagli realistici dell’implementazione, tuttavia, le cose sono cambiate.

Il primo passo per il RICG è stato di sviluppare un documento di Use Cases and Requirements per le element queries, che descrivesse e illustrasse il problema, non che proponesse una soluzione ma solo la descrizione di un problema chiaro, che va risolto. Finora, è un documento molto più centrato del documento di Use Cases and Requirements per le immagini responsive perché il suo unico obiettivo è di risolvere una questione generale.

Man mano che si è sparsa la voce di questo documento, si è cominciato a pensare un po’ di più al problema, proprio come speravamo. Questo ha portato molti di noi alla stessa conclusione, prima che cominciassimo perfino a trafficare con una specifica proposta: le element queries non possono funzionare.

Questo è stato documentato altrove con maggiori dettagli, ma l’idea è questa: stiamo usando CSS per controllare CSS e questo significa che possiamo causare infiniti loop di cambiamenti di stile. Se possiamo dire a un elemento di assumere nuovi stili usando una element query, cosa succede se usiamo quella element query per cambiare la larghezza dell’elemento a una in cui non si applica più la element query?

.our-element:media(min-width: 500px) {
	width: 499px;
}

Ora, dal momento che le query non si abbinano più, la nuova larghezza non viene più applicata. Dal momento che la nuova larghezza non viene mai applicata, la element query andrebbe bene di nuovo, quindi verrebbe applicata la nuova larghezza, quindi la query non andrebbe più bene, quindi la nuova larghezza non verrebbe applicata… E così via all’infinito. Il browser, posto di fronte a queste situazioni dovrebbe buttare via completamente gli stili della element query? Buttare via solo la nuova larghezza? Cancellarsi dalla macchina dell’utente? Avviare un fuoco di dimensioni piccole o medie? Nulla di tutto questo suona particolarmente attraente, men che meno dal punto di vista dei produttori di browser. Non permetterebbero mai che qualcosa con questo tipo di potenziale di errore veda la luce del giorno. Game over.

Notizia eccitante, vero? Abbiamo capito che le element queries erano impossibili in una frazione del tempo che ci è voluto per capire che le immagini responsive erano impossibili. La prima volta, intendo.

Ok, ascoltatemi. Le “element queries” sono fuori dai giochi, ovvio, ma il problema non è cambiato: il documento Use Cases and Requirements non se ne va da nessuna parte. Dobbiamo ancora risolvere questo problema e adesso sappiamo come non risolverlo: gli elementi non possono assumere nuovi stili basandosi sulle loro proprietà.

Facciano il loro ingresso le container queries#section4

Per risolvere il problema delineato nel documento Use Cases and Requirements, abbiamo bisogno di reinquadrare il modo in cui parliamo di una potenziale soluzione. Dal momento che una soluzione non può permettere a un elemento di riassegnare degli stili a sé stesso, possiamo costruire quei vincoli nella specifica: le queries attaccate a un elemento possono influenzare solo lo styling degli elementi figlio di quell’elemento.

Armati della nostra nuova conoscenza dell’impossibile, la ricerca per le element queries è stata reinquadrata come container queries.

Come cambierebbe il progetto Whitworth questa cosa? Beh, niente: stavamo solo assegnando stili agli elementi figlio di .mod comunque, non a .mod stesso. Potrebbe semplicemente comportare pochi cambiamenti di comportamento in termini del modo in cui il browser tratta questi elementi. Il browser potrebbe finire con un implicito stile overflow a livello del browser su qualsiasi elemento con una query attaccata, per prevenire il comportamento da loop infinito-molto più prevedibile del browser che sceglie pezzi di stylesheet per evitare di fondersi.

Quindi, come facciamo a far sì che le cose continuino a muoversi partendo da questo punto? Trovando la prossima questione impossibile da risolvere. Finora, le “container queries” hanno tenuto testa a più esami accurati delle “element queries”, ma parlare è facile. Questa questione della ricorsione è grande, ma gratta appena la superficie delle questioni potenziali in cui potremmo imbatterci mentre una soluzione comincia a prendere forma.

Faremo molti più progressi sperimentando con questo pattern, che non è la cosa più facile da fare quando si ha a che fare con sintassi che non esistono ancora. È il modo in cui è finita con la primissima versione di Picturefill di Scott Jehl così tanti anni fa. Senza armeggiare, senza in realtà usare qualcosa che assomigli al markup pattern di picture, non riuscivamo a dare molto senso alla specifica che stavamo per scrivere. Quindi, nello stesso spirito, il RICG ha avviato un repository di demo per le container query, usando lo stesso shim delle nostre demo di Whitworth. Come potete provare questa sintassi per conto vostro e contribuire a trovare i problemi in cui ci imbatteremo quando useremo questo tipo di CSS pattern sul serio – quel genere di problemi che si possono trovare solo realizzando qualcosa? Clonate il repo, copiate la directory demo-template in /demos, date un nome alla vostra demo e create qualcosa usando le container queries. Assicuratevi di farci sapere come va, sia che vada bene sia che vada male.

Escano di scena le container queries..?#section5

La prima versione della specifica delle container queries sta prendendo forma, ma potrebbe benissimo avere anche i giorni contati. Il documento Use Cases and Requirements non sta andando da nessuna parte però: il problema che dobbiamo risolvere è là fuori, incombente, e chiede in qualche modo di essere risolto.

Per quel che ne sappiamo, la risposta potrebbe essere questa primissima proposta. Oppure potrebbe essere qualcosa di completamente differente, qualcosa che non abbiamo nemmeno cominciato a prendere in considerazione. Ma il RICG non è un decision-maker nel mondo degli standard web e non è nostra intenzione diventarlo. Vogliamo essere un punto di raccolta per fornire alcune strade ai designer e ai developers che magari non hanno alcuna voglia di avventurarsi sugli arcani listservs e sui canali IRC per essere coinvolti negli standard che influenzano il loro lavoro quotidiano.

Ci sarà moltissimo altro lavoro da fare dopo questo e avremo bisogno di tutti voi per portarlo a termine. Con il vostro aiuto, con migliaia di voci di designer e developer che contribuiscono alla ricerca, è solo questione di tempo per trovare insieme una risposta. L’abbiamo già fatto in passato e lo faremo ancora.

Fatevi forza, bandite la frase “element queries” dal vostro vocabolario per sempre. Andiamo a lavorare sulle container queries, indipendentemente da quale nome o forma prenderanno in futuro.

Illustrazioni: {carlok}

Nessun commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Altro da ALA

Webwaste

In questo estratto da World Wide Waste, Gerry McGovern esamina l'impatto ambientale di siti web pieni zeppi di asset inutili. Digital is physical. Sembra economico e gratis ma non lo è: ci costa la Terra.
Industry