CSS Shapes

Introdução

Retângulos aninhados em retângulos: esse é o formato que usamos há anos para criar páginas web. As CSS possibilitaram aos autores criar diferentes formas geométricas de modo a libertá-los das restrições impostas por um modelo retangular rígido. Contudo, formas geométricas assim criadas não conseguem afetar o modelo retangular de distribuição dos conteúdos dentro delas e muito menos fazer com que os demais elementos da página deixem de interagir com elas como se retangulares fossem (e são).

A especificação denominada CSS Shapes foi criada com a finalidade de oferecer uma alternativa para o modelo retangular. Criada pela Adobe em meados do ano 2012, sua finalidade é oferecer mecanismos que permitam aos autores controlar a distribuição de conteúdos dentro e em redor de formas complexas, controle este que não é possível nem mesmo com uso de JavaScript.

Por exemplo: observe na figura mostrada a seguir como o texto flui acompanhando o formato circular da imagem. Sem uso das CSS Shapes o sofisticado toque de distribuição do texto, tal como mostrado, não seria possível e o texto fluiria em formato retangular.

Imagem de layout de uma revista mostrando um texto ao redor de uma forma circular.
Notar como o texto flui acompanhando o formato circular das tijelas de sopa. Usando CSS Shapes poderemos criar esse efeito de fluidez do texto em desenvolvimento web.

Vamos examinar como é o funcionamento de Shapes e como você poderá começar a usá-la.

Suporte nos navegadores

Na data em que esta matéria foi escrita CSS Shapes era suportada pelo Webkit Nightly e Chrome Canary, mas, convém ressaltar que o Módulo 1 da sua especificação alcançou o status de Candidata a Recomendação do W3C. Assim, as propriedades e a sintaxe definidas na especificação são estáveis. Em breve o suporte à Shapes será implementado nos demais navegadores. O Módulo 1 de Shapes trata das propriedades destinadas a definir a maneira como o conteúdo flui ao redor de uma forma. Mais especificamente, o Módulo 1 descreve a propriedade shape-outside e suas propriedades relacionadas.

Nota do Maujor: para suporte atual consulte caniuse (abre em nova janela)

Usar Shapes em combinação com outras funcionalidades de ponta, tais como Clipping and Masking, CSS Filters e Compositing and Blending, permite aos autores desenvolver projetos muito mais elegantes e sofisticados sem necessidade de uso de editores gráficos, tais como, Photoshop ou InDesign.

Futuros Módulos das CSS Shapes definirão as funcionalidades para inserção de conteúdo dentro das formas. Por exemplo: hoje, é fácil criar uma forma rômbica com uso de CSS: basta rotacionar 45o. um elemento e a seguir rotacionar seu conteúdo ao contrário de modo que ele volte para a posição horizontal na página. Contudo, o resultado assim obtido não permite que o conteúdo acompanhe internamente o formato rômbico, ao contrário ele será de formato retangular. Quando for implementada a propriedade CSS Shapes shape-inside será possível fazer com que o conteúdo acompanhe uma forma rômbica, possibilitando a criação de layout semelhante ao mostrado na figura a seguir.

Imagem de layout de uma revista mostrando uma forma rômbica.
Em breve CSS Shapes possibilitará criar textos dentro de formas rômbicas tal como mostrado na figura.

Criando uma forma com CSS Shape

Cria-se uma forma com uso das propriedades para Shapes. A sintaxe prevê a definição de uma função como valor da propriedade. Na função você define os parâmetros necessários para criar determinada forma aplicá-la ao elemento.

Gráfico das partes constituintes da regra para criar uma forma.

As funções destinadas a criação de formas são as seguintes:

Cada forma é definida por um conjunto de pontos. Umas funções admitem pontos como parâmetros, outras distâncias, mas todas elas constroem a forma tomando um conjunto de pontos para o elemento. Nos exemplos a seguir mostraremos como definir os parâmetors em uma função para criar formas.

Uma forma poderá, também, ser criada a partir de uma imagem com canal alpha. Passando-se uma imagem para uma propriedade CSS Shape o navegador extrai a forma da imagem baseado em uma shape-image-threshold. A forma é definida pelos pixels cujo valor alpha é maior do que um valor mínimo (threshold). A imagem deverá ser CORS compatible. Se por qualquer motivo a imagem não for renderizada (por exemplo: não existe) nenhuma forma será criada e aplicada.

As propriedades que aceitam as funções mencionadas anteriomente como valor são:

Usando a propriedade shape-outside em conjunto com a propriedade shape-margin é possível criar margens em volta das formas de modo a espaçar o conteúdo da forma. A propriedade shape-margin poderá ser usada em conjunto com a propriedade shape-outside assim como a propriedade shape-padding poderá ser usada em conjunto com a propriedade shape-inside que cria um espaçamento interno entre a forma e o conteúdo.

Usar propriedades e funções para declarar uma forma para um elemento requer apenas e tão somente uma linha de CSS:

.element { shape-outside: circle(); }
/* o conteúdo fluirá em volta de um círculo definido para o elemento */

ou:

.element { shape-outside: url(path/to/image-with-shape.png); }

Mas…

A linha de código CSS mostrada somente criará uma forma se duas condições foram satisfeitas:

  1. O elemento deverá ser "flutuado". No futuro será possível aplicar formas em elementos "não flutuados", mas por enquanto ainda não.
  2. O elemento deverá ter dimensões intrínsecas. A largura e a altura do elemento serão usadas para que se estabeleça um sistema de coordenadas para o elemento.

Conforme foi dito anteriomente a forma é definida por um conjunto de pontos. Pontos estes definidos por coordenadas. Em consequência o navegador precisa de um sistema de coordenadas para poder posicionar cada ponto no elemento. Então, para que se defina uma forma as declarações CSS devem ser como mostrado no exemplo a seguir:

.element {
	float: left;
	height: 10em;
	width: 15em;
	shape-outside: circle();
}

Declarar dimensões intrínsecas para um elemento não afeta sua responsividade (veremos isso adiante).

Formas são definidas por um par de coordenadas que definem um ponto, então é óbvio que alterando as coordenadas de um ponto alteraremos a forma. Por exemplo: a imagem a seguir mostra uma forma hexagonal criada com uso da função polygon(). A forma é constituida por seis pontos. Alterando-se a coordenada horizontal do ponto na cor laranja altera-se a forma e também afeta a maneira como o conteúdo flui fora ou dentro de um elemento ao qual essa forma for aplicada.

Imagem mostrando o que acontece quando se altera a coordenada de um ponto da forma.
Se o elemento ao qual foi aplicada a forma mostrada nessa imagem for flutuado à direita o conteúdo à sua esquerda será afetado quando a coordenada do ponto na cor laranja for alterada na função polygon().

Box de referência para uma forma

Formas são definidas e criadas dentro de um box de referência que é o box usado para desenhar a forma no elemento. Além das propriedades intrínsecas height e width os boxes model para elementos—margin box, content box, padding box e border box—são também usados como referência para definir os limites da forma em um elemento.

Por padrão o margin box é usado como referência para criar formas, assim se você aplicar uma forma a um elemento que tenha uma margem inferior a forma se estenderá até ocupar a área da margem inferior e não a borda. Se você quiser alterar o box referência padrão poderá definir outro valor para ele na declaração que define a função que cria a forma conforme exemplo mostrado a seguir:

shape-outside: circle(250px at 50% 50%) padding-box;

A palavra-chave padding-box constante da declaração mostrada anteriormente, especifica que a forma será criada e aplicada até os limites da área de padding do elemento. A função circle() destina-se a criar e definir dimensões e posicionamento de uma forma circular.

Usando as funções para formas

Nesse primeiro exemplo iremos criar um texto que flua ao redor de um avatar circular, tal como aqueles destinados a ser mostrado em um perfil de usuário ou depoimento.

Screenshot mostrando um avatar circular de usuário com texto fluindo ao redor.
Com CSS Shapes é possível obter o efeito de texto fluindo ao redor de uma imagem circular em lugar de formato retangular para o texto.

Usaremos a função circle() para aplicar uma forma circular à imagem do perfil de usuário conforme marcação mostrada a seguir:

<img src="https://api.randomuser.me/0.3.2/portraits/men/7.jpg" alt="imagem do perfil" />

<p>Lorem ipsum dolor sit amet...</p>

<p>Assumenda blanditiis voluptas tempore...</p>

Talvez você esteja perguntando "Por que não usar a propriedade border-radius para arredondar a imagem?" A resposta é: porque a propriedade border-radius não exerce qualquer efeito sobre o fluxo do conteúdo dentro ou fora do elemento, é aplicada ao elemento, mas não altera a disposição do seu conteúdo e nem a dos conteúdos em volta. Essa propriedade afeta apenas e tão somente a borda do elemento e seu fundo, fundo este que é ajustado para ser contido dentro das bordas curvadas. O conteúdo dentro do elemento permanece com sua forma retangular e o conteúdo fora do elemento interage com ele como se ele fosse (e é) retangular.

Vamos usar a propriedade border-radius para fazer a imagem circular. Aliás é ela que usamos para arredondar imagens ou outros elementos em uma página:

img {
	float: left;
	width: 150px;
	height: 150px;
	border-radius: 50%;
	margin-right: 15px;
}
Screenshot mostrando como um texto flui em torno de uma imagem sem aplicação de forma a ela.
Sem uso de CSS Shapes o texto interage com a imagem como se ela fosse retangular e flui em volta dela em uma forma retangular e não circular.

Em navegadores que não suportam CSS Shapes o conteúdo em redor de uma imagem circular flui em volta dela como se ela não fosse circular. Esse é o efeito fallback mostrado em navegadores antigos.

Para fazer o texto fluir de acordo com a forma usamos as propriedades para formas (shapes).

img {
	float: left;
	width: 150px;
	height: 150px;
	border-radius: 50%;
	margin-right: 15px;
	shape-outside: circle();
	shape-margin: 15px;
}

Esse código faz com que o texto interaja com a forma forma circular aplicada à imagem e assim flua ao redor dela como mostrado no primeiro screenshot. (O resultado ao "vivo" pode ser visualizado no Codepen - abre em nova janela.) Em navegadores que não suportam CSS Shapes a renderização é como mostrado na segunda imagem.

A função circle() pode ser usada com ou sem parâmetros. A sintaxe é como mostrada a seguir:

circle() = circle( [<shape-radius>]? [at <position>]? )

O ponto de interrogação indica que os parâmetros são opcionais e podem ser omitidos. Caso você omita um parâmetro ele será tomado como sendo de valor padrão. Definir simplesmente circle() sem posicionamento, faz com que a forma circular seja posicionada no centro do elemento ao qual está sendo aplicada.

É válido usar qualquer unidade de medida, (px, em, pt, etc.) para especificar o raio do círculo. Pode-se ainda usar tanto closest-side como furthest-side, mas closest-side é o valor padrão para o raio, ou seja, o navegador usará a medida do raio como sendo aquela que vai do centro do elemento até o lado mais próximo do centro. O valor farthest-side usa a medida do raio como sendo aquela que vai do centro do elemento até o lado mais distante do centro.

shape-outside: circle(farthest-side at 25% 25%);
/* define um círculo com raio igual à medida que vai do centro do elemento até
o lado mais distante do centro e centro posicionado nas coordenadas 25% 25% no sistema
de coordenadas do elemento */

shape-inside: circle(250px at 500px 300px);
/* define um círculo com raio igual a 250px  e centro posicionado nas coordenadas
500px horizontal e 300px vertical no sistema de coordenadas do elemento*/
Ilustração mostrando uma explicação visual dos valores closest-side e farthest-side.

A função ellipse() é semelhante à função circle() admitindo a mesma lista de valores com a diferença que em lugar de um raio como parâmetro admite dois raios: um raio segundo o eixo dos x (x-axis) e outro segundo o eixo dos y (y-axis).

ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )

Ainda que não diretamente relacionada com círculos ou elipses, a função inset() é usada para criar forma retangulares dentro de um elemento. Elementos são retangulares por padrão, então qual a utilidade de se criar formas retangulares. A função inset() destina-se a criar retângulos com cantos arredondados e assim definir conteúdos que fluam ao redor dos cantos.

Imagem msotrando conteúdo fluindo em forma criada com e sem uso da função inset()

A função inset() admite de um a quatro valores que indicam a distância até o limite interior dos boxes de referência. Esses valores definem o posicionamento da forma retangular dentro do elemento. A função admite ainda um parâmetro adicional para definir o raio de curvatura dos cantos do retângulo interno. A definição destes raios é igual a definição com uso de border-radius, isto é com um até quatro valores, em conjunto com a palavra-chave round.

inset() = inset( offset{1,4} [round <border-radius>]? )

O código a seguir destina-se a criar um retângulo com cantos arredondados em um elemento flutuado.

.element {
	float: left;
	width: 250px;
	height: 150px;
	shape-outside: inset(0px round 100px) border-box;
}

Veja um exemplo ao vivo no Codepen (abre em nova janela).

A última função que estudaremos é a polygon() que define formas arbitrárias complexas usando qualquer quantidade de pontos. A função admite como parâmetro um conjunto de pares de coordenadas de pontos que criarão a forma.

No exemplo mostrado a seguir uma imagem flutuada à direita ocupa toda a altura da tela usando unidades de medida viewport. O objetivo e fazer com que o texto flua em volta da ampulheta. Para isso usamos a função polygon() e definimos um forma igual à forma da imagem.

Screenshot mostrando o resultado da aplicação da função polygon() para definir uma forma de modo a que o texto flua em volta dela.

O código CSS para obter o efeito mostrado é mostrado a seguir:

img.right {
	float: right;
	height: 100vh;
	width: calc(100vh + 100vh/4);
	shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
}

É válido definir as coordenadas com uso de unidades de medidas de comprimento CSS ou percentagem, tal como mostramos nesse exemplo

O código mostrado produz o efeito, mas como pode-se observar não afeta as porções da imagem que se encontram fora dos limites da forma. O fato é que a aplicação de uma forma a uma imagem, um elemento, um container, ou seja lá o que for, não afeta nada a não ser a área onde o conteúdo flui. Fundos, bordas e tudo mais permanecem inalterados.

Para visualizar a forma do polígono que criamos há necessidade de recortar as partes da imagem que ficaram fora da forma (polígono). Aqui entra em cena a propriedade clip-path prevista na especificação do W3C denominada CSS Masking Module.

A função e valores definidos para a propriedade clip-path são as mesmas definidas para a propriedade shape. Passando os mesmos valores que definem uma forma poligonal criada com a propriedade shape-outside para a propriedade clip-path as partes da imagem que estão fora da forma serão recortadas.

img.right {
	float: right;
	height: 100vh;
	width: calc(100vh + 100vh/4);
	shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
	/* clip the image to the defined shape */
	clip-path: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
}

O resultado deste código é como mostrado na imagem a seguir:

Screenshot mostrado o resultado da aplicação da propriedade clip-path em uma forma com afinalidade de recortar a imagem

A propriedade clip-path, atualmente, é suportada com uso de prefixos funcionando no Chrome com uso do prefixo -webkit- adicionado à propriedade clip-path. Confira uma demonstração ao vivo no Codepen (abre em nova janela).

A propriedade clip-path é um excelente complemento para as propriedades shape uma vez que ela facilita a visualização de uma forma criada eliminando as partes fora da formas, assim, é bem provável que você venha a usar essa propriedade com bastante frequência quando desenvolvendo com CSS Shapes.

A função polygon() admite, opcionalmente, as palavra-chave nonzero e evenodd. Elas se destinam a definir como serão preenchidas as áreas internas de uma forma. Para detalhes ver a propriedade SVG fill-rule.

Definindo forma com uso de imagem

Para definir uma forma com uso de imagens é necessário que a imagem tenha um canal alpha de onde o navegador extrairá a forma.

Nessas condições a forma é definida pelos pixels cujo valor alpha seja superior a um mínimo estabelecido (threshold). O valor padrão para threshold é 0.0 (totalmente transparente), mas podemos alterar esse valor com uso da propriedade shape-image-threshold. Assim, todo pixel não transparente fará parte da forma extraída da imagem.

No futuro a especificação para as CSS Shapes definirão uma chave de permuta para permitir que se use os dados de luminância da imagem em lugar dos dados do canal alpha para definir a forma. A propriedade shape-image-threshold será extendida de modo a permitir aplicar threshold para alpha ou luminância dependendo do estado da chave de permuta.

No exemplo a seguir usaremos uma imagem para definir uma forma e fazer com que um texto flua ao redor dela:

Usando a propriedade shape-outside com um valor de url() apontando para a imagem é possível fazer com que um texto flua ao redor de uma forma criada com a imagem de uma folha.

.leaf-shaped-element {
	float: left;
	width: 400px;
	height: 400px;
	shape-outside: url(leaf.png);
	shape-margin: 15px;
	shape-image-threshold: 0.5;
	background: #009966 url(path/to/background-image.jpg);
	mask-image: url(leaf.png);
}

Uma vez que estamos aplicando uma imagem de fundo ao elemento faz-se necessário que a imagem seja recortada nas suas partes fora da forma. A propriedade mask-image (com prefixos apropriados) das especificações CSS denominada Masking Module cumpre essa finalidade uma vez que a propriedade clip-path necessita de uma imagem com canal alpha e esse não é o caso desse exemplo. O resultado é mostrado na imagem a seguir:

Screenshot mostrando um texto em volta de uma forma extraida de uma imagem sem canal alpha.

Para criar formas complexas podemos usar uma imagem. Isso possibilita a que possamos usar uma imagem com canal alpha no Photoshop em lugar de definir manualmente os pontos da forma.

Com uso das funções previstas em CSS Shapes não é possível criar formas múltiplas em um único elemento. Contudo se uma imagem contém múltiplas áreas o navegador será capaz de extraí-las desde que você tenha usado a imagem para criar a forma.

CSS Shapes em design responsivo

O que dizer das CSS Shapes no fluxo de desenvolvimento responsivo? A especificação para shape-outside levou em consideração o design responsivo permitindo que não somente as dimensões do elemento, mas também as coordenadas dos pontos da forma (parâmetros das funções) sejam definidas tanto em unidades de comprimento CSS como em porcentagens. Isso significa que um elemento ao qual foi definida a propriedade shape-outside seja tão responsivo quanto qualquer outro elemento com dimensões definidas em porcentagem.

shape-inside ainda não é responsivo, mas isso deve-se ao fato que ele foi transferido para o Módulo 2 da especificação. Muitas das limitações de suas funcionalidades constantes do Módulo 1 serão resolvidas no Módulo 2.

Ferramentas para Shapes

Criar formas complexas com uso das funções previstas nas especificações pode se tornar uma grande dor de cabeça especialmente se estivermos usando a função polygon(). Agradecemos ao time da Adobe para a plataforma web que tem trabalhado em uma ferramenta interativa que simplifica a tarefa de criação de formas. Bear Travis criou uma série de ferramentas para Shapes que nos possibilita criar formas poligonais visualmente. A ferramenta transforma a forma visual em uma função capar de criar a forma. A ferramenta é útil, mas não foi desenvolvida para criar uma forma a partir the uma imagem, pois não existe uma forma de fornecer a imagem para a ferramenta e ela gerar a função.

Captura de tela mostrando o Shapes Editor e a edição de formas no navegador.
Editando uma forma poligonal no navegador com uso do Brackets editor no modo "live preview". Captura de tela é cortesia de Razvan Caliman.

Essa ferramenta é indispensável pois ela facilita a criação, edição e debug de CSS Shapes.

O futuro: CSS Exclusions

No início as especificações CSS Shapes englobavam as CSS Shapes e as especificações para CSS Exclusions, mas elas foram separadas. Enquanto CSS Shapes define as propriedades shape-inside e shape-outside, CSS Exclusions define propriedades que permitem fluir textos em volta de elementos não flutuados, tais como aqueles posicionados de forma absoluta. Isso possibilitará que textos fluam por todos os lados da forma conforme mostrado na imagem a seguir:

Screenshot de uma revista com texto fluindo em volta de um  pullquote.
No futruro, CSS Exclusions permitirá fluir conteúdos em volta de formas, tais como, pullquote como mostrado no layout da revista. O pullquote poderá ser circular com texto em volta dele. Imagem por Justin Thomas Kay.

Layouts similares, com uma forma posicionada no centro de um artigo e com o texto fluindo em volta será possível.

Shapes adiante

As especificações atuais para CSS Shapes são apenas um primeiro passo. Em breve novas opções nos darão muito mais controle sobre a criação de formas e inserção de conteúdos dentro e fora delas, facilitando a criação de efeitos complexos com simples linha de código. O foco atual dos editores das especificações é em shape-outside e provavelmente veremos CSS Shapes implementada em mais navegadores até o final de 2014.

Você pode usar Shapes hoje como parte do fluxo do incremento progressivo (progressive enhancement) conhecendo os fallbacks para navegadores em suporte. Eu mesmo, recentemente comecei a usar no meu website e o fallback é muito "normal." Para projeto mais complexos você pode usar script para detectar o suporte às CSS Shapes pelo navegador e prover o fallback apropriado se não hover um. Você pode ainda extender Modernizr com uso deste script com a finalidade de verificar se shape-outside é suportada ou download a custom build incluindo o teste.

CSS Shapes veio para preencher mais uma lacuna que separa a mídia impressa da web. Os exemplos apresentados nessa matéria são simples, mas fornecer ao leitor uma visão dos fundamentos necessários à criação, desde já, de suas próprias formas complexas tais como aquelas encontradas em revistas ou posters impressos. Seja explorando as funcionalidades dos layouts não retangulares ou das formas animadas—a hora para experiências é agora.

Sobre a autora

Sara Soueidan
Sara Soueidan é uma desenvolvedora front-end freelance no Líbano. Ela gosta de ensinar e escrever tutoriais no seu blog e no Codrops onde é autora e membro do time. Ela pode ser contactada pelo Twitter e Github.
Translated with the permission of A List Apart and the author.

Compartilhe essa matéria com seus amigos

logo twitter logo facebook logo linkedin logo reddit

Conheça os livros do Maujor®

Ir para a página de entrada nos sites dos livros.