Nella maggior parte dei siti web moderni si seguono tecniche di responsive web design per assicurarsi che siano belli, leggibili e utilizzabili su dispositivi di qualsiasi dimensione dello schermo, cioè telefoni cellulari, tablet, computer portatili, monitor di PC desktop, televisori, proiettori e altro.

I siti che utilizzano queste tecniche hanno un unico template che modifica il layout in base alle dimensioni dello schermo:

  • Gli schermi più piccoli mostrano normalmente una vista lineare a colonna singola dove i controlli dell’UI come i menu sono attivati cliccando sulle icone (hamburger).
  • Gli schermi più grandi mostrano una maggiore quantità di informazioni, forse con barre laterali allineate orizzontalmente. I controlli UI come le voci di menu possono essere sempre visibili per garantire un accesso più facile.

Una parte importante del responsive web design è l’implementazione di una media query CSS o JavaScript per rilevare le dimensioni del dispositivo e servire automaticamente il design giusto per quella dimensione. Spiegheremo perché queste query sono importanti e come utilizzarle, ma prima parliamo del responsive design in generale.

Perché il Design Reattivo è Importante?

È impossibile fornire un singolo layout di pagina e aspettarsi che vada bene ovunque.

Quando i telefoni cellulari ottennero per la prima volta un accesso rudimentale al web nei primi anni 2000, i proprietari di siti spesso creavano due o tre template di pagina separati, vagamente basati sulle visualizzazioni, per cellulari e desktop. Questo divenne sempre più impraticabile man mano che la varietà di dispositivi cresceva in modo esponenziale.

Oggi ci sono numerose dimensioni di schermo che vanno dai minuscoli display da polso agli enormi monitor da 8 K e oltre. Anche se considerate solo i telefoni cellulari, i dispositivi recenti possono avere una risoluzione più alta di molti computer portatili di fascia bassa.

Inoltre, l’utilizzo dei cellulari ha superato quello dei computer desktop. A meno che il vostro sito non abbia un target specifico di utenti, potete aspettarvi che la maggior parte delle persone vi acceda da uno smartphone. I dispositivi a piccolo schermo non sono più un ripiego e dovrebbero essere considerati fin dall’inizio, nonostante la maggior parte dei web designer, sviluppatori e clienti continuino ad utilizzare un PC standard.

Google ha riconosciuto l’importanza dei dispositivi mobili. I siti si classificano meglio nella ricerca di Google quando sono utilizzabili e funzionano bene su uno smartphone. Un buon contenuto rimane vitale, ma un sito che carica lentamente e che non riesce ad adattarsi alle dimensioni dello schermo della vostra base di utenti potrebbe danneggiare il vostro business.

Infine, considerate anche l’accessibilità. Un sito che funziona correttamente per tutti, non importa quale dispositivo si stia utilizzando, raggiungerà un pubblico più vasto. L’accessibilità è un requisito legale in molti paesi, ma anche se non lo è nel vostro paese, tenete sempre conto che più spettatori porteranno più conversioni e una maggiore redditività.

Come Funziona il Design Reattivo?

A base del responsive design ci sono le media query: una tecnologia CSS che fa sì che vengano applicati gli stili in base a indicatori come il tipo di output (schermo, stampante o anche discorso), le dimensioni dello schermo, le proporzioni del display, l’orientamento del dispositivo, la profondità del colore e la precisione del puntatore. Le media query possono anche prendere in considerazione le preferenze dell’utente, comprese le animazioni ridotte, la modalità chiaro/scuro e un maggiore contrasto.

Gli esempi che abbiamo mostrato mostrano le media query che utilizzano solo la larghezza dello schermo, ma i siti possono essere molto più flessibili. Per i dettagli, fate riferimento al set completo di opzioni su MDN.

Il supporto delle media query è eccellente ed è presente nei browser da più di un decennio. Solo IE8 e inferiori non le supporta. Ignora gli stili applicati dalle media query, ma questo a volte può essere un vantaggio (si legga di più nella sezione Best Practices qui sotto).

Ci sono tre modi standard per applicare gli stili utilizzando le media query. Il primo carica fogli di stile specifici nel codice HTML. Ad esempio, il seguente tag carica il foglio di stile wide.css quando un dispositivo ha uno schermo largo almeno 800 pixel:

<link rel="stylesheet" media="screen and (min-width: 800px)" href="wide.css" />

In secondo luogo, i fogli di stile possono essere caricati condizionatamente nei file CSS utilizzando una at-rule @import:

/* main.css */ @import url('wide.css') screen and (min-width: 800px);

Più normalmente, applicherete le media query nei fogli di stile utilizzando un blocco at-rule CSS @media che modifica stili specifici. Ad esempio:

/* default styles */
main {
  width: 400px;
}

/* styles applied when screen has a width of at least 800px */
@media screen and (min-width: 800px) {
  main {
    width: 760px;
  }
}

Gli sviluppatori possono applicare qualsiasi regola di media query sia necessaria per adattare il layout di un sito.

Best Practice per le Media Query

Quando sono state concepite le media query per la prima volta, molti siti hanno optato per un insieme di layout rigidamente fissi. Questo è concettualmente più facile da progettare e codificare perché replica efficacemente un insieme limitato di modelli di pagina. Ad esempio:

  1. Le larghezze dello schermo inferiori a 600px utilizzano un layout mobile di 400px.
  2. Le larghezze dello schermo tra 600px e 999px utilizzano un layout tipo tablet di 600px.
  3. Le larghezze dello schermo superiori a 1.000px utilizzano un layout tipo desktop di 1000px.

La tecnica è imperfetta. I risultati su schermi molto piccoli e molto grandi possono apparire scadenti e può essere richiesta la manutenzione dei CSS quando i dispositivi e le dimensioni dello schermo cambiano nel tempo.

Un’opzione migliore è quella di utilizzare un design fluido mobile-first con punti di rottura che adattano il layout a certe dimensioni. In sostanza, il layout predefinito utilizza gli stili più semplici per piccoli schermi che posizionano gli elementi in blocchi verticali lineari.

Ad esempio, <article> e <aside> in un contenitore <main>:

/* default small-screen device */
main {
  width: 100%;
}

article, aside {
  width: 100%;
  padding: 2em;
}

Ecco il risultato in tutti i browser – anche quelli molto vecchi che non supportano le media query:

Esempio di screenshot senza supporto alle media query.
Esempio di screenshot senza supporto alle media query.

Quando le media query sono supportate e lo schermo supera una larghezza specifica, diciamo 500px, <article> e <aside> possono essere posizionati orizzontalmente. Questo esempio utilizza una griglia CSS, in cui il contenuto primario utilizza circa due terzi della larghezza e il contenuto secondario utilizza il restante terzo:

/* larger device */
@media (min-width: 500px) {
  main {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 2em;
  }

  article, aside {
    width: auto;
    padding: 0;
  }
}

Ecco il risultato su schermi più grandi:

sempio di screenshot con supporto delle media query.
Esempio di screenshot con supporto delle media query.

Alternative alle Media Query

I design reattivi possono anche essere implementati nei moderni CSS utilizzando proprietà più recenti che adattano intrinsecamente il layout senza esaminare le dimensioni della finestra. Le opzioni includono:

  • calc, min-width, max-width, min-height, max-height, e la più recente proprietà clamp possono tutti definire dimensioni che dimensionano gli elementi secondo limiti noti e spazio disponibile.
  • Le unità viewport vw, vh, vmin, e vmax possono dimensionare gli elementi secondo le frazioni di dimensione dello schermo.
  • Il testo può essere mostrato in colonne CSS che appaiono o scompaiono quando lo spazio lo permette.
  • Gli elementi possono essere dimensionati secondo le dimensioni degli elementi figli utilizzando le dimensioni min-content, fit-content, e max-content.
  • CSS flexbox può avvolgere – o non avvolgere – gli elementi quando iniziano a superare lo spazio disponibile.
  • Gli elementi della griglia CSS possono essere dimensionati con unità fr frazionarie proporzionali. La funzione CSS repeat può essere utilizzata insieme a minmax, auto-fit e auto-fill per allocare lo spazio disponibile.
  • Le nuove e (attualmente) sperimentali container query CSS possono reagire allo spazio parziale di cui dispone un componente all’interno di un layout.

Queste opzioni vanno oltre lo scopo di questo articolo, ma sono spesso più pratiche delle media query più crude, che possono rispondere solo alle dimensioni dello schermo. Se potete ottenere un layout senza media query, probabilmente sarà necessario meno codice, sarà più efficiente e richiederà una minore manutenzione nel tempo.

Detto questo, ci sono situazioni in cui le media query rimangono l’unica opzione di layout possibile. Rimangono essenziali quando avete bisogno di considerare altri fattori dello schermo come le proporzioni, l’orientamento del dispositivo, la profondità del colore, la precisione del puntatore o le preferenze dell’utente come le animazioni ridotte e la modalità chiaro/scuro.

Avete Bisogno di Media Query in JavaScript?

Fino ad ora abbiamo parlato soprattutto di CSS. Questo perché la maggior parte dei problemi di layout possono – e dovrebbero – essere risolti solo con i CSS.

Ma ci sono situazioni in cui è pratico usare una media query JavaScript invece dei CSS, come ad esempio quando:

  • Un componente, come un menu, ha funzionalità diverse su schermi piccoli e grandi.
  • Passare da e verso il ritratto/paesaggio influenza la funzionalità di una web app.
  • Un gioco basato sul tocco deve cambiare il layout del <canvas> o adattare i pulsanti di controllo.
  • Una web app aderisce alle preferenze dell’utente come la modalità scuro/chiaro, l’animazione ridotta, la precisione del tocco, ecc.

Le sezioni seguenti mostrano tre soluzioni in cui si utilizzano le media query – o opzioni simili alle media-query – in JavaScript. Tutti gli esempi restituiscono una stringa di stato dove:

  • vista piccola = uno schermo con una larghezza inferiore a 400 pixel;
  • vista media = uno schermo con una larghezza tra 400 e 799 pixel; e
  • vista grande = uno schermo con una larghezza di 800 pixel o più.

Opzione 1: Controllare le Dimensioni della Finestra di Visualizzazione

Nei giorni bui, prima che le media query fossero implementate, questa era l’unica opzione. JavaScript ascoltava gli eventi di “ridimensionamento” del browser, analizzava le dimensioni della finestra utilizzando window.innerWidth e window.innerHeight (o document.body.clientWidth e document.body.clientHeight nei vecchi IE) e reagiva di conseguenza.

Questo codice emette la stringa calcolata small, medium o large nella console:

const
  screen = {
    small: 0,
    medium: 400,
    large: 800
  };

// observe window resize
window.addEventListener('resize', resizeHandler);

// initial call
resizeHandler();

// calculate size
function resizeHandler() {

  // get window width
  const iw = window.innerWidth;
 
  // determine named size
  let size = null;
  for (let s in screen) {
    if (iw >= screen[s]) size = s;
  }

  console.log(size);
}

Ne potete vedere una dimostrazione funzionante qui. (Se usate un browser desktop, aprite questo link in una nuova finestra per facilitare il ridimensionamento. Gli utenti mobili possono ruotare il dispositivo)

L’esempio qui sopra esamina la dimensione della finestra mentre il browser viene ridimensionato; determina se è piccola, media o grande e la imposta come classe sull’elemento body, che cambia il colore di sfondo.

I vantaggi di questo metodo sono:

  • Funziona in ogni browser che può eseguire JavaScript – anche in applicazioni antiquate.
  • Cattura le dimensioni esatte e può reagire di conseguenza.

Gli svantaggi:

  • È una tecnica vecchia che richiede un considerevole codice.
  • È troppo esatta? Avete davvero bisogno di sapere quando la larghezza è 966px invece di 967px?
  • Potreste aver bisogno di abbinare manualmente le dimensioni ad una media query CSS corrispondente.
  • Gli utenti possono ridimensionare il browser rapidamente, facendo sì che la funzione handler venga eseguita di nuovo ogni volta. Questo può sovraccaricare i browser più vecchi e lenti strozzando l’evento. Può essere attivato solo una volta ogni 500 millisecondi.

In sintesi, non monitorate le dimensioni della finestra a meno che non abbiate requisiti di dimensionamento molto specifici e complessi.

Opzione 2: Definire e Monitorare una Proprietà CSS Personalizzata (Variabile)

Questa è una tecnica un po’ insolita che cambia il valore di una stringa di una proprietà CSS personalizzata quando viene attivata una media query. Le proprietà personalizzate sono supportate da tutti i browser moderni (ma non in IE).

Nell’esempio qui sotto, la proprietà personalizzata --screen custom property è impostata su “small”, “medium” o “large” all’interno di un blocco di codice @media:

body {
  --screen: "small";
  background-color: #cff;
  text-align: center;
}

@media (min-width: 400px) {
 
  body {
    --screen: "medium";
    background-color: #fcf;
  }
 
}

@media (min-width: 800px) {
 
  body {
    --screen: "large";
    background-color: #ffc;
  }
 
}

Il valore può essere emesso in CSS da solo utilizzando uno pseudo-elemento (questo deve essere contenuto tra apici singoli o doppi):

p::before {
  content: var(--screen);
}

Potete recuperare il valore della proprietà personalizzata con JavaScript:

const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen');

Questa non è proprio tutta la storia, però, perché il valore restituito contiene tutti gli spazi bianchi e i caratteri di citazione definiti dopo i due punti nel CSS. La stringa sarà ‘”large”‘, quindi è necessario fare un po’ di ordine:

// returns small, medium, or large in a string
const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen')
                 .replace(/\W/g, '');

Potete vederne una dimostrazione funzionante qui. (Se usate un browser desktop, aprite questo link in una nuova finestra per facilitare il ridimensionamento. Gli utenti mobili possono ruotare il dispositivo)

L’esempio esamina il valore CSS ogni due secondi. Richiede un po’ di codice JavaScript, ma è necessario per rilevare i cambiamenti – non potete rilevare automaticamente se il valore della proprietà personalizzata è cambiato utilizzando i CSS.

Non è nemmeno possibile scrivere il valore in uno pseudo-elemento e rilevare il cambiamento utilizzando un DOM Mutation Observer. Gli pseudo-elementi non sono una parte “reale” del DOM!

I vantaggi:

  • È una tecnica semplice che utilizza principalmente i CSS e corrisponde alle vere media query.
  • Allo stesso tempo può essere modificata qualsiasi altra proprietà CSS.
  • Non c’è bisogno di duplicare o analizzare le stringhe delle media query JavaScript.

Lo svantaggio principale è che non potete reagire automaticamente ad un cambiamento nella dimensione della finestra del browser. Se l’utente ruota il suo telefono dall’orientamento verticale a quello orizzontale, JavaScript non lo saprà mai. Potete verificare i cambiamenti frequentemente, ma questo è inefficiente e porta al ritardo che vedete nella nostra dimostrazione.

Il monitoraggio delle proprietà CSS personalizzate è una tecnica innovativa, ma è pratica solo quando:

  1. Il layout può essere fissato nel momento in cui la pagina viene resa inizialmente. Un chiosco o un terminale point-of-sale è una possibilità, ma è probabile che questi abbiano risoluzioni fisse e un unico layout, quindi le media query JavaScript diventano irrilevanti.
  2. Il sito o l’applicazione esegue già frequenti funzioni basate sul tempo, come l’animazione di un gioco. La proprietà personalizzata potrebbe essere controllata contemporaneamente per determinare se sono necessari dei cambiamenti di layout.

Opzione 3: Utilizzare l’API matchMedia

L’API matchMedia è un po’ insolita ma vi permette di implementare una media query in JavaScript. È supportata dalla maggior parte dei browser, da IE10 in su. Il costruttore restituisce un oggetto MediaQueryList che dispone di una proprietà matches che assume il valore true o false per la sua specifica media query.

Il seguente codice produce true quando la larghezza della finestra del browser è di 800px o superiore:

const mqLarge = window.matchMedia( '(min-width: 800px)' );
console.log( mqLarge.matches );

All’oggetto MediaQueryList può essere applicato un evento “change”. Questo viene attivato ogni volta che lo stato della proprietà matches cambia: Diventa true (oltre 800px) dopo essere stato precedentemente false (sotto 800px) o viceversa.

Alla funzione di gestione ricevente viene passato come primo parametro l’oggetto MediaQueryList:

const mqLarge = window.matchMedia( '(min-width: 800px)' );
mqLarge.addEventListener('change', mqHandler);

// media query handler function
function mqHandler(e) {
 
  console.log(
    e.matches ? 'large' : 'not large'
  );
 
}

Il gestore viene eseguito solo quando cambia la proprietà matches. Non verrà eseguito quando la pagina viene caricata inizialmente, quindi potete invocare direttamente la funzione per determinare lo stato iniziale:

// initial state
mqHandler(mqLarge);

L’API funziona bene quando vi muovete tra due stati distinti. Per analizzare tre o più stati, come small, medium e large, sarà richiesta una maggiore quantità di codice.

Iniziate definendo un oggetto “state” dello schermo con gli oggetti matchMedia associati:

const
  screen = {
    small : null,
    medium: window.matchMedia( '(min-width: 400px)' ),
    large: window.matchMedia( '(min-width: 800px)' )
  };

Non è necessario definire un oggetto matchMedia sullo stato small perché il gestore dell’evento medium si attiverà quando si sposta tra small e medium.

Possono quindi essere impostati gli event listener per gli eventi medium e large. Questi invocano la stessa funzione gestore mqHandler():

// media query change events
for (let [scr, mq] di Object.entries(screen)) {
  if (mq) mq.addEventListener('change', mqHandler);
}

La funzione handler deve controllare tutti gli oggetti MediaQueryList per determinare se small, medium o large è attualmente attivo. Le corrispondenze devono essere eseguite in ordine di dimensione perché una larghezza di 999px corrisponderebbe sia a medium che a large – solo la più grande dovrebbe “vincere”:

// media query handler function
function mqHandler() {

  let size = null;
  for (let [scr, mq] di Object.entries(screen)) {
    if (!mq || mq.matches) size = scr;
  }
 
  console.log(size);
 
}

Qui trovate una dimostrazione funzionante. (Se usate un browser desktop, aprite questo link in una nuova finestra per facilitarne il ridimensionamento. Gli utenti mobili possono ruotare il dispositivo)

Esempi di utilizzo sono:

  1. Media query nei CSS per impostare e visualizzare una proprietà personalizzata (come mostrato nell‘opzione 2 vista sopra)
  2. Media query identiche negli oggetti matchMedia per monitorare i cambiamenti di dimensione in JavaScript. L’output JavaScript cambierà esattamente nello stesso momento.

I principali vantaggi dell’utilizzo dell’API matchMedia sono:

  • È guidata dagli eventi ed efficiente nel processare i cambiamenti delle media query.
  • Utilizza stringhe di media query identiche ai CSS.

Gli svantaggi:

  • Gestire due o più media query richiede più attenzione e logica nel codice.
  • Probabilmente dovrete duplicare le stringhe media query sia nel codice CSS che in quello JavaScript. Questo potrebbe portare ad errori se non li tenete sincronizzati.

Per evitare discrepanze tra le media query, potreste considerare l’utilizzo di token di progettazione nel vostro sistema di compilazione. Le stringhe di media query sono definite in un file JSON (o simile) e i valori sono inseriti nel codice CSS e JavaScript al momento della compilazione.

In sintesi, l’API matchMedia è probabilmente il modo più efficiente e pratico per implementare una media query JavaScript. Ha alcune particolarità, ma è l’opzione migliore nella maggior parte dei casi.

Riepilogo

Le opzioni di dimensionamento CSS intrinseche sono sempre più praticabili, ma le media query rimangono la base del responsive web design per la maggior parte dei siti. Saranno sempre necessarie per gestire layout più complessi e preferenze dell’utente, come la modalità chiaro/scuro.

Cercate di mantenere le media query solo sui CSS, dove possibile. Ma quando non avete altra scelta che avventurarvi nel regno di JavaScript, l’API matchMedia fornisce un controllo aggiuntivo per i componenti JavaScript media query che richiedono funzionalità aggiuntive in base alle dimensioni.

Avete altri suggerimenti per implementare una media query JavaScript? Condivideteli nella sezione dei commenti!

Craig Buckler

Web developer freelance del Regno Unito, scrittore e divulgatore. È in giro da molto tempo e sproloquia su standard e prestazioni.