Responsive Design con Bootstrap

Moreno Iacomino

Introduzione

Nello sviluppo frontend di applicazioni web moderne, l'implementazione di un layout semplice, responsive e facilmente mantenibile, diventa ben presto una forte esigenza per far fronte alle molteplici (e spesso comuni) specifiche di progetto.

Bootstrap, il più comune ed usato framework CSS, permette di raggiungere facilmente questo scopo: grazie ad una struttura solida di templating, script e classi CSS, è possibile creare pagine web velocemente senza preoccuparsi di dover implementare da zero aspetti comuni, ma fondamentali come spacing, sizing e disposizione spaziale degli elementi, aspetti che spesso si traducono in muri di codice CSS.

Sul sito ufficiale di Bootsrap possiamo trovare la reference ufficiale per includere Bootstrap nei propri progetti.

Elementi principali del layout system

Per raggiungere l'obiettivo di questo articolo, ci basteranno tre concetti fondamentali di Bootstrap:

  • i breakpoints
  • i containers
  • il grid system
Breakpoints (per l'implementazione responsive)

Breakpoints definiscono delle soglie di comportamento del componente HTML che le usa in base alla larghezza del viewport: in questo modo è possibile definire delle regole differenti per i vari devices su cui verrà visualizzata l'applicazione. Di seguito la definizione di default dei breakpoints (possono essere sovrascritte):

    $grid-breakpoints: (
    xs: 0,
    sm: 576px,
    md: 768px,
    lg: 992px,
    xl: 1200px,
    xxl: 1400px
    );

Come vedremo fra poco, queste variabili vengono utilizzate in combinazione ad altre classi CSS per fare riferimento a quella specifica soglia. Per fare un esempio con la misura xs  è come dire al componente:

"se la larghezza del viewport si trova fra gli 0 e i 576 pixel (xs), applica delle regole, appena supera quest'ultimo valore applica, altre regole".

Containers (per inscatolare gli elementi)

I containers sono gli elementi principali di layout di Bootstrap e permettono di "wrappare" gli elementi di una pagina web in un unico elemento padre che si ridimensiona (ovvero setta la sua larghezza massima) sfruttando i breakpoints.

Ci sono tre tipi differenti di container:

     <div class="container">...<div>
 <div class="container-sm">...</div> <!-- può essere anche container-xs, container-md ecc...-->
 <div class="container-fluid">...</div>

Il primo container, definito semplicemente come classe "container", setta la sua max-width con dei valori precisi legati ai breakpoints (mostrati nello specchietto poco più sotto), per cui se un device fornisce un viewport con una dimensione compresa fra due breakpoints, la massima larghezza del container sarà comunque quella relativa all'ultimo breakpoint raggiunto.

Il secondo container, definito come "container-{breakpoint}", garantisce una max-width del 100% del viewport fino al raggiungimento del breakpoint indicato, dopodichè si comporta come il container semplice: nell'esempio, se il viewport ha una larghezza minore di 576 (soglia di sm), il container sarà a larghezza piena, superati i 576 avrà il valore relativo alla soglia sm.

Il terzo container, definito con la classe fissa "container-fluid", garantisce sempre una max-width del 100% per qualunque viewport: sarà quello che useremo per il nostro esempio di site page.

Qui sotto una tabella riassuntiva dell'andamento della max-width dei containers:

dimensione container bootstrap
Grid System (per disporre gli elementi)

Il concetto che sta alla base del grid system di Bootstrap è che lo spazio orizzontale possa essere visto come una tabella suddivisa in 12 colonne. Prendiamo questo esempio:

<div class="container">
<div class="row">
<div class="col">
Column
</div>
<div class="col">
Column
</div>
<div class="col">
Column
</div>
</div>
</div>

Grazie alle classi "row" e "column" abbiamo definito tre elementi di ugual larghezza, ovvero 4/12, che manterranno questa proporzione qualunque sia la larghezza del loro contenitore. Modifichiamo il codice in questo modo:

<div class="container">
<div class="row">
<div class="col-8">
Column
</div>
<div class="col">
Column
</div>
<div class="col">
Column
</div>
</div>
</div>

La classe "col-8" indica che il primo dei tre div occupa una larghezza di 8 su 12 colonne, mentre gli altri due, avendo semplicemente "col", avranno la stessa larghezza fra di loro, ovvero 2 colonne ciascuno, per arrivare ad un totale di 12 colonne. Un risultato equivalente si sarebbe ottenuto scrivendo il codice in questa maniera:

<div class="container">
<div class="row">
<div class="col-8">
Column
</div>
<div class="col-2">
Column
</div>
<div class="col-2">
Column
</div>
</div>
</div>

Possiamo quindi utilizzare la classe semplice "col" per creare dei layout automatizzati e simmetrici (come ad esempio una gallery di immagini ad ugual larghezza), mentre la classe "col-{n}" per layout più specifici (come una sidebar di un sito ed il suo contenuto).

E' possibile combinare le classi "col" e "col-{n}" con i breakpoints, per creare disposizioni differenti degli elementi a seconda del vieport:

<div class="row">
<div class="col-6 col-md-4">.col-6 .col-md-4</div>
<div class="col-6 col-md-4">.col-6 .col-md-4</div>
<div class="col-6 col-md-4">.col-6 .col-md-4</div>
</div>

In questo modo, i div si disporranno due in una riga e uno in un'altra in modalità mobile (quindi con max-width: 50%, mentre entreranno tutti in una singola riga in modalità desktop (dove il viewport avrà raggiunto il breakpoint md).

Nella documentazione ufficiale è possibile trovare più esempi di combinazioni che si possono fare col grid system.

Creazione layout di pagina

A questo punto, mettendo insieme questi concetti, siamo in grado di costruire lo scheletro di una page site che possa ospitare i nostri contenuti.

<!doctype html>
<html lang="en">

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
  integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"     crossorigin="anonymous">

  <title>Hello, world!</title>
  <style>
.header {
height10vh;
background-color#F0F8FF;
  }

.main {
height80vh;
  }

.sidebar {
background-color#CDFFCD;
  }

.sidebar .list-group-item {
border1px solid rgba(000.125!important;
background-color#8AFF8A;
  }

.content {
background-color#FFFF00;
  }

.content .card {
background-color#FFCC00;
  }

.footer {
height10vh;
background-color#FAEBD7;
  }

@media (max-width768px) {
.sidebar {
height20vh !important;
  }
  }
  </style>
</head>

<body>
  <div class="container-fluid vh-100">
  <div class="row border header">
  <div class="col-12">
HEADER
  </div>
  </div>
  <div class="row border overflow-hidden main">
  <div class="col-12 col-md-2 h-100 overflow-auto border sidebar">
  SCROLLABLE SIDEBAR
  <ul class="list-group flex-sm-column flex-row">
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  <li class="list-group-item m-1">Sidebar item</li>
  </ul>
  </div>
  <div class="col-12 col-md-10 border overflow-auto h-100 content">
  SCROLLABLE CONTENT
  <div class="row">
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  <div class="col-12 col-md-6">
  <div class="card my-2">
  <div class="card-body">
  <h3 class="card-title">Title</h3>
  <p class="card-text">Text</p>
  </div>
  </div>
  </div>
  </div>
  </div>
  </div>
  <div class="row border footer">
  <div class="col-12">
FOOTER
  </div>
  </div>
  </div>


  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
  integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
  crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
  integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
  crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
  integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
  crossorigin="anonymous"></script>
</body>

</html>

Il risultato di questo codice sarà il seguente su un widescreen:

Risultato codice bootstrap

Mentre su uno schermo mobile in portrait vedremo questo:

Risulatato codice responsive

Concetti chiave

Come si vede, in questo esempio sono stati utilizzati tutti i concetti sopracitati, insieme ad altre piccole accortezze, sempre riguardanti Bootstrap:

  • La classe "container-fluid" come wrapper di tutta la pagina
  • Il grid system e i breakpoints per fare in modo che, ad esempio, le card dello scrollable content siano a larghezza massima in modalità mobile e a metà larghezza in modalità desktop
  • Una media query per fare in modo che in modalità mobile la sidebar abbia una propria altezza (il 20% del viewport verticale)
  • La classe "my-2" che garantisce un margine verticale (m -> margine; y -> asse y, quindi sia top che bottom; 2 -> valore numerico in un range da 1 a 5, maggiore è il numero più alto è il margine);
  • La classe "m-1" che garantisce un margine in tutte le direzioni (in questo caso, la mancanza di x/y dopo la m indica che il margine è applicato ovunque)
  • La classe "overflow-auto" che, insieme alle classi "h-100" e "vh-100", attiva lo scroll in caso il contenuto superi l'altezza del container (come succede appunto nel blocco principale)
  • La classe "border" che attiva i bordi su tutti e quattro i lati (anche in questo caso, questa classe può essere combinata con dei suffissi per specificare su quale lato attivare il bordo, ottenendo ad esempio "border-top" o "border-left")

L'utilizzo di queste classi ci ha permesso di scrivere pochissimo "pure css", limitato quasi esclusivamente alla definizione dei colori di background a scopo di maggiore comprensione.