Tipografie receptivă la fluid cu dimensionare CSS Poly Fluid

Publicat: 2022-03-10
Rezumat rapid ↬ Aspectele fluide au fost o parte normală a dezvoltării front-end de ani de zile. Ideea de tipografie fluidă, totuși, este relativ nouă și nu a fost încă explorată pe deplin. Până acum, ideea majorității dezvoltatorilor de tipografie fluidă folosește pur și simplu unități Viewport, poate cu unele dimensiuni minime și maxime. În acest articol, o vom duce la un alt nivel. Vom examina cum să creăm o tipografie scalabilă și fluidă în mai multe puncte de întrerupere și dimensiuni de font predefinite folosind funcții bine acceptate de browser și unele algebră de bază. Cea mai bună parte este că puteți automatiza totul folosind Sass.

În acest articol, o vom duce la un alt nivel. Vom examina cum să creăm o tipografie scalabilă și fluidă în mai multe puncte de întrerupere și dimensiuni de font predefinite folosind funcții bine acceptate de browser și unele algebră de bază. Cea mai bună parte este că puteți automatiza totul folosind Sass.

Citiți suplimentare despre SmashingMag:

  • Tipografie cu adevărat fluidă cu unități vh și vw
  • Modele tipografice în designul buletinului informativ prin e-mail HTML
  • Exemplele bune, rele și mari de tipografie web
  • Instrumente și resurse pentru o tipografie web mai semnificativă

Când lucrați cu designeri creativi pe design de pagini web, este destul de obișnuit să primiți mai multe planșe/aspecte Sketch sau Photoshop, câte una pentru fiecare punct de întrerupere. În acel design, elementele (cum ar fi un titlu h1 ) vor avea de obicei dimensiuni diferite la fiecare punct de întrerupere. De exemplu:

Mai multe după săritură! Continuați să citiți mai jos ↓
  1. h1 la aspectul mic ar putea fi 22px
  2. h1 la aspectul mediu ar putea fi 24px
  3. h1 la aspectul mare ar putea fi 34px

CSS minim pentru aceasta utilizează interogări media:

 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; } }

Acesta este un prim pas bun, dar limitați font-size doar la ceea ce a fost specificat de designer la punctele de întrerupere furnizate. Ce ar spune designerul dacă ai întreba „Care ar trebui să fie font-size la o fereastră de vizualizare de 850 px?” Răspunsul în cele mai multe cazuri este că ar fi undeva între 24 și 34 px. Dar în acest moment, este doar 24 px conform CSS-ului tău, ceea ce probabil nu este ceea ce și-a imaginat designerul.

Opțiunea dvs. în acest moment este să calculați care ar trebui să fie acea dimensiune și să adăugați un alt punct de întrerupere. E destul de ușor. Dar cum rămâne cu toate celelalte rezoluții? Care ar trebui să fie font-size la 800px lățime? Dar 900px? Dar 935px? Evident, designerul nu vă va oferi un aspect complet pentru fiecare rezoluție posibilă. Chiar dacă ar fi făcut-o, ar trebui să adăugați zeci (sau sute) de puncte de întrerupere pentru toate dimensiunile diferitelor font-sizes care sunt dorite de designer? Desigur că nu.

Aspectul dvs. se scalează deja fluid cu lățimea ferestrei dvs. de vizualizare. Nu ar fi frumos dacă tipografia dvs. s-ar scala în mod previzibil cu aspectul dvs. fluid? Ce altceva putem face pentru a îmbunătăți acest lucru?

Unități de vizualizare pentru salvare?

Unitățile de vizualizare sunt un alt pas în direcția corectă. Acestea permit textului să se redimensioneze fluid odată cu machetele. Și suportul pentru browser este excelent în aceste zile.

Pot folosi Viewport?
(Vezi versiunea mare)

Dar viabilitatea unităților Viewport depinde foarte mult de design-urile creative originale pentru o pagină web. Ar fi grozav să setați font-size folosind vw și gata:

 h1 { font-size: 2vw; } 

Dar acest lucru funcționează numai dacă tablourile tale creative iau în considerare acest lucru. A ales designerul o dimensiune a textului care să fie exact 2% din lățimea fiecărei plăci de artă? Desigur că nu. Să calculăm care ar trebui să fie valoarea vw pentru fiecare dintre punctele noastre de întrerupere:

Dimensiune 22px px @ 576px lățime = 22576 *100 = 3,82 24px 24 px dimensiune @ 768px lățime = 24768 *100 = 3,13 34px 34 px dimensiune @ 992px lățime = 34992 * 100 px *

Sunt aproape, dar nu sunt toți la fel. Deci, ar trebui în continuare să utilizați interogări media pentru a trece între dimensiunile textului și ar mai exista salturi. Și luați în considerare acest efect secundar ciudat:

@ 767px, 3,82% din lățimea ferestrei de vizualizare este de 29px. Dacă fereastra de vizualizare este cu 1 pixel mai lată, font-size scade brusc la 24 de pixeli . Această animație a unei ferestre de vizualizare redimensionată demonstrează acest efect secundar nedorit:

Această schimbare dramatică a dimensiunii fontului nu este aproape sigur ceea ce și-a imaginat designerul. Deci, cum rezolvăm această problemă?

Regresie liniară statistică?

Aștepta. Ce? Da, acesta este un articol despre CSS, dar unele matematice de bază pot merge mult către o soluție elegantă a problemei noastre.

În primul rând, să ne trasăm rezoluțiile și dimensiunile corespunzătoare ale textului pe un grafic:

Graficul de dispersie a mărimii fontului și a lățimii Viewport corespunzătoare
Graficul de dispersie a font-size și a lățimii ferestrei de vizualizare corespunzătoare (Foi de calcul Google) (Vedeți versiunea mare)

Aici puteți vedea o diagramă de dispersie a dimensiunilor textului specificate de designer la lățimile definite ale ferestrei de vizualizare. Axa x este lățimea ferestrei de vizualizare, iar axa y este font-size . Vezi acea linie? Asta se numește linie de tendință . Este o modalitate de a găsi o valoare interpolată font-size pentru orice lățime a ferestrei de vizualizare, pe baza datelor furnizate.

Linia de tendință este cheia pentru toate acestea

Dacă ați putea seta font-size funcție de această linie de tendință, ați avea un h1 care se scalează fără probleme la toate rezoluțiile care ar fi aproape de a se potrivi cu ceea ce a intenționat designerul. Mai întâi, să ne uităm la matematică. Linia dreaptă este definită de această ecuație:

Definirea ecuației liniare
Definirea ecuației liniare
  • m = panta
  • b = intersecția cu y
  • x = lățimea curentă a ferestrei de vizualizare
  • y = font-size rezultată

Există mai multe metode pentru a determina panta și intersecția cu y. Când sunt implicate mai multe valori, o metodă comună este potrivirea celor mai mici pătrate:

Cele mai mici pătrate

Odată ce executați acele calcule, aveți ecuația dvs. de tendință.

Cum folosesc asta în CSS?

Bine, asta devine destul de greu la matematică. Cum folosim de fapt aceste lucruri în dezvoltarea web front-end? Răspunsul este CSS calc() ! Încă o dată, o tehnologie CSS destul de nouă, care este foarte bine susținută.

Pot folosi Calc?
(Vezi versiunea mare)

Puteți utiliza ecuația liniei de tendință astfel:

 h1 { font-size: calc({slope}*100vw + {y-intercept}px); }

Odată ce găsești panta și interceptarea y, trebuie doar să le conectezi!

Notă: trebuie să înmulțiți panta cu 100 , deoarece o utilizați ca unitate vw , care este 1/100 din lățimea Viewportului.

Poate fi automatizat?

Am portat metoda de potrivire a celor mai mici pătrate într-o funcție Sass ușor de utilizat:

 /// 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}); }

Funcționează asta cu adevărat? Deschideți acest CodePen și redimensionați fereastra browserului. Funcționează! Dimensiunile fonturilor sunt destul de apropiate de ceea ce cerea designul original și se scalează fără probleme cu aspectul dvs.

Testul SCSS Least Squares Fit user="jakobud"]Vedeți testul SCSS Least Squares Fit de Jake Wilson (@jakobud) pe CodePen.

Testul SCSS Least Squares Fit user=“jakobud”]Vedeți testul SCSS Least Squares Fit de Jake Wilson (@jakobud) pe CodePen.

Acum, desigur, nu este perfect. Valorile sunt apropiate de designul original, dar nu se potrivesc deloc. Acest lucru se datorează faptului că o linie de tendință liniară este o aproximare a anumitor dimensiuni de font la lățimi specifice ferestrelor de vizualizare. Aceasta este moștenirea regresiei liniare. Există întotdeauna o eroare în rezultatele dvs. Este un compromis între simplitate și acuratețe. De asemenea, rețineți, cu cât dimensiunile textului dvs. sunt mai variate, cu atât vor exista mai multe erori în linia de tendință.

Putem face mai bine decât asta?

Potrivirea celor mai mici pătrate polinomiale

Pentru a obține o linie de tendință mai precisă, trebuie să vă uitați la subiecte mai avansate, cum ar fi o linie de tendință de regresie polinomială care ar putea arăta cam așa:

Linia de tendință de regresie polinomială
Linie de tendință de regresie polinomială (Google Spreadsheets) (Vezi versiunea mare)

Acum seamănă mai mult! Mult mai precisă decât linia noastră dreaptă. O ecuație de regresie polinomială de bază arată astfel:

O ecuație polinomială de gradul 3
O ecuație polinomială de gradul 3

Cu cât îți dorești curba mai precisă, cu atât ecuația devine mai complicată. Din păcate, nu puteți face acest lucru în CSS . calc() pur și simplu nu poate face acest tip de matematică avansată. Mai exact, nu puteți calcula exponenți:

 font-size: calc(3vw * 3vw); /* This doesn't work in CSS */

Deci, până când calc() acceptă acest tip de matematică neliniară, suntem blocați doar cu ecuații liniare . Mai putem face ceva pentru a îmbunătăți acest lucru?

Puncte de întrerupere și ecuații liniare multiple

Ce se întâmplă dacă am calcula doar o linie dreaptă între fiecare pereche de puncte de întrerupere? Ceva de genul:

Linii de tendință de regresie liniară între mai multe perechi de valori
Linii de tendință de regresie liniară între mai multe perechi de valori (Google Spreadsheets) (Vedeți versiunea mare)

Deci, în acest exemplu, vom calcula linia dreaptă între 22px și 24px și apoi alta între 24px și 34px . Sass ar arăta astfel:

 // SCSS h1 { @media (min-width:576px) { font-size: calc(???); } @media (min-width:768px) { font-size: calc(???); } }

Am putea folosi metoda de potrivire a celor mai mici pătrate pentru acele valori calc() , dar deoarece este doar o linie dreaptă între 2 puncte, matematica ar putea fi foarte simplificată. Vă amintiți ecuația pentru o linie dreaptă?

Definirea ecuației liniare
Definirea ecuației liniare

Deoarece acum vorbim despre doar 2 puncte, găsirea pantei (m) și a intersecției cu y (b) este trivială:

Aflarea pantei și intersecția cu y a unei ecuații liniare
Aflarea pantei și intersecția cu y a unei ecuații liniare

Iată o funcție Sass pentru aceasta:

 /// 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}); }

Acum, utilizați doar funcția de interpolare liniară pe mai multe puncte de întrerupere din Sass. De asemenea, să introducem câteva 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; } }

Și generează acest 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; } } 

Sfântul Graal al dimensionării CSS?

Să încheiem totul într-un mixin frumos Sass (pentru cei leneși și eficienți!). Am inventat această metodă 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)); } }

Acest mixin Sass necesită câteva funcții Sass în următoarele aspecte Github:

  • interpolare liniară
  • hartă-sortare
  • listă-sortare
  • lista-eliminare

Mixin-ul poly-fluid-sizing() va efectua interpolare liniară pe fiecare pereche de lățimi ale ferestrei și va stabili o dimensiune minimă și maximă. Puteți importa acest lucru în orice proiect Sass și îl puteți utiliza cu ușurință fără a fi nevoie să cunoașteți matematica din spatele lui. Iată ultimul CodePen care utilizează această metodă.

Dimensionarea polifluidului folosind ecuații liniare, unități de vizualizare și calc() user="jakobud"]Vedeți Dimensiunea polifluidului stilou folosind ecuații liniare, unități de vizualizare și calc()"] Dimensiune polifluid folosind ecuații liniare, unități de vizualizare și calc() de Jake Wilson (@jakobud) pe CodePen.

Dimensionarea polifluidului folosind ecuații liniare, unități de vizualizare și calc() user=“jakobud”]Vedeți Dimensiunea polifluidului stilou folosind ecuații liniare, unități de vizualizare și calc()“] Dimensiune polifluid folosind ecuații liniare, unități de vizualizare și calc() de Jake Wilson (@jakobud) pe CodePen.

Câteva Note

  • Evident, această metodă se aplică nu numai font-size ci și oricărei proprietăți de unitate/lungime ( margin , padding etc.). Treceți numele proprietății dorite în mixin ca șir.
  • Harta Sass a perechilor lățime + dimensiune a ferestrei de vizualizare poate fi transmisă în orice ordine în mixin-ul poly-fluid-sizing() . Aceasta va sorta automat harta în funcție de lățimea ferestrei de la cea mai mică la cea mai mare . Deci ai putea trece într-o hartă ca aceasta și ar funcționa foarte bine:
 $map: (576px: 22px, 320px: 18px, 992px: 34px, 768px: 24px); @include poly-fluid-sizing('font-size', $map);
  • O limitare a acestei metode este că nu puteți trece în unități mixte în mixin. De exemplu, 3em @ 576px lățime. Sass pur și simplu nu va ști ce să facă matematic acolo.

Concluzie

Este acesta cel mai bun lucru pe care îl putem face? Este Poly Fluid Sizing Sfântul Graal al dimensionării unității de fluid în CSS? Pot fi. CSS acceptă în prezent animație neliniară și funcții de sincronizare a tranziției, așa că poate există o șansă ca calc() să o accepte într-o zi. Dacă se întâmplă acest lucru, regresia polinomială neliniară ar putea merita o privire din nou. Dar poate nu... Scalare liniară ar putea fi oricum superioară.

Am început să explorez această idee la începutul lui 2017 și, în cele din urmă, am dezvoltat soluția de mai sus. De atunci, am văzut câțiva dezvoltatori venind cu idei similare și piese diferite ale acestui puzzle. M-am gândit că este timpul să vă împărtășesc metoda mea și cum am ajuns acolo. Unități de vizualizare. Calc(). Sass. Puncte de întrerupere. Niciunul dintre aceste lucruri nu este nou. Toate sunt caracteristici ale browserului care există de ani de zile (cu diferite grade de suport). Le-am folosit împreună doar într-un mod care nu fusese încă pe deplin explorat. Nu vă fie teamă să priviți instrumentele pe care le folosiți în fiecare zi și să vă gândiți imediat cum le puteți utiliza mai bine și să vă dezvoltați setul de abilități.