11 novembre 2026
Lascia che sia il CSS a decidere il layout: gallerie che si adattano al numero di elementi
Con :has(), :nth-child() e una custom property puoi ottenere griglie “intelligenti” senza classi modificatrici, props o condizioni in JavaScript.
Costruire una gallery che cambi numero di colonne in base a quanti item contiene è più semplice (e pulito) di quanto sembri: il CSS moderno può “contare” i figli e reagire di conseguenza. In questo articolo vediamo come combinare :has(), :nth-child(), odd/even e una CSS custom property per ottenere layout resilienti e auto-adattivi.
Quando progetti un componente “gallery”, la tentazione è sempre la stessa: aggiungere una prop, una classe modificatrice o una condizione lato JS per dire quante colonne usare.
Il problema è che così il layout smette di essere un comportamento emergente del contenuto e diventa una configurazione manuale. E ogni volta che il numero di elementi cambia (filtri, paginazione, A/B test, contenuti dinamici) ti ritrovi a inseguire edge case.
Con un po’ di CSS moderno, invece, puoi far sì che sia il componente a adattarsi automaticamente in base a quanti elementi contiene.
Obiettivo: una griglia che “capisce” quanti item ha
Immagina una regola semplice e pragmatica, ad esempio:
- 2 o 4 item → 2 colonne (più armonioso)
- 6 item → 3 colonne (spesso perfetto)
- 7+ item → 4 colonne (riempi meglio lo spazio)
Niente classi tipo .gallery--two-cols, niente JS che conta i figli. Solo CSS.
Base: una grid con numero di colonne configurabile
Partiamo con una grid normale, ma invece di fissare direttamente il numero di colonne, lo facciamo passare da una custom property.
.gallery {
--gallery-columns: 3; /* default / fallback */
display: grid;
grid-template-columns: repeat(var(--gallery-columns), 1fr);
gap: 1rem;
}
Questo dettaglio è fondamentale: il layout diventa “parametrico”. Noi, poi, cambiamo --gallery-columns solo quando serve.
Far “contare” il CSS: :has() + :nth-child()
Il selettore :has() permette di selezionare un elemento in base a ciò che contiene. È esattamente quello che ci serve.
Caso 1: “ha almeno N figli”
Per esempio: “questa gallery ha 6 o più elementi”.
.gallery:has(> :nth-child(6)) {
/* qui dentro sei sicuro che esistono almeno 6 children */
}
Questa forma è utilissima per soglie: 6+, 7+, 10+…
Caso 2: “ha esattamente N figli”
Se vuoi essere più preciso, puoi verificare che l’nth-child(N) sia anche :last-child.
.gallery:has(> :nth-child(5):last-child) {
/* esattamente 5 elementi */
}
È potente, ma può diventare verboso se inizi a gestire molti numeri.
Pari e dispari: una scorciatoia pratica
Per layout responsive rispetto al contenuto, spesso ti basta distinguere pari/dispari.
Esempio concettuale: “se il numero di item è pari, fai X”. In CSS lo puoi esprimere cercando un figlio che sia contemporaneamente even e last-child.
.gallery:has(> :nth-child(even):last-child) {
/* numero di figli pari */
}
.gallery:has(> :nth-child(odd):last-child) {
/* numero di figli dispari */
}
È un pattern sorprendentemente utile per gallerie e card grid.
Mettiamo insieme una logica realistica (senza over-engineering)
Torniamo alla regola obiettivo:
- default 3 colonne
- 2 o 4 item → 2 colonne
- 6 item → rimane 3 (quindi non tocchiamo)
- 7+ item → 4 colonne
1) Default: 3 colonne
Già fatto:
.gallery {
--gallery-columns: 3;
display: grid;
grid-template-columns: repeat(var(--gallery-columns), 1fr);
gap: 1rem;
}
2) Se il numero di figli è pari → 2 colonne… ma non se sono 6+
Qui vogliamo intercettare i casi “belli” (2 e 4), evitando di degradare i 6 a 2 colonne.
.gallery:has(> :nth-child(even):last-child):not(:has(> :nth-child(6))) {
--gallery-columns: 2;
}
Cosa sta succedendo:
:has(> :nth-child(even):last-child)→ il totale è pari:not(:has(> :nth-child(6)))→ esclude 6 o più elementi
Risultato pratico: colpisce 2 e 4, non colpisce 6, 8, 10…
3) Se ha 7 o più elementi → 4 colonne
.gallery:has(> :nth-child(7)) {
--gallery-columns: 4;
}
Nota importante: l’ordine delle regole conta. Se definisci il caso “7+” dopo, sovrascrive eventuali impostazioni precedenti.
Perché questo approccio è più “resiliente”
- Niente API extra del componente: il markup resta pulito.
- Meno stati da gestire: il componente reagisce al contenuto, non a un flag.
- Meno bug ai bordi: filtri, sorting e contenuti dinamici non rompono il layout.
- Separazione dei ruoli: il layout è responsabilità del CSS, non del rendering.
Non significa “mai JS”: significa usare JS solo quando serve davvero, e non per compensare una cosa che il CSS può già esprimere.
Considerazioni pratiche (da non ignorare)
:has()è una feature moderna: in molti progetti ormai è utilizzabile serenamente, ma va comunque verificata la compatibilità con i browser target.- Se devi supportare browser più vecchi, puoi mantenere
--gallery-columns: 3come fallback e accettare un comportamento meno sofisticato. - Mantieni le regole poche e leggibili: la potenza dei selettori non deve trasformarsi in un puzzle.
Sintesi
Una gallery “intelligente” non richiede per forza classi modificatrici o logiche condizionali nel codice applicativo. Con una grid parametrica via custom property e qualche selettore mirato (:has() + :nth-child()), puoi far sì che il layout segua automaticamente il contenuto.
Il risultato è un CSS più robusto: meno configurazione manuale, meno ramificazioni, più coerenza visiva. In altre parole: lascia che sia il CSS a fare il lavoro sporco del layout.