CSS Battle – 21 marzo 2024

Tenersi in allenamento fa sempre bene. CSS Battle è un sito che propone quotidianamente delle sfide da portare a termine utilizzando HTML/CSS. Si tratta di un’ottima palestra per i developer non solo per non dimenticare i fondamentali ma per imparare qualcosa in più.

Mar 21, 2024 | Web Development

CSS Battle (https://cssbattle.dev/) è la palestra digitale per chi si occupa di front-end development. Gli addetti ai lavori sanno bene che l'unico modo di imparare il codice è scriverlo, scriverlo e scriverlo ma non sempre si hanno dei progetti interessati sotto mano per apprendere ed evitare i soliti banali esercizi. Nasce così CSS Battle, che ci pone di fronte a una divertente sfida: ogni giorno viene pubblicata una figura, una forma, da replicare utilizzando l'HTML/CSS tramite la loro piattaforma online. Tutto qua, semplice semplice. Ci viene fornito un template di base con colori e dimensioni del quadro per riferimento, il resto tocca a noi.
In realtà l'obbiettivo reale, la vera challenge, sarebbe rifare la figura con il minor numero di caratteri possibili. Tuttavia non è mio intento in questa sede approfondire l'argomento, anche perché spesso si tratta di piccoli espedienti come minimizzare il codice o utilizzare determinati selettori.

Penso sia interessante pubblicare in questo blog, saltuariamente, qualche Battle che metta in luce l'utilizzo di una regola in una particolare situazione, accompagnata dal codice con relativa spiegazione e motivazione delle scelte. Potrà essere utile da un lato ai già esperti per rinfrescare alcuni concetti, dall'altro ai semplici curiosi che vogliono capire il dietro le quinte dello sviluppo di una interfaccia (perlomeno le basi).

aldo giovanni giacomo pignolo
La soluzione non è unica
La soluzione che vi propongo è una delle possibili. Effettuerò delle scelte con una determinata intenzione, ma ciò non significa che la strada per raggiungere l'obbiettivo sia univoca, anzi. Il fascino del CSS è proprio la possibilità di offrire moltissime regole che impostano il progetto diversamente ma perseguono lo stesso scopo. Ai colleghi consiglio di provare a elaborare la propria soluzione.

CSS Battle: l'interfaccia

Figura 1 - Interfaccia iniziale della CSS Battle

Cliccando sulla Battle del giorno, apparirà questa interfaccia dalla struttura molto semplice.
Sul lato sinistro c'è l'editor di testo che possiamo modificare a piacimento. Notiamo che ci viene fornito un template di base da cui partire con un div, e alcune regole CSS inserite tra i tag style.

Sulla destra invece ci sono due riquadri: il primo, con lo sfondo bianco e il quadrato rosso, è il render del nostro codice. Qualsiasi modifica nell'editor verrà instantaneamente renderizzata qui, senza bisogno di salvataggi o altri file. Quello che vediamo al momento non è altro che il template citato prima: un div, il quadrato rosso, stilizzato secondo le regole inserite nel tag style dell'editor, cioè width: 100px;, height: 100px; e background: #dd6b4d;.

Una funzionalità particolarmente utile è la possibilità, passando sopra il riquadro con il mouse, di avere l'ascissa di un punto secondo un asse che va da sinistra a destra. Si può fare la stessa cosa con l'ordinata premendo il tasto Shift. Quanto appena descritto fa parte dell'opzione "Slide and compare" (il checkbox sopra il riquadro); si può aggiungere eventualemente anche il "Diff", che sovrappone la nostra forma con quella finale e a colpo d'occhio evidenzia le discrepanze tra le due forme.

Il secondo riquadro è la forma finale che noi dobbiamo copiare. Sopra viene riportata la dimensione (400x300px) e nella parte inferiore i codici dei colori utilizzati, copiabili cliccandoci sopra.

Primo approccio: analisi della forma

Prima di scrivere, ragioniamo sulla forma che abbiamo davanti e su come potrebbe essere una ipotetica struttura.

Figura 2 - L'intera figura è centrata rispetto al quadro.

Notiamo innanzitutto che tutta la figura è centrata rispetto al quadro. Quindi dovremo sicuramente applicare le regole che ci permettono di centrare il contenitore dell'intera forma. In questo caso userei il display: flex; sul contenitore e allineando al centro orizzontalmente e verticalente i children con justify-content: center; e align-items: center;.
Questo è solo uno dei tanti metodi possibili per centrare il div contenitore: per un ripasso più approfondito vi rimando al mio articolo su come centrare un div.

Cominciamo a mettere le mani in pasta. Per chiarezza di esposizione manterrò le impostazioni dell'editor di CSS Battle, con colori e suddivisione tra HTML e CSS tra i tag style.
Consideriamo il div attuale, il quadrato, come il contenitore della nostra futura forma che andremo a costruire. Lo chiameremo container. L'obbiettivo è centrare container ma sappiamo che le regole scritte per la centratura si applicano al genitore. Potremmo dunque inserire un altro div con dentro il nostro container ma è più facile e veloce sfruttare il body, implicitamente presente nel DOM del nostro progetto. Nota: ricordiamoci il reset dei margin.
Manteniamo gli stili del container per constatare visivamente l'efficacia del nostro codice.

In sintesi il codice è questo:

<div container>
</div>
<style>
body {
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

[container] {
  width: 100px;
  height: 100px;
  background: #dd6b4d;
}
</style>
aldo giovanni giacomo pignolo
Container non è una classe
Nel codice appena scritto ho utilizzato un espediente per velocizzare la scrittura. Al posto di aggiungere al div il tradizionale class="container" ho preferito inserire un attributo inventato chiamato sempre container accessibile nel CSS con la sintassi [container], che ho stilizzato così come avrei fatto con una classe. Il risultato non cambia, varia solo il selettore.

La figura: suddivisione

Ora possiamo occuparci della figura. Si può facilmente dedurre che sia composta da 4 elementi: un trapezio rovesciato nella parte superiore e da due quadrilateri laterali più un pentagono centrale nella parte inferiore. Possiamo strutturare il progetto in questo modo: all'interno del container creiamo:

  • un div, con l'attributo a, che sarà il nostro trapezio;
  • un div, con l'attributo box, che sarà il contenitore dei 3 elementi inferiori;
  • tre div, con rispettivamente gli attributi b, c e d. b e d saranno i rettangoli, c il pentagono.
Figura 3 - La figura è composta da 4 elementi: uno superiore e 3 inferiori.

Impostato l'HTML, pensiamo al CSS.
Innanzitutto eliminiamo le proprietà del container, non ci servono. Poi possiamo cominciare assegnando i colori di background: al body diamo la regolabackground: #301E53, per il div [a] usiamo background: #766D94, a [b], [c] e [d] assegniamo background: #A6A0BE. I codici HEX dei colori sono copiabili dall'apposita sezione nella parte destra dell'interfaccia.

Notiamo però un problema: nella parte inferiore b, c e d devono essere allineati e non uno sotto l'altro. Ecco perché li ho inseriti all'interno del div box. A quest'ultimo possiamo assegnare le regole display: flex; e justify-content: center; per centrare i 3 quadrilateri. Non ci serve align-items: center; perché gli elementi non sono allineati al centro rispetto a box.

<div container>
   <div a></div>
   <div box>
      <div b></div>
      <div c></div>
      <div d></div>
   </div>
</div>
<style>
body {
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #301E53;
}

[a] {
  background: #766D94;
}
[box] {
  display: flex;
  justify-content: center;
}
[b], [c], [d] {
  background: #A6A0BE;
}
</style>

Se non vedete nulla, non preoccupatevi, è normale. Manca ancora qualche regola che aggiungeremo nella fase seguente.

La figura: dimensione degli elementi

Questa fase è molto facile e veloce, quindi andiamo dritti al punto. Utilizzando la funzione "Slide & Compare", che ho descritto a inizio articolo, individuiamo la larghezza e l'altezza di ogni singolo elemento. Considerateli tutti inscritti in un rettangolo come nella figura seguente e determiniamo la width e le height di ogni forma di conseguenza. Ricordiamoci che il riquadro è in pixel e misura 400x300, quindi possiamo utilizzare le misure in pixel per non complicarci la vita con le relative.

Figura 4 - Ogni elemento è inscrivibile in un rettangolo, di cui possiamo sapere width e height.

Dato che stiamo parlando di spaziatura, approfittiamone per aggiungere le distanze tra i vari elementi. Per lo spazio tra parte superiore e inferiore sarà sufficiente dare ad [a] un margin-bottom: 10px;, mentre per intervallare i 3 quadrilateri viola chiaro possiamo usare gap: 10px;, regola autoesplicativa, su box.
Facciamo anche un'altra piccola modifica: separiamo [c] da [b] e [d] perché ha dimensioni diverse. La regola sul colore del background sarà ripetuta, ma in questo modo il codice sarà più chiaro.
Il codice risultante sarà il seguente:

<div container>
   <div a></div>
   <div box>
      <div b></div>
      <div c></div>
      <div d></div>
   </div>
</div>
<style>
body {
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #301E53;
}

[a] {
  width: 300px;
  height: 40px;
  background: #766D94;
  margin-bottom: 10px;
}
[box] {
  display: flex;
  justify-content: center;
  gap: 10px;
}
[c] {
  width: 300px;
  height: 40px;
  background: #A6A0BE;
}
[b], [d] {
  width: 70px;
  height: 50px;
  background: #A6A0BE;
}
</style>

Abbiamo ottenuto la figura seguente, composta da rettangoli pieni. In bianco ho segnato l'ultima operazione da fare: sagomare gli elementi [a] e [c] dando loro la forma rispettivamente di un trapezio e di un pentagono rovesciati.

Figura 5 - Questo è il risultato che abbiamo ottenuto. Ci resta solo da sagomare gli elementi a e c (tratteggiati in bianco).

Ultima fase: clip-path

Per completare l'opera ci viene in aiuto una regola poco conosciuta ma in questo caso fondamentale, perché ci permette in modo pratico e veloce di raggiungere il nostro obbiettivo.
Clip-path è una proprietà che in sintesi crea una maschera al div a cui è applicata, in modo tale che l'area dentro la maschera sia visibile il resto nascosto. Questo significa che noi possiamo ritagliare un elemento seguendo una forma di input a nostra scelta. Le forme che possiamo usare sono tante (rect, circle, ellipse...) ma in questa sede approfondiamo solo polygon().
Come dice il nome, dichiariamo di voler creare una maschera dalla forma generica di un poligono.

Clip-path: polygon()

Il funzionamento di clip-path è più intuitivo di quanto sembri, previo un ripasso degli assi cartesiani. Abbiamo detto che tra le parentesi dobbiamo definire il tracciato del poligono-maschera e possiamo farlo stabilendo le coordinate dei vertici, tenendo in considerazione una coppia di assi cartesiani che hanno origine dal vertice top-left dell'elemento. Questo significa che l'origine sarà definita dai valori (0, 0) che corrisponderà al vertice in alto a sinistra del nostro div. Gli altri vertici andranno di conseguenza seguendo sempre gli assi di riferimento. Per le unità di misura useremo un mix tra i pixel e la percentuale.

Per amore della sintesi, ecco un'immagine che vale più di mille parole.

Figura 6 - Per quanto riguarda il trapezio, vediamo gli assi cartesiani da considerare per definire il clip-path: polygon() e i suoi vertici.

Il ragionamento è chiaro: inseriremo tra le parentesi di polygon() quattro coppie di valori che definiscono i quattro vertici del poligono seguendo i valori rispetto agli assi cartesiani. Così otteniamo il trapezio.
Attenzione all'ordine: le coppie di vertici saranno scritte a partire da top-left e proseguono in senso orario. Quindi il secondo sarà quello top-right, poi bottom-right e così via.

Il pentagono [c]

Per il pentagono il ragionamento è identico, con una piccola differenza. Se per il trapezio abbiamo inserito quattro coppie di valori perché quattro sono i vertici del poligono, per il pentagono sarà sufficiente inserire una quinta coppia. Seguendo l'ordine delle coppie, andremo a definire un punto tra i vertici bottom-right e bottom-left; in altre parole il punto aggiuntivo sarà il quarto.

Risultato finale

Abbiamo raggiunto il nostro obbiettivo. Abbiamo replicato perfettamente la figura proposta. Ecco lo screenshot che attesta il 100% di fedeltà.

Figura 6 - Come vedete la figura che abbiamo creato si sovrappone al 100% con l'originale.

Di seguito il codice completo:

<div container>
   <div a></div>
   <div box>
      <div b></div>
      <div c></div>
      <div d></div>
   </div>
</div>
<style>
body {
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #301E53;
}

[a] {
  width: 300px;
  height: 40px;
  background: #766D94;
  margin-bottom: 10px;
  clip-path: polygon(0 0, 100% 0, 87% 100%, 13% 100%);
}
[box] {
  display: flex;
  justify-content: center;
  gap: 10px;
}
[c] {
  width: 300px;
  height: 40px;
  background: #A6A0BE;
  clip-path: polygon(0 0, 100% 0, 100% 55px, 50% 100%, 0 55px);
}
[b], [d] {
  width: 70px;
  height: 50px;
  background: #A6A0BE;
}
</style>

Conclusioni

Spero che questo spiegone della CSS Battle vi sia piaciuto. Ho cercato di essere quanto più esaustivo possibile, ma i concetti sono tanti e non era il caso di allungare ulteriormente il post. Mi sono divertito a scriverlo, rendendomi però conto di quanto sia molto più complesso spiegare l'esercizio piuttosto che farlo nella pratica.

L'argomento HTML/CSS è vasto ed è sempre meglio affidarsi a un professionista per impostare correttamente un progetto. Se avete bisogno di un sito web o di un'app o volete fare una chiaccherata su una vostra idea di prodotto digitale, non esitate a contattarmi.

Alla prossima CSS Battle!