Tipografia responsiva a fluidos com dimensionamento de fluido CSS Poly

Publicados: 2022-03-10
Resumo rápido ↬ Os layouts fluidos são uma parte normal do desenvolvimento de front-end há anos. A ideia de tipografia fluida, no entanto, é relativamente nova e ainda não foi totalmente explorada. Até agora, a ideia da maioria dos desenvolvedores de tipografia fluida é simplesmente usar unidades Viewport, talvez com alguns tamanhos mínimos e máximos. Neste artigo, vamos levá-lo para outro nível. Vamos examinar como criar tipografia fluida e escalável em vários pontos de interrupção e tamanhos de fonte predefinidos usando recursos de navegador bem suportados e algumas álgebra básica. A melhor parte é que você pode automatizar tudo usando Sass.

Neste artigo, vamos levá-lo para outro nível. Vamos examinar como criar tipografia fluida e escalável em vários pontos de interrupção e tamanhos de fonte predefinidos usando recursos de navegador bem suportados e algumas álgebra básica. A melhor parte é que você pode automatizar tudo usando Sass.

Leitura adicional no SmashingMag:

  • Tipografia verdadeiramente fluida com unidades vh e vw
  • Padrões tipográficos no design de newsletter de e-mail HTML
  • Os bons, os maus e os grandes exemplos de tipografia na web
  • Ferramentas e recursos para uma tipografia web mais significativa

Ao trabalhar com designers criativos em designs de páginas da Web, é bastante comum receber várias pranchetas/layouts do Sketch ou do Photoshop, uma para cada ponto de interrupção. Nesse design, os elementos (como um título h1 ) geralmente terão tamanhos diferentes em cada ponto de interrupção. Por exemplo:

Mais depois do salto! Continue lendo abaixo ↓
  1. O h1 no layout pequeno pode ser 22px
  2. O h1 no layout médio pode ser de 24px
  3. O h1 no layout grande pode ser 34px

O CSS mínimo para isso usa consultas de mídia:

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

Este é um bom primeiro passo, mas você está limitando o font-size apenas ao que foi especificado pelo designer nos pontos de interrupção fornecidos. O que o designer diria se você perguntasse: “Qual deve ser o font-size em uma janela de visualização de 850px de largura?” A resposta na maioria dos casos é que seria algo entre 24px e 34px. Mas agora, são apenas 24px de acordo com seu CSS, o que provavelmente não é o que o designer estava imaginando.

Sua opção neste momento é calcular qual deve ser esse tamanho e adicionar outro ponto de interrupção. Isso é bastante fácil. Mas e todas as outras resoluções? Qual deve ser o font-size em 800px de largura? E quanto a 900px? E quanto a 935px? Obviamente, o designer não fornecerá um layout completo para todas as resoluções possíveis. Mesmo se o fizessem, você deveria adicionar dezenas (ou centenas) de pontos de interrupção para todos os diferentes font-sizes desejados pelo designer? Claro que não.

Seu layout já está dimensionando de forma fluida com a largura da sua janela de visualização. Não seria bom se sua tipografia fosse dimensionada de forma previsível com seu layout fluido? O que mais podemos fazer para melhorar isso?

Unidades de viewport para o resgate?

As unidades de viewport são outro passo na direção certa. Eles permitem que seu texto seja redimensionado com fluidez com seus layouts. E o suporte do navegador é ótimo hoje em dia.

Posso usar a viewport?
(Ver versão grande)

Mas a viabilidade das unidades Viewport depende muito dos designs criativos originais de uma página da web. Seria ótimo apenas definir o font-size usando vw e pronto:

 h1 { font-size: 2vw; } 

Mas isso só funciona se suas pranchetas criativas levarem isso em consideração. O designer escolheu um tamanho de texto que fosse exatamente 2% da largura de cada uma de suas pranchetas? Claro que não. Vamos calcular qual seria o valor de vw para cada um dos nossos pontos de interrupção:

Tamanho de 22px @ 576px de largura = 22576 *100 = 24px Tamanho de 24px @ 768px de largura = 24768 *100 = 34px Tamanho de 34px @ 992px de largura = 34992 *100 = 3,43vw

Eles são próximos, mas não são todos iguais. Portanto, você ainda precisaria usar consultas de mídia para fazer a transição entre tamanhos de texto e ainda haveria saltos. E considere este estranho efeito colateral:

@ 767px, 3,82% da largura da janela de visualização é 29px. Se a janela de visualização for 1 pixel mais larga, o font-size cairá repentinamente para 24px . Esta animação de uma viewport sendo redimensionada demonstra esse efeito colateral indesejável:

Essa mudança dramática no tamanho da fonte quase definitivamente não é o que o designer estava imaginando. Então, como resolvemos esse problema?

Regressão linear estatística?

Esperar. Que? Sim, este é um artigo sobre CSS, mas um pouco de matemática básica pode ajudar bastante em uma solução elegante para o nosso problema.

Primeiro, vamos plotar nossas resoluções e tamanhos de texto correspondentes em um gráfico:

Gráfico de dispersão do tamanho da fonte e largura da viewport correspondente
Gráfico de dispersão do font-size e largura da janela de visualização correspondente (Google Spreadsheets) (Ver versão grande)

Aqui você pode ver um gráfico de dispersão dos tamanhos de texto especificados pelo designer nas larguras da janela de visualização definidas. O eixo x é a largura da janela de visualização e o eixo y é o font-size . Vê aquela linha? Isso é chamado de linha de tendência . É uma maneira de encontrar um valor font-size interpolado para qualquer largura de janela de visualização, com base nos dados fornecidos.

A linha de tendência é a chave para tudo isso

Se você pudesse definir o font-size acordo com essa linha de tendência, você teria um h1 que escala suavemente em todas as resoluções que se aproximassem de corresponder ao que o designer pretendia. Primeiro, vamos olhar para a matemática. A linha reta é definida por esta equação:

Definição de equação linear
Definição de equação linear
  • m = inclinação
  • b = a interceptação em y
  • x = a largura atual da janela de visualização
  • y = o font-size resultante

Existem vários métodos para determinar a inclinação e a interceptação em y. Quando vários valores estão envolvidos, um método comum é o ajuste dos mínimos quadrados:

Mínimos Quadrados

Depois de executar esses cálculos, você tem sua equação de linha de tendência.

Como faço para usar isso em CSS?

Ok, isso está ficando muito pesado na matemática. Como realmente usamos essas coisas no desenvolvimento web front-end? A resposta é CSS calc() ! Mais uma vez, uma tecnologia CSS relativamente nova que é muito bem suportada.

Posso usar o Calc?
(Ver versão grande)

Você pode usar a equação da linha de tendência assim:

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

Depois de encontrar sua inclinação e interceptação em y, basta conectá-los!

Nota: Você tem que multiplicar a inclinação por 100 , já que a está usando como uma unidade vw que é 1/100 da largura da Viewport.

Isso pode ser automatizado?

Eu portei o método de ajuste de mínimos quadrados para uma função Sass fácil de usar:

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

Isso realmente funciona? Abra este CodePen e redimensione a janela do seu navegador. Funciona! Os tamanhos de fonte são bastante próximos do que o design original estava pedindo e se ajustam suavemente ao seu layout.

Least Squares Fit SCSS test user="jakobud"]Veja o teste Pen Least Squares Fit SCSS por Jake Wilson (@jakobud) no CodePen.

Least Squares Fit SCSS test user=“jakobud”]Veja o teste Pen Least Squares Fit SCSS por Jake Wilson (@jakobud) no CodePen.

Agora, reconhecidamente, não é perfeito. Os valores estão próximos do projeto original, mas não coincidem. Isso ocorre porque uma linha de tendência linear é uma aproximação de tamanhos de fonte específicos em larguras de janela de visualização específicas. Isso é herdado da regressão linear. Sempre há algum erro em seus resultados. É uma troca de simplicidade versus precisão. Além disso, lembre-se de que quanto mais variados forem os tamanhos de texto, mais erros haverá em sua linha de tendência.

Podemos fazer melhor do que isso?

Ajuste de Mínimos Quadrados Polinomial

Para obter uma linha de tendência mais precisa, você precisa examinar tópicos mais avançados, como uma linha de tendência de regressão polinomial que pode ser algo assim:

Linha de tendência de regressão polinomial
Linha de tendência de regressão polinomial (Google Spreadsheets) (Ver versão ampliada)

Agora é mais assim ! Muito mais preciso que nossa linha reta. Uma equação básica de regressão polinomial se parece com isso:

Uma equação polinomial de 3º grau
Uma equação polinomial de 3º grau

Quanto mais precisa você quiser sua curva, mais complicada será a equação. Infelizmente, você não pode fazer isso em CSS . calc() simplesmente não pode fazer esse tipo de matemática avançada. Especificamente, você não pode calcular expoentes:

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

Portanto, até que calc() suporte esse tipo de matemática não linear, estamos presos apenas a equações lineares . Há mais alguma coisa que possamos fazer para melhorar isso?

Pontos de interrupção e várias equações lineares

E se estivéssemos calculando apenas uma linha reta entre cada par de pontos de interrupção? Algo assim:

Linhas de tendência de regressão linear entre vários pares de valores
Linhas de tendência de regressão linear entre vários pares de valores (Google Spreadsheets) (Ver versão ampliada)

Então, neste exemplo, calcularíamos a linha reta entre 22px e 24px e depois outra entre 24px e 34px . O Sas ficaria assim:

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

Poderíamos usar o método de ajuste de mínimos quadrados para esses valores calc() , mas como é apenas uma linha reta entre 2 pontos, a matemática pode ser bastante simplificada. Lembre-se da equação para uma linha reta?

Definição de equação linear
Definição de equação linear

Como estamos falando de apenas 2 pontos agora, encontrar a inclinação (m) e a interceptação em y (b) é trivial:

Encontrando a inclinação e a interceptação em y de uma equação linear
Encontrando a inclinação e a interceptação em y de uma equação linear

Aqui está uma função Sass para isso:

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

Agora, basta usar a função de interpolação linear em vários pontos de interrupção em seu Sass. Além disso, vamos lançar alguns font-sizes mínimo e máximo:

 // 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 gera este 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; } } 

O Santo Graal do dimensionamento CSS?

Vamos encerrar tudo isso em um bom mixin Sass (para os preguiçosos e eficientes!). Estou criando este método 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)); } }

Este mixin Sass requer algumas funções Sass nas seguintes gists do Github:

  • interpolação linear
  • classificação de mapa
  • lista de classificação
  • lista-remover

O poly-fluid-sizing() executará a interpolação linear em cada par de larguras de viewport e definirá um tamanho mínimo e máximo. Você pode importar isso para qualquer projeto Sass e utilizá-lo facilmente sem precisar saber nada da matemática por trás dele. Aqui está o CodePen final que usa esse método.

Poly Fluid Sizing usando equações lineares, unidades de viewport e calc() user="jakobud"]Veja a caneta Poly Fluid Sizing usando equações lineares, unidades de viewport e calc()"] Poly Fluid Sizing usando equações lineares, unidades de viewport e calc() por Jake Wilson (@jakobud) no CodePen.

Poly Fluid Sizing usando equações lineares, unidades de viewport e calc() user=“jakobud”]Veja o Pen Poly Fluid Sizing usando equações lineares, viewport units e calc()“] Poly Fluid Sizing usando equações lineares, unidades de viewport e calc() por Jake Wilson (@jakobud) no CodePen.

Algumas notas

  • Obviamente, esse método se aplica não apenas ao font-size mas a qualquer propriedade de unidade/comprimento ( margin , padding , etc). Você passa o nome da propriedade desejada no mixin como uma string.
  • O mapa Sass dos pares de valores de largura + tamanho da janela de visualização pode ser passado em qualquer ordem para o poly-fluid-sizing() . Ele classificará automaticamente o mapa de acordo com a largura da janela de visualização do menor para o maior . Então você poderia passar um mapa como este e funcionaria bem:
 $map: (576px: 22px, 320px: 18px, 992px: 34px, 768px: 24px); @include poly-fluid-sizing('font-size', $map);
  • Uma limitação para este método é que você não pode passar unidades mistas para o mixin. Por exemplo, 3em @ 576px largura. Sass simplesmente não saberá o que fazer matematicamente lá.

Conclusão

Isto é o melhor que podemos fazer? O dimensionamento de fluidos poli é o Santo Graal do dimensionamento de unidades de fluidos em CSS? Pode ser. CSS atualmente suporta animações não lineares e funções de tempo de transição, então talvez haja uma chance de que calc() também o suporte algum dia. Se isso acontecer, a regressão polinomial não linear pode valer a pena dar uma olhada novamente. Mas talvez não… A escala linear pode ser superior de qualquer maneira.

Comecei a explorar essa ideia no início de 2017 e acabei desenvolvendo a solução acima. Desde então, tenho visto alguns desenvolvedores terem ideias semelhantes e peças diferentes desse quebra-cabeça. Achei que era hora de compartilhar meu método e como cheguei lá. Unidades de viewport. Calc(). Sass. Pontos de interrupção. Nenhuma dessas coisas é nova. Eles são todos os recursos do navegador que existem há anos (com vários graus de suporte). Eu só os usei juntos de uma maneira que ainda não havia sido totalmente explorada. Nunca tenha medo de olhar para as ferramentas que você usa todos os dias e pensar fora da caixa sobre como você pode utilizá-las melhor e aumentar seu conjunto de habilidades.