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.
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.
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.
As funções destinadas a criação de formas são as seguintes:
circle()
ellipse()
inset()
polygon()
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:
shape-outside
: Conteúdo flui em volta (outside) da formashape-inside
: Conteúdo flui dentro da forma
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:
- O elemento deverá ser "flutuado". No futuro será possível aplicar formas em elementos "não flutuados", mas por enquanto ainda não.
- 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.
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.
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;
}
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*/
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.
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.
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:
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:
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.
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:
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
Translated with the permission of A List Apart and the author.Conheça os livros do Maujor®
Ir para a página de entrada nos sites dos livros.