Primeiros passos com os formatos CSS

Como incluir conteúdo em caminhos personalizados

Razvan Caliman
Razvan Caliman

Por muito tempo, os designers da Web foram forçados a criar dentro das restrições do retângulo. A maioria do conteúdo na Web ainda está preso em caixas simples porque a maioria das iniciativas criativas em layouts não retangulares terminam em frustração. Isso está prestes a mudar com a introdução das formas CSS, disponíveis a partir do Chrome 37. As formas CSS permitem que designers da Web envolvam o conteúdo em caminhos personalizados, como círculos, elipses e polígonos, se livrando das restrições do retângulo.

As formas podem ser definidas manualmente ou inferidas de imagens.

Vamos conferir um exemplo muito simples.

Talvez você tenha sido tão ingênuo quanto eu ao flutuar uma imagem com partes transparentes, esperando que o conteúdo fosse ajustado e preenchesse as lacunas, apenas para se decepcionar com a forma retangular que persiste ao redor do elemento. As formas CSS podem ser usadas para resolver esse problema.

Como extrair uma forma de uma imagem
<img class="element" src="image.png" />
<p>Lorem ipsum…</p>

<style>
.element{
  shape-outside: url(image.png);
  shape-image-threshold: 0.5;
  float: left;
}
</style>

A declaração CSS shape-outside: url(image.png) instrui o navegador a extrair uma forma da imagem.

A propriedade shape-image-threshold define o nível mínimo de opacidade dos pixels que serão usados para criar a forma. O valor precisa estar no intervalo entre 0.0 (totalmente transparente) e 1.0 (totalmente opaco). Portanto, shape-image-threshold: 0.5 significa que apenas pixels com opacidade de 50% ou mais serão usados para criar a forma.

A propriedade float é fundamental. Embora a propriedade shape-outside defina a forma da área em torno da qual o conteúdo será ajustado, sem o float, você não vai notar os efeitos da forma.

Os elementos têm uma área flutuante no lado oposto do valor float. Por exemplo, se um elemento com uma imagem de xícara de café estiver flutuando à esquerda, a área flutuante será criada à direita da xícara. Embora seja possível criar uma imagem com lacunas nos dois lados, o conteúdo só será ajustado à forma no lado oposto designado pela propriedade de flutuação, à esquerda ou à direita, nunca nos dois.

No futuro, será possível usar shape-outside em elementos que não flutuam com as exclusões de CSS de introdução.

Como criar formas manualmente

Além de extrair formas de imagens, você também pode codificá-las manualmente. É possível escolher entre alguns valores funcionais para criar formas: circle(), ellipse(), inset() e polygon(). Cada função de forma aceita um conjunto de coordenadas e é associada a uma caixa de referência que estabelece o sistema de coordenadas. Mais informações sobre as caixas de referência em breve.

Função circle()

Ilustração do valor da forma circle()

A notação completa para um valor de forma de círculo é circle(r at cx cy), em que r é o raio do círculo, enquanto cx e cy são as coordenadas do centro do círculo nos eixos X e Y. As coordenadas do centro do círculo são opcionais. Se você omitir esses valores, o centro do elemento (a interseção das diagonais) será usado como padrão.

.element{
  shape-outside: circle(50%);
  width: 300px;
  height: 300px;
  float: left;
}

No exemplo acima, o conteúdo envolve o lado de fora de um caminho circular. O único argumento 50% especifica o raio do círculo, que, neste caso específico, equivale à metade da largura ou altura do elemento. Mudar as dimensões do elemento vai influenciar o raio da forma circular. Este é um exemplo básico de como as formas CSS podem ser responsivas.

Antes de continuar, uma observação rápida: é importante lembrar que as formas CSS influenciam apenas a forma da área flutuante ao redor de um elemento. Se o elemento tiver um plano de fundo, ele não será cortado pela forma. Para conseguir esse efeito, use as propriedades de CSS Masking: clip-path ou mask-image. A propriedade clip-path é muito útil porque segue a mesma notação dos shapes do CSS. Assim, você pode reutilizar valores.

Ilustração da forma &quot;circle()&quot; + clip-path

As ilustrações neste documento usam o recorte para destacar a forma e ajudar você a entender os efeitos.

Voltando à forma circular.

Ao usar porcentagens para o raio do círculo, o valor é calculado com uma fórmula um pouco mais complexa: sqrt(largura^2 + altura^2) / sqrt(2). É útil entender isso porque ajuda a imaginar como será a forma do círculo resultante se as dimensões do elemento não forem iguais.

Todos os tipos de unidade CSS podem ser usados nas coordenadas da função de forma: px, em, rem, vw, vh e assim por diante. Você pode escolher o que for mais flexível ou rígido para suas necessidades.

É possível ajustar a posição do círculo definindo valores explícitos para as coordenadas do centro.

.element{
  shape-outside: circle(50% at 0 0);
}

Isso posiciona o centro do círculo na origem do sistema de coordenadas. O que é o sistema de coordenadas? É aqui que apresentamos as caixas de referência.

Caixas de referência para formas CSS

A caixa de referência é uma caixa virtual ao redor do elemento, que estabelece o sistema de coordenadas usado para desenhar e posicionar a forma. A origem do sistema de coordenadas está no canto superior esquerdo, com o eixo X apontando para a direita e o eixo Y apontando para baixo.

Sistema de coordenadas para formas CSS

Lembre-se de que shape-outside altera a forma da área flutuante em torno da qual o conteúdo será agrupado. A área flutuante se estende até as bordas externas da caixa definida pela propriedade margin. Isso é chamado de margin-box e é a caixa de referência padrão de uma forma se nenhuma for mencionada explicitamente.

As duas declarações CSS a seguir têm resultados idênticos:

.element{
  shape-outside: circle(50% at 0 0);
  /* identical to: */
  shape-outside: circle(50% at 0 0) margin-box;
}

Ainda não definimos uma margem para o elemento. Nesse ponto, é seguro presumir que a origem do sistema de coordenadas e o centro do círculo estão no canto superior esquerdo da área de conteúdo do elemento. Isso muda quando você define uma margem:

.element{
  shape-outside: circle(50% at 0 0) margin-box;
  margin: 100px;
}

A origem do sistema de coordenadas agora está fora da área de conteúdo do elemento (100 px para cima e 100 px para a esquerda), assim como o centro do círculo. O valor calculado do raio do círculo também aumenta para considerar a superfície aumentada do sistema de coordenadas estabelecido pela caixa de referência margin-box.

Sistema de coordenadas de margem da caixa com e sem margem
Há algumas opções de caixa de referência para escolher: "margin-box", "border-box", "padding-box" e "content-box". Os nomes deles implicam os limites. Explicamos anteriormente a "margin-box". A "border-box" é limitada pelas bordas externas do elemento, a "padding-box" é limitada pelo padding do elemento, enquanto a "content-box" é idêntica à área de superfície real usada pelo conteúdo dentro de um elemento.
Ilustração de todas as caixas de referência

Apenas uma caixa de referência pode ser usada por vez com uma declaração shape-outside. Cada caixa de referência vai influenciar a forma de maneira diferente e, às vezes, sutil. Há outro artigo que aborda mais detalhes e ajuda você a entender as caixas de referência para formas CSS.

Função ellipse()

Ilustração do valor de forma ellipse()

As elipses parecem círculos amassados. Eles são definidos como ellipse(rx ry at cx cy), em que rx e ry são os raios da elipse nos eixos X e Y, enquanto cx e cy são as coordenadas do centro da elipse.

.element{
  shape-outside: ellipse(150px 300px at 50% 50%);
  width: 300px;
  height: 600px;
}

Os valores percentuais serão calculados com base nas dimensões do sistema de coordenadas. Não é preciso fazer cálculos matemáticos aqui. Você pode omitir as coordenadas do centro da elipse, e elas serão inferidas do centro do sistema de coordenadas.

Os raios nos eixos X e Y também podem ser definidos com palavras-chave: farthest-side gera um raio igual à distância entre o centro da elipse e o lado da caixa de referência mais distante dele, enquanto closest-side significa exatamente o oposto: use a distância mais curta entre o centro e um lado.

.element{
  shape-outside: ellipse(closest-side farthest-side at 50% 50%);
  /* identical to: */
  shape-outside: ellipse(150px 300px at 50% 50%);
  width: 300px;
  height: 600px;
}

Isso pode ser útil quando as dimensões do elemento (ou a caixa de referência) mudam de maneira imprevisível, mas você quer que a forma de elipse se adapte.

As mesmas palavras-chave farthest-side e closest-side também podem ser usadas para o raio na função de forma circle().

Função polygon()

Ilustração do valor da forma polygon()

Se círculos e elipses forem muito limitados, a função de forma poligonal abre um mundo de opções. O formato é polygon(x1 y1, x2 y2, ...), em que você especifica pares de coordenadas x y para cada vértice (ponto) de um polígono. O número mínimo de pares para especificar um polígono é três, um triângulo.

.element{
  shape-outside: polygon(0 0, 0 300px, 300px 600px);
  width: 300px;
  height: 600px;
}

Os vértices são colocados no sistema de coordenadas. Para polígonos responsivos, você pode usar valores percentuais para algumas ou todas as coordenadas.

.element{
  /* polygon responsive to font-size*/
  shape-outside: polygon(0 0, 0 100%, 100% 100%);
  width: 20em;
  height: 40em;
}

Há um parâmetro fill-rule opcional, importado do SVG, que instrui o navegador a considerar a “parte interna” de um polígono em caso de caminhos autocruzados ou formas fechadas. Joni Trythall explica muito bem como a propriedade fill-rule funciona no SVG. Se não for definido, o padrão de fill-rule será nonzero.

.element{
  shape-outside: polygon(0 0, 0 100%, 100% 100%);
  /* identical to: */
  shape-outside: polygon(nonzero, 0 0, 0 100%, 100% 100%);
}

Função inset()

A função de forma inset() permite criar formas retangulares para agrupar o conteúdo. Isso pode parecer contra-intuitivo, considerando a premissa inicial de que o CSS Shapes libera conteúdo da Web de caixas simples. Pode ser. Ainda não encontrei um caso de uso para inset() que não possa ser alcançado com flutuações e margens ou com um polygon(). No entanto, inset() oferece uma expressão mais legível para formas retangulares do que polygon().

A notação completa para uma função de forma de inserção é inset(top right bottom left border-radius). Os quatro primeiros argumentos de posição são deslocamentos para dentro das bordas do elemento. O último argumento é o raio da borda da forma retangular. Ela é opcional, então você pode deixar de fora. Ele segue a notação abreviada border-radius que você já usa no CSS.

.element{
  shape-outside: inset(100px 100px 100px 100px);
  /* yields a rectangular shape which is 100px inset on all sides */
  float: left;
}

Como criar formas usando caixas de referência

Se você não especificar uma função de forma para a propriedade shape-outside, poderá permitir que o navegador derive uma forma da caixa de referência do elemento. A caixa de referência padrão é margin-box. Até agora, nada exótico, é assim que os números flutuantes já funcionam. Mas, ao aplicar essa técnica, você pode reutilizar a geometria de um elemento. Vamos analisar a propriedade border-radius.

Se você usar o efeito para arredondar os cantos de um elemento flutuante, vai conseguir o efeito de recorte, mas a área flutuante vai permanecer retangular. Adicione shape-outside: border-box para envolver o contorno criado pelo border-radius.

Extrair uma forma do border-radius de um elemento usando a caixa de referência de border-box
.element{
  border-radius: 50%;
  shape-outside: border-box;
  float: left;
}

É claro que você pode usar todas as caixas de referência dessa maneira. Aqui está outro uso para formas derivadas: deslocamento de citações.

Como criar uma citação destacada deslocada usando a caixa de referência da caixa de conteúdo

É possível alcançar o efeito de citação deslocada usando apenas as propriedades de flutuação e margem. Mas isso exige que você posicione o elemento de citação na árvore HTML no ponto em que quer renderizá-lo.

Veja como conseguir o mesmo efeito de citação deslocada com mais flexibilidade:

.pull-quote{
  shape-outside: content-box;
  margin-top: 200px;
  float: left;
}

Definimos explicitamente a caixa de referência content-box para o sistema de coordenadas da forma. Nesse caso, a quantidade de conteúdo no pull quote define a forma em torno da qual o conteúdo externo será agrupado. A propriedade margin-top é usada aqui para posicionar (compensar) o pull quote, independentemente da posição dele na árvore HTML.

Margem da forma

Você vai notar que o conteúdo que envolve uma forma pode esfregar muito contra o elemento. É possível adicionar espaçamento ao redor da forma com a propriedade shape-margin.

.element{
  shape-outside: circle(40%);
  shape-margin: 1em;
  float: left;
}

O efeito é semelhante ao que você já conhece da propriedade margin normal, mas o shape-margin afeta apenas o espaço ao redor do valor shape-outside. O espaçamento será adicionado ao redor da forma somente se houver espaço para isso no sistema de coordenadas. É por isso que, no exemplo acima, o raio do círculo é definido como 40%, e não 50%. Se o raio fosse definido como 50%, o círculo ocuparia todo o espaço no sistema de coordenadas, não deixando espaço para o efeito de shape-margin. Lembre-se de que a forma é limitada ao margin-box do elemento (o elemento e o margin ao redor dele). Se a forma for maior e transbordar, ela será cortada para o margin-box, e você terá uma forma retangular.

É importante entender que shape-margin aceita apenas um valor positivo. Não tem uma notação longa. O que é uma shape-margin-top para um círculo?

Como animar formas

É possível misturar formas CSS com muitos outros recursos do CSS, como transições e animações. No entanto, é importante ressaltar que os usuários acham muito irritante quando o layout do texto muda enquanto eles estão lendo. Preste muita atenção à experiência se você decidir animar formas.

É possível animar os raios e os centros das formas circle() e ellipse(), desde que eles sejam definidos em valores que o navegador possa interpolar. É possível ir de circle(30%) para circle(50%). No entanto, a animação entre circle(closest-side) e circle(farthest-side) vai sobrecarregar o navegador.

.element{
  shape-outside: circle(30%);
  transition: shape-outside 1s;
  float: left;
}

.element:hover{
  shape-outside: circle(50%);
}
GIF de um círculo animado

É possível conseguir efeitos mais interessantes ao animar formas polygon(), mas é importante observar que o polígono precisa ter o mesmo número de vértices entre os dois estados de animação. O navegador não pode interpolar se você adicionar ou remover vértices.

Um truque é adicionar a quantidade máxima de vértices necessários e posicioná-los agrupados no estado de animação em que você quer menos bordas percebidas na forma.

.element{
  /* four vertices (looks like rectangle) */
  shape-outside: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  transition: shape-outside 1s;
}

.element:hover{
  /* four vertices, but second and third overlap (looks like triangle) */
  shape-outside: polygon(0 0, 100% 50%, 100% 50%, 0 100%);
}
GIF de triângulo animado

Envolvimento do conteúdo dentro de uma forma

Captura de tela da demonstração de Alice no País das Maravilhas usando formas CSS para agrupar o conteúdo

O rascunho inicial da especificação de formas do CSS incluía uma propriedade shape-inside que permitia agrupar conteúdo dentro de uma forma. Houve até implementações no Chrome e no WebKit por um tempo. No entanto, o agrupamento de conteúdo posicionado de forma arbitrária em um caminho personalizado exige muito mais esforço e pesquisa para cobrir todos os cenários possíveis e evitar bugs. Por isso, a propriedade shape-inside foi adiada para o nível 2 das formas CSS, e as implementações dela foram retiradas.

No entanto, com algum esforço e um pouco de compromisso, ainda é possível conseguir o efeito de agrupar o conteúdo dentro de uma forma personalizada. O hack é usar dois elementos flutuantes com shape-outside, posicionados em lados opostos de um contêiner. O compromisso é que você precisa usar um ou dois elementos vazios que não têm significado semântico, mas servem como suportes para criar a ilusão de uma forma.

<div>
  <div class='left-shape'></div>
  <div class='right-shape'></div>

  Lorem ipsum...
</div>

A posição dos elementos de suporte .left-shape e .right-shape na parte de cima do contêiner é importante porque eles flutuam para a esquerda e para a direita para contornar o conteúdo.

.left-shape{
  shape-outside: polygon(0 0, ...);
  float: left;
  width: 50%;
  height: 100%;
}

.right-shape{
  shape-outside: polygon(50% 0, ...);
  float: right;
  width: 50%;
  height: 100%;
}
Ilustração da solução alternativa para a demonstração de forma interna para Alice

Esse estilo faz com que os dois struts flutuantes ocupem todo o espaço dentro do elemento, mas as propriedades shape-outside reservam espaço para o restante do conteúdo.

Se as formas CSS não forem compatíveis com o navegador, isso vai gerar efeitos feios ao empurrar todo o conteúdo para baixo. Por isso, é importante usar o recurso de maneira cada vez mais aprimorada.

Nos exemplos de animação de forma anteriores, você notará que o texto que muda de lugar pode ser incômodo. Nem todos os casos de uso exigem uma forma animada. Mas você pode animar outras propriedades que interagem com as formas CSS para adicionar efeitos onde for necessário.

Na demonstração de formas CSS Alice no País das Maravilhas, usamos a posição de rolagem para mudar a margem superior do conteúdo. O texto é comprimido entre dois elementos flutuantes. À medida que ele se move para baixo, ele precisa ser redimensionado de acordo com o shape-outside dos dois elementos flutuantes. Isso dá a impressão de que o texto está indo para o buraco do coelho e contribui para a experiência de contar histórias. Quase sem custo financeiro? Talvez. Mas parece legal.

Como o layout de texto é feito de forma nativa pelo navegador, o desempenho é melhor do que usar uma solução JavaScript. No entanto, mudar a margin-top ao rolar a tela aciona muitos eventos de relayout e pintura, o que pode reduzir significativamente a performance. Use com cuidado! No entanto, o uso de formas CSS sem animação não tem um impacto perceptível na performance.

Aprimoramento progressivo

Comece presumindo que o navegador não tem suporte a formas CSS e crie com base nisso quando detectar o recurso. O Modernizr é uma boa solução para fazer a detecção de recursos, e há um teste para formas CSS na seção 'Detecções não principais'.

Alguns navegadores oferecem a detecção de recursos no CSS pela regra @supports sem a necessidade de bibliotecas externas. O Google Chrome, que também oferece suporte a formas CSS, entende a regra @supports. Confira como usá-lo para aprimorar progressivamente:

.element{
  /* styles for all browsers */
}

@supports (shape-outside: circle(50%)){
  /* styles only for browsers which support CSS Shapes */
  .element{
    shape-outside: circle(50%);
  }
}

Lea Verou escreveu mais sobre como usar a regra @supports do CSS.

Eliminação de ambiguidade de exclusões do CSS

O que conhecemos hoje como formas CSS costumava ser chamado de exclusões e formas CSS nos primeiros dias da especificação. A mudança de nome pode parecer uma nuance, mas é muito importante. As exclusões do CSS, agora uma especificação separada, permitem que o conteúdo seja envolvido em elementos posicionados de forma arbitrária, sem a necessidade de uma propriedade de flutuação. Imagine envolver o conteúdo em um elemento posicionado de forma absoluta. Esse é um caso de uso para exclusões de CSS. As formas CSS definem apenas o caminho em torno do qual o conteúdo será agrupado.

Portanto, formas e exclusões não são a mesma coisa, mas se complementam. As formas CSS estão disponíveis nos navegadores, mas as exclusões CSS ainda não foram implementadas com a interação de formas.

Ferramentas para trabalhar com formas CSS

É possível criar caminhos em ferramentas clássicas de criação de imagens, mas nenhuma delas, no momento em que este artigo foi escrito, exporta a sintaxe necessária para valores de formas CSS. Mesmo que o fizessem, trabalhar dessa forma não seria muito prático.

As formas CSS são usadas no navegador, onde reagem a outros elementos na página. É muito útil visualizar os efeitos da edição da forma no conteúdo que a envolve. Há algumas ferramentas para ajudar você com esse fluxo de trabalho:

Brackets: a extensão CSS Shapes Editor para Brackets usa o modo de visualização em tempo real do editor de código para sobrepor um editor interativo para editar valores de forma.

Google Chrome: a extensão CSS Shapes Editor para Google Chrome estende as ferramentas para desenvolvedores do navegador com controles para criar e editar formas. Ele coloca um editor interativo sobre o elemento selecionado.

O inspetor no Google Chrome tem suporte integrado para destacar formas. Passe o cursor sobre um elemento com uma propriedade shape-outside para que ele acenda e ilustre a forma.

Formas de imagens: se você prefere gerar imagens e extrair formas delas, Rebecca Hauck escreveu um bom tutorial para o Photoshop.

Preenchimento: o Google Chrome é o primeiro navegador importante a enviar formas CSS. O recurso será compatível com o iOS 8 e o Safari 8 da Apple em breve. Outros fornecedores de navegadores podem considerar isso no futuro. Até lá, há um polyfill de formas CSS para fornecer suporte básico.

Conclusão

Em uma Web em que o conteúdo fica principalmente preso em caixas simples, as formas CSS oferecem uma maneira de criar layouts expressivos, preenchendo a lacuna de fidelidade entre o design da Web e o impresso. É claro que as formas podem ser usadas de forma indevida e criar distrações. No entanto, quando aplicadas com bom gosto e bom senso, as formas podem melhorar a apresentação do conteúdo e concentrar a atenção do usuário de uma maneira única.

Deixo aqui uma coleção de trabalhos de outras pessoas, principalmente de impressão, que demonstram usos interessantes de layout não retangular. Espero que isso inspire você a testar as formas CSS e experimentar novas ideias de design.

Agradecemos a Pearl Chen, Alan Stearns e Zoltan Horvath por revisar este artigo e fornecer insights valiosos.