Tipografia reattiva ai fluidi con dimensionamento fluido CSS Poly
Pubblicato: 2022-03-10In questo articolo, lo porteremo a un altro livello. Esamineremo come creare una tipografia scalabile e fluida su più punti di interruzione e dimensioni dei caratteri predefinite utilizzando funzionalità del browser ben supportate e alcune algebre di base. La parte migliore è che puoi automatizzare tutto usando Sass.
Ulteriori letture su SmashingMag:
- Tipografia veramente fluida con unità vh e vw
- Modelli tipografici nel design della newsletter e-mail HTML
- Il buono, il cattivo e i grandi esempi di tipografia web
- Strumenti e risorse per una tipografia Web più significativa
Quando si lavora con designer creativi sui progetti di pagine Web, è abbastanza comune ricevere più tavole da disegno/layout di Sketch o Photoshop, uno per ogni punto di interruzione. In quel progetto, gli elementi (come un'intestazione h1
) avranno generalmente dimensioni diverse a ciascun punto di interruzione. Per esempio:
- L'
h1
nel layout piccolo potrebbe essere22px
- L'
h1
con il layout medio potrebbe essere24px
- L'
h1
nel layout grande potrebbe essere34px
Il CSS minimo indispensabile per questo utilizza media query:
h1 { font-size: 22px; } @media (min-width:576px) { h1 { font-size: 22px; } } @media (min-width:768px) { h1 { font-size: 24px; } } @media (min-width:992px) { h1 { font-size: 34px; } }
Questo è un buon primo passo, ma stai limitando la font-size
del carattere solo a ciò che è stato specificato dal designer nei punti di interruzione forniti. Cosa direbbe il designer se chiedessi: "Quale dovrebbe essere la font-size
del carattere in una finestra ampia 850 px?" La risposta nella maggior parte dei casi è che sarebbe tra 24px e 34px. Ma in questo momento, è solo 24px secondo il tuo CSS, che probabilmente non è ciò che il designer aveva immaginato.
La tua opzione a questo punto è calcolare quale dovrebbe essere quella dimensione e aggiungere un altro punto di interruzione. È abbastanza facile. Ma che dire di tutte le altre risoluzioni? Quale dovrebbe essere la font-size
del carattere a 800 px di larghezza? E 900px? E 935px? Ovviamente il designer non ti fornirà un layout completo per ogni singola risoluzione possibile. Anche se lo facessero, dovresti aggiungere dozzine (o centinaia) di punti di interruzione per tutte le diverse font-sizes
dei caratteri desiderate dal designer? Ovviamente no.
Il tuo layout si sta già ridimensionando in modo fluido con la larghezza della tua finestra. Non sarebbe bello se la tua tipografia si adattasse in modo prevedibile al tuo layout fluido? Cos'altro possiamo fare per migliorare questo aspetto?
Unità Viewport in soccorso?
Le unità Viewport sono un altro passo nella giusta direzione. Consentono al tuo testo di ridimensionarsi in modo fluido con i tuoi layout. E il supporto del browser è ottimo in questi giorni.
Ma la fattibilità delle unità Viewport dipende molto dai progetti creativi originali per una pagina web. Sarebbe fantastico impostare la font-size
del carattere usando vw
e il gioco è fatto:
h1 { font-size: 2vw; }
Ma questo funziona solo se le tue tavole da disegno creative ne tengono conto. Il designer ha scelto una dimensione del testo che corrispondesse esattamente al 2% della larghezza di ciascuna delle sue tavole da disegno? Ovviamente no. Calcoliamo quale dovrebbe essere il valore vw
per ciascuno dei nostri punti di interruzione:
Dimensioni 22px
@ 576px
larghezza = 22 ⁄ 576 *100 = 24px
Dimensioni 24px @ 768px
larghezza = 24 ⁄ 768 *100 = 34px
Dimensioni 34px @ 992px
larghezza = 34 ⁄ 992 *100 = 3.43vw
Sono vicini ma non sono tutti uguali. Quindi dovresti comunque utilizzare le query multimediali per passare tra le dimensioni del testo e ci sarebbero comunque dei salti. E considera questo strano effetto collaterale:
@ 767px, il 3,82% della larghezza della finestra è 29px. Se il viewport è più largo di 1 pixel, la font-size
del carattere torna improvvisamente a 24px . Questa animazione di una finestra che viene ridimensionata dimostra questo effetto collaterale indesiderabile:
Questo drastico cambiamento nella dimensione del carattere non è quasi sicuramente ciò che il designer aveva immaginato. Allora come risolviamo questo problema?
Regressione lineare statistica?
Attesa. Che cosa? Sì, questo è un articolo sui CSS, ma un po' di matematica di base può fare molto verso una soluzione elegante al nostro problema.
Innanzitutto, tracciamo le nostre risoluzioni e le dimensioni del testo corrispondenti su un grafico:
Qui puoi vedere un grafico a dispersione delle dimensioni del testo specificate dal designer alle larghezze della finestra definite. L'asse x è la larghezza della finestra e l'asse y è la font-size
del carattere. Vedi quella linea? Si chiama trendline . È un modo per trovare un valore interpolato font-size
per qualsiasi larghezza della finestra, in base ai dati forniti.
La Trendline è la chiave di tutto questo
Se potessi impostare la font-size
del carattere in base a questa linea di tendenza, avresti un h1 che si adatta senza problemi a tutte le risoluzioni che si avvicinerebbero a corrispondere a ciò che il designer intendeva. Per prima cosa, diamo un'occhiata alla matematica. La retta è definita da questa equazione:
- m = pendenza
- b = l'intercetta y
- x = la larghezza della finestra corrente
- y = la
font-size
risultante
Esistono diversi metodi per determinare la pendenza e l'intercetta y. Quando sono coinvolti più valori, un metodo comune è l'adattamento dei minimi quadrati:
Una volta eseguiti questi calcoli, hai la tua equazione della linea di tendenza.
Come lo uso nei CSS?
Ok, la matematica sta diventando piuttosto pesante. Come utilizziamo effettivamente questa roba nello sviluppo web front-end? La risposta è CSS calc()
! Ancora una volta, una tecnologia CSS abbastanza nuova che è molto ben supportata.
Puoi usare l'equazione della linea di tendenza in questo modo:
h1 { font-size: calc({slope}*100vw + {y-intercept}px); }
Una volta trovata la pendenza e l'intercettazione a y, basta collegarli!
Nota: devi moltiplicare la pendenza per 100
poiché la stai utilizzando come unità vw
che è 1/100 della larghezza della finestra.
Può essere automatizzato?
Ho portato il metodo di adattamento ai minimi quadrati in una funzione Sass facile da usare:
/// least-squares-fit /// Calculate the least square fit linear regression of provided values /// @param {map} $map - A Sass map of viewport width and size value combinations /// @return Linear equation as a calc() function /// @example /// font-size: least-squares-fit((576px: 24px, 768px: 24px, 992px: 34px)); /// @author Jake Wilson <[email protected]> @function least-squares-fit($map) { // Get the number of provided breakpoints $length: length(map-keys($map)); // Error if the number of breakpoints is < 2 @if ($length < 2) { @error "leastSquaresFit() $map must be at least 2 values" } // Calculate the Means $resTotal: 0; $valueTotal: 0; @each $res, $value in $map { $resTotal: $resTotal + $res; $valueTotal: $valueTotal + $value; } $resMean: $resTotal/$length; $valueMean: $valueTotal/$length; // Calculate some other stuff $multipliedDiff: 0; $squaredDiff: 0; @each $res, $value in $map { // Differences from means $resDiff: $res - $resMean; $valueDiff: $value - $valueMean; // Sum of multiplied differences $multipliedDiff: $multipliedDiff + ($resDiff * $valueDiff); // Sum of squared resolution differences $squaredDiff: $squaredDiff + ($resDiff * $resDiff); } // Calculate the Slope $m: $multipliedDiff / $squaredDiff; // Calculate the Y-Intercept $b: $valueMean - ($m * $resMean); // Return the CSS calc equation @return calc(#{$m*100}vw + #{$b}); }
Funziona davvero? Apri questo CodePen e ridimensiona la finestra del browser. Funziona! Le dimensioni dei caratteri sono abbastanza vicine a quelle richieste dal design originale e si adattano perfettamente al tuo layout.
Ora, devo ammettere, non è perfetto. I valori sono vicini al design originale ma non corrispondono del tutto. Ciò è dovuto al fatto che una linea di tendenza lineare è un'approssimazione di dimensioni di caratteri specifiche a larghezze di visualizzazione specifiche. Questo è ereditario della regressione lineare. C'è sempre qualche errore nei tuoi risultati. È un compromesso tra semplicità e precisione. Inoltre, tieni presente che più le dimensioni del tuo testo sono varie, maggiore sarà l'errore nella tua linea di tendenza.
Possiamo fare di meglio?
Minimi quadrati polinomiali Fit
Per ottenere una linea di tendenza più accurata, è necessario esaminare argomenti più avanzati, come una linea di tendenza di regressione polinomiale che potrebbe assomigliare a questa:
Ora è più così ! Molto più preciso della nostra linea retta. Un'equazione di regressione polinomiale di base è simile alla seguente:
Più precisa vuoi la tua curva, più complicata diventa l'equazione. Sfortunatamente, non puoi farlo in CSS . calc()
semplicemente non può fare questo tipo di matematica avanzata. In particolare, non puoi calcolare gli esponenti:
font-size: calc(3vw * 3vw); /* This doesn't work in CSS */
Quindi, finché calc()
non supporta questo tipo di matematica non lineare, siamo bloccati solo con equazioni lineari . C'è qualcos'altro che possiamo fare per migliorare questo aspetto?
Punti di interruzione ed equazioni lineari multiple
E se calcolassimo solo una linea retta tra ogni coppia di punti di interruzione? Qualcosa come questo:
Quindi in questo esempio calcoleremo la linea retta tra 22px
e 24px
e poi un'altra tra 24px
e 34px
. Il Sass sarebbe così:
// SCSS h1 { @media (min-width:576px) { font-size: calc(???); } @media (min-width:768px) { font-size: calc(???); } }
Potremmo usare il metodo di adattamento dei minimi quadrati per quei valori calc()
ma poiché è solo una linea retta tra 2 punti, la matematica potrebbe essere notevolmente semplificata. Ricordi l'equazione per una retta?
Poiché ora stiamo parlando di soli 2 punti, trovare la pendenza (m) e l'intercetta y (b) è banale:
Ecco una funzione Sass per questo:
/// linear-interpolation /// Calculate the definition of a line between two points /// @param $map - A Sass map of viewport widths and size value pairs /// @returns A linear equation as a calc() function /// @example /// font-size: linear-interpolation((320px: 18px, 768px: 26px)); /// @author Jake Wilson <[email protected]> @function linear-interpolation($map) { $keys: map-keys($map); @if (length($keys) != 2) { @error "linear-interpolation() $map must be exactly 2 values"; } // The slope $m: (map-get($map, nth($keys, 2)) - map-get($map, nth($keys, 1)))/(nth($keys, 2) - nth($keys,1)); // The y-intercept $b: map-get($map, nth($keys, 1)) - $m * nth($keys, 1); // Determine if the sign should be positive or negative $sign: "+"; @if ($b < 0) { $sign: "-"; $b: abs($b); } @return calc(#{$m*100}vw #{$sign} #{$b}); }
Ora, usa semplicemente la funzione di interpolazione lineare su più punti di interruzione nel tuo Sass. Inoltre, aggiungiamo alcune font-sizes
:
// SCSS h1 { // Minimum font-size font-size: 22px; // Font-size between 576 - 768 @media (min-width:576px) { $map: (576px: 22px, 768px: 24px); font-size: linear-interpolation($map); } // Font-size between 768 - 992 @media (min-width:768px) { $map: (768px: 24px, 992px: 34px); font-size: linear-interpolation($map); } // Maximum font-size @media (min-width:992px) { font-size: 34px; } }
E genera questo CSS:
h1 { font-size: 22px; } @media (min-width: 576px) { h1 { font-size: calc(1.04166667vw + 16px); } } @media (min-width: 768px) { h1 { font-size: calc(4.46428571vw - 10.28571429px); } } @media (min-width: 992px) { h1 { font-size: 34px; } }
Il Santo Graal del dimensionamento CSS?
Avvolgiamo tutto questo in un bel mixin Sass (per i pigri ed efficienti!). Sto coniando questo metodo Poly Fluid Sizing :
/// poly-fluid-sizing /// Generate linear interpolated size values through multiple break points /// @param $property - A string CSS property name /// @param $map - A Sass map of viewport unit and size value pairs /// @requires function linear-interpolation /// @requires function map-sort /// @example /// @include poly-fluid-sizing('font-size', (576px: 22px, 768px: 24px, 992px: 34px)); /// @author Jake Wilson <[email protected]> @mixin poly-fluid-sizing($property, $map) { // Get the number of provided breakpoints $length: length(map-keys($map)); // Error if the number of breakpoints is < 2 @if ($length < 2) { @error "poly-fluid-sizing() $map requires at least values" } // Sort the map by viewport width (key) $map: map-sort($map); $keys: map-keys($map); // Minimum size #{$property}: map-get($map, nth($keys,1)); // Interpolated size through breakpoints @for $i from 1 through ($length - 1) { @media (min-width:nth($keys,$i)) { $value1: map-get($map, nth($keys,$i)); $value2: map-get($map, nth($keys,($i + 1))); // If values are not equal, perform linear interpolation @if ($value1 != $value2) { #{$property}: linear-interpolation((nth($keys,$i): $value1, nth($keys,($i+1)): $value2)); } @else { #{$property}: $value1; } } } // Maxmimum size @media (min-width:nth($keys,$length)) { #{$property}: map-get($map, nth($keys,$length)); } }
Questo mixin Sass richiede alcune funzioni Sass nei seguenti concetti Github:
- interpolazione lineare
- map-sort
- elenco-ordinamento
- lista-rimozione
Il mixin poly-fluid-sizing()
eseguirà l'interpolazione lineare su ciascuna coppia di larghezze della finestra e imposterà una dimensione minima e massima. Puoi importarlo in qualsiasi progetto Sass e utilizzarlo facilmente senza dover conoscere la matematica dietro di esso. Ecco l'ultimo CodePen che utilizza questo metodo.
Alcune note
- Ovviamente questo metodo si applica non solo alla
font-size
ma a qualsiasi proprietà di unità/lunghezza (margin
,padding
, ecc.). Si passa il nome della proprietà desiderata nel mixin come una stringa. - La mappa Sass delle coppie di valori di larghezza + dimensione della finestra può essere passata in qualsiasi ordine nel mixin
poly-fluid-sizing()
. Ordina automaticamente la mappa in base alla larghezza del Viewport dal più basso al più alto . Quindi potresti passare una mappa come questa e funzionerebbe bene:
$map: (576px: 22px, 320px: 18px, 992px: 34px, 768px: 24px); @include poly-fluid-sizing('font-size', $map);
- Una limitazione per questo metodo è che non puoi passare in unità miste nel mixin. Ad esempio,
3em
@576px
larghezza. Sass non saprà davvero cosa fare matematicamente lì.
Conclusione
È questo il meglio che possiamo fare? Il Poly Fluid Sizing è il Santo Graal del dimensionamento dell'unità fluida nei CSS? Forse. I CSS attualmente supportano le funzioni di animazione non lineare e tempo di transizione, quindi forse c'è la possibilità che anche calc()
lo supporti un giorno. Se ciò accade, vale la pena dare un'occhiata di nuovo alla regressione polinomiale non lineare. Ma forse no... Il ridimensionamento lineare potrebbe comunque essere superiore.
Ho iniziato a esplorare questa idea all'inizio del 2017 e alla fine ho sviluppato la soluzione di cui sopra. Da allora, ho visto alcuni sviluppatori elaborare idee simili e diversi pezzi di questo puzzle. Ho pensato che fosse giunto il momento per me di condividere il mio metodo e come ci sono arrivato. Unità Viewport. Calc(). Sass. Punti di interruzione. Nessuna di queste cose è nuova. Sono tutte funzionalità del browser che esistono da anni (con vari gradi di supporto). Li ho usati insieme solo in un modo che non era stato ancora completamente esplorato. Non aver mai paura di guardare gli strumenti che usi ogni giorno e pensare fuori dagli schemi a come utilizzarli meglio e far crescere il tuo set di abilità.